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

#ifndef COMPONENTS_VIZ_SERVICE_DISPLAY_OVERLAY_CANDIDATE_H_
#define COMPONENTS_VIZ_SERVICE_DISPLAY_OVERLAY_CANDIDATE_H_

#include <optional>
#include <variant>
#include <vector>

#include "base/containers/flat_map.h"
#include "base/memory/raw_ptr.h"
#include "build/build_config.h"
#include "components/viz/common/quads/aggregated_render_pass.h"
#include "components/viz/common/quads/texture_draw_quad.h"
#include "components/viz/common/quads/tile_draw_quad.h"
#include "components/viz/common/resources/resource_id.h"
#include "components/viz/common/resources/shared_image_format.h"
#include "components/viz/service/viz_service_export.h"
#include "gpu/command_buffer/common/mailbox.h"
#include "third_party/skia/include/private/chromium/GrDeferredDisplayList.h"
#include "ui/gfx/buffer_types.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/geometry/rect_f.h"
#include "ui/gfx/geometry/size.h"
#include "ui/gfx/geometry/transform.h"
#include "ui/gfx/hdr_metadata.h"
#include "ui/gfx/overlay_layer_id.h"
#include "ui/gfx/overlay_priority_hint.h"
#include "ui/gfx/overlay_transform.h"
#include "ui/gfx/overlay_type.h"
#include "ui/gfx/video_types.h"

namespace gfx {
class Rect;
}  // namespace gfx

namespace viz {
class AggregatedRenderPassDrawQuad;
class DrawQuad;
class DisplayResourceProvider;

class VIZ_SERVICE_EXPORT OverlayCandidate {
 public:
  // When adding or changing these return status' check how the callee uses
  // these failure codes. Currently, these feed into the logging via the enum
  // |DelegationStatus|.
  enum class CandidateStatus {
    kSuccess,
    kFailNotOverlay,
    kFailNotAxisAligned,
    kFailNotAxisAligned3dTransform,
    kFailNotAxisAligned2dShear,
    kFailNotAxisAligned2dRotation,
    kFailColorMatrix,
    kFailOpacity,
    kFailBlending,
    kFailQuadNotSupported,
    kFailVisible,
    kFailFormat,
    kFailNearFilter,
    kFailPriority,
    kFailRoundedDisplayMasksNotSupported,
    kFailMaskFilterNotSupported,
    kFailHasTransformButCantClip,
    kFailRpdqWithTransform,
    kFailBackdropFilter,
  };
  using TrackingId = uint32_t;
  static constexpr TrackingId kDefaultTrackingId{0};

  // Returns true if |quad| will not block quads underneath from becoming
  // an overlay.
  static bool IsInvisibleQuad(const DrawQuad* quad);

  // Returns true if `quad` contains rounded display masks textures.
  static bool QuadHasRoundedDisplayMasks(const DrawQuad* quad);

  // Modifies the |candidate|'s |display_rect| to be clipped within |clip_rect|.
  // This function will also update the |uv_rect| based on what clipping was
  // applied to |display_rect|.
  // |clip_rect| should be in the same space as |candidate|'s |display_rect|,
  // and |candidate| should not have an arbitrary transform.
  static void ApplyClip(OverlayCandidate& candidate,
                        const gfx::RectF& clip_rect);

  // Returns true if the |quad| cannot be displayed on the main plane. This is
  // used in conjuction with protected content that can't be GPU composited and
  // will be shown via an overlay.
  static bool RequiresOverlay(const DrawQuad* quad);

  // Returns |candidate|'s |display_rect| transformed to its target space.
  // If |candidate| holds an arbitrary transform, this will be the smallest axis
  // aligned bounding rect containing |transform| applied to |display_rect|.
  // If |candidate| holds an overlay transform, this will just be
  // |display_rect|, which is already in its target space.
  static gfx::RectF DisplayRectInTargetSpace(const OverlayCandidate& candidate);

