blob: 8383aef63191a4341ae2412f4c9576cd56783027 [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 <array>
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/skia/include/core/SkBitmap.h"
namespace skia {
namespace {
TEST(Geometry, ScaleRectProportional) {
SkRect output_rect;
output_rect = ScaleSkRectProportional(SkRect::MakeXYWH(0, 0, 20, 20),
SkRect::MakeXYWH(0, 0, 40, 40),
SkRect::MakeXYWH(10, 10, 20, 20));
EXPECT_EQ(output_rect, SkRect::MakeXYWH(5, 5, 10, 10));
output_rect = ScaleSkRectProportional(SkRect::MakeXYWH(0, 10, 20, 80),
SkRect::MakeXYWH(10, 20, 40, 80),
SkRect::MakeXYWH(0, 30, 60, 20));
EXPECT_EQ(output_rect, SkRect::MakeXYWH(-5, 20, 30, 20));
}
TEST(Geometry, TileSimple) {
SkBitmap bm0;
SkBitmap bm1;
// Simple tiling that divides the region into two tiles.
bm0.allocPixels(SkImageInfo::MakeN32Premul(20, 40));
bm1.allocPixels(SkImageInfo::MakeN32Premul(10, 20));
SkRect dest_rect = SkRect::MakeXYWH(10, 10, 20, 40);
std::vector<SkRect> source_rects = {
SkRect::MakeXYWH(0, 0, 20, 40),
SkRect::MakeXYWH(0, 0, 10, 20),
};
std::vector<sk_sp<SkImage>> source_images = {
SkImages::RasterFromBitmap(bm0),
SkImages::RasterFromBitmap(bm1),
};
int32_t source_max_size = 22;
Tiling t(dest_rect, source_rects, source_images, source_max_size);
EXPECT_EQ(1, t.GetTileCountX());
EXPECT_EQ(2, t.GetTileCountY());
SkRect tile_dest_rect;
std::vector<SkRect> tile_source_rects;
std::vector<std::optional<SkIRect>> tile_subset_rects;
t.GetTileRect(0, 0, tile_dest_rect, tile_source_rects, tile_subset_rects);
EXPECT_EQ(tile_dest_rect, SkRect::MakeXYWH(10, 10, 20, 20));
EXPECT_EQ(tile_source_rects[0], SkRect::MakeXYWH(0, 0, 20, 20));
EXPECT_EQ(tile_source_rects[1], SkRect::MakeXYWH(0, 0, 10, 10));
EXPECT_EQ(tile_subset_rects[0].value(), SkIRect::MakeXYWH(0, 0, 20, 20));
EXPECT_EQ(tile_subset_rects[1].value(), SkIRect::MakeXYWH(0, 0, 10, 10));
t.GetTileRect(0, 1, tile_dest_rect, tile_source_rects, tile_subset_rects);
EXPECT_EQ(tile_dest_rect, SkRect::MakeXYWH(10, 30, 20, 20));
EXPECT_EQ(tile_source_rects[0], SkRect::MakeXYWH(0, 0, 20, 20));
EXPECT_EQ(tile_source_rects[1], SkRect::MakeXYWH(0, 0, 10, 10));
EXPECT_EQ(tile_subset_rects[0].value(), SkIRect::MakeXYWH(0, 20, 20, 20));
EXPECT_EQ(tile_subset_rects[1].value(), SkIRect::MakeXYWH(0, 10, 10, 10));
}
TEST(Geometry, TileSmallX) {
SkBitmap bm0;
// A tiling that has very different width and height.
bm0.allocPixels(SkImageInfo::MakeN32Premul(5, 50));
SkRect dest_rect = SkRect::MakeXYWH(0, 0, 5, 50);
std::vector<SkRect> source_rects = {
SkRect::MakeXYWH(0, 0, 5, 50),
};
std::vector<sk_sp<SkImage>> source_images = {
SkImages::RasterFromBitmap(bm0),
};
int32_t source_max_size = 21;
Tiling t(dest_rect, source_rects, source_images, source_max_size);
EXPECT_EQ(1, t.GetTileCountX());
EXPECT_EQ(3, t.GetTileCountY());
SkRect tile_dest_rect;
std::vector<SkRect> tile_source_rects;
std::vector<std::optional<SkIRect>> tile_subset_rects;
t.GetTileRect(0, 0, tile_dest_rect, tile_source_rects, tile_subset_rects);
EXPECT_EQ(tile_dest_rect, SkRect::MakeXYWH(0, 0, 5, 20));
EXPECT_EQ(tile_source_rects[0], SkRect::MakeXYWH(0, 0, 5, 20));
EXPECT_EQ(tile_subset_rects[0].value(), SkIRect::MakeXYWH(0, 0, 5, 20));
t.GetTileRect(0, 1, tile_dest_rect, tile_source_rects, tile_subset_rects);
EXPECT_EQ(tile_dest_rect, SkRect::MakeXYWH(0, 20, 5, 20));
EXPECT_EQ(tile_source_rects[0], SkRect::MakeXYWH(0, 0, 5, 20));
EXPECT_EQ(tile_subset_rects[0].value(), SkIRect::MakeXYWH(0, 20, 5, 20));
t.GetTileRect(0, 2, tile_dest_rect, tile_source_rects, tile_subset_rects);
EXPECT_EQ(tile_dest_rect, SkRect::MakeXYWH(0, 40, 5, 10));
EXPECT_EQ(tile_source_rects[0], SkRect::MakeXYWH(0, 0, 5, 10));
EXPECT_EQ(tile_subset_rects[0].value(), SkIRect::MakeXYWH(0, 40, 5, 10));
}
TEST(Geometry, TileSmallY) {
SkBitmap bm0;
// A tiling that has very different width and height.
bm0.allocPixels(SkImageInfo::MakeN32Premul(50, 5));
SkRect dest_rect = SkRect::MakeXYWH(0, 0, 50, 5);
std::vector<SkRect> source_rects = {
SkRect::MakeXYWH(0, 0, 50, 5),
};
std::vector<sk_sp<SkImage>> source_images = {
SkImages::RasterFromBitmap(bm0),
};
int32_t source_max_size = 21;
Tiling t(dest_rect, source_rects, source_images, source_max_size);
EXPECT_EQ(3, t.GetTileCountX());
EXPECT_EQ(1, t.GetTileCountY());
SkRect tile_dest_rect;
std::vector<SkRect> tile_source_rects;
std::vector<std::optional<SkIRect>> tile_subset_rects;
t.GetTileRect(0, 0, tile_dest_rect, tile_source_rects, tile_subset_rects);
EXPECT_EQ(tile_dest_rect, SkRect::MakeXYWH(0, 0, 20, 5));
EXPECT_EQ(tile_source_rects[0], SkRect::MakeXYWH(0, 0, 20, 5));
EXPECT_EQ(tile_subset_rects[0].value(), SkIRect::MakeXYWH(0, 0, 20, 5));
t.GetTileRect(1, 0, tile_dest_rect, tile_source_rects, tile_subset_rects);
EXPECT_EQ(tile_dest_rect, SkRect::MakeXYWH(20, 0, 20, 5));
EXPECT_EQ(tile_source_rects[0], SkRect::MakeXYWH(0, 0, 20, 5));
EXPECT_EQ(tile_subset_rects[0].value(), SkIRect::MakeXYWH(20, 0, 20, 5));
t.GetTileRect(2, 0, tile_dest_rect, tile_source_rects, tile_subset_rects);
EXPECT_EQ(tile_dest_rect, SkRect::MakeXYWH(40, 0, 10, 5));
EXPECT_EQ(tile_source_rects[0], SkRect::MakeXYWH(0, 0, 10, 5));
EXPECT_EQ(tile_subset_rects[0].value(), SkIRect::MakeXYWH(40, 0, 10, 5));
}
TEST(Geometry, Overflow) {
SkBitmap bm0;
SkBitmap bm1;
// The tiling will overflow the bounds of bm1. Ensure that its subsets
// are set to empty instead of overflowing values.
bm0.allocPixels(SkImageInfo::MakeN32Premul(40, 80));
bm1.allocPixels(SkImageInfo::MakeN32Premul(10, 20));
SkRect dest_rect = SkRect::MakeXYWH(10, 10, 20, 40);
std::vector<SkRect> source_rects = {
SkRect::MakeXYWH(10, 10, 20, 40),
SkRect::MakeXYWH(5, 5, 10, 20),
};
std::vector<sk_sp<SkImage>> source_images = {
SkImages::RasterFromBitmap(bm0),
SkImages::RasterFromBitmap(bm1),
};
int32_t source_max_size = 15;
Tiling t(dest_rect, source_rects, source_images, source_max_size);
constexpr int kTilesX = 2;
constexpr int kTilesY = 3;
EXPECT_EQ(kTilesX, t.GetTileCountX());
EXPECT_EQ(kTilesY, t.GetTileCountY());
// Save the output from all tiles for comparison.
std::array<std::array<SkRect, kTilesY>, kTilesX> t_dest;
std::array<std::array<SkRect, kTilesY>, kTilesX> t_base;
std::array<std::array<SkRect, kTilesY>, kTilesX> t_gain;
std::array<std::array<SkIRect, kTilesY>, kTilesX> s_base;
std::array<std::array<SkIRect, kTilesY>, kTilesX> s_gain;
for (int x = 0; x < kTilesX; ++x) {
for (int y = 0; y < kTilesY; ++y) {
SkRect tile_dest_rect;
std::vector<SkRect> tile_source_rects;
std::vector<std::optional<SkIRect>> tile_subset_rects;
t.GetTileRect(x, y, tile_dest_rect, tile_source_rects, tile_subset_rects);
t_dest[x][y] = tile_dest_rect;
t_base[x][y] = tile_source_rects[0];
t_gain[x][y] = tile_source_rects[1];
s_base[x][y] = tile_subset_rects[0].value();
s_gain[x][y] = tile_subset_rects[1].value();
}
}
EXPECT_EQ(t_dest[0][0], SkRect::MakeLTRB(10, 10, 23, 24));
EXPECT_EQ(t_dest[0][1], SkRect::MakeLTRB(10, 24, 23, 38));
EXPECT_EQ(t_dest[0][2], SkRect::MakeLTRB(10, 38, 23, 50));
EXPECT_EQ(t_dest[1][0], SkRect::MakeLTRB(23, 10, 30, 24));
EXPECT_EQ(t_dest[1][1], SkRect::MakeLTRB(23, 24, 30, 38));
EXPECT_EQ(t_dest[1][2], SkRect::MakeLTRB(23, 38, 30, 50));
EXPECT_EQ(t_base[0][0], SkRect::MakeLTRB(0, 0, 13, 14));
EXPECT_EQ(t_base[0][1], SkRect::MakeLTRB(0, 0, 13, 14));
EXPECT_EQ(t_base[0][2], SkRect::MakeLTRB(0, 0, 13, 12));
EXPECT_EQ(t_base[1][0], SkRect::MakeLTRB(0, 0, 7, 14));
EXPECT_EQ(t_base[1][1], SkRect::MakeLTRB(0, 0, 7, 14));
EXPECT_EQ(t_base[1][2], SkRect::MakeLTRB(0, 0, 7, 12));
EXPECT_EQ(t_gain[0][0], SkRect::MakeLTRB(0, 0, 6.5, 7));
EXPECT_EQ(t_gain[0][1], SkRect::MakeLTRB(0, 0, 6.5, 7));
EXPECT_EQ(t_gain[0][2], SkRect::MakeLTRB(0, 0, 6.5, 6));
EXPECT_EQ(t_gain[1][0], SkRect::MakeEmpty());
EXPECT_EQ(t_gain[1][1], SkRect::MakeEmpty());
EXPECT_EQ(t_gain[1][2], SkRect::MakeEmpty());
EXPECT_EQ(s_base[0][0], SkIRect::MakeLTRB(10, 10, 23, 24));
EXPECT_EQ(s_base[0][1], SkIRect::MakeLTRB(10, 24, 23, 38));
EXPECT_EQ(s_base[0][2], SkIRect::MakeLTRB(10, 38, 23, 50));
EXPECT_EQ(s_base[1][0], SkIRect::MakeLTRB(23, 10, 30, 24));
EXPECT_EQ(s_base[1][1], SkIRect::MakeLTRB(23, 24, 30, 38));
EXPECT_EQ(s_base[1][2], SkIRect::MakeLTRB(23, 38, 30, 50));
EXPECT_EQ(s_gain[0][0], SkIRect::MakeLTRB(5, 5, 10, 12));
EXPECT_EQ(s_gain[0][1], SkIRect::MakeLTRB(5, 12, 10, 19));
EXPECT_EQ(s_gain[0][2], SkIRect::MakeLTRB(5, 19, 10, 20));
EXPECT_EQ(s_gain[1][0], SkIRect::MakeEmpty());
EXPECT_EQ(s_gain[1][1], SkIRect::MakeEmpty());
EXPECT_EQ(s_gain[1][2], SkIRect::MakeEmpty());
}
TEST(Geometry, Fractional) {
SkBitmap bm0;
SkBitmap bm1;
// Test where the source rects are not pixel-aligned.
bm0.allocPixels(SkImageInfo::MakeN32Premul(30, 50));
SkRect dest_rect = SkRect::MakeXYWH(0, 0, 20, 40);
std::vector<SkRect> source_rects = {
SkRect::MakeXYWH(5.5, 5.5, 20, 40),
};
std::vector<sk_sp<SkImage>> source_images = {
SkImages::RasterFromBitmap(bm0),
};
int32_t source_max_size = 20;
Tiling t(dest_rect, source_rects, source_images, source_max_size);
constexpr int kTilesX = 2;
constexpr int kTilesY = 3;
EXPECT_EQ(kTilesX, t.GetTileCountX());
EXPECT_EQ(kTilesY, t.GetTileCountY());
// Save the output from all tiles for comparison.
std::array<std::array<SkRect, kTilesY>, kTilesX> out_dest;
std::array<std::array<SkRect, kTilesY>, kTilesX> out_source;
std::array<std::array<SkIRect, kTilesY>, kTilesX> out_subset;
for (int x = 0; x < kTilesX; ++x) {
for (int y = 0; y < kTilesY; ++y) {
SkRect tile_dest_rect;
std::vector<SkRect> tile_source_rects;
std::vector<std::optional<SkIRect>> tile_subset_rects;
t.GetTileRect(x, y, tile_dest_rect, tile_source_rects, tile_subset_rects);
out_dest[x][y] = tile_dest_rect;
out_source[x][y] = tile_source_rects[0];
out_subset[x][y] = tile_subset_rects[0].value();
}
}
// The subsets for the image will be overlapping (e.g, the
// first entry ends at Y=25, then the next one starts at
// Y=24).
EXPECT_EQ(out_subset[0][0], SkIRect::MakeLTRB(5, 5, 24, 25));
EXPECT_EQ(out_subset[0][1], SkIRect::MakeLTRB(5, 24, 24, 44));
EXPECT_EQ(out_subset[0][2], SkIRect::MakeLTRB(5, 43, 24, 46));
EXPECT_EQ(out_subset[1][0], SkIRect::MakeLTRB(23, 5, 26, 25));
EXPECT_EQ(out_subset[1][1], SkIRect::MakeLTRB(23, 24, 26, 44));
EXPECT_EQ(out_subset[1][2], SkIRect::MakeLTRB(23, 43, 26, 46));
// All of the sources are aligned to a half-pixel.
EXPECT_EQ(out_source[0][0], SkRect::MakeLTRB(0.5, 0.5, 18.5, 19.5));
EXPECT_EQ(out_source[0][1], SkRect::MakeLTRB(0.5, 0.5, 18.5, 19.5));
EXPECT_EQ(out_source[0][2], SkRect::MakeLTRB(0.5, 0.5, 18.5, 2.5));
EXPECT_EQ(out_source[1][0], SkRect::MakeLTRB(0.5, 0.5, 2.5, 19.5));
EXPECT_EQ(out_source[1][1], SkRect::MakeLTRB(0.5, 0.5, 2.5, 19.5));
EXPECT_EQ(out_source[1][2], SkRect::MakeLTRB(0.5, 0.5, 2.5, 2.5));
// The destination tiles cover `dest_rect`.
EXPECT_EQ(out_dest[0][0], SkRect::MakeLTRB(0, 0, 18, 19));
EXPECT_EQ(out_dest[0][1], SkRect::MakeLTRB(0, 19, 18, 38));
EXPECT_EQ(out_dest[0][2], SkRect::MakeLTRB(0, 38, 18, 40));
EXPECT_EQ(out_dest[1][0], SkRect::MakeLTRB(18, 0, 20, 19));
EXPECT_EQ(out_dest[1][1], SkRect::MakeLTRB(18, 19, 20, 38));
EXPECT_EQ(out_dest[1][2], SkRect::MakeLTRB(18, 38, 20, 40));
}
} // namespace
} // namespace skia