#include <array>
#ifdef UNSAFE_BUFFERS_BUILD
#pragma allow_unsafe_buffers
#endif
#include <cmath>
#include <memory>
#include <tuple>
#include <vector>
#include "base/command_line.h"
#include "base/files/file_path.h"
#include "base/functional/bind.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/raw_ptr_exclusion.h"
#include "base/path_service.h"
#include "base/test/test_switches.h"
#include "build/build_config.h"
#include "cc/base/completion_event.h"
#include "cc/paint/display_item_list.h"
#include "cc/paint/paint_filter.h"
#include "cc/paint/paint_flags.h"
#include "cc/paint/paint_image_builder.h"
#include "cc/raster/playback_image_provider.h"
#include "cc/test/fake_paint_image_generator.h"
#include "cc/test/pixel_comparator.h"
#include "cc/test/pixel_test_utils.h"
#include "cc/tiles/gpu_image_decode_cache.h"
#include "components/viz/common/resources/shared_image_format.h"
#include "components/viz/service/gl/gpu_service_impl.h"
#include "components/viz/test/buildflags.h"
#include "components/viz/test/paths.h"
#include "components/viz/test/test_gpu_service_holder.h"
#include "components/viz/test/test_in_process_context_provider.h"
#include "gpu/command_buffer/client/client_shared_image.h"
#include "gpu/command_buffer/client/raster_implementation.h"
#include "gpu/command_buffer/client/shared_image_interface.h"
#include "gpu/command_buffer/common/shared_image_usage.h"
#include "gpu/command_buffer/service/gr_shader_cache.h"
#include "gpu/command_buffer/service/graphite_utils.h"
#include "gpu/config/gpu_finch_features.h"
#include "ipc/common/gpu_client_ids.h"
#include "skia/ext/font_utils.h"
#include "skia/ext/legacy_display_globals.h"
#include "skia/ext/skcolorspace_trfn.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/skia/include/core/SkAlphaType.h"
#include "third_party/skia/include/core/SkColor.h"
#include "third_party/skia/include/core/SkColorSpace.h"
#include "third_party/skia/include/core/SkColorType.h"
#include "third_party/skia/include/core/SkGraphics.h"
#include "third_party/skia/include/core/SkImage.h"
#include "third_party/skia/include/core/SkPictureRecorder.h"
#include "third_party/skia/include/core/SkRect.h"
#include "third_party/skia/include/core/SkSurface.h"
#include "third_party/skia/include/core/SkTextBlob.h"
#include "third_party/skia/include/core/SkYUVAInfo.h"
#include "third_party/skia/include/gpu/GpuTypes.h"
#include "third_party/skia/include/gpu/ganesh/GrDirectContext.h"
#include "third_party/skia/include/gpu/ganesh/GrTypes.h"
#include "third_party/skia/include/gpu/ganesh/SkSurfaceGanesh.h"
#include "third_party/skia/include/gpu/graphite/Context.h"
#include "third_party/skia/include/gpu/graphite/GraphiteTypes.h"
#include "third_party/skia/include/gpu/graphite/Surface.h"
#include "ui/gfx/geometry/rect_conversions.h"
#include "ui/gfx/geometry/skia_conversions.h"
#include "ui/gl/gl_implementation.h"
#if BUILDFLAG(IS_ANDROID)
#include "base/android/android_info.h"
#endif
namespace cc {
namespace {
SkV4 SkColorToSkV4(SkColor4f color) {
return SkV4{color.fR, color.fG, color.fB, color.fA};
}
scoped_refptr<DisplayItemList> MakeNoopDisplayItemList() {
auto display_item_list = base::MakeRefCounted<DisplayItemList>();
display_item_list->StartPaint();
display_item_list->push<SaveOp>();
display_item_list->push<RestoreOp>();
display_item_list->EndPaintOfUnpaired(gfx::Rect(10000, 10000));
display_item_list->Finalize();
return display_item_list;
}
SkBitmap MakeSolidColorBitmap(gfx::Size size,
SkColor4f color,
SkAlphaType alpha_type = kPremul_SkAlphaType) {
SkBitmap bitmap;
bitmap.allocPixels(SkImageInfo::Make(size.width(), size.height(),
kN32_SkColorType, alpha_type));
bitmap.eraseColor(color);
return bitmap;
}
sk_sp<SkImage> MakeSkImage(const gfx::Size& size,
sk_sp<SkColorSpace> color_space = nullptr) {
SkBitmap bitmap;
bitmap.allocPixelsFlags(
SkImageInfo::MakeN32Premul(size.width(), size.height(), color_space),
SkBitmap::kZeroPixels_AllocFlag);
SkCanvas canvas(bitmap, SkSurfaceProps{});
canvas.drawColor(SkColors::kMagenta);
SkPaint green;
green.setColor(SkColors::kGreen);
canvas.drawRect(SkRect::MakeXYWH(10, 20, 30, 40), green);
return SkImages::RasterFromBitmap(bitmap);
}
constexpr size_t kCacheLimitBytes = 1024 * 1024;
constexpr PaintFlags::FilterQuality kDefaultFilterQuality =
PaintFlags::FilterQuality::kNone;
class OopPixelTest : public testing::Test,
public gpu::raster::GrShaderCache::Client {
public:
OopPixelTest() : gr_shader_cache_(kCacheLimitBytes, this) {}
void SetUp() override { InitializeOOPContext(); }
void StoreShader(const std::string& key, const std::string& shader) override {
}
void InitializeOOPContext() {
if (oop_image_cache_)
oop_image_cache_.reset();
raster_context_provider_ =
base::MakeRefCounted<viz::TestInProcessContextProvider>(
viz::TestContextType::kGpuRaster, false,
&gr_shader_cache_, &use_shader_cache_shm_count_);
gpu::ContextResult result =
raster_context_provider_->BindToCurrentSequence();
DCHECK_EQ(result, gpu::ContextResult::kSuccess);
const int raster_max_texture_size =
raster_context_provider_->ContextCapabilities().max_texture_size;
oop_image_cache_ = std::make_unique<GpuImageDecodeCache>(
raster_context_provider_.get(), kRGBA_8888_SkColorType, kWorkingSetSize,
raster_max_texture_size, nullptr);
}
class RasterOptions {
public:
RasterOptions() = default;
explicit RasterOptions(const gfx::Size& playback_size) {
resource_size = playback_size;
content_size = resource_size;
full_raster_rect = gfx::Rect(playback_size);
playback_rect = gfx::Rect(playback_size);
}
SkColor4f background_color = SkColors::kBlack;
int msaa_sample_count = 0;
bool use_lcd_text = false;
PlaybackImageProvider::RasterMode image_provider_raster_mode =
PlaybackImageProvider::RasterMode::kSoftware;
gfx::Size resource_size;
gfx::Size content_size;
gfx::Rect full_raster_rect;
gfx::Rect playback_rect;
gfx::Vector2dF post_translate = {0.f, 0.f};
float post_scale = 1.f;
TargetColorParams target_color_params;
bool requires_clear = false;
bool preclear = false;
SkColor4f preclear_color;
RAW_PTR_EXCLUSION ImageDecodeCache* image_cache = nullptr;
std::vector<scoped_refptr<DisplayItemList>> additional_lists;
raw_ptr<PaintShader> shader_with_animated_images = nullptr;
};
SkBitmap Raster(scoped_refptr<DisplayItemList> display_item_list,
const gfx::Size& playback_size) {
RasterOptions options(playback_size);
return Raster(display_item_list, options);
}
SkBitmap Raster(scoped_refptr<DisplayItemList> display_item_list,
const RasterOptions& options) {
std::optional<PlaybackImageProvider::Settings> settings;
settings.emplace(PlaybackImageProvider::Settings());
settings->raster_mode = options.image_provider_raster_mode;
PlaybackImageProvider image_provider(oop_image_cache_.get(),
options.target_color_params,
std::move(settings));
int width = options.resource_size.width();
int height = options.resource_size.height();
auto* ri = raster_context_provider_->RasterInterface();
auto* sii = raster_context_provider_->SharedImageInterface();
gpu::SharedImageUsageSet flags = gpu::SHARED_IMAGE_USAGE_RASTER_READ |
gpu::SHARED_IMAGE_USAGE_RASTER_WRITE;
auto client_shared_image = sii->CreateSharedImage(
{viz::SinglePlaneFormat::kRGBA_8888, gfx::Size(width, height),
options.target_color_params.color_space, flags, "TestLabel"},
gpu::kNullSurfaceHandle);
EXPECT_TRUE(client_shared_image->mailbox().Verify());
std::unique_ptr<gpu::RasterScopedAccess> ri_access =
client_shared_image->BeginRasterAccess(
ri, client_shared_image->creation_sync_token(), false);
gpu::raster::MsaaMode msaa_mode = options.msaa_sample_count > 0
? gpu::raster::kMSAA
: gpu::raster::kNoMSAA;
if (options.preclear) {
ri->BeginRasterCHROMIUM(
options.preclear_color,
options.preclear, options.msaa_sample_count,
msaa_mode, options.use_lcd_text,
true, options.target_color_params.color_space,
options.target_color_params.GetHdrHeadroom(),
client_shared_image->mailbox().name);
ri->EndRasterCHROMIUM();
}
ri->BeginRasterCHROMIUM(
options.background_color,
!options.preclear, options.msaa_sample_count, msaa_mode,
options.use_lcd_text,
true, options.target_color_params.color_space,
options.target_color_params.GetHdrHeadroom(),
client_shared_image->mailbox().name);
size_t max_op_size_limit =
gpu::raster::RasterInterface::kDefaultMaxOpSizeHint;
ri->RasterCHROMIUM(
display_item_list.get(), &image_provider, options.content_size,
options.full_raster_rect, options.playback_rect, options.post_translate,
gfx::Vector2dF(options.post_scale, options.post_scale),
options.requires_clear, nullptr,
&max_op_size_limit);
for (const auto& list : options.additional_lists) {
ri->RasterCHROMIUM(list.get(), &image_provider, options.content_size,
options.full_raster_rect, options.playback_rect,
options.post_translate,
gfx::Vector2dF(options.post_scale, options.post_scale),
options.requires_clear,
nullptr,
&max_op_size_limit);
}
ri->EndRasterCHROMIUM();
EXPECT_EQ(ri->GetError(), static_cast<unsigned>(GL_NO_ERROR));
SkBitmap result = ReadbackMailbox(ri, client_shared_image->mailbox(),
options.resource_size);
gpu::SyncToken sync_token =
gpu::RasterScopedAccess::EndAccess(std::move(ri_access));
sii->DestroySharedImage(sync_token, std::move(client_shared_image));
return result;
}
SkBitmap ReadbackMailbox(gpu::raster::RasterInterface* ri,
const gpu::Mailbox& mailbox,
const gfx::Size& image_size,
sk_sp<SkColorSpace> color_space = nullptr) {
SkImageInfo image_info = SkImageInfo::MakeN32Premul(
image_size.width(), image_size.height(), color_space);
SkBitmap result;
result.allocPixels(image_info);
ri->ReadbackImagePixels(mailbox, image_info, image_info.minRowBytes(), 0, 0,
0, result.getPixels());
return result;
}
scoped_refptr<gpu::ClientSharedImage> CreateClientSharedImage(
gpu::raster::RasterInterface* ri,
gpu::SharedImageInterface* sii,
const RasterOptions& options,
viz::SharedImageFormat format,
std::optional<gfx::ColorSpace> color_space = std::nullopt) {
gpu::SharedImageUsageSet flags = gpu::SHARED_IMAGE_USAGE_RASTER_READ |
gpu::SHARED_IMAGE_USAGE_RASTER_WRITE;
auto client_shared_image = sii->CreateSharedImage(
{format, options.resource_size,
color_space.value_or(options.target_color_params.color_space), flags,
"TestLabel"},
gpu::kNullSurfaceHandle);
EXPECT_TRUE(client_shared_image->mailbox().Verify());
return client_shared_image;
}
gpu::SyncToken UploadPixels(
gpu::raster::RasterInterface* ri,
const scoped_refptr<gpu::ClientSharedImage>& shared_image,
const SkImageInfo& info,
const SkBitmap& bitmap) {
auto ri_access = shared_image->BeginRasterAccess(
ri, shared_image->creation_sync_token(), false);
ri->WritePixels(shared_image->mailbox(), 0,
0,
0,
SkPixmap(info, bitmap.getPixels(), info.minRowBytes()));
EXPECT_EQ(ri->GetError(), static_cast<unsigned>(GL_NO_ERROR));
return gpu::RasterScopedAccess::EndAccess(std::move(ri_access));
}
void UploadPixelsYUV(
gpu::raster::RasterInterface* ri,
const scoped_refptr<gpu::ClientSharedImage>& shared_image,
const SkYUVAPixmaps& yuv_pixmap) {
auto ri_access = shared_image->BeginRasterAccess(
ri, shared_image->creation_sync_token(), false);
ri->WritePixelsYUV(shared_image->mailbox(), yuv_pixmap);
EXPECT_EQ(ri->GetError(), static_cast<unsigned>(GL_NO_ERROR));
gpu::RasterScopedAccess::EndAccess(std::move(ri_access));
}
void ExpectEquals(
const SkBitmap& actual,
const base::FilePath::StringType& ref_filename,
const PixelComparator& comparator = ExactPixelComparator()) {
base::FilePath test_data_dir;
ASSERT_TRUE(
base::PathService::Get(viz::Paths::DIR_TEST_DATA, &test_data_dir));
base::FilePath png_path = test_data_dir.Append(ref_filename);
auto* cmd = base::CommandLine::ForCurrentProcess();
if (cmd->HasSwitch(switches::kRebaselinePixelTests)) {
EXPECT_TRUE(WritePNGFile(actual, png_path, true));
} else {
EXPECT_TRUE(MatchesPNGFile(actual, png_path, comparator));
}
}
void ExpectEquals(
SkBitmap actual,
SkBitmap expected,
const PixelComparator& comparator = ExactPixelComparator()) {
EXPECT_TRUE(MatchesBitmap(actual, expected, comparator));
}
protected:
static constexpr size_t kWorkingSetSize = 64 * 1024 * 1024;
scoped_refptr<viz::TestInProcessContextProvider> raster_context_provider_;
std::unique_ptr<GpuImageDecodeCache> oop_image_cache_;
gl::DisableNullDrawGLBindings enable_pixel_output_;
std::unique_ptr<ImageProvider> image_provider_;
int color_space_id_ = 0;
gpu::raster::GrShaderCache gr_shader_cache_;
gpu::GpuProcessShmCount use_shader_cache_shm_count_;
};
class OopClearPixelTest : public OopPixelTest,
public ::testing::WithParamInterface<bool> {
public:
bool IsPartialRaster() const { return GetParam(); }
};
TEST_F(OopPixelTest, DrawColor) {
gfx::Rect rect(10, 10);
auto display_item_list = base::MakeRefCounted<DisplayItemList>();
display_item_list->StartPaint();
display_item_list->push<DrawColorOp>(SkColors::kBlue, SkBlendMode::kSrc);
display_item_list->EndPaintOfUnpaired(rect);
display_item_list->Finalize();
SkBitmap expected = MakeSolidColorBitmap(rect.size(), SkColors::kBlue);
auto actual = Raster(display_item_list, rect.size());
ExpectEquals(actual, expected);
}
TEST_F(OopPixelTest, DrawColorWithTargetColorSpace) {
gfx::Rect rect(10, 10);
auto display_item_list = base::MakeRefCounted<DisplayItemList>();
display_item_list->StartPaint();
display_item_list->push<DrawColorOp>(SkColors::kBlue, SkBlendMode::kSrc);
display_item_list->EndPaintOfUnpaired(rect);
display_item_list->Finalize();
gfx::ColorSpace target_color_space = gfx::ColorSpace::CreateXYZD50();
RasterOptions options(rect.size());
options.target_color_params.color_space = target_color_space;
SkBitmap expected = MakeSolidColorBitmap(
rect.size(), SkColor4f::FromColor(SkColorSetARGB(255, 38, 15, 221)));
auto actual = Raster(display_item_list, options);
ExpectEquals(actual, expected);
}
TEST_F(OopPixelTest, DrawRect) {
gfx::Rect rect(10, 10);
auto color_paint = [](int r, int g, int b) {
PaintFlags flags;
flags.setColor(SkColorSetARGB(255, r, g, b));
return flags;
};
std::vector<std::pair<SkRect, PaintFlags>> input = {
{SkRect::MakeXYWH(0, 0, 5, 5), color_paint(0, 0, 255)},
{SkRect::MakeXYWH(5, 0, 5, 5), color_paint(0, 255, 0)},
{SkRect::MakeXYWH(0, 5, 5, 5), color_paint(0, 255, 255)},
{SkRect::MakeXYWH(5, 5, 5, 5), color_paint(255, 0, 0)}};
auto display_item_list = base::MakeRefCounted<DisplayItemList>();
for (auto& op : input) {
display_item_list->StartPaint();
display_item_list->push<DrawRectOp>(op.first, op.second);
display_item_list->EndPaintOfUnpaired(
gfx::ToEnclosingRect(gfx::SkRectToRectF(op.first)));
}
display_item_list->Finalize();
auto actual = Raster(std::move(display_item_list), rect.size());
std::vector<SkPMColor> expected_pixels(rect.width() * rect.height());
for (int h = 0; h < rect.height(); ++h) {
auto start = expected_pixels.begin() + h * rect.width();
SkPMColor left_color = SkPreMultiplyColor(
h < 5 ? input[0].second.getColor() : input[2].second.getColor());
SkPMColor right_color = SkPreMultiplyColor(
h < 5 ? input[1].second.getColor() : input[3].second.getColor());
std::fill(start, start + 5, left_color);
std::fill(start + 5, start + 10, right_color);
}
SkBitmap expected;
expected.installPixels(
SkImageInfo::MakeN32Premul(rect.width(), rect.height()),
expected_pixels.data(), rect.width() * sizeof(SkPMColor));
ExpectEquals(actual, expected);
}
TEST_F(OopPixelTest, DrawRecordPaintFilterTranslatedBounds) {
gfx::Size output_size(10, 10);
PaintFlags internal_flags;
internal_flags.setColor(SkColors::kGreen);
PaintOpBuffer filter_buffer;
filter_buffer.push<DrawRectOp>(
SkRect::MakeLTRB(output_size.width() / 2.f, 0.f, output_size.width(),
output_size.height()),
internal_flags);
sk_sp<RecordPaintFilter> record_filter = sk_make_sp<RecordPaintFilter>(
filter_buffer.ReleaseAsRecord(),
SkRect::MakeLTRB(output_size.width() / 2.f, output_size.height() / 2.f,
output_size.width(), output_size.height()));
PaintFlags record_flags;
record_flags.setImageFilter(record_filter);
auto display_item_list = base::MakeRefCounted<DisplayItemList>();
display_item_list->StartPaint();
display_item_list->push<DrawColorOp>(SkColors::kWhite, SkBlendMode::kSrc);
display_item_list->push<SaveLayerOp>(record_flags);
display_item_list->push<RestoreOp>();
display_item_list->EndPaintOfUnpaired(gfx::Rect(output_size));
display_item_list->Finalize();
SkImageInfo ii =
SkImageInfo::MakeN32Premul(output_size.width(), output_size.height());
SkBitmap expected;
expected.allocPixels(ii, ii.minRowBytes());
expected.eraseColor(SkColors::kWhite);
expected.erase(
SkColors::kGreen.toSkColor(),
SkIRect::MakeLTRB(output_size.width() / 2, output_size.height() / 2,
output_size.width(), output_size.height()));
auto actual = Raster(display_item_list, output_size);
ExpectEquals(actual, expected);
}
TEST_F(OopPixelTest, DrawImage) {
constexpr gfx::Rect rect(100, 100);
sk_sp<SkImage> image = MakeSkImage(rect.size());
const PaintImage::Id kSomeId = 32;
auto builder =
PaintImageBuilder::WithDefault().set_image(image, 0).set_id(kSomeId);
auto paint_image = builder.TakePaintImage();
auto display_item_list = base::MakeRefCounted<DisplayItemList>();
display_item_list->StartPaint();
SkSamplingOptions sampling(
PaintFlags::FilterQualityToSkSamplingOptions(kDefaultFilterQuality));
display_item_list->push<DrawImageOp>(paint_image, 0.f, 0.f, sampling,
nullptr);
display_item_list->EndPaintOfUnpaired(rect);
display_item_list->Finalize();
auto actual = Raster(display_item_list, rect.size());
ExpectEquals(actual, FILE_PATH_LITERAL("oop_draw_image.png"));
}
TEST_F(OopPixelTest, DrawImageAlpha) {
constexpr gfx::Rect rect(100, 100);
auto bitmap_black = MakeSolidColorBitmap(rect.size(), SkColors::kBlack);
const SkColor4f kTestColor = {128.f / 255.f, 0.f, 0.f, 128.f / 255.f};
auto bitmap_premul =
MakeSolidColorBitmap(gfx::Size(50, 50), kTestColor, kPremul_SkAlphaType);
auto bitmap_unpremul = MakeSolidColorBitmap(gfx::Size(50, 50), kTestColor,
kUnpremul_SkAlphaType);
EXPECT_NE(bitmap_premul.getAddr32(0, 0), bitmap_unpremul.getAddr32(0, 0));
const PaintImage::Id kIdBlack = 32;
const PaintImage::Id kIdPremul = 33;
const PaintImage::Id kIdUnpremul = 34;
auto paint_image_black =
PaintImageBuilder::WithDefault()
.set_image(SkImages::RasterFromBitmap(bitmap_black), 0)
.set_id(kIdBlack)
.TakePaintImage();
auto paint_image_premul =
PaintImageBuilder::WithDefault()
.set_image(SkImages::RasterFromBitmap(bitmap_premul), 1)
.set_id(kIdPremul)
.TakePaintImage();
auto paint_image_unpremul =
PaintImageBuilder::WithDefault()
.set_image(SkImages::RasterFromBitmap(bitmap_unpremul), 2)
.set_id(kIdUnpremul)
.TakePaintImage();
auto display_item_list = base::MakeRefCounted<DisplayItemList>();
display_item_list->StartPaint();
SkSamplingOptions sampling(
PaintFlags::FilterQualityToSkSamplingOptions(kDefaultFilterQuality));
display_item_list->push<DrawImageOp>(paint_image_black, 0.f, 0.f, sampling,
nullptr);
display_item_list->push<DrawImageOp>(paint_image_premul, 0.f, 25.f, sampling,
nullptr);
display_item_list->push<DrawImageOp>(paint_image_unpremul, 50.f, 25.f,
sampling, nullptr);
display_item_list->EndPaintOfUnpaired(rect);
display_item_list->Finalize();
auto actual = Raster(display_item_list, rect.size());
ExpectEquals(actual, FILE_PATH_LITERAL("oop_alpha_premul_unpremul.png"));
}
TEST_F(OopPixelTest, DrawImageScaled) {
constexpr gfx::Rect rect(100, 100);
sk_sp<SkImage> image = MakeSkImage(rect.size());
auto builder = PaintImageBuilder::WithDefault().set_image(image, 0).set_id(
PaintImage::GetNextId());
auto paint_image = builder.TakePaintImage();
auto display_item_list = base::MakeRefCounted<DisplayItemList>();
display_item_list->StartPaint();
display_item_list->push<ScaleOp>(0.5f, 0.5f);
SkSamplingOptions sampling(
PaintFlags::FilterQualityToSkSamplingOptions(kDefaultFilterQuality));
display_item_list->push<DrawImageOp>(paint_image, 0.f, 0.f, sampling,
nullptr);
display_item_list->EndPaintOfUnpaired(rect);
display_item_list->Finalize();
auto actual = Raster(display_item_list, rect.size());
ExpectEquals(actual, FILE_PATH_LITERAL("oop_draw_image_scaled.png"));
}
TEST_F(OopPixelTest, DrawImageShaderScaled) {
constexpr gfx::Rect rect(100, 100);
sk_sp<SkImage> image = MakeSkImage(rect.size());
auto builder = PaintImageBuilder::WithDefault().set_image(image, 0).set_id(
PaintImage::GetNextId());
auto paint_image = builder.TakePaintImage();
auto paint_image_shader = PaintShader::MakeImage(
paint_image, SkTileMode::kRepeat, SkTileMode::kRepeat, nullptr);
auto display_item_list = base::MakeRefCounted<DisplayItemList>();
display_item_list->StartPaint();
display_item_list->push<ScaleOp>(0.5f, 0.5f);
PaintFlags flags;
flags.setShader(paint_image_shader);
flags.setFilterQuality(kDefaultFilterQuality);
display_item_list->push<DrawRectOp>(gfx::RectToSkRect(rect), flags);
display_item_list->EndPaintOfUnpaired(rect);
display_item_list->Finalize();
auto actual = Raster(display_item_list, rect.size());
ExpectEquals(actual, FILE_PATH_LITERAL("oop_draw_image_shader_scaled.png"));
}
TEST_F(OopPixelTest, DrawRecordShaderWithImageScaled) {
constexpr gfx::Rect rect(100, 100);
sk_sp<SkImage> image = MakeSkImage(rect.size());
auto builder = PaintImageBuilder::WithDefault().set_image(image, 0).set_id(
PaintImage::GetNextId());
auto paint_image = builder.TakePaintImage();
PaintOpBuffer paint_buffer;
SkSamplingOptions sampling(
PaintFlags::FilterQualityToSkSamplingOptions(kDefaultFilterQuality));
paint_buffer.push<DrawImageOp>(paint_image, 0.f, 0.f, sampling, nullptr);
auto paint_record_shader = PaintShader::MakePaintRecord(
paint_buffer.ReleaseAsRecord(), gfx::RectToSkRect(rect),
SkTileMode::kRepeat, SkTileMode::kRepeat, nullptr);
auto display_item_list = base::MakeRefCounted<DisplayItemList>();
display_item_list->StartPaint();
display_item_list->push<ScaleOp>(0.5f, 0.5f);
PaintFlags raster_flags;
raster_flags.setShader(paint_record_shader);
raster_flags.setFilterQuality(kDefaultFilterQuality);
display_item_list->push<DrawRectOp>(gfx::RectToSkRect(rect), raster_flags);
display_item_list->EndPaintOfUnpaired(rect);
display_item_list->Finalize();
auto actual = Raster(display_item_list, rect.size());
ExpectEquals(actual, FILE_PATH_LITERAL("oop_draw_record_shader.png"));
}
TEST_F(OopPixelTest, DrawRecordShaderTranslatedTileRect) {
int x_offset = 3901;
int y_offset = -234;
PaintFlags internal_flags;
internal_flags.setColor(SkColors::kGreen);
PaintOpBuffer shader_buffer;
shader_buffer.push<DrawRectOp>(SkRect::MakeXYWH(x_offset, y_offset, 1, 2),
internal_flags);
SkRect tile_rect = SkRect::MakeXYWH(x_offset, y_offset, 2, 3);
sk_sp<PaintShader> paint_record_shader = PaintShader::MakePaintRecord(
shader_buffer.ReleaseAsRecord(), tile_rect, SkTileMode::kRepeat,
SkTileMode::kRepeat, nullptr,
PaintShader::ScalingBehavior::kRasterAtScale);
paint_record_shader->set_has_animated_images(true);
gfx::Size output_size(10, 10);
auto display_item_list = base::MakeRefCounted<DisplayItemList>();
display_item_list->StartPaint();
display_item_list->push<DrawColorOp>(SkColors::kWhite, SkBlendMode::kSrc);
display_item_list->push<ScaleOp>(2.f, 2.f);
PaintFlags raster_flags;
raster_flags.setShader(paint_record_shader);
SkRect offset_rect = SkRect::MakeXYWH(2, 1, 10, 10);
display_item_list->push<DrawRectOp>(offset_rect, raster_flags);
display_item_list->EndPaintOfUnpaired(gfx::Rect(output_size));
display_item_list->Finalize();
auto actual = Raster(display_item_list, output_size);
ExpectEquals(actual, FILE_PATH_LITERAL("oop_draw_record_shader_tiled.png"));
}
TEST_F(OopPixelTest, DrawImageWithTargetColorSpace) {
constexpr gfx::Rect rect(100, 100);
sk_sp<SkImage> image = MakeSkImage(rect.size());
const PaintImage::Id kSomeId = 32;
auto builder =
PaintImageBuilder::WithDefault().set_image(image, 0).set_id(kSomeId);
auto paint_image = builder.TakePaintImage();
auto display_item_list = base::MakeRefCounted<DisplayItemList>();
display_item_list->StartPaint();
SkSamplingOptions sampling(
PaintFlags::FilterQualityToSkSamplingOptions(kDefaultFilterQuality));
display_item_list->push<DrawImageOp>(paint_image, 0.f, 0.f, sampling,
nullptr);
display_item_list->EndPaintOfUnpaired(rect);
display_item_list->Finalize();
RasterOptions options(rect.size());
options.target_color_params.color_space =
gfx::ColorSpace::CreateDisplayP3D65();
auto actual = Raster(display_item_list, options);
#if BUILDFLAG(IS_ANDROID)
FuzzyPixelOffByOneComparator comparator;
#else
ExactPixelComparator comparator;
#endif
ExpectEquals(actual, FILE_PATH_LITERAL("oop_image_target_color_space.png"),
comparator);
EXPECT_NE(actual.getColor(0, 0), SkColors::kMagenta.toSkColor());
}
TEST_F(OopPixelTest, DrawGainmapImage) {
constexpr gfx::Size kSize(8, 8);
constexpr gfx::Rect kRect(kSize);
const float kDegamma = 2.2f;
const float kGamma = 1 / kDegamma;
auto base_color_space =
SkColorSpace::MakeRGB(SkNamedTransferFn::k2Dot2, SkNamedGamut::kSRGB);
auto base_info = SkImageInfo::MakeN32Premul(kSize.width(), kSize.height(),
base_color_space);
auto base_image_generator = sk_make_sp<FakePaintImageGenerator>(base_info);
const float kBaseLinear = 0.5f;
const float kBaseSignal = std::pow(kBaseLinear, kGamma);
{
SkBitmap bitmap;
bitmap.installPixels(base_image_generator->GetPixmap());
SkCanvas canvas(bitmap, SkSurfaceProps{});
SkColor4f color{kBaseSignal, kBaseSignal, kBaseSignal, 1.f};
canvas.drawColor(color);
}
const float kHeadroom = 2.f;
const float kRatioMax = 4.f;
SkGainmapInfo gainmap_info;
auto gain_info = SkImageInfo::MakeN32Premul(kSize.width(), kSize.height(),
SkColorSpace::MakeSRGB());
auto gain_image_generator = sk_make_sp<FakePaintImageGenerator>(gain_info);
{
gainmap_info.fDisplayRatioSdr = 1.f;
gainmap_info.fDisplayRatioHdr = kHeadroom;
gainmap_info.fEpsilonSdr = {0.f, 0.f, 0.f, 1.f};
gainmap_info.fEpsilonHdr = {0.f, 0.f, 0.f, 1.f};
gainmap_info.fGainmapRatioMin = {1.f, 1.f, 1.f, 1.f};
gainmap_info.fGainmapRatioMax = {kRatioMax, kRatioMax, kRatioMax, 1.f};
}
{
SkBitmap bitmap;
bitmap.installPixels(gain_image_generator->GetPixmap());
SkCanvas canvas(bitmap, SkSurfaceProps{});
SkColor4f color{0.f, std::log(kHeadroom) / std::log(kRatioMax), 1.f, 1.f};
canvas.drawColor(color);
}
static int counter = 0;
const PaintImage::Id kSomeId = 32 + counter++;
auto paint_image =
PaintImageBuilder::WithDefault()
.set_id(kSomeId)
.set_paint_image_generator(base_image_generator)
.set_gainmap_paint_image_generator(gain_image_generator, gainmap_info)
.TakePaintImage();
auto display_item_list = base::MakeRefCounted<DisplayItemList>();
display_item_list->StartPaint();
SkSamplingOptions sampling =
SkSamplingOptions(SkCubicResampler::CatmullRom());
display_item_list->push<DrawImageOp>(paint_image, 0.f, 0.f, sampling,
nullptr);
display_item_list->EndPaintOfUnpaired(kRect);
display_item_list->Finalize();
float kEps = 16.f / 255.f;
const float kDestScale = kHeadroom;
auto dest_color_space = SkColorSpace::MakeRGB(
skia::ScaleTransferFunction(SkNamedTransferFn::k2Dot2, kDestScale),
SkNamedGamut::kSRGB);
{
RasterOptions options(kSize);
options.target_color_params.color_space =
gfx::ColorSpace(*dest_color_space);
options.target_color_params.hdr_headroom = 0.f;
auto result = Raster(display_item_list, options);
auto out_color = result.getColor4f(0, 0);
EXPECT_NEAR(out_color.fR, std::pow(kBaseLinear / kDestScale, kGamma), kEps);
EXPECT_NEAR(out_color.fG, std::pow(kBaseLinear / kDestScale, kGamma), kEps);
EXPECT_NEAR(out_color.fB, std::pow(kBaseLinear / kDestScale, kGamma), kEps);
}
{
RasterOptions options(kSize);
options.target_color_params.color_space =
gfx::ColorSpace(*dest_color_space);
options.target_color_params.hdr_headroom = std::log2(kDestScale);
auto result = Raster(display_item_list, options);
auto out_color = result.getColor4f(0, 0);
EXPECT_NEAR(out_color.fR, std::pow(0.5f / kDestScale, kGamma), kEps);
EXPECT_NEAR(out_color.fG, std::pow(1.0f / kDestScale, kGamma), kEps);
EXPECT_NEAR(out_color.fB, std::pow(2.0f / kDestScale, kGamma), kEps);
}
}
TEST_F(OopPixelTest, DrawGainmapImageCubic) {
constexpr uint32_t kSrcSize = 2;
constexpr uint32_t kDstSize = 4;
const float kRatioMax = 4.f;
SkGainmapInfo gainmap_info = {
{1.f, 1.f, 1.f, 1.f},
{kRatioMax, kRatioMax, kRatioMax, 1.f},
{1.f, 1.f, 1.f, 1.f},
{0.f, 0.f, 0.f, 1.f},
{0.f, 0.f, 0.f, 1.f},
1.f,
kRatioMax,
SkGainmapInfo::BaseImageType::kSDR,
SkGainmapInfo::Type::kDefault,
nullptr,
};
auto info = SkImageInfo::MakeN32Premul(kSrcSize, kSrcSize,
SkColorSpace::MakeSRGBLinear());
std::array<sk_sp<FakePaintImageGenerator>, 2> generators;
std::array<std::array<uint8_t, 2>, 2> pixel_values = {
{{100, 20},
{static_cast<uint8_t>(
std::round(255.f * std::log(2.f) / std::log(kRatioMax))),
static_cast<uint8_t>(
std::round(255.f * std::log(3.f) / std::log(kRatioMax)))}}};
for (int i = 0; i < 2; ++i) {
generators[i] = sk_make_sp<FakePaintImageGenerator>(info);
SkPixmap pm = generators[i]->GetPixmap();
for (size_t x = 0; x < kSrcSize; ++x) {
for (size_t y = 0; y < kSrcSize; ++y) {
uint32_t* pixel = pm.writable_addr32(x, y);
uint8_t v = pixel_values[i][x];
*pixel = SkColorSetARGB(255, v, v, v);
}
}
}
static int counter = 0;
const PaintImage::Id kSomeId = 32 + counter++;
auto paint_image =
PaintImageBuilder::WithDefault()
.set_id(kSomeId)
.set_paint_image_generator(generators[0])
.set_gainmap_paint_image_generator(generators[1], gainmap_info)
.TakePaintImage();
auto display_item_list = base::MakeRefCounted<DisplayItemList>();
{
display_item_list->StartPaint();
display_item_list->push<DrawImageRectOp>(
paint_image, SkRect::MakeWH(kSrcSize, kSrcSize),
SkRect::MakeWH(kDstSize, kDstSize),
SkSamplingOptions(SkCubicResampler::CatmullRom()), nullptr,
SkCanvas::kStrict_SrcRectConstraint);
display_item_list->EndPaintOfUnpaired(gfx::Rect(0, 0, kDstSize, kDstSize));
display_item_list->Finalize();
}
RasterOptions options(gfx::Size(kDstSize, kDstSize));
{
auto dest_color_space = SkColorSpace::MakeSRGBLinear();
options.target_color_params.color_space =
gfx::ColorSpace(*dest_color_space);
options.target_color_params.hdr_headroom = std::log2(kRatioMax);
}
auto result = Raster(display_item_list, options);
for (int i = 0; i < 4; ++i) {
float base_value =
((3.f - i) * pixel_values[0][0] + i * pixel_values[0][1]) /
(255.f * 3.f);
float gain_value =
((3.f - i) * pixel_values[1][0] + i * pixel_values[1][1]) /
(255.f * 3.f);
float expected = base_value * std::exp(gain_value * std::log(kRatioMax));
float actual = result.getColor4f(i, 0).fR;
float kEpsilon = 16.f / 255.f;
EXPECT_NEAR(expected, actual, kEpsilon);
}
}
TEST_F(OopPixelTest, DrawGainmapImageFiltering) {
constexpr gfx::Size kSize(4, 4);
constexpr gfx::Rect kRect(kSize);
constexpr gfx::Size kGainSize(2, 2);
float kValueBase = 0.25f;
auto base_info = SkImageInfo::MakeN32Premul(kSize.width(), kSize.height(),
SkColorSpace::MakeSRGB());
auto base_image_generator = sk_make_sp<FakePaintImageGenerator>(base_info);
{
SkBitmap bitmap;
bitmap.installPixels(base_image_generator->GetPixmap());
SkCanvas canvas(bitmap, SkSurfaceProps{});
SkPaint paint;
paint.setColor({kValueBase, kValueBase, kValueBase, 1.f},
SkColorSpace::MakeSRGBLinear().get());
canvas.drawRect(SkRect(0, 0, 4, 4), paint);
}
const float kRatioMax = 4.f;
auto gain_info = SkImageInfo::MakeN32Premul(
kGainSize.width(), kGainSize.height(), SkColorSpace::MakeSRGBLinear());
auto gain_image_generator = sk_make_sp<FakePaintImageGenerator>(gain_info);
SkGainmapInfo gainmap_info = {
{1.f, 1.f, 1.f, 1.f},
{kRatioMax, kRatioMax, kRatioMax, 1.f},
{1.f, 1.f, 1.f, 1.f},
{0.f, 0.f, 0.f, 1.f},
{0.f, 0.f, 0.f, 1.f},
1.f,
kRatioMax,
SkGainmapInfo::BaseImageType::kSDR,
SkGainmapInfo::Type::kDefault,
nullptr,
};
float kGainValue0 = std::log(2.f) / std::log(kRatioMax);
float kGainValue1 = std::log(3.f) / std::log(kRatioMax);
{
SkBitmap bitmap;
bitmap.installPixels(gain_image_generator->GetPixmap());
SkCanvas canvas(bitmap, SkSurfaceProps{});
SkPaint paint;
paint.setColor({kGainValue0, kGainValue0, kGainValue0, 1.f});
canvas.drawRect(SkRect::MakeXYWH(0, 0, 1, 2), paint);
paint.setColor({kGainValue1, kGainValue1, kGainValue1, 1.f});
canvas.drawRect(SkRect::MakeXYWH(1, 0, 1, 2), paint);
}
static int counter = 0;
const PaintImage::Id kSomeId = 32 + counter++;
auto paint_image =
PaintImageBuilder::WithDefault()
.set_id(kSomeId)
.set_paint_image_generator(base_image_generator)
.set_gainmap_paint_image_generator(gain_image_generator, gainmap_info)
.TakePaintImage();
auto display_item_list = base::MakeRefCounted<DisplayItemList>();
{
display_item_list->StartPaint();
display_item_list->push<DrawImageOp>(
paint_image, 0.f, 0.f, SkSamplingOptions(SkFilterMode::kNearest),
nullptr);
display_item_list->EndPaintOfUnpaired(kRect);
display_item_list->Finalize();
}
RasterOptions options(kSize);
{
auto dest_color_space = SkColorSpace::MakeSRGBLinear();
options.target_color_params.color_space =
gfx::ColorSpace(*dest_color_space);
options.target_color_params.hdr_headroom = std::log2(kRatioMax);
}
auto result = Raster(display_item_list, options);
for (int i = 0; i < 4; ++i) {
float gain_value = ((3.f - i) * kGainValue0 + i * kGainValue1) / 3.f;
float expected = kValueBase * std::exp(gain_value * std::log(kRatioMax));
float actual = result.getColor4f(i, 0).fR;
float kEpsilon = 16.f / 255.f;
EXPECT_NEAR(expected, actual, kEpsilon);
}
}
TEST_F(OopPixelTest, DrawHdrImageWithMetadata) {
constexpr gfx::Size kSize(8, 8);
constexpr gfx::Rect kRect(kSize);
constexpr float kContentAvgNits = 100;
#if BUILDFLAG(IS_ANDROID)
constexpr float kEpsilon = 1 / 16.f;
#elif BUILDFLAG(IS_IOS) && BUILDFLAG(SKIA_USE_METAL)
constexpr float kEpsilon = 1 / 12.f;
#else
constexpr float kEpsilon = 1 / 32.f;
#endif
const auto make_image = [&](float pixel_value) {
SkBitmap bitmap;
bitmap.allocPixelsFlags(
SkImageInfo::MakeN32Premul(kSize.width(), kSize.height(),
SkColorSpace::MakeSRGB()),
SkBitmap::kZeroPixels_AllocFlag);
SkCanvas canvas(bitmap, SkSurfaceProps{});
SkColor4f color{pixel_value, pixel_value, pixel_value, 1.f};
canvas.drawColor(color);
return SkImages::RasterFromBitmap(bitmap)->reinterpretColorSpace(
gfx::ColorSpace::CreateHDR10().ToSkColorSpace());
};
sk_sp<SkImage> image_500_nits = make_image(0.6765848107833876f);
sk_sp<SkImage> image_250_nits = make_image(0.6025591549907524f);
const auto make_display_item_list = [&](sk_sp<SkImage> image,
std::optional<float> peak_luminance =
std::nullopt,
std::optional<float> white_luminance =
std::nullopt,
PaintFlags* paint_flags = nullptr) {
auto image_generator =
sk_make_sp<FakePaintImageGenerator>(image->imageInfo());
{
ImageHeaderMetadata image_metadata;
image_metadata.hdr_metadata.emplace();
if (peak_luminance.has_value()) {
image_metadata.hdr_metadata->cta_861_3.emplace(peak_luminance.value(),
kContentAvgNits);
}
if (white_luminance.has_value()) {
image_metadata.hdr_metadata->ndwl.emplace(white_luminance.value());
}
image_generator->SetImageHeaderMetadata(image_metadata);
EXPECT_TRUE(image->peekPixels(&image_generator->GetPixmap()));
}
static int id_counter = 0;
const PaintImage::Id kSomeId = 32 + id_counter++;
auto paint_image = PaintImageBuilder::WithDefault()
.set_id(kSomeId)
.set_paint_image_generator(image_generator)
.TakePaintImage();
auto display_item_list = base::MakeRefCounted<DisplayItemList>();
display_item_list->StartPaint();
SkSamplingOptions sampling(
PaintFlags::FilterQualityToSkSamplingOptions(kDefaultFilterQuality));
display_item_list->push<DrawImageOp>(paint_image, 0.f, 0.f, sampling,
paint_flags);
display_item_list->EndPaintOfUnpaired(kRect);
display_item_list->Finalize();
return display_item_list;
};
RasterOptions options(kSize);
options.target_color_params.color_space = gfx::ColorSpace::CreateSRGBLinear();
{
constexpr float kExpected = 1.0;
auto actual =
Raster(make_display_item_list(image_500_nits, 500.f), options);
auto color = actual.getColor4f(0, 0);
EXPECT_NEAR(color.fR, kExpected, kEpsilon);
EXPECT_NEAR(color.fG, kExpected, kEpsilon);
EXPECT_NEAR(color.fB, kExpected, kEpsilon);
}
constexpr float kExpected10kToSdr = 0.7114198123454021f;
{
auto actual =
Raster(make_display_item_list(image_500_nits, 10000.f), options);
auto color = actual.getColor4f(0, 0);
EXPECT_NEAR(color.fR, kExpected10kToSdr, kEpsilon);
EXPECT_NEAR(color.fG, kExpected10kToSdr, kEpsilon);
EXPECT_NEAR(color.fB, kExpected10kToSdr, kEpsilon);
}
{
auto actual = Raster(
make_display_item_list(image_250_nits, 10000.f / 2.f, 203.f / 2.f),
options);
auto color = actual.getColor4f(0, 0);
EXPECT_NEAR(color.fR, kExpected10kToSdr, kEpsilon);
EXPECT_NEAR(color.fG, kExpected10kToSdr, kEpsilon);
EXPECT_NEAR(color.fB, kExpected10kToSdr, kEpsilon);
}
{
constexpr float kExpected = 0.933675419515227f;
constexpr float kDstHeadroom = 1.5f;
options.target_color_params.hdr_headroom = std::log2(kDstHeadroom);
auto actual =
Raster(make_display_item_list(image_500_nits, 10000.f), options);
auto color = actual.getColor4f(0, 0);
EXPECT_NEAR(color.fR, kExpected, kEpsilon);
EXPECT_NEAR(color.fG, kExpected, kEpsilon);
EXPECT_NEAR(color.fB, kExpected, kEpsilon);
}
{
PaintFlags sdr_paint_flags;
sdr_paint_flags.setDynamicRangeLimit(PaintFlags::DynamicRangeLimitMixture(
PaintFlags::DynamicRangeLimit::kStandard));
scoped_refptr<DisplayItemList> display_item_list_10k_nits_sdr =
make_display_item_list(image_500_nits, 10000.f, std::nullopt,
&sdr_paint_flags);
auto actual = Raster(display_item_list_10k_nits_sdr, options);
auto color = actual.getColor4f(0, 0);
EXPECT_NEAR(color.fR, kExpected10kToSdr, kEpsilon);
EXPECT_NEAR(color.fG, kExpected10kToSdr, kEpsilon);
EXPECT_NEAR(color.fB, kExpected10kToSdr, kEpsilon);
}
}
TEST_F(OopPixelTest, DrawImageWithSourceColorSpace) {
constexpr gfx::Rect rect(100, 100);
auto color_space = gfx::ColorSpace::CreateDisplayP3D65().ToSkColorSpace();
sk_sp<SkImage> image = MakeSkImage(rect.size(), color_space);
const PaintImage::Id kSomeId = 32;
auto builder =
PaintImageBuilder::WithDefault().set_image(image, 0).set_id(kSomeId);
auto paint_image = builder.TakePaintImage();
EXPECT_EQ(paint_image.color_space(), color_space.get());
auto display_item_list = base::MakeRefCounted<DisplayItemList>();
display_item_list->StartPaint();
SkSamplingOptions sampling(
PaintFlags::FilterQualityToSkSamplingOptions(kDefaultFilterQuality));
display_item_list->push<DrawImageOp>(paint_image, 0.f, 0.f, sampling,
nullptr);
display_item_list->EndPaintOfUnpaired(rect);
display_item_list->Finalize();
RasterOptions options(rect.size());
auto actual = Raster(display_item_list, options);
#if BUILDFLAG(IS_ANDROID)
auto comparator = FuzzyPixelComparator()
.SetErrorPixelsPercentageLimit(100.0f)
.SetAvgAbsErrorLimit(1.2f)
.SetAbsErrorLimit(2);
#else
ExactPixelComparator comparator;
#endif
ExpectEquals(actual,
FILE_PATH_LITERAL("oop_draw_image_source_color_space.png"),
comparator);
}
TEST_F(OopPixelTest, DrawImageWithSourceAndTargetColorSpace) {
constexpr gfx::Rect rect(100, 100);
auto color_space = gfx::ColorSpace::CreateXYZD50().ToSkColorSpace();
sk_sp<SkImage> image = MakeSkImage(rect.size(), color_space);
const PaintImage::Id kSomeId = 32;
auto builder =
PaintImageBuilder::WithDefault().set_image(image, 0).set_id(kSomeId);
auto paint_image = builder.TakePaintImage();
EXPECT_EQ(paint_image.color_space(), color_space.get());
auto display_item_list = base::MakeRefCounted<DisplayItemList>();
display_item_list->StartPaint();
SkSamplingOptions sampling(
PaintFlags::FilterQualityToSkSamplingOptions(kDefaultFilterQuality));
display_item_list->push<DrawImageOp>(paint_image, 0.f, 0.f, sampling,
nullptr);
display_item_list->EndPaintOfUnpaired(rect);
display_item_list->Finalize();
RasterOptions options(rect.size());
options.target_color_params.color_space =
gfx::ColorSpace::CreateDisplayP3D65();
auto actual = Raster(display_item_list, options);
#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_IOS)
FuzzyPixelOffByOneComparator comparator;
#else
ExactPixelComparator comparator;
#endif
ExpectEquals(actual, FILE_PATH_LITERAL("oop_draw_image_both_color_space.png"),
comparator);
}
#if BUILDFLAG(IS_FUCHSIA) && defined(ARCH_CPU_ARM64)
#define MAYBE_DrawImageReinterpretedAsSRGB DISABLED_DrawImageReinterpretedAsSRGB
#else
#define MAYBE_DrawImageReinterpretedAsSRGB DrawImageReinterpretedAsSRGB
#endif
TEST_F(OopPixelTest, MAYBE_DrawImageReinterpretedAsSRGB) {
constexpr gfx::Rect rect(100, 100);
auto image_color_space = gfx::ColorSpace::CreateHDR10().ToSkColorSpace();
sk_sp<SkImage> image = MakeSkImage(rect.size());
image = image->reinterpretColorSpace(image_color_space);
const PaintImage::Id kSomeId = 32;
auto builder = PaintImageBuilder::WithDefault()
.set_image(image, 0)
.set_id(kSomeId)
.set_reinterpret_as_srgb(true);
auto paint_image = builder.TakePaintImage();
auto display_item_list = base::MakeRefCounted<DisplayItemList>();
display_item_list->StartPaint();
SkSamplingOptions sampling(
PaintFlags::FilterQualityToSkSamplingOptions(kDefaultFilterQuality));
PaintFlags flags;
display_item_list->push<DrawImageOp>(paint_image, 0.f, 0.f, sampling, &flags);
display_item_list->EndPaintOfUnpaired(rect);
display_item_list->Finalize();
RasterOptions options(rect.size());
options.target_color_params.color_space =
gfx::ColorSpace::CreateDisplayP3D65();
auto actual = Raster(display_item_list, options);
#if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_IOS)
FuzzyPixelOffByOneComparator comparator;
#else
ExactPixelComparator comparator;
#endif
ExpectEquals(actual, FILE_PATH_LITERAL("oop_image_target_color_space.png"),
comparator);
}
TEST_F(OopPixelTest, DrawImageWithSetMatrix) {
constexpr gfx::Rect rect(100, 100);
sk_sp<SkImage> image = MakeSkImage(rect.size());
const PaintImage::Id kSomeId = 32;
auto builder =
PaintImageBuilder::WithDefault().set_image(image, 0).set_id(kSomeId);
auto paint_image = builder.TakePaintImage();
auto display_item_list = base::MakeRefCounted<DisplayItemList>();
display_item_list->StartPaint();
SkSamplingOptions sampling(
PaintFlags::FilterQualityToSkSamplingOptions(kDefaultFilterQuality));
display_item_list->push<SetMatrixOp>(SkM44::Scale(0.5f, 0.5f));
display_item_list->push<DrawImageOp>(paint_image, 0.f, 0.f, sampling,
nullptr);
display_item_list->EndPaintOfUnpaired(rect);
display_item_list->Finalize();
auto actual = Raster(display_item_list, rect.size());
ExpectEquals(actual, FILE_PATH_LITERAL("oop_draw_image_matrix.png"));
}
namespace {
class TestMailboxBacking : public TextureBacking {
public:
explicit TestMailboxBacking(gpu::Mailbox mailbox, SkImageInfo info)
: mailbox_(mailbox), info_(info) {}
const SkImageInfo& GetSkImageInfo() override { return info_; }
gpu::Mailbox GetMailbox() const override { return mailbox_; }
sk_sp<SkImage> GetSkImageViaReadback() override { return nullptr; }
bool readPixels(const SkImageInfo& dstInfo,
void* dstPixels,
size_t dstRowBytes,
int srcX,
int srcY) override {
return false;
}
private:
gpu::Mailbox mailbox_;
SkImageInfo info_;
};
}
TEST_F(OopPixelTest, DrawMailboxBackedImage) {
RasterOptions options(gfx::Size(16, 16));
options.image_provider_raster_mode = PlaybackImageProvider::RasterMode::kGpu;
SkImageInfo backing_info = SkImageInfo::MakeN32Premul(
options.resource_size.width(), options.resource_size.height());
SkBitmap expected_bitmap;
expected_bitmap.allocPixels(backing_info);
SkCanvas canvas(expected_bitmap, SkSurfaceProps{});
canvas.drawColor(SkColors::kMagenta);
SkPaint green;
green.setColor(SkColors::kGreen);
canvas.drawRect(SkRect::MakeXYWH(1, 2, 3, 4), green);
auto* ri = raster_context_provider_->RasterInterface();
auto* sii = raster_context_provider_->SharedImageInterface();
scoped_refptr<gpu::ClientSharedImage> src_client_si = CreateClientSharedImage(
ri, sii, options, viz::SinglePlaneFormat::kRGBA_8888);
UploadPixels(ri, src_client_si, expected_bitmap.info(), expected_bitmap);
auto src_paint_image =
PaintImageBuilder::WithDefault()
.set_id(PaintImage::GetNextId())
.set_texture_backing(sk_sp<TestMailboxBacking>(new TestMailboxBacking(
src_client_si->mailbox(), backing_info)),
PaintImage::GetNextContentId())
.TakePaintImage();
auto display_item_list = base::MakeRefCounted<DisplayItemList>();
display_item_list->StartPaint();
display_item_list->push<DrawImageOp>(src_paint_image, 0.f, 0.f);
display_item_list->EndPaintOfUnpaired(gfx::Rect(options.resource_size));
display_item_list->Finalize();
auto actual_bitmap = Raster(display_item_list, options);
ExpectEquals(actual_bitmap, expected_bitmap);
}
TEST_F(OopPixelTest, Preclear) {
gfx::Rect rect(10, 10);
auto display_item_list = base::MakeRefCounted<DisplayItemList>();
display_item_list->Finalize();
RasterOptions options;
options.resource_size = rect.size();
options.full_raster_rect = rect;
options.playback_rect = rect;
options.background_color = SkColors::kMagenta;
options.preclear = true;
options.preclear_color = SkColors::kGreen;
auto actual = Raster(display_item_list, options);
auto expected = MakeSolidColorBitmap(rect.size(), SkColors::kGreen);
ExpectEquals(actual, expected);
}
TEST_P(OopClearPixelTest, ClearingOpaqueCorner) {
RasterOptions options;
gfx::Point arbitrary_offset(10, 20);
options.resource_size = gfx::Size(10, 10);
options.full_raster_rect = gfx::Rect(arbitrary_offset, gfx::Size(8, 7));
options.content_size = gfx::Size(options.full_raster_rect.right(),
options.full_raster_rect.bottom());
if (IsPartialRaster()) {
options.playback_rect = gfx::Rect(options.full_raster_rect.x() + 1,
options.full_raster_rect.y() + 1,
options.full_raster_rect.width() - 1,
options.full_raster_rect.height() - 1);
} else {
options.playback_rect = options.full_raster_rect;
}
options.background_color = SkColors::kGreen;
float arbitrary_scale = 0.25f;
options.post_scale = arbitrary_scale;
options.requires_clear = false;
options.preclear = true;
options.preclear_color = SkColors::kRed;
auto display_item_list = MakeNoopDisplayItemList();
auto result = Raster(display_item_list, options);
SkBitmap bitmap;
bitmap.allocPixelsFlags(
SkImageInfo::MakeN32Premul(options.resource_size.width(),
options.resource_size.height()),
SkBitmap::kZeroPixels_AllocFlag);
SkCanvas canvas(bitmap, SkSurfaceProps{});
canvas.drawColor(options.preclear_color);
SkPaint green;
green.setColor(options.background_color);
if (IsPartialRaster()) {
canvas.drawRect(SkRect::MakeXYWH(7, 1, 2, 7), green);
canvas.drawRect(SkRect::MakeXYWH(1, 6, 8, 2), green);
} else {
canvas.drawRect(SkRect::MakeXYWH(7, 0, 2, 8), green);
canvas.drawRect(SkRect::MakeXYWH(0, 6, 9, 2), green);
}
ExpectEquals(result, bitmap);
}
TEST_F(OopPixelTest, ClearingOpaqueCornerExactEdge) {
RasterOptions options;
gfx::Point arbitrary_offset(10, 20);
options.resource_size = gfx::Size(10, 10);
options.full_raster_rect = gfx::Rect(arbitrary_offset, options.resource_size);
options.content_size = gfx::Size(options.full_raster_rect.right(),
options.full_raster_rect.bottom());
options.playback_rect = options.full_raster_rect;
options.background_color = SkColors::kGreen;
float arbitrary_scale = 0.25f;
options.post_scale = arbitrary_scale;
options.requires_clear = false;
options.preclear = true;
options.preclear_color = SkColors::kRed;
auto display_item_list = MakeNoopDisplayItemList();
auto oop_result = Raster(display_item_list, options);
SkBitmap bitmap;
bitmap.allocPixelsFlags(
SkImageInfo::MakeN32Premul(options.resource_size.width(),
options.resource_size.height()),
SkBitmap::kZeroPixels_AllocFlag);
SkCanvas canvas(bitmap, SkSurfaceProps{});
canvas.drawColor(options.preclear_color);
SkPaint green;
green.setColor(options.background_color);
canvas.drawRect(SkRect::MakeXYWH(9, 0, 1, 10), green);
canvas.drawRect(SkRect::MakeXYWH(0, 9, 10, 1), green);
ExpectEquals(oop_result, bitmap);
}
TEST_F(OopPixelTest, ClearingOpaqueCornerPartialRaster) {
RasterOptions options;
options.resource_size = gfx::Size(10, 10);
gfx::Point arbitrary_offset(30, 12);
options.full_raster_rect = gfx::Rect(arbitrary_offset, gfx::Size(8, 7));
options.content_size = gfx::Size(options.full_raster_rect.right(),
options.full_raster_rect.bottom());
options.playback_rect =
gfx::Rect(arbitrary_offset.x() + 5, arbitrary_offset.y() + 3, 2, 3);
options.background_color = SkColors::kGreen;
options.requires_clear = false;
options.preclear = true;
options.preclear_color = SkColors::kRed;
EXPECT_NE(options.playback_rect.right(), options.full_raster_rect.right());
EXPECT_NE(options.playback_rect.bottom(), options.full_raster_rect.bottom());
auto display_item_list = MakeNoopDisplayItemList();
auto oop_result = Raster(display_item_list, options);
SkBitmap bitmap;
bitmap.allocPixelsFlags(
SkImageInfo::MakeN32Premul(options.resource_size.width(),
options.resource_size.height()),
SkBitmap::kZeroPixels_AllocFlag);
SkCanvas canvas(bitmap, SkSurfaceProps{});
canvas.drawColor(options.preclear_color);
ExpectEquals(oop_result, bitmap);
}
TEST_P(OopClearPixelTest, ClearingOpaqueLeftEdge) {
RasterOptions options;
options.resource_size = gfx::Size(10, 10);
int arbitrary_y = 10;
options.full_raster_rect = gfx::Rect(0, arbitrary_y, 3, 10);
options.content_size = gfx::Size(options.full_raster_rect.right() + 1000,
options.full_raster_rect.bottom() + 1000);
if (IsPartialRaster()) {
options.playback_rect = gfx::Rect(options.full_raster_rect.x(),
options.full_raster_rect.y() + 1,
options.full_raster_rect.width() - 1,
options.full_raster_rect.height() - 2);
} else {
options.playback_rect = options.full_raster_rect;
}
options.background_color = SkColors::kGreen;
options.post_translate = gfx::Vector2dF(0.3f, 0.7f);
options.requires_clear = false;
options.preclear = true;
options.preclear_color = SkColors::kRed;
auto display_item_list = MakeNoopDisplayItemList();
auto result = Raster(display_item_list, options);
SkBitmap bitmap;
bitmap.allocPixelsFlags(
SkImageInfo::MakeN32Premul(options.resource_size.width(),
options.resource_size.height()),
SkBitmap::kZeroPixels_AllocFlag);
SkCanvas canvas(bitmap, SkSurfaceProps{});
canvas.drawColor(options.preclear_color);
SkPaint green;
green.setColor(options.background_color);
if (IsPartialRaster()) {
canvas.drawRect(SkRect::MakeXYWH(0, 1, 1, 8), green);
} else {
canvas.drawRect(SkRect::MakeXYWH(0, 0, 1, 10), green);
}
ExpectEquals(result, bitmap);
}
TEST_P(OopClearPixelTest, ClearingOpaqueRightEdge) {
RasterOptions options;
gfx::Point arbitrary_offset(30, 40);
options.resource_size = gfx::Size(10, 10);
options.full_raster_rect = gfx::Rect(arbitrary_offset, gfx::Size(3, 10));
options.content_size = gfx::Size(options.full_raster_rect.right(),
options.full_raster_rect.bottom() + 1000);
if (IsPartialRaster()) {
options.playback_rect = gfx::Rect(options.full_raster_rect.x() + 1,
options.full_raster_rect.y() + 1,
options.full_raster_rect.width() - 1,
options.full_raster_rect.height() - 2);
} else {
options.playback_rect = options.full_raster_rect;
}
options.background_color = SkColors::kGreen;
float arbitrary_scale = 0.25f;
options.post_scale = arbitrary_scale;
options.requires_clear = false;
options.preclear = true;
options.preclear_color = SkColors::kRed;
auto display_item_list = MakeNoopDisplayItemList();
auto result = Raster(display_item_list, options);
SkBitmap bitmap;
bitmap.allocPixelsFlags(
SkImageInfo::MakeN32Premul(options.resource_size.width(),
options.resource_size.height()),
SkBitmap::kZeroPixels_AllocFlag);
SkCanvas canvas(bitmap, SkSurfaceProps{});
canvas.drawColor(options.preclear_color);
SkPaint green;
green.setColor(options.background_color);
if (IsPartialRaster()) {
canvas.drawRect(SkRect::MakeXYWH(2, 1, 2, 8), green);
} else {
canvas.drawRect(SkRect::MakeXYWH(2, 0, 2, 10), green);
}
ExpectEquals(result, bitmap);
}
TEST_P(OopClearPixelTest, ClearingOpaqueTopEdge) {
RasterOptions options;
options.resource_size = gfx::Size(10, 10);
int arbitrary_x = 10;
options.full_raster_rect = gfx::Rect(arbitrary_x, 0, 10, 5);
options.content_size = gfx::Size(options.full_raster_rect.right() + 1000,
options.full_raster_rect.bottom() + 1000);
if (IsPartialRaster()) {
options.playback_rect = gfx::Rect(options.full_raster_rect.x() + 1,
options.full_raster_rect.y(),
options.full_raster_rect.width() - 2,
options.full_raster_rect.height() - 1);
} else {
options.playback_rect = options.full_raster_rect;
}
options.background_color = SkColors::kGreen;
options.post_translate = gfx::Vector2dF(0.3f, 0.7f);
options.requires_clear = false;
options.preclear = true;
options.preclear_color = SkColors::kRed;
auto display_item_list = MakeNoopDisplayItemList();
auto result = Raster(display_item_list, options);
SkBitmap bitmap;
bitmap.allocPixelsFlags(
SkImageInfo::MakeN32Premul(options.resource_size.width(),
options.resource_size.height()),
SkBitmap::kZeroPixels_AllocFlag);
SkCanvas canvas(bitmap, SkSurfaceProps{});
canvas.drawColor(options.preclear_color);
SkPaint green;
green.setColor(options.background_color);
if (IsPartialRaster()) {
canvas.drawRect(SkRect::MakeXYWH(1, 0, 8, 1), green);
} else {
canvas.drawRect(SkRect::MakeXYWH(0, 0, 10, 1), green);
}
ExpectEquals(result, bitmap);
}
TEST_P(OopClearPixelTest, ClearingOpaqueBottomEdge) {
RasterOptions options;
gfx::Point arbitrary_offset(10, 20);
options.resource_size = gfx::Size(10, 10);
options.full_raster_rect = gfx::Rect(arbitrary_offset, gfx::Size(10, 5));
options.content_size = gfx::Size(options.full_raster_rect.right() + 1000,
options.full_raster_rect.bottom());
if (IsPartialRaster()) {
options.playback_rect = gfx::Rect(options.full_raster_rect.x() + 1,
options.full_raster_rect.y() + 1,
options.full_raster_rect.width() - 2,
options.full_raster_rect.height() - 1);
} else {
options.playback_rect = options.full_raster_rect;
}
options.background_color = SkColors::kGreen;
float arbitrary_scale = 0.25f;
options.post_scale = arbitrary_scale;
options.requires_clear = false;
options.preclear = true;
options.preclear_color = SkColors::kRed;
auto display_item_list = MakeNoopDisplayItemList();
auto result = Raster(display_item_list, options);
SkBitmap bitmap;
bitmap.allocPixelsFlags(
SkImageInfo::MakeN32Premul(options.resource_size.width(),
options.resource_size.height()),
SkBitmap::kZeroPixels_AllocFlag);
SkCanvas canvas(bitmap, SkSurfaceProps{});
canvas.drawColor(options.preclear_color);
SkPaint green;
green.setColor(options.background_color);
if (IsPartialRaster()) {
canvas.drawRect(SkRect::MakeXYWH(1, 4, 8, 2), green);
} else {
canvas.drawRect(SkRect::MakeXYWH(0, 4, 10, 2), green);
}
ExpectEquals(result, bitmap);
}
TEST_F(OopPixelTest, ClearingOpaqueInternal) {
RasterOptions options;
gfx::Point arbitrary_offset(35, 12);
options.resource_size = gfx::Size(10, 10);
options.full_raster_rect = gfx::Rect(arbitrary_offset, options.resource_size);
options.content_size = gfx::Size(1000, 1000);
options.playback_rect = options.full_raster_rect;
options.background_color = SkColors::kGreen;
options.post_translate = gfx::Vector2dF(0.3f, 0.7f);
float arbitrary_scale = 1.2345f;
options.post_scale = arbitrary_scale;
options.requires_clear = false;
options.preclear = true;
options.preclear_color = SkColors::kRed;
auto display_item_list = MakeNoopDisplayItemList();
auto oop_result = Raster(display_item_list, options);
SkBitmap bitmap;
bitmap.allocPixelsFlags(
SkImageInfo::MakeN32Premul(options.resource_size.width(),
options.resource_size.height()),
SkBitmap::kZeroPixels_AllocFlag);
SkCanvas canvas(bitmap, SkSurfaceProps{});
canvas.drawColor(options.preclear_color);
ExpectEquals(oop_result, bitmap);
}
TEST_F(OopPixelTest, ClearingTransparentCorner) {
RasterOptions options;
gfx::Point arbitrary_offset(5, 8);
options.resource_size = gfx::Size(10, 10);
options.full_raster_rect = gfx::Rect(arbitrary_offset, gfx::Size(8, 7));
options.content_size = gfx::Size(options.full_raster_rect.right(),
options.full_raster_rect.bottom());
options.playback_rect = options.full_raster_rect;
options.background_color = SkColors::kTransparent;
float arbitrary_scale = 3.7f;
options.post_scale = arbitrary_scale;
options.requires_clear = true;
options.preclear = true;
options.preclear_color = SkColors::kRed;
auto display_item_list = MakeNoopDisplayItemList();
auto oop_result = Raster(display_item_list, options);
SkBitmap bitmap;
bitmap.allocPixelsFlags(
SkImageInfo::MakeN32Premul(options.resource_size.width(),
options.resource_size.height()),
SkBitmap::kZeroPixels_AllocFlag);
SkCanvas canvas(bitmap, SkSurfaceProps{});
canvas.drawColor(SkColors::kTransparent);
ExpectEquals(oop_result, bitmap);
}
TEST_F(OopPixelTest, ClearingTransparentInternalTile) {
RasterOptions options;
gfx::Point arbitrary_offset(100, 200);
options.resource_size = gfx::Size(10, 10);
options.full_raster_rect = gfx::Rect(arbitrary_offset, options.resource_size);
options.content_size = gfx::Size(1000, 1000);
options.playback_rect = options.full_raster_rect;
options.background_color = SkColors::kTransparent;
float arbitrary_scale = 3.7f;
options.post_scale = arbitrary_scale;
options.requires_clear = true;
options.preclear = true;
options.preclear_color = SkColors::kRed;
auto display_item_list = base::MakeRefCounted<DisplayItemList>();
auto oop_result = Raster(display_item_list, options);
SkBitmap bitmap;
bitmap.allocPixelsFlags(
SkImageInfo::MakeN32Premul(options.resource_size.width(),
options.resource_size.height()),
SkBitmap::kZeroPixels_AllocFlag);
SkCanvas canvas(bitmap, SkSurfaceProps{});
canvas.drawColor(SkColors::kTransparent);
ExpectEquals(oop_result, bitmap);
}
TEST_F(OopPixelTest, ClearingTransparentCornerPartialRaster) {
RasterOptions options;
options.resource_size = gfx::Size(10, 10);
gfx::Point arbitrary_offset(30, 12);
options.full_raster_rect = gfx::Rect(arbitrary_offset, gfx::Size(8, 7));
options.content_size = gfx::Size(options.full_raster_rect.right(),
options.full_raster_rect.bottom());
options.playback_rect =
gfx::Rect(arbitrary_offset.x() + 5, arbitrary_offset.y() + 3, 2, 4);
options.background_color = SkColors::kTransparent;
float arbitrary_scale = 0.23f;
options.post_scale = arbitrary_scale;
options.requires_clear = true;
options.preclear = true;
options.preclear_color = SkColors::kRed;
auto display_item_list = MakeNoopDisplayItemList();
auto oop_result = Raster(display_item_list, options);
SkBitmap bitmap;
bitmap.allocPixelsFlags(
SkImageInfo::MakeN32Premul(options.resource_size.width(),
options.resource_size.height()),
SkBitmap::kZeroPixels_AllocFlag);
SkCanvas canvas(bitmap, SkSurfaceProps{});
canvas.drawColor(options.preclear_color);
canvas.translate(-arbitrary_offset.x(), -arbitrary_offset.y());
canvas.clipRect(gfx::RectToSkRect(options.playback_rect));
canvas.drawColor(SkColors::kTransparent, SkBlendMode::kSrc);
ExpectEquals(oop_result, bitmap);
}
TEST_F(OopPixelTest, DrawRectPlaybackRect) {
PaintFlags flags;
flags.setColor(SkColorSetARGB(255, 250, 10, 20));
gfx::Rect draw_rect(3, 1, 8, 9);
auto display_item_list = base::MakeRefCounted<DisplayItemList>();
display_item_list->StartPaint();
display_item_list->push<DrawRectOp>(gfx::RectToSkRect(draw_rect), flags);
display_item_list->EndPaintOfUnpaired(draw_rect);
display_item_list->Finalize();
RasterOptions options;
options.full_raster_rect = gfx::Rect(1, 2, 10, 10);
options.resource_size = options.full_raster_rect.size();
options.content_size = gfx::Size(options.full_raster_rect.right(),
options.full_raster_rect.bottom());
options.playback_rect = gfx::Rect(4, 2, 5, 6);
options.background_color = SkColors::kMagenta;
auto actual = Raster(display_item_list, options);
ExpectEquals(actual, FILE_PATH_LITERAL("oop_draw_rect_playback_rect.png"));
}
TEST_F(OopPixelTest, DrawRectScaleTransformOptions) {
PaintFlags flags;
flags.setColor(SkColorSetARGB(128, 64, 128, 32));
flags.setAntiAlias(true);
gfx::Rect draw_rect(3, 4, 8, 9);
auto display_item_list = base::MakeRefCounted<DisplayItemList>();
display_item_list->StartPaint();
display_item_list->push<DrawRectOp>(gfx::RectToSkRect(draw_rect), flags);
display_item_list->EndPaintOfUnpaired(draw_rect);
display_item_list->Finalize();
RasterOptions options;
options.resource_size = {20, 20};
options.content_size = {25, 25};
options.full_raster_rect = {5, 5, 20, 20};
options.playback_rect = {5, 5, 13, 9};
options.background_color = SkColors::kCyan;
options.post_translate = {0.5f, 0.25f};
options.post_scale = 2.f;
auto actual = Raster(display_item_list, options);
ExpectEquals(actual, FILE_PATH_LITERAL("oop_draw_rect_scale_transform.png"));
}
TEST_F(OopPixelTest, DrawRectTransformOptionsFullRaster) {
PaintFlags flags;
flags.setColor(SkColorSetRGB(64, 128, 32));
flags.setAntiAlias(true);
gfx::Rect draw_rect(0, 0, 19, 19);
auto display_item_list = base::MakeRefCounted<DisplayItemList>();
display_item_list->StartPaint();
display_item_list->push<DrawRectOp>(gfx::RectToSkRect(draw_rect), flags);
display_item_list->EndPaintOfUnpaired(draw_rect);
display_item_list->Finalize();
RasterOptions options;
options.resource_size = {20, 20};
options.content_size = {25, 25};
options.full_raster_rect = {5, 5, 20, 20};
options.playback_rect = {5, 5, 20, 20};
options.preclear = true;
options.preclear_color = SkColors::kRed;
options.post_translate = {0.5f, 0.25f};
options.post_scale = 2.f;
auto actual = Raster(display_item_list, options);
auto expected = MakeSolidColorBitmap(
options.resource_size,
SkColor4f::FromColor(SkColorSetARGB(255, 64, 128, 32)));
ExpectEquals(actual, expected);
}
TEST_F(OopPixelTest, DrawRectQueryMiddleOfDisplayList) {
auto display_item_list = base::MakeRefCounted<DisplayItemList>();
std::vector<SkColor> colors = {
SkColorSetARGB(255, 0, 0, 255), SkColorSetARGB(255, 0, 255, 0),
SkColorSetARGB(255, 0, 255, 255), SkColorSetARGB(255, 255, 0, 0),
SkColorSetARGB(255, 255, 0, 255), SkColorSetARGB(255, 255, 255, 0),
SkColorSetARGB(255, 255, 255, 255)};
for (int i = 0; i < 20; ++i) {
gfx::Rect draw_rect(0, i, 1, 1);
PaintFlags flags;
flags.setColor(colors[i % colors.size()]);
display_item_list->StartPaint();
display_item_list->push<DrawRectOp>(gfx::RectToSkRect(draw_rect), flags);
display_item_list->EndPaintOfUnpaired(draw_rect);
}
display_item_list->Finalize();
RasterOptions options;
options.resource_size = {10, 10};
options.content_size = {20, 20};
options.full_raster_rect = {0, 10, 1, 10};
options.playback_rect = {0, 10, 1, 10};
options.background_color = SkColors::kGray;
options.post_translate = {0.f, 0.f};
options.post_scale = 2.f;
auto actual = Raster(display_item_list, options);
ExpectEquals(actual, FILE_PATH_LITERAL("oop_draw_rect_query.png"),
FuzzyPixelOffByOneComparator());
}
TEST_F(OopPixelTest, DrawRectColorSpace) {
RasterOptions options;
options.resource_size = gfx::Size(100, 100);
options.content_size = options.resource_size;
options.full_raster_rect = gfx::Rect(options.content_size);
options.playback_rect = options.full_raster_rect;
options.target_color_params.color_space =
gfx::ColorSpace::CreateDisplayP3D65();
auto display_item_list = base::MakeRefCounted<DisplayItemList>();
display_item_list->StartPaint();
PaintFlags flags;
flags.setStyle(PaintFlags::kFill_Style);
flags.setColor(SkColors::kGreen);
display_item_list->push<DrawRectOp>(
gfx::RectToSkRect(gfx::Rect(options.resource_size)), flags);
display_item_list->EndPaintOfUnpaired(options.full_raster_rect);
display_item_list->Finalize();
SkBitmap expected = MakeSolidColorBitmap(
options.resource_size,
SkColor4f::FromColor(SkColorSetARGB(255, 117, 251, 76)));
auto actual = Raster(display_item_list, options);
ExpectEquals(actual, expected);
}
sk_sp<SkTextBlob> BuildTextBlob(
sk_sp<SkTypeface> typeface = skia::DefaultTypeface(),
bool use_lcd_text = false) {
if (!typeface) {
typeface = skia::MakeTypefaceFromName("monospace", SkFontStyle());
}
SkFont font;
font.setTypeface(typeface);
font.setHinting(SkFontHinting::kNormal);
font.setSize(8.f);
font.setBaselineSnap(false);
font.setLinearMetrics(true);
if (use_lcd_text) {
font.setSubpixel(true);
font.setEdging(SkFont::Edging::kSubpixelAntiAlias);
}
return SkTextBlob::MakeFromString("Hamburgefons", font);
}
static constexpr SkScalar kTextBlobY = 16.f;
enum class TextBlobStrategy {
kDirect,
kDrawRecord,
kRecordShader,
kRecordFilter
};
enum class FilterStrategy {
kNone,
kPaintFlags,
kSaveLayer
};
enum class MatrixStrategy {
kIdentity,
kScaled,
kComplex,
kPerspective,
};
enum class LCDStrategy { kNo, kYes };
using TextBlobTestConfig = ::testing::
tuple<TextBlobStrategy, FilterStrategy, MatrixStrategy, LCDStrategy>;
class OopTextBlobPixelTest
: public OopPixelTest,
public ::testing::WithParamInterface<TextBlobTestConfig> {
public:
void RunTest() {
RasterOptions options;
options.resource_size = gfx::Size(100, 100);
options.content_size = options.resource_size;
options.full_raster_rect = gfx::Rect(options.content_size);
options.playback_rect = options.full_raster_rect;
options.target_color_params.color_space = gfx::ColorSpace::CreateSRGB();
options.use_lcd_text = UseLcdText();
auto display_item_list = base::MakeRefCounted<DisplayItemList>();
display_item_list->StartPaint();
display_item_list->push<ConcatOp>(GetMatrix());
const bool save_layer =
GetFilterStrategy(GetParam()) == FilterStrategy::kSaveLayer;
sk_sp<PaintFilter> filter = MakeFilter();
if (save_layer) {
PaintFlags layer_flags;
layer_flags.setImageFilter(std::move(filter));
filter = nullptr;
display_item_list->push<SaveLayerOp>(layer_flags);
}
PushDrawOp(display_item_list, std::move(filter));
if (save_layer) {
display_item_list->push<RestoreOp>();
}
display_item_list->EndPaintOfUnpaired(options.full_raster_rect);
display_item_list->Finalize();
auto actual = Raster(display_item_list, options);
auto expected = GetExpected(options.resource_size);
float error_pixels_percentage = 0.f;
int max_abs_error = 0;
#if BUILDFLAG(IS_ANDROID)
const int sdk = base::android::android_info::sdk_int();
if (sdk <= base::android::android_info::SDK_VERSION_MARSHMALLOW) {
error_pixels_percentage = 10.f;
max_abs_error = 20;
} else {
error_pixels_percentage = 1.5f;
max_abs_error = 2;
}
#endif
float avg_error = max_abs_error;
if (GetMatrixStrategy(GetParam()) == MatrixStrategy::kComplex) {
const bool is_record_filter =
GetTextBlobStrategy(GetParam()) == TextBlobStrategy::kRecordFilter;
error_pixels_percentage =
std::max(is_record_filter ? 12.f : 0.2f, error_pixels_percentage);
max_abs_error = std::max(is_record_filter ? 246 : 2, max_abs_error);
avg_error = std::max(is_record_filter ? 59.6f : 2.f, avg_error);
} else if (GetMatrixStrategy(GetParam()) == MatrixStrategy::kPerspective) {
switch (GetTextBlobStrategy(GetParam())) {
case TextBlobStrategy::kRecordFilter:
error_pixels_percentage = std::max(13.f, error_pixels_percentage);
max_abs_error = std::max(255, max_abs_error);
avg_error = std::max(62.4f, avg_error);
break;
case TextBlobStrategy::kRecordShader:
error_pixels_percentage = std::max(19.0f, error_pixels_percentage);
#if BUILDFLAG(IS_ANDROID)
max_abs_error = std::max(237, max_abs_error);
avg_error = std::max(61.4f, avg_error);
#else
max_abs_error = std::max(229, max_abs_error);
avg_error = std::max(40.2f, avg_error);
#endif
break;
default:
error_pixels_percentage = std::max(4.0f, error_pixels_percentage);
max_abs_error = std::max(36, max_abs_error);
avg_error = std::max(36.0f, avg_error);
break;
}
}
auto comparator =
FuzzyPixelComparator()
.SetErrorPixelsPercentageLimit(error_pixels_percentage)
.SetAvgAbsErrorLimit(avg_error)
.SetAbsErrorLimit(max_abs_error);
ExpectEquals(actual, expected, comparator);
}
SkBitmap GetExpected(const gfx::Size& image_size) {
SkBitmap bitmap;
base::WaitableEvent waitable;
auto* gpu_service = viz::TestGpuServiceHolder::GetInstance();
gpu_service->gpu_main_thread_task_runner()->PostTask(
FROM_HERE,
base::BindOnce(&OopTextBlobPixelTest::DrawExpectedOnGpuThread,
base::Unretained(this), image_size, std::ref(bitmap),
std::ref(waitable)));
waitable.Wait();
DCHECK(!bitmap.drawsNothing());
return bitmap;
}
void DrawExpectedOnGpuThread(const gfx::Size& image_size,
SkBitmap& expected,
base::WaitableEvent& waitable) {
auto* gpu_service = viz::TestGpuServiceHolder::GetInstance()->gpu_service();
auto context_state = gpu_service->GetContextState();
ASSERT_TRUE(context_state->MakeCurrent(nullptr));
gpu::raster::GrShaderCache::ScopedCacheUse cache_use(
gpu_service->gr_shader_cache(), gpu::kDisplayCompositorClientId);
SkImageInfo image_info =
SkImageInfo::MakeN32Premul(image_size.width(), image_size.height());
SkSurfaceProps surface_props =
UseLcdText() ? skia::LegacyDisplayGlobals::GetSkSurfaceProps(0)
: SkSurfaceProps(0, kUnknown_SkPixelGeometry);
sk_sp<SkSurface> surface = nullptr;
if (context_state->gr_context()) {
surface = SkSurfaces::RenderTarget(
context_state->gr_context(), skgpu::Budgeted::kNo, image_info, 0,
kTopLeft_GrSurfaceOrigin, &surface_props);
} else if (context_state->graphite_shared_context()) {
surface = SkSurfaces::RenderTarget(
context_state->gpu_main_graphite_recorder(), image_info,
skgpu::Mipmapped::kNo, &surface_props);
} else {
NOTREACHED();
}
SkCanvas* canvas = surface->getCanvas();
canvas->clear(SkColors::kBlack);
DrawExpectedToCanvas(*canvas);
expected.allocPixels(image_info);
bool success = false;
if (context_state->gr_context()) {
context_state->gr_context()->flushAndSubmit(surface.get(),
GrSyncCpu::kNo);
success = surface->readPixels(expected, 0, 0);
} else if (context_state->graphite_shared_context()) {
success = gpu::GraphiteReadPixelsSync(
context_state->graphite_shared_context(),
context_state->gpu_main_graphite_recorder(), surface.get(),
image_info, expected.pixmap().writable_addr(),
expected.pixmap().rowBytes(), 0, 0);
}
ASSERT_TRUE(success);
waitable.Signal();
}
void DrawExpectedToCanvas(SkCanvas& canvas) {
TextBlobTestConfig config = GetParam();
canvas.setMatrix(GetMatrix());
TextBlobStrategy strategy = GetTextBlobStrategy(config);
sk_sp<SkImageFilter> filter;
if (GetFilterStrategy(config) != FilterStrategy::kNone) {
filter =
SkImageFilters::Blur(.1f, .1f, SkTileMode::kDecal, nullptr, nullptr);
}
const bool save_layer =
GetFilterStrategy(config) == FilterStrategy::kSaveLayer;
SkPaint save_paint;
if (save_layer) {
save_paint.setImageFilter(std::move(filter));
filter = nullptr;
canvas.saveLayer(nullptr, &save_paint);
}
SkPaint text_paint;
text_paint.setColor(SkColors::kGreen);
if (filter && (strategy == TextBlobStrategy::kDirect ||
strategy == TextBlobStrategy::kDrawRecord)) {
text_paint.setImageFilter(std::move(filter));
filter = nullptr;
}
auto text_blob = BuildTextBlob(skia::DefaultTypeface(), UseLcdText());
if (strategy == TextBlobStrategy::kDirect) {
canvas.drawTextBlob(std::move(text_blob), 0, kTextBlobY, text_paint);
} else {
SkPictureRecorder recorder;
SkCanvas* record_canvas =
recorder.beginRecording(SkRect::MakeWH(100, 100));
record_canvas->drawTextBlob(std::move(text_blob), 0, kTextBlobY,
text_paint);
sk_sp<SkPicture> recording = recorder.finishRecordingAsPicture();
if (strategy == TextBlobStrategy::kDrawRecord) {
canvas.drawPicture(recording.get());
} else if (strategy == TextBlobStrategy::kRecordShader) {
SkRect shader_rect = SkRect::MakeWH(25, 25);
auto draw_as_shader =
recording->makeShader(SkTileMode::kRepeat, SkTileMode::kRepeat,
SkFilterMode::kLinear, nullptr, &shader_rect);
SkPaint shader_paint;
shader_paint.setShader(std::move(draw_as_shader));
if (filter) {
shader_paint.setImageFilter(std::move(filter));
filter = nullptr;
}
canvas.drawRect(SkRect::MakeWH(50, 50), shader_paint);
} else {
DCHECK_EQ(strategy, TextBlobStrategy::kRecordFilter);
sk_sp<SkImageFilter> draw_as_filter =
SkImageFilters::Picture(std::move(recording));
if (filter) {
draw_as_filter = SkImageFilters::Compose(std::move(filter),
std::move(draw_as_filter));
filter = nullptr;
}
SkPaint filter_paint;
filter_paint.setImageFilter(std::move(draw_as_filter));
canvas.drawRect(SkRect::MakeWH(50, 50), filter_paint);
}
}
if (save_layer)
canvas.restore();
}
sk_sp<PaintFilter> MakeFilter() {
if (GetFilterStrategy(GetParam()) == FilterStrategy::kNone) {
return nullptr;
} else {
return sk_make_sp<BlurPaintFilter>(.1f, .1f, SkTileMode::kDecal, nullptr);
}
}
SkM44 GetMatrix() {
MatrixStrategy strategy = GetMatrixStrategy(GetParam());
SkM44 m;
if (strategy != MatrixStrategy::kIdentity) {
m.preScale(2.0f, 2.0f);
if (strategy == MatrixStrategy::kComplex) {
SkM44 skew = SkM44();
skew.setRC(0, 1, 2.f);
skew.setRC(1, 0, 2.f);
m.preConcat(skew);
} else if (strategy == MatrixStrategy::kPerspective) {
SkM44 persp = SkM44::Perspective(0.01f, 10.f, SK_ScalarPI / 3.f);
persp.preTranslate(0.f, 5.f, -0.1f);
persp.preConcat(SkM44::Rotate({0.f, 1.f, 0.f}, 0.008f ));
m.postConcat(persp);
}
}
return m;
}
void PushDrawOp(scoped_refptr<DisplayItemList> display_list,
sk_sp<PaintFilter> filter) {
TextBlobStrategy strategy = GetTextBlobStrategy(GetParam());
auto text_blob = BuildTextBlob(skia::DefaultTypeface(), UseLcdText());
PaintFlags text_flags;
text_flags.setStyle(PaintFlags::kFill_Style);
text_flags.setColor(SkColors::kGreen);
if (filter && (strategy == TextBlobStrategy::kDirect ||
strategy == TextBlobStrategy::kDrawRecord)) {
text_flags.setImageFilter(std::move(filter));
filter = nullptr;
}
if (strategy == TextBlobStrategy::kDirect) {
display_list->push<DrawTextBlobOp>(std::move(text_blob), 0.0f, kTextBlobY,
text_flags);
return;
}
PaintOpBuffer paint_buffer;
paint_buffer.push<DrawTextBlobOp>(std::move(text_blob), 0.0f, kTextBlobY,
text_flags);
if (strategy == TextBlobStrategy::kDrawRecord) {
display_list->push<DrawRecordOp>(paint_buffer.ReleaseAsRecord());
return;
}
PaintFlags record_flags;
if (strategy == TextBlobStrategy::kRecordShader) {
auto paint_record_shader = PaintShader::MakePaintRecord(
paint_buffer.ReleaseAsRecord(), SkRect::MakeWH(25, 25),
SkTileMode::kRepeat, SkTileMode::kRepeat, nullptr,
PaintShader::ScalingBehavior::kRasterAtScale);
paint_record_shader->set_has_animated_images(true);
record_flags.setShader(paint_record_shader);
record_flags.setImageFilter(std::move(filter));
} else {
DCHECK(strategy == TextBlobStrategy::kRecordFilter);
sk_sp<PaintFilter> paint_record_filter = sk_make_sp<RecordPaintFilter>(
paint_buffer.ReleaseAsRecord(), SkRect::MakeWH(100, 100));
if (filter) {
paint_record_filter = sk_make_sp<ComposePaintFilter>(
std::move(filter), std::move(paint_record_filter));
}
record_flags.setImageFilter(std::move(paint_record_filter));
}
record_flags.setFilterQuality(PaintFlags::FilterQuality::kLow);
display_list->push<DrawRectOp>(SkRect::MakeWH(50, 50), record_flags);
}
static TextBlobStrategy GetTextBlobStrategy(
const TextBlobTestConfig& config) {
return ::testing::get<0>(config);
}
static FilterStrategy GetFilterStrategy(const TextBlobTestConfig& config) {
return ::testing::get<1>(config);
}
static MatrixStrategy GetMatrixStrategy(const TextBlobTestConfig& config) {
return ::testing::get<2>(config);
}
static LCDStrategy GetLCDStrategy(const TextBlobTestConfig& config) {
return ::testing::get<3>(config);
}
bool UseLcdText() const {
return GetLCDStrategy(GetParam()) == LCDStrategy::kYes;
}
static std::string PrintTestName(
const ::testing::TestParamInfo<TextBlobTestConfig>& info) {
std::stringstream ss;
switch (GetTextBlobStrategy(info.param)) {
case TextBlobStrategy::kDirect:
ss << "Direct";
break;
case TextBlobStrategy::kDrawRecord:
ss << "DrawRecord";
break;
case TextBlobStrategy::kRecordShader:
ss << "RecordShader";
break;
case TextBlobStrategy::kRecordFilter:
ss << "RecordFilter";
break;
}
ss << "_";
switch (GetFilterStrategy(info.param)) {
case FilterStrategy::kNone:
ss << "NoFilter";
break;
case FilterStrategy::kPaintFlags:
ss << "FilterOnPaint";
break;
case FilterStrategy::kSaveLayer:
ss << "FilterOnLayer";
break;
}
ss << "_";
switch (GetMatrixStrategy(info.param)) {
case MatrixStrategy::kIdentity:
ss << "IdentityCTM";
break;
case MatrixStrategy::kScaled:
ss << "ScaledCTM";
break;
case MatrixStrategy::kComplex:
ss << "ComplexCTM";
break;
case MatrixStrategy::kPerspective:
ss << "PerspectiveCTM";
break;
}
ss << "_";
switch (GetLCDStrategy(info.param)) {
case LCDStrategy::kNo:
ss << "NoLCD";
break;
case LCDStrategy::kYes:
ss << "LCD";
break;
}
return ss.str();
}
};
TEST_P(OopTextBlobPixelTest, Config) {
RunTest();
}
INSTANTIATE_TEST_SUITE_P(
P,
OopTextBlobPixelTest,
::testing::Combine(::testing::Values(TextBlobStrategy::kDirect,
TextBlobStrategy::kDrawRecord,
TextBlobStrategy::kRecordShader,
TextBlobStrategy::kRecordFilter),
::testing::Values(FilterStrategy::kNone,
FilterStrategy::kPaintFlags,
FilterStrategy::kSaveLayer),
::testing::Values(MatrixStrategy::kIdentity,
MatrixStrategy::kScaled,
MatrixStrategy::kComplex,
MatrixStrategy::kPerspective),
::testing::Values(LCDStrategy::kNo, LCDStrategy::kYes)),
OopTextBlobPixelTest::PrintTestName);
void ClearFontCache(CompletionEvent* event) {
SkGraphics::PurgeFontCache();
event->Signal();
}
TEST_F(OopPixelTest, DrawTextMultipleRasterCHROMIUM) {
RasterOptions options;
options.resource_size = gfx::Size(100, 100);
options.content_size = options.resource_size;
options.full_raster_rect = gfx::Rect(options.content_size);
options.playback_rect = options.full_raster_rect;
options.target_color_params.color_space = gfx::ColorSpace::CreateSRGB();
auto sk_typeface_1 = skia::MakeTypefaceFromName("monospace", SkFontStyle());
auto sk_typeface_2 = skia::MakeTypefaceFromName("roboto", SkFontStyle());
auto display_item_list = base::MakeRefCounted<DisplayItemList>();
display_item_list->StartPaint();
PaintFlags flags;
flags.setStyle(PaintFlags::kFill_Style);
flags.setColor(SkColors::kGreen);
display_item_list->push<DrawTextBlobOp>(BuildTextBlob(sk_typeface_1), 0.0f,
kTextBlobY, flags);
display_item_list->EndPaintOfUnpaired(options.full_raster_rect);
display_item_list->Finalize();
auto display_item_list_2 = base::MakeRefCounted<DisplayItemList>();
display_item_list_2->StartPaint();
display_item_list_2->push<DrawTextBlobOp>(BuildTextBlob(sk_typeface_2), 0.0f,
kTextBlobY, flags);
display_item_list_2->EndPaintOfUnpaired(options.full_raster_rect);
display_item_list_2->Finalize();
options.additional_lists = {display_item_list_2};
Raster(display_item_list, options);
EXPECT_GT(SkGraphics::GetFontCacheUsed(), 0u);
CompletionEvent event;
raster_context_provider_->ExecuteOnGpuThread(
base::BindOnce(&ClearFontCache, &event));
event.Wait();
EXPECT_EQ(SkGraphics::GetFontCacheUsed(), 0u);
}
TEST_F(OopPixelTest, DrawTextBlobPersistentShaderCache) {
RasterOptions options;
options.resource_size = gfx::Size(100, 100);
options.content_size = options.resource_size;
options.full_raster_rect = gfx::Rect(options.content_size);
options.playback_rect = options.full_raster_rect;
options.target_color_params.color_space = gfx::ColorSpace::CreateSRGB();
auto display_item_list = base::MakeRefCounted<DisplayItemList>();
display_item_list->StartPaint();
PaintFlags flags;
flags.setStyle(PaintFlags::kFill_Style);
flags.setColor(SkColors::kGreen);
display_item_list->push<DrawTextBlobOp>(BuildTextBlob(), 0.0f, kTextBlobY,
flags);
display_item_list->EndPaintOfUnpaired(options.full_raster_rect);
display_item_list->Finalize();
auto actual = Raster(display_item_list, options);
SkBitmap expected =
MakeSolidColorBitmap(options.resource_size, SkColors::kBlack);
SkCanvas canvas(expected, SkSurfaceProps{});
canvas.drawColor(SkColors::kBlack);
SkPaint paint;
paint.setColor(SkColors::kGreen);
canvas.drawTextBlob(BuildTextBlob(), 0, kTextBlobY, paint);
auto comparator = FuzzyPixelComparator()
.SetErrorPixelsPercentageLimit(1.0f)
.SetAbsErrorLimit(1);
ExpectEquals(actual, expected, comparator);
InitializeOOPContext();
actual = Raster(display_item_list, options);
ExpectEquals(actual, expected, comparator);
}
TEST_F(OopPixelTest, WritePixels) {
gfx::Size dest_size(10, 10);
RasterOptions options(dest_size);
auto* ri = raster_context_provider_->RasterInterface();
auto* sii = raster_context_provider_->SharedImageInterface();
auto dest_client_si = CreateClientSharedImage(
ri, sii, options, viz::SinglePlaneFormat::kRGBA_8888);
std::vector<SkPMColor> expected_pixels(dest_size.width() * dest_size.height(),
SkPreMultiplyARGB(255, 0, 0, 255));
SkBitmap expected;
expected.installPixels(
SkImageInfo::MakeN32Premul(dest_size.width(), dest_size.height()),
expected_pixels.data(), dest_size.width() * sizeof(SkColor));
gpu::SyncToken sync_token =
UploadPixels(ri, dest_client_si, expected.info(), expected);
auto ri_access =
dest_client_si->BeginRasterAccess(ri, sync_token, true);
SkBitmap actual =
ReadbackMailbox(ri, dest_client_si->mailbox(), options.resource_size);
sync_token = gpu::RasterScopedAccess::EndAccess(std::move(ri_access));
sii->DestroySharedImage(sync_token, std::move(dest_client_si));
ExpectEquals(actual, expected);
}
TEST_F(OopPixelTest, CopySharedImage) {
const gfx::Size size(16, 16);
auto* ri = raster_context_provider_->RasterInterface();
auto* sii = raster_context_provider_->SharedImageInterface();
const gfx::ColorSpace source_color_space = gfx::ColorSpace::CreateSRGB();
const gfx::ColorSpace dest_color_space =
gfx::ColorSpace::CreateDisplayP3D65();
SkBitmap upload_bitmap;
{
upload_bitmap.allocPixels(SkImageInfo::MakeN32Premul(
size.width(), size.height(), source_color_space.ToSkColorSpace()));
SkCanvas canvas(upload_bitmap, SkSurfaceProps{});
SkPaint paint;
paint.setColor(SkColors::kGreen);
canvas.drawRect(SkRect::MakeWH(size.width(), size.height()), paint);
}
scoped_refptr<gpu::ClientSharedImage> source_client_si;
{
RasterOptions options(size);
options.target_color_params.color_space = source_color_space;
source_client_si = CreateClientSharedImage(
ri, sii, options, viz::SinglePlaneFormat::kRGBA_8888);
auto ri_access = source_client_si->BeginRasterAccess(
ri, source_client_si->creation_sync_token(), false);
ri->WritePixels(source_client_si->mailbox(), 0,
0, GL_TEXTURE_2D, upload_bitmap.pixmap());
gpu::RasterScopedAccess::EndAccess(std::move(ri_access));
}
scoped_refptr<gpu::ClientSharedImage> dest_client_si;
{
RasterOptions options(size);
options.target_color_params.color_space = dest_color_space;
dest_client_si = CreateClientSharedImage(
ri, sii, options, viz::SinglePlaneFormat::kRGBA_8888);
auto src_ri_access = source_client_si->BeginRasterAccess(
ri, source_client_si->creation_sync_token(), true);
auto dest_ri_access = dest_client_si->BeginRasterAccess(
ri, dest_client_si->creation_sync_token(), false);
ri->CopySharedImage(source_client_si->mailbox(), dest_client_si->mailbox(),
0, 0, 0, 0, size.width(), size.height());
gpu::RasterScopedAccess::EndAccess(std::move(dest_ri_access));
gpu::RasterScopedAccess::EndAccess(std::move(src_ri_access));
}
SkBitmap readback_bitmap;
{
readback_bitmap.allocPixels(SkImageInfo::MakeN32Premul(
size.width(), size.height(), dest_color_space.ToSkColorSpace()));
ri->ReadbackImagePixels(dest_client_si->mailbox(), readback_bitmap.info(),
readback_bitmap.rowBytes(), 0, 0,
0, readback_bitmap.getPixels());
}
EXPECT_EQ(*upload_bitmap.getAddr32(0, 0), *readback_bitmap.getAddr32(0, 0));
}
#if !BUILDFLAG(IS_ANDROID_EMULATOR)
class OopYUVToRGBPixelTest
: public OopPixelTest,
public ::testing::WithParamInterface<gfx::ColorSpace> {
public:
gfx::ColorSpace DestinationColorSpace() const { return GetParam(); }
};
TEST_P(OopYUVToRGBPixelTest, CopyI420SharedImage) {
const gfx::ColorSpace dest_color_space = DestinationColorSpace();
RasterOptions options(gfx::Size(16, 16));
RasterOptions uv_options(gfx::Size(options.resource_size.width() / 2,
options.resource_size.height() / 2));
auto* ri = raster_context_provider_->RasterInterface();
auto* sii = raster_context_provider_->SharedImageInterface();
auto dest_client_si = CreateClientSharedImage(
ri, sii, options, viz::SinglePlaneFormat::kRGBA_8888, dest_color_space);
scoped_refptr<gpu::ClientSharedImage> yuv_client_si =
CreateClientSharedImage(ri, sii, options, viz::MultiPlaneFormat::kI420,
gfx::ColorSpace::CreateREC709());
SkPixmap pixmaps[SkYUVAInfo::kMaxPlanes] = {};
SkImageInfo y_info = SkImageInfo::Make(
options.resource_size.width(), options.resource_size.height(),
kGray_8_SkColorType, kPremul_SkAlphaType,
options.target_color_params.color_space.ToSkColorSpace());
SkImageInfo uv_info = SkImageInfo::Make(
uv_options.resource_size.width(), uv_options.resource_size.height(),
kGray_8_SkColorType, kPremul_SkAlphaType,
uv_options.target_color_params.color_space.ToSkColorSpace());
SkBitmap y_bitmap;
y_bitmap.allocPixels(y_info);
memset(y_bitmap.getPixels(), 0x1d, y_bitmap.computeByteSize());
pixmaps[0] = SkPixmap(y_info, y_bitmap.getPixels(), y_info.minRowBytes());
SkBitmap u_bitmap;
u_bitmap.allocPixels(uv_info);
memset(u_bitmap.getPixels(), 0xff, u_bitmap.computeByteSize());
pixmaps[1] = SkPixmap(uv_info, u_bitmap.getPixels(), uv_info.minRowBytes());
SkBitmap v_bitmap;
v_bitmap.allocPixels(uv_info);
memset(v_bitmap.getPixels(), 0x6b, v_bitmap.computeByteSize());
pixmaps[2] = SkPixmap(uv_info, v_bitmap.getPixels(), uv_info.minRowBytes());
SkYUVColorSpace yuv_color_space = kIdentity_SkYUVColorSpace;
SkYUVAInfo yuv_info = SkYUVAInfo(
SizeToSkISize(options.resource_size), SkYUVAInfo::PlaneConfig::kY_U_V,
SkYUVAInfo::Subsampling::k420, yuv_color_space);
SkYUVAPixmaps yuv_pixmap =
SkYUVAPixmaps::FromExternalPixmaps(yuv_info, pixmaps);
UploadPixelsYUV(ri, yuv_client_si, yuv_pixmap);
auto src_ri_access = yuv_client_si->BeginRasterAccess(
ri, yuv_client_si->creation_sync_token(), true);
auto dest_ri_access = dest_client_si->BeginRasterAccess(
ri, dest_client_si->creation_sync_token(), false);
ri->CopySharedImage(yuv_client_si->mailbox(), dest_client_si->mailbox(), 0, 0,
0, 0, options.resource_size.width(),
options.resource_size.height());
SkBitmap actual_bitmap =
ReadbackMailbox(ri, dest_client_si->mailbox(), options.resource_size,
dest_color_space.ToSkColorSpace());
SkColor expected_color = SkColorSetARGB(255, 0, 0, 254);
SkBitmap expected_bitmap = MakeSolidColorBitmap(
options.resource_size, SkColor4f::FromColor(expected_color));
auto comparator = FuzzyPixelComparator()
.SetErrorPixelsPercentageLimit(100.0f)
.SetAbsErrorLimit(2);
ExpectEquals(actual_bitmap, expected_bitmap, comparator);
gpu::RasterScopedAccess::EndAccess(std::move(src_ri_access));
gpu::SyncToken sync_token =
gpu::RasterScopedAccess::EndAccess(std::move(dest_ri_access));
sii->DestroySharedImage(sync_token, std::move(dest_client_si));
sii->DestroySharedImage(sync_token, std::move(yuv_client_si));
}
INSTANTIATE_TEST_SUITE_P(
P,
OopYUVToRGBPixelTest,
::testing::Values(gfx::ColorSpace(gfx::ColorSpace::PrimaryID::BT2020,
gfx::ColorSpace::TransferID::SRGB),
gfx::ColorSpace()));
TEST_F(OopPixelTest, CopyNV12SharedImage) {
RasterOptions options(gfx::Size(16, 16));
RasterOptions uv_options(gfx::Size(options.resource_size.width() / 2,
options.resource_size.height() / 2));
auto* ri = raster_context_provider_->RasterInterface();
auto* sii = raster_context_provider_->SharedImageInterface();
scoped_refptr<gpu::ClientSharedImage> dest_client_si =
CreateClientSharedImage(ri, sii, options,
viz::SinglePlaneFormat::kRGBA_8888);
scoped_refptr<gpu::ClientSharedImage> y_uv_client_si =
CreateClientSharedImage(ri, sii, options, viz::MultiPlaneFormat::kNV12,
gfx::ColorSpace::CreateJpeg());
SkPixmap pixmaps[SkYUVAInfo::kMaxPlanes] = {};
SkImageInfo y_info = SkImageInfo::Make(
options.resource_size.width(), options.resource_size.height(),
kGray_8_SkColorType, kPremul_SkAlphaType,
options.target_color_params.color_space.ToSkColorSpace());
SkImageInfo uv_info = SkImageInfo::Make(
uv_options.resource_size.width(), uv_options.resource_size.height(),
kR8G8_unorm_SkColorType, kPremul_SkAlphaType,
uv_options.target_color_params.color_space.ToSkColorSpace());
SkBitmap y_bitmap;
y_bitmap.allocPixels(y_info);
memset(y_bitmap.getPixels(), 0x1d, y_bitmap.computeByteSize());
pixmaps[0] = SkPixmap(y_info, y_bitmap.getPixels(), y_info.minRowBytes());
SkBitmap uv_bitmap;
uv_bitmap.allocPixels(uv_info);
uint8_t* uv_pix = static_cast<uint8_t*>(uv_bitmap.getPixels());
for (size_t i = 0; i < uv_bitmap.computeByteSize(); i += 2) {
uv_pix[i] = 0xff;
uv_pix[i + 1] = 0x6d;
}
pixmaps[1] = SkPixmap(uv_info, uv_bitmap.getPixels(), uv_info.minRowBytes());
SkYUVColorSpace yuv_color_space = kJPEG_SkYUVColorSpace;
SkYUVAInfo yuv_info = SkYUVAInfo(
SizeToSkISize(options.resource_size), SkYUVAInfo::PlaneConfig::kY_UV,
SkYUVAInfo::Subsampling::k420, yuv_color_space);
SkYUVAPixmaps yuv_pixmap =
SkYUVAPixmaps::FromExternalPixmaps(yuv_info, pixmaps);
UploadPixelsYUV(ri, y_uv_client_si, yuv_pixmap);
auto src_ri_access = y_uv_client_si->BeginRasterAccess(
ri, y_uv_client_si->creation_sync_token(), true);
auto dest_ri_access = dest_client_si->BeginRasterAccess(
ri, dest_client_si->creation_sync_token(), false);
ri->CopySharedImage(y_uv_client_si->mailbox(), dest_client_si->mailbox(), 0,
0, 0, 0, options.resource_size.width(),
options.resource_size.height());
SkBitmap actual_bitmap =
ReadbackMailbox(ri, dest_client_si->mailbox(), options.resource_size);
SkBitmap expected_bitmap = MakeSolidColorBitmap(
options.resource_size,
SkColor4f::FromColor(SkColorSetARGB(255, 2, 0, 254)));
ExpectEquals(actual_bitmap, expected_bitmap);
gpu::RasterScopedAccess::EndAccess(std::move(src_ri_access));
gpu::SyncToken sync_token =
gpu::RasterScopedAccess::EndAccess(std::move(dest_ri_access));
sii->DestroySharedImage(sync_token, std::move(dest_client_si));
sii->DestroySharedImage(sync_token, std::move(y_uv_client_si));
}
#endif
class OopPathPixelTest : public OopPixelTest,
public ::testing::WithParamInterface<bool> {
public:
bool AllowInlining() const { return GetParam(); }
void RunTest() {
auto* ri = static_cast<gpu::raster::RasterImplementation*>(
raster_context_provider_->RasterInterface());
uint32_t max_inlined_entry_size =
AllowInlining() ? std::numeric_limits<uint32_t>::max() : 0u;
ri->set_max_inlined_entry_size_for_testing(max_inlined_entry_size);
RasterOptions options;
options.resource_size = gfx::Size(100, 100);
options.content_size = options.resource_size;
options.full_raster_rect = gfx::Rect(options.content_size);
options.playback_rect = options.full_raster_rect;
options.target_color_params.color_space = gfx::ColorSpace::CreateSRGB();
auto display_item_list = base::MakeRefCounted<DisplayItemList>();
display_item_list->StartPaint();
display_item_list->push<DrawColorOp>(SkColors::kWhite, SkBlendMode::kSrc);
PaintFlags flags;
flags.setStyle(PaintFlags::kFill_Style);
flags.setColor(SkColors::kGreen);
display_item_list->push<DrawPathOp>(SkPath::Circle(20, 20, 10), flags);
flags.setColor(SkColors::kBlue);
display_item_list->push<DrawRectOp>(SkRect::MakeWH(10, 10), flags);
display_item_list->EndPaintOfUnpaired(options.full_raster_rect);
display_item_list->Finalize();
FuzzyPixelComparator comparator =
FuzzyPixelComparator().SetErrorPixelsPercentageLimit(0.08f);
auto* cmd = base::CommandLine::ForCurrentProcess();
if (features::IsSkiaGraphiteEnabled(cmd)) {
comparator.SetErrorPixelsPercentageLimit(0.5f);
}
auto actual = Raster(display_item_list, options);
ExpectEquals(actual, FILE_PATH_LITERAL("oop_path.png"), comparator);
}
};
TEST_P(OopPathPixelTest, Basic) {
RunTest();
}
TEST_F(OopPixelTest, RecordShaderExceedsMaxTextureSize) {
const int max_texture_size =
raster_context_provider_->ContextCapabilities().max_texture_size;
const SkRect rect = SkRect::MakeWH(max_texture_size + 10, 10);
PaintOpBuffer shader_buffer;
shader_buffer.push<DrawColorOp>(SkColors::kWhite, SkBlendMode::kSrc);
PaintFlags flags;
flags.setStyle(PaintFlags::kFill_Style);
flags.setColor(SkColors::kGreen);
shader_buffer.push<DrawRectOp>(rect, flags);
auto shader = PaintShader::MakePaintRecord(shader_buffer.ReleaseAsRecord(),
rect, SkTileMode::kRepeat,
SkTileMode::kRepeat, nullptr);
RasterOptions options;
options.resource_size = gfx::Size(100, 100);
options.content_size = gfx::Size(rect.width(), rect.height());
options.full_raster_rect = gfx::Rect(options.content_size);
options.playback_rect = options.full_raster_rect;
options.target_color_params.color_space = gfx::ColorSpace::CreateSRGB();
auto display_item_list = base::MakeRefCounted<DisplayItemList>();
display_item_list->StartPaint();
display_item_list->push<DrawColorOp>(SkColors::kWhite, SkBlendMode::kSrc);
flags.setShader(shader);
display_item_list->push<DrawRectOp>(rect, flags);
display_item_list->EndPaintOfUnpaired(options.full_raster_rect);
display_item_list->Finalize();
auto actual = Raster(display_item_list, options);
ExpectEquals(actual,
FILE_PATH_LITERAL("oop_record_shader_max_texture_size.png"));
}
INSTANTIATE_TEST_SUITE_P(P, OopClearPixelTest, ::testing::Bool());
INSTANTIATE_TEST_SUITE_P(P, OopPathPixelTest, ::testing::Bool());
TEST_F(OopPixelTest, SkSLCommandShader) {
const std::string_view kDrawRedRect(R"(
uniform float u_border_alpha;
uniform float2 u_top_left;
uniform float2 u_btm_right;
uniform vec4 u_border_color;
uniform vec4 u_center_color;
uniform int u_nudge;
vec4 main(float2 coord) {
float2 adjusted = u_top_left;
adjusted.x = u_top_left.x + float(u_nudge);
if (all(greaterThanEqual(coord, adjusted)) &&
all(lessThan(coord, u_btm_right))) {
return u_center_color;
} else {
return vec4(u_border_color.xyz, u_border_alpha);
}
}
)");
auto shader = PaintShader::MakeSkSLCommand(
kDrawRedRect,
{{.name = SkString("u_border_alpha"), .value = 0.5f}},
{{.name = SkString("u_top_left"), .value = SkV2{23.f, 25.f}},
{.name = SkString("u_btm_right"), .value = SkV2{75.f, 75.f}}},
{{.name = SkString("u_border_color"),
.value = SkColorToSkV4(SkColors::kRed)},
{.name = SkString("u_center_color"),
.value = SkColorToSkV4(SkColors::kGreen)}},
{{.name = SkString("u_nudge"), .value = 2}}, nullptr);
ASSERT_TRUE(shader);
const gfx::Size rect(100, 100);
RasterOptions options(rect);
options.preclear = true;
options.preclear_color = SkColors::kWhite;
PaintFlags flags;
flags.setShader(std::move(shader));
auto display_item_list = base::MakeRefCounted<DisplayItemList>();
display_item_list->StartPaint();
display_item_list->push<DrawRectOp>(gfx::RectToSkRect(gfx::Rect(rect)),
flags);
display_item_list->EndPaintOfUnpaired(options.full_raster_rect);
display_item_list->Finalize();
auto actual = Raster(display_item_list, options);
SkBitmap expected = MakeSolidColorBitmap(rect, SkColors::kWhite);
SkCanvas canvas(expected, SkSurfaceProps{});
SkPaint red;
red.setColor(SkColors::kRed);
red.setAlphaf(0.5f);
canvas.drawRect(SkRect::MakeXYWH(0, 0, 100, 100), red);
SkPaint green;
green.setColor(SkColors::kGreen);
canvas.drawRect(SkRect::MakeXYWH(25, 25, 50, 50), green);
EXPECT_TRUE(MatchesBitmap(actual, expected,
ManhattanDistancePixelComparator(5)));
}
}
}