blob: f3f15eb06074da26dc1d19ebd72f6b70ebedb73d [file] [log] [blame]
// 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