910e62b5创建于 1月15日历史提交
// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "skia/ext/geometry.h"

#include "third_party/skia/include/core/SkMatrix.h"
#include "third_party/skia/include/core/SkRect.h"

namespace skia {

SkRect ScaleSkRectProportional(const SkRect& output_bounds,
                               const SkRect& input_bounds,
                               const SkRect& input_rect) {
  float scale_x = output_bounds.width() / input_bounds.width();
  float scale_y = output_bounds.height() / input_bounds.height();
  return SkRect::MakeLTRB(
      (input_rect.fLeft - input_bounds.fLeft) * scale_x + output_bounds.fLeft,
      (input_rect.fTop - input_bounds.fTop) * scale_y + output_bounds.fTop,
      (input_rect.fRight - input_bounds.fRight) * scale_x +
          output_bounds.fRight,
      (input_rect.fBottom - input_bounds.fBottom) * scale_y +
          output_bounds.fBottom);
}

Tiling::Tiling(const SkRect& dest_rect,
               std::vector<SkRect> source_rects,
               std::vector<sk_sp<SkImage>> source_images,
               int32_t source_max_size)
    : dest_rect_(dest_rect),
      source_count_(source_rects.size()),
      source_rects_(std::move(source_rects)),
      source_images_(std::move(source_images)) {
  if (source_max_size == 0) {
    // A maximum size of 0 is specified for software rasterization.
    tile_size_ = SkSize::Make(dest_rect_.width(), dest_rect_.height());
  } else {
    // Compute the amount by which `dest_rect_` would need to be scaled down to
    // ensure all of `source_rects` fit in `source_max_size`.
    float tile_dest_width = dest_rect_.width();
    float tile_dest_height = dest_rect_.height();
    for (size_t i = 0; i < source_count_; ++i) {
      // Source images that are already textures don't need to be tiled for
      // upload.
      if (source_images_[i]->isTextureBacked()) {
        continue;
      }

      // Find the dest tile width and height that corresponds to sampling
      // `source_max_size` pixels. In this computation, add a padding of 2
      // pixels to the source rect, to account for sampling pixels outside
      // of the rect.
      float kPadding = 2.f;
      tile_dest_width = std::min(
          tile_dest_width,
          dest_rect_.width() *
              (source_max_size / (source_rects_[i].width() + kPadding)));
      tile_dest_height = std::min(
          tile_dest_height,
          dest_rect_.height() *
              (source_max_size / (source_rects_[i].height() + kPadding)));
    }

    // Prefer integer-sized tiles, so round down to the nearest integer, unless
    // that would take us to zero.
    if (tile_dest_width > 1.f) {
      tile_dest_width = std::floor(tile_dest_width);
    }
    if (tile_dest_height > 1.f) {
      tile_dest_height = std::floor(tile_dest_height);
    }

    tile_size_ = SkSize::Make(tile_dest_width, tile_dest_height);
  }
  tile_count_ =
      SkISize::Make(std::ceil(dest_rect_.width() / tile_size_.width()),
                    std::ceil(dest_rect_.height() / tile_size_.height()));
}

Tiling::~Tiling() = default;

void Tiling::GetTileRect(
    int x,
    int y,
    SkRect& tile_dest_rect,
    std::vector<SkRect>& tile_source_rects,
    std::vector<std::optional<SkIRect>>& tile_source_subset_rects) {
  // Compute the destination tile rect.
  tile_dest_rect =
      SkRect::MakeLTRB(dest_rect_.x() + x * tile_size_.width(),
                       dest_rect_.y() + y * tile_size_.height(),
                       dest_rect_.x() + (x + 1) * tile_size_.width(),
                       dest_rect_.y() + (y + 1) * tile_size_.height());
  tile_dest_rect.intersect(dest_rect_);

  tile_source_rects.resize(source_count_);
  tile_source_subset_rects.resize(source_count_);
  for (size_t i = 0; i < source_rects_.size(); ++i) {
    // Compute the tile's source rect to have the same relationship to the
    // source rect as the tile's dest rect has to the dest rect.
    SkRect source_rect =
        ScaleSkRectProportional(source_rects_[i], dest_rect_, tile_dest_rect);

    const auto& image = source_images_[i];

    // If the image is texture-backed, then use it directly.
    if (image->isTextureBacked()) {
      tile_source_rects[i] = source_rect;
      tile_source_subset_rects[i] = std::nullopt;
      continue;
    }

    // Otherwise, find the subset of the image that could be sampled, and report
    // that subset rectangle and the source rectangle to sample it.
    SkIRect subset_rect = SkIRect::MakeEmpty();
    source_rect.roundOut(&subset_rect);
    if (subset_rect.intersect(image->bounds())) {
      source_rect =
          skia::ScaleSkRectProportional(SkRect::Make(subset_rect.size()),
                                        SkRect::Make(subset_rect), source_rect);
    } else {
      subset_rect = SkIRect::MakeEmpty();
      source_rect = SkRect::MakeEmpty();
    }
    tile_source_rects[i] = source_rect;
    tile_source_subset_rects[i] = subset_rect;
  }
}

}  // namespace skia