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

#include "components/viz/common/quads/texture_draw_quad.h"

#include <stddef.h>

#include "base/check.h"
#include "base/strings/stringprintf.h"
#include "base/trace_event/traced_value.h"
#include "cc/base/math_util.h"
#include "components/viz/common/quads/draw_quad.h"
#include "components/viz/common/resources/resource_id.h"
#include "ui/gfx/color_utils.h"
#include "ui/gfx/geometry/rect_f.h"

namespace viz {

TextureDrawQuad::TextureDrawQuad()
    : nearest_neighbor(false),
      secure_output_only(false),
      is_video_frame(false),
      protected_video_type(gfx::ProtectedVideoType::kClear) {
  static_assert(static_cast<int>(gfx::ProtectedVideoType::kMaxValue) < 4,
                "protected_video_type needs more bits in order to represent "
                "all the enum values");
}

TextureDrawQuad::TextureDrawQuad(const TextureDrawQuad& other) = default;

TextureDrawQuad::~TextureDrawQuad() = default;

void TextureDrawQuad::SetNew(const SharedQuadState* shared_quad_state,
                             const gfx::Rect& rect,
                             const gfx::Rect& visible_rect,
                             bool needs_blending,
                             ResourceId resource,
                             const gfx::PointF& top_left,
                             const gfx::PointF& bottom_right,
                             SkColor4f background,
                             bool nearest,
                             bool secure_output,
                             gfx::ProtectedVideoType video_type,
                             bool is_tex_coords_normalized) {
  CHECK_NE(resource, kInvalidResourceId);
  this->needs_blending = needs_blending;
  DrawQuad::SetAll(shared_quad_state, DrawQuad::Material::kTextureContent, rect,
                   visible_rect, needs_blending);
  resource_id = resource;
  tex_coord_rect_ = gfx::BoundingRect(top_left, bottom_right);
  background_color = background;
  nearest_neighbor = nearest;
  secure_output_only = secure_output;
  protected_video_type = video_type;
  is_normalized_coords = is_tex_coords_normalized;
}

void TextureDrawQuad::SetAll(const SharedQuadState* shared_quad_state,
                             const gfx::Rect& rect,
                             const gfx::Rect& visible_rect,
                             bool needs_blending,
                             ResourceId resource,
                             const gfx::PointF& top_left,
                             const gfx::PointF& bottom_right,
                             SkColor4f background,
                             bool nearest,
                             bool secure_output,
                             gfx::ProtectedVideoType video_type,
                             bool is_tex_coords_normalized) {
  CHECK_NE(resource, kInvalidResourceId);
  DrawQuad::SetAll(shared_quad_state, DrawQuad::Material::kTextureContent, rect,
                   visible_rect, needs_blending);
  resource_id = resource;
  tex_coord_rect_ = gfx::BoundingRect(top_left, bottom_right);
  background_color = background;
  nearest_neighbor = nearest;
  secure_output_only = secure_output;
  protected_video_type = video_type;
  is_normalized_coords = is_tex_coords_normalized;
}

const TextureDrawQuad* TextureDrawQuad::MaterialCast(const DrawQuad* quad) {
  CHECK_EQ(quad->material, DrawQuad::Material::kTextureContent);
  return static_cast<const TextureDrawQuad*>(quad);
}

void TextureDrawQuad::ExtendValue(base::trace_event::TracedValue* value) const {
  cc::MathUtil::AddToTracedValue("tex_coord_rect", tex_coord_rect_, value);
  value->SetString("background_color",
                   color_utils::SkColor4fToRgbaString(background_color));
  value->SetString("dynamic_range_limit", dynamic_range_limit.ToString());
  value->SetBoolean("nearest_neighbor", nearest_neighbor);
  value->SetBoolean("secure_output_only", secure_output_only);
  value->SetBoolean("is_video_frame", is_video_frame);
  value->SetBoolean("force_rgbx", force_rgbx);
  value->SetBoolean("is_normalized_coords", is_normalized_coords);
  value->SetInteger("protected_video_type",
                    static_cast<int>(protected_video_type));
  value->SetInteger("overlay_priority_hint",
                    static_cast<int>(overlay_priority_hint));
  if (damage_rect) {
    cc::MathUtil::AddToTracedValue("damage_rect", *damage_rect, value);
  }

  value->SetString(
      "rounded_display_masks_info",
      base::StringPrintf(
          "%d,%d,is_horizontally_positioned=%d",
          rounded_display_masks_info
              .radii[RoundedDisplayMasksInfo::kOriginRoundedDisplayMaskIndex],
          rounded_display_masks_info
              .radii[RoundedDisplayMasksInfo::kOtherRoundedDisplayMaskIndex],
          static_cast<int>(
              rounded_display_masks_info.is_horizontally_positioned)));
}

TextureDrawQuad::RoundedDisplayMasksInfo::RoundedDisplayMasksInfo() = default;

// static
TextureDrawQuad::RoundedDisplayMasksInfo
TextureDrawQuad::RoundedDisplayMasksInfo::CreateRoundedDisplayMasksInfo(
    int origin_rounded_display_mask_radius,
    int other_rounded_display_mask_radius,
    bool is_horizontally_positioned) {
  RoundedDisplayMasksInfo info;
  info.radii[kOriginRoundedDisplayMaskIndex] =
      origin_rounded_display_mask_radius;
  info.radii[kOtherRoundedDisplayMaskIndex] = other_rounded_display_mask_radius;
  info.is_horizontally_positioned = is_horizontally_positioned;

  return info;
}

// static
std::array<
    gfx::RectF,
    TextureDrawQuad::RoundedDisplayMasksInfo::kMaxRoundedDisplayMasksCount>
TextureDrawQuad::RoundedDisplayMasksInfo::GetRoundedDisplayMasksBounds(
    const DrawQuad* quad) {
  std::array<gfx::RectF, RoundedDisplayMasksInfo::kMaxRoundedDisplayMasksCount>
      mask_rects;

  const TextureDrawQuad* texture_quad = quad->DynamicCast<TextureDrawQuad>();
  if (!texture_quad) {
    return mask_rects;
  }

  TextureDrawQuad::RoundedDisplayMasksInfo mask_info =
      texture_quad->rounded_display_masks_info;

  if (mask_info.IsEmpty()) {
    return mask_rects;
  }

  const gfx::Transform& transform =
      quad->shared_quad_state->quad_to_target_transform;
  const gfx::RectF target_rect = transform.MapRect(gfx::RectF(quad->rect));

  const int16_t origin_mask_radius =
      mask_info.radii[TextureDrawQuad::RoundedDisplayMasksInfo::
                          kOriginRoundedDisplayMaskIndex];
  mask_rects[RoundedDisplayMasksInfo::kOriginRoundedDisplayMaskIndex] =
      gfx::RectF(target_rect.x(), target_rect.y(), origin_mask_radius,
                 origin_mask_radius);

  const int16_t other_mask_radius =
      mask_info.radii[TextureDrawQuad::RoundedDisplayMasksInfo::
                          kOtherRoundedDisplayMaskIndex];
  if (mask_info.is_horizontally_positioned) {
    mask_rects[RoundedDisplayMasksInfo::kOtherRoundedDisplayMaskIndex] =
        gfx::RectF(target_rect.x() + target_rect.width() - other_mask_radius,
                   target_rect.y(), other_mask_radius, other_mask_radius);
  } else {
    mask_rects[RoundedDisplayMasksInfo::kOtherRoundedDisplayMaskIndex] =
        gfx::RectF(target_rect.x(),
                   target_rect.y() + target_rect.height() - other_mask_radius,
                   other_mask_radius, other_mask_radius);
  }

  return mask_rects;
}

bool TextureDrawQuad::RoundedDisplayMasksInfo::IsEmpty() const {
  return radii[kOriginRoundedDisplayMaskIndex] == 0 &&
         radii[kOtherRoundedDisplayMaskIndex] == 0;
}

}  // namespace viz