  OverlayCandidate();
  OverlayCandidate(const OverlayCandidate& other);
  ~OverlayCandidate();

  // If the quad doesn't require blending.
  bool is_opaque : 1 = false;
  // If the quad has a mask filter.
  bool has_mask_filter : 1 = false;
  // To be modified by the implementer if this candidate can go into
  // an overlay.
  bool overlay_handled : 1 = false;

  // Is true if an HW overlay is required for the quad content.
  bool requires_overlay = false;

  // Helps to identify whether this is a solid color quad or not.
  bool is_solid_color : 1 = false;

  // Helps to identify whether this candidate has rounded-display masks or not.
  bool has_rounded_display_masks : 1 = false;

  // Whether this overlay candidate represents the root render pass.
  bool is_root_render_pass : 1 = false;

  // Whether this overlay candidate is a render pass draw quad.
  bool is_render_pass_draw_quad : 1 = false;

  // If we need nearest neighbor filter for displaying this overlay.
  bool nearest_neighbor_filter : 1 = false;

  // If true, we need to run a detiling image processor on the quad before we
  // can scan it out.
  bool needs_detiling : 1 = false;

  // If true, this candidate uses low latency rendering and we need
  // to increase its overlay priority.
  bool low_latency_rendering : 1 = false;

  // Rect in content space that, when combined with |transform|, is the bounds
  // to position the overlay to. When |transform| is a |gx::OverlayTransform|,
  // this is the bounds of the quad rect with its transform applied, so that
  // content and target space for this overlay are the same.
  //
  // Implementer must convert to integer coordinates if setting
  // |overlay_handled| to true.
  gfx::RectF display_rect;

  // Format of the buffer to scanout.
  SharedImageFormat format = SinglePlaneFormat::kRGBA_8888;

  gfx::ProtectedVideoType protected_video_type =
      gfx::ProtectedVideoType::kClear;

  // Hints for overlay prioritization when delegated composition is used.
  gfx::OverlayPriorityHint priority_hint = gfx::OverlayPriorityHint::kNone;

  // ColorSpace of the buffer for scanout.
  gfx::ColorSpace color_space;
  // Optional HDR Metadata for the buffer.
  gfx::HDRMetadata hdr_metadata;
  // Size of the resource, in pixels.
  gfx::Size resource_size_in_pixels;

  // Crop within the buffer to be placed inside |display_rect|.
  gfx::RectF uv_rect = gfx::RectF(0.f, 0.f, 1.f, 1.f);
  // Clip rect in the target space after composition, or nullopt if the quad is
  // not clipped.
  std::optional<gfx::Rect> clip_rect;

  // Texture resource to present in an overlay.
  ResourceId resource_id = kInvalidResourceId;
  // Mailbox from resource_id. It is used by SkiaRenderer.
  gpu::Mailbox mailbox;

#if BUILDFLAG(IS_WIN)
  // Indication of the overlay to be detected as possible full screen
  // letterboxing.
  // During video display, sometimes the video image does not have the same
  // shape or Picture Aspect Ratio as the display area. Letterboxing is the
  // process of scaling a widescreen image to fit a specific display, like 4:3.
  // The reverse case, scaling a 4:3 image to fit a widescreen display, is
  // sometimes called pillarboxing. However here letterboxing is also used in a
  // general sense, to mean scaling a video image to fit any given display area.
  // Check out more information from
  // https://learn.microsoft.com/en-us/windows/win32/medfound/picture-aspect-ratio#letterboxing.
  // Two conditions to make possible_video_fullscreen_letterboxing be true:
  // 1. Current page is in full screen mode which is decided by
  // AggregatedFrame::page_fullscreen_mode.
  // 2. IsPossibleFullScreenLetterboxing helper from
  // DCLayerOverlayProcessor returns true, which basically means the draw
  // quad beneath the overlay quad touches two sides of the screen while
  // starting at display origin (0, 0). Then before swap chain presentation and
  // with possible_video_fullscreen_letterboxing be true, some necessary
  // adjustment is done in order to make the video be equidistant from the sides
  // off the screen. That is, it needs to be CENTERED for the sides that are not
  // touching the screen. At this point, Desktop Window Manager(DWM) considers
  // the video as full screen letterboxing.
  bool possible_video_fullscreen_letterboxing = false;
#endif

#if BUILDFLAG(IS_ANDROID)
  // Is video using SurfaceView-like architecture. It's currently actually uses
  // `DialogOverlay` in browser instead of actual SurfaceView. But "SurfaceView"
  // is used throughout the code so is used here as well for consistency.
  bool is_video_in_surface_view = false;
  // Crop within the buffer to be placed inside |display_rect| before
  // |clip_rect| was applied. Valid only for surface control.
  gfx::RectF unclipped_uv_rect = gfx::RectF(0.f, 0.f, 1.f, 1.f);
  // |display_rect| before |clip_rect| was applied. Valid only for surface
  // control.
  gfx::RectF unclipped_display_rect = gfx::RectF(0.f, 0.f, 1.f, 1.f);
#endif

  // Stacking order of the overlay plane relative to the main surface,
  // which is 0. Signed to allow for "underlays".
  int plane_z_order = 0;

  // The total area in square pixels of damage for this candidate's quad. This
  // is an estimate when 'EstimateOccludedDamage' function is used.
  float damage_area_estimate = 0.f;

  // Damage in viz Display space, the same space as |display_rect|;
  gfx::RectF damage_rect;

  static constexpr uint32_t kInvalidDamageIndex = UINT_MAX;
  // Damage index for |SurfaceDamageRectList|.
  uint32_t overlay_damage_index = kInvalidDamageIndex;

  // Represents either a background of this overlay candidate or a color of a
  // solid color quad, which can be checked via the |is_solid_color|.
  std::optional<SkColor4f> color;

  // If |rpdq| is present, then the renderer must draw the filter effects and
  // copy the result into the buffer backing of a render pass.
  raw_ptr<const AggregatedRenderPassDrawQuad, DanglingUntriaged> rpdq = nullptr;

  // Quad |shared_quad_state| opacity is ubiquitous for quad types
  // AggregateRenderPassDrawQuad, TileDrawQuad, SolidColorDrawQuad. A delegate
  // context must support non opaque opacity for these types.
  float opacity = 1.0f;

  // Specifies the rounded corners of overlay candidate, in target space.
  gfx::RRectF rounded_corners;

#if BUILDFLAG(IS_APPLE)
  // Layers in a non-zero sorting context exist in the same 3D space and should
  // intersect.
  unsigned sorting_context_id = 0;

  // The edge anti-aliasing mask property for the CALayer.
  unsigned edge_aa_mask = 0;
#endif  // BUILDFLAG(IS_APPLE)

  // A (ideally) unique key used to temporally identify a specific overlay
  // candidate. This key can have collisions more that would be expected by the
  // birthday paradox of 32 bits. If two or more candidates come from the same
  // surface and have the same |DrawQuad::rect| they will have the same
  // |tracking_id|.
  TrackingId tracking_id = kDefaultTrackingId;

#if BUILDFLAG(IS_WIN)
  // Used to identify overlays that originate from the same cc layer.
  gfx::OverlayLayerId layer_id;
#endif

  // Transformation to apply to layer during composition.
  // Note: A |gfx::OverlayTransform| transforms the buffer within its bounds and
  // does not affect |display_rect|.
  std::variant<gfx::OverlayTransform, gfx::Transform> transform =
      gfx::OVERLAY_TRANSFORM_NONE;

  // Default overlay type.
  gfx::OverlayType overlay_type = gfx::OverlayType::kSimple;
};

using OverlayCandidateList = std::vector<OverlayCandidate>;

}  // namespace viz

#endif  // COMPONENTS_VIZ_SERVICE_DISPLAY_OVERLAY_CANDIDATE_H_
