chromium/ash/wm/raster_scale/raster_scale_controller.h

// Copyright 2023 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef ASH_WM_RASTER_SCALE_RASTER_SCALE_CONTROLLER_H_
#define ASH_WM_RASTER_SCALE_RASTER_SCALE_CONTROLLER_H_

#include <memory>
#include <vector>

#include "ash/ash_export.h"
#include "base/containers/flat_map.h"
#include "base/containers/flat_set.h"
#include "base/memory/raw_ptr.h"
#include "base/scoped_multi_source_observation.h"
#include "ui/aura/window.h"
#include "ui/aura/window_observer.h"

namespace ash {

// ScopedPauseRasterScaleUpdates pauses any updates to all windows raster scales
// until it is destructed. Once it is destructed and there are no other
// ScopedPauseRasterScaleUpdates objects, windows that still exist will have
// their raster scales updated if they have changed.
class ASH_EXPORT ScopedPauseRasterScaleUpdates {
 public:
  ScopedPauseRasterScaleUpdates();

  ScopedPauseRasterScaleUpdates(const ScopedPauseRasterScaleUpdates&) = delete;
  ScopedPauseRasterScaleUpdates& operator=(
      const ScopedPauseRasterScaleUpdates&) = delete;
  ~ScopedPauseRasterScaleUpdates();
};

// ScopedSetRasterScale keeps the raster scale property of the given window
// at or above the given value while in scope. This is necessary because,
// for example, if a window is shown both in overview mode and virtual desks
// preview, we don't want to reduce its raster scale to the tiny preview
// for the virtual desks preview. Instead, we want to use the raster scale
// for the largest preview of the window currently visible.
class ASH_EXPORT ScopedSetRasterScale : public aura::WindowObserver {
 public:
  ScopedSetRasterScale(aura::Window* window, float raster_scale);

  ScopedSetRasterScale(const ScopedSetRasterScale&) = delete;
  ScopedSetRasterScale& operator=(const ScopedSetRasterScale&) = delete;
  ~ScopedSetRasterScale() override;

  float raster_scale() const { return raster_scale_; }

  void UpdateScale(float raster_scale);

  static void SetOrUpdateRasterScale(aura::Window* window,
                                     float raster_scale,
                                     std::unique_ptr<ScopedSetRasterScale>* p);

 private:
  void Shutdown();

  // aura::WindowObserver:
  void OnWindowDestroying(aura::Window* window) override;

  raw_ptr<aura::Window> window_;
  float raster_scale_;
};

// RasterScaleController keeps track of a list of raster scales for each window.
// It is necessary to do this, because we always want to raster each window at
// the largest scale it needs to be displayed at. For example, if a window is
// displayed in overview mode and previewed in a virtual desk, both of those may
// want different scale factors. But, since the window is displayed larger in
// overview mode, we want to make sure to raster it at the higher scale to avoid
// blurriness.
class ASH_EXPORT RasterScaleController : public aura::WindowObserver {
 public:
  RasterScaleController();

  RasterScaleController(const RasterScaleController&) = delete;
  RasterScaleController& operator=(const RasterScaleController&) = delete;
  ~RasterScaleController() override;

  // With raster slop (see comment on `raster_scale_slop_`), there is degenerate
  // exponential updating behaviour as raster scale tends towards 0. The
  // `kMinimumRasterScale` value denotes a minimum value for raster scale. This
  // is set such that the largest windows we expect (e.g. on 4k displays) can
  // still have their raster scales reduced down so the width and height of
  // their buffers is on the order of a few hundred pixels at most.
  static inline constexpr float kMinimumRasterScale = 0.05;

  // Computes the appropriate raster scale given a transform. Normally we expect
  // x and y scaling to be the same, but in case they are not, this takes the
  // larger of the two as the raster scale, to make sure that the scale along
  // that axis is rasterised at a high enough scale.
  static float RasterScaleFromTransform(const gfx::Transform& transform);

  // Adds a raster scale to be tracked for a window. The kRasterScale property
  // for this window will be set to the largest raster scale currently active,
  // or 1.0 if there are no raster scales active.
  //
  // N.B. This relies on float equality. Currently, this is okay because all
  // usages of this are from ScopedSetRasterScale which uses the same float
  // value for push and pop. We will need to change the design if raster_scale
  // is being recomputed between the push and pop.
  void PushRasterScale(aura::Window* window, float raster_scale);

  void PopRasterScale(aura::Window* window, float raster_scale);

  float ComputeRasterScaleForWindow(aura::Window* window);

  float raster_scale_slop_proportion() const {
    return raster_scale_slop_proportion_;
  }

  void set_raster_scale_slop_proportion_for_testing(
      float raster_scale_slop_proportion) {
    raster_scale_slop_proportion_ = raster_scale_slop_proportion;
  }

 private:
  friend class ScopedPauseRasterScaleUpdates;

  void Pause();
  void Unpause();

  void MaybeSetRasterScale(aura::Window* window);

  // aura::WindowObserver:
  void OnWindowDestroying(aura::Window* window) override;

  int pause_count_ = 0;

  base::flat_map<aura::Window*, std::vector<float>> window_scales_;

  // Holds a set of windows that have had their raster scales change while
  // RasterScaleController is paused.
  base::flat_set<raw_ptr<aura::Window, CtnExperimental>> pending_windows_;

  // Raster scale won't be updated for a window unless the currently requested
  // raster scale is more than `raster_scale_slop_proportion_` different by
  // proportion to the currently set (via the raster scale window property)
  // value. As a special case, requesting the raster scale to 1.0 will always
  // update the raster scale window property. This is to prevent windows from
  // getting stuck in non-1.0f raster scales when all `ScopedSetRasterScale`s
  // are released. This value was determined by eyeballing the sharpness at a
  // reduced raster scale. Once the difference in raster scale proportion starts
  // exceeding around 15%, it starts becoming noticeable. Set this to 10% as a
  // safe value.
  float raster_scale_slop_proportion_ = 0.1;

  base::ScopedMultiSourceObservation<aura::Window, aura::WindowObserver>
      windows_observation_{this};
};

}  // namespace ash

#endif  // ASH_WM_RASTER_SCALE_RASTER_SCALE_CONTROLLER_H_