Commit 046e446b authored by Yuri Wiitala's avatar Yuri Wiitala

Implement SkColorSpace struct traits in skia.mojom.ImageInfo.

Completes the skia mojom struct traits implementation for ImageInfo by
using Skia's built-in SkColorSpace::serialize() functionality. This
allows for exact SkColorSpaces to be efficiently transmitted, alongside
things like SkBitmaps, through mojo message pipes.

Later work to improve color space management in Chromium will depend on
this change. (See crbugs for examples.)

Bug: 758057, 809385
Change-Id: I1193f81c727d8663370fd6fd802edd9dd397abff
Reviewed-on: https://chromium-review.googlesource.com/c/1357633Reviewed-by: default avatarRobert Sesek <rsesek@chromium.org>
Reviewed-by: default avatarChris Palmer <palmer@chromium.org>
Cr-Commit-Position: refs/heads/master@{#615611}
parent 0c01026d
...@@ -238,6 +238,8 @@ TEST_F(StructTraitsTest, CopyOutputRequest_BitmapRequest) { ...@@ -238,6 +238,8 @@ TEST_F(StructTraitsTest, CopyOutputRequest_BitmapRequest) {
[](const base::Closure& quit_closure, const gfx::Rect& expected_rect, [](const base::Closure& quit_closure, const gfx::Rect& expected_rect,
std::unique_ptr<CopyOutputResult> result) { std::unique_ptr<CopyOutputResult> result) {
EXPECT_EQ(expected_rect, result->rect()); EXPECT_EQ(expected_rect, result->rect());
// Note: CopyOutputResult plumbing for bitmap requests is tested in
// StructTraitsTest.CopyOutputResult_Bitmap.
quit_closure.Run(); quit_closure.Run();
}, },
run_loop.QuitClosure(), result_rect))); run_loop.QuitClosure(), result_rect)));
...@@ -264,7 +266,8 @@ TEST_F(StructTraitsTest, CopyOutputRequest_BitmapRequest) { ...@@ -264,7 +266,8 @@ TEST_F(StructTraitsTest, CopyOutputRequest_BitmapRequest) {
EXPECT_EQ(result_rect, output->result_selection()); EXPECT_EQ(result_rect, output->result_selection());
SkBitmap bitmap; SkBitmap bitmap;
bitmap.allocN32Pixels(result_rect.width(), result_rect.height()); bitmap.allocPixels(SkImageInfo::MakeN32Premul(
result_rect.width(), result_rect.height(), SkColorSpace::MakeSRGB()));
output->SendResult( output->SendResult(
std::make_unique<CopyOutputSkBitmapResult>(result_rect, bitmap)); std::make_unique<CopyOutputSkBitmapResult>(result_rect, bitmap));
// If the CopyOutputRequest callback is called, this ends. Otherwise, the test // If the CopyOutputRequest callback is called, this ends. Otherwise, the test
...@@ -312,6 +315,8 @@ TEST_F(StructTraitsTest, CopyOutputRequest_TextureRequest) { ...@@ -312,6 +315,8 @@ TEST_F(StructTraitsTest, CopyOutputRequest_TextureRequest) {
[](const base::Closure& quit_closure, const gfx::Rect& expected_rect, [](const base::Closure& quit_closure, const gfx::Rect& expected_rect,
std::unique_ptr<CopyOutputResult> result) { std::unique_ptr<CopyOutputResult> result) {
EXPECT_EQ(expected_rect, result->rect()); EXPECT_EQ(expected_rect, result->rect());
// Note: CopyOutputResult plumbing for texture requests is tested in
// StructTraitsTest.CopyOutputResult_Texture.
quit_closure.Run(); quit_closure.Run();
}, },
run_loop_for_result.QuitClosure(), result_rect))); run_loop_for_result.QuitClosure(), result_rect)));
...@@ -327,7 +332,7 @@ TEST_F(StructTraitsTest, CopyOutputRequest_TextureRequest) { ...@@ -327,7 +332,7 @@ TEST_F(StructTraitsTest, CopyOutputRequest_TextureRequest) {
base::RunLoop run_loop_for_release; base::RunLoop run_loop_for_release;
output->SendResult(std::make_unique<CopyOutputTextureResult>( output->SendResult(std::make_unique<CopyOutputTextureResult>(
result_rect, mailbox, sync_token, gfx::ColorSpace(), result_rect, mailbox, sync_token, gfx::ColorSpace::CreateSRGB(),
SingleReleaseCallback::Create(base::Bind( SingleReleaseCallback::Create(base::Bind(
[](const base::Closure& quit_closure, [](const base::Closure& quit_closure,
const gpu::SyncToken& expected_sync_token, const gpu::SyncToken& expected_sync_token,
...@@ -1193,7 +1198,7 @@ TEST_F(StructTraitsTest, CopyOutputResult_Bitmap) { ...@@ -1193,7 +1198,7 @@ TEST_F(StructTraitsTest, CopyOutputResult_Bitmap) {
SkBitmap bitmap; SkBitmap bitmap;
const sk_sp<SkColorSpace> adobe_rgb = SkColorSpace::MakeRGB( const sk_sp<SkColorSpace> adobe_rgb = SkColorSpace::MakeRGB(
SkColorSpace::kSRGB_RenderTargetGamma, SkColorSpace::kAdobeRGB_Gamut); SkColorSpace::kSRGB_RenderTargetGamma, SkColorSpace::kAdobeRGB_Gamut);
bitmap.allocN32Pixels(7, 8, adobe_rgb != nullptr); bitmap.allocPixels(SkImageInfo::MakeN32Premul(7, 8, adobe_rgb));
bitmap.eraseARGB(123, 213, 77, 33); bitmap.eraseARGB(123, 213, 77, 33);
std::unique_ptr<CopyOutputResult> input = std::unique_ptr<CopyOutputResult> input =
std::make_unique<CopyOutputSkBitmapResult>(result_rect, bitmap); std::make_unique<CopyOutputSkBitmapResult>(result_rect, bitmap);
...@@ -1214,7 +1219,7 @@ TEST_F(StructTraitsTest, CopyOutputResult_Bitmap) { ...@@ -1214,7 +1219,7 @@ TEST_F(StructTraitsTest, CopyOutputResult_Bitmap) {
// Check that the pixels are the same as the input and the color spaces are // Check that the pixels are the same as the input and the color spaces are
// equivalent. // equivalent.
SkBitmap expected_bitmap; SkBitmap expected_bitmap;
expected_bitmap.allocN32Pixels(7, 8, adobe_rgb != nullptr); expected_bitmap.allocPixels(SkImageInfo::MakeN32Premul(7, 8, adobe_rgb));
expected_bitmap.eraseARGB(123, 213, 77, 33); expected_bitmap.eraseARGB(123, 213, 77, 33);
EXPECT_EQ(expected_bitmap.computeByteSize(), out_bitmap.computeByteSize()); EXPECT_EQ(expected_bitmap.computeByteSize(), out_bitmap.computeByteSize());
EXPECT_EQ(0, std::memcmp(expected_bitmap.getPixels(), out_bitmap.getPixels(), EXPECT_EQ(0, std::memcmp(expected_bitmap.getPixels(), out_bitmap.getPixels(),
......
...@@ -24,17 +24,10 @@ enum AlphaType { ...@@ -24,17 +24,10 @@ enum AlphaType {
UNPREMUL, UNPREMUL,
}; };
// Capture of the (simple) gamma of SkColorSpace. Need to eventually
// serialize the actual colorspace object.
enum ColorProfileType {
LINEAR,
SRGB,
};
struct ImageInfo { struct ImageInfo {
ColorType color_type; ColorType color_type;
AlphaType alpha_type; AlphaType alpha_type;
ColorProfileType profile_type; array<uint8> serialized_color_space; // Empty means "null" SkColorSpace.
uint32 width; uint32 width;
uint32 height; uint32 height;
}; };
...@@ -47,17 +47,6 @@ SkAlphaType MojoAlphaTypeToSk(skia::mojom::AlphaType type) { ...@@ -47,17 +47,6 @@ SkAlphaType MojoAlphaTypeToSk(skia::mojom::AlphaType type) {
return kUnknown_SkAlphaType; return kUnknown_SkAlphaType;
} }
sk_sp<SkColorSpace> MojoProfileTypeToSk(skia::mojom::ColorProfileType type) {
switch (type) {
case skia::mojom::ColorProfileType::LINEAR:
return nullptr;
case skia::mojom::ColorProfileType::SRGB:
return SkColorSpace::MakeSRGB();
}
NOTREACHED();
return nullptr;
}
skia::mojom::ColorType SkColorTypeToMojo(SkColorType type) { skia::mojom::ColorType SkColorTypeToMojo(SkColorType type) {
switch (type) { switch (type) {
case kUnknown_SkColorType: case kUnknown_SkColorType:
...@@ -97,12 +86,6 @@ skia::mojom::AlphaType SkAlphaTypeToMojo(SkAlphaType type) { ...@@ -97,12 +86,6 @@ skia::mojom::AlphaType SkAlphaTypeToMojo(SkAlphaType type) {
return skia::mojom::AlphaType::UNKNOWN; return skia::mojom::AlphaType::UNKNOWN;
} }
skia::mojom::ColorProfileType SkColorSpaceToMojo(SkColorSpace* cs) {
if (cs && cs->gammaCloseToSRGB())
return skia::mojom::ColorProfileType::SRGB;
return skia::mojom::ColorProfileType::LINEAR;
}
} // namespace } // namespace
// static // static
...@@ -120,10 +103,26 @@ StructTraits<skia::mojom::ImageInfoDataView, SkImageInfo>::alpha_type( ...@@ -120,10 +103,26 @@ StructTraits<skia::mojom::ImageInfoDataView, SkImageInfo>::alpha_type(
} }
// static // static
skia::mojom::ColorProfileType std::vector<uint8_t>
StructTraits<skia::mojom::ImageInfoDataView, SkImageInfo>::profile_type( StructTraits<skia::mojom::ImageInfoDataView,
const SkImageInfo& info) { SkImageInfo>::serialized_color_space(const SkImageInfo& info) {
return SkColorSpaceToMojo(info.colorSpace()); std::vector<uint8_t> serialized_color_space;
if (auto* sk_color_space = info.colorSpace()) {
serialized_color_space.resize(sk_color_space->writeToMemory(nullptr));
// Assumption 1: Since a "null" SkColorSpace is represented as an empty byte
// array, the serialization of a non-null SkColorSpace should produce at
// least one byte.
CHECK_GT(serialized_color_space.size(), 0u);
// Assumption 2: Serialized data should be reasonably small, since
// SkImageInfo should efficiently pass through mojo message pipes. As of
// this writing, the max would be 80 bytes. However, that could change in
// the future. So, set an upper-bound of 1 KB here.
CHECK_LE(serialized_color_space.size(), 1024u);
sk_color_space->writeToMemory(serialized_color_space.data());
} else {
// Represent the "null" color space as an empty byte vector.
}
return serialized_color_space;
} }
// static // static
...@@ -142,10 +141,20 @@ uint32_t StructTraits<skia::mojom::ImageInfoDataView, SkImageInfo>::height( ...@@ -142,10 +141,20 @@ uint32_t StructTraits<skia::mojom::ImageInfoDataView, SkImageInfo>::height(
bool StructTraits<skia::mojom::ImageInfoDataView, SkImageInfo>::Read( bool StructTraits<skia::mojom::ImageInfoDataView, SkImageInfo>::Read(
skia::mojom::ImageInfoDataView data, skia::mojom::ImageInfoDataView data,
SkImageInfo* info) { SkImageInfo* info) {
*info = SkImageInfo::Make(data.width(), data.height(), mojo::ArrayDataView<uint8_t> serialized_color_space;
MojoColorTypeToSk(data.color_type()), data.GetSerializedColorSpaceDataView(&serialized_color_space);
MojoAlphaTypeToSk(data.alpha_type()), sk_sp<SkColorSpace> sk_color_space;
MojoProfileTypeToSk(data.profile_type())); if (serialized_color_space.size() != 0u) {
sk_color_space = SkColorSpace::Deserialize(serialized_color_space.data(),
serialized_color_space.size());
CHECK(sk_color_space); // Deserialize() returns nullptr on invalid input.
} else {
// Empty byte array is interpreted as "null."
}
*info = SkImageInfo::Make(
data.width(), data.height(), MojoColorTypeToSk(data.color_type()),
MojoAlphaTypeToSk(data.alpha_type()), std::move(sk_color_space));
return true; return true;
} }
......
...@@ -5,6 +5,8 @@ ...@@ -5,6 +5,8 @@
#ifndef SKIA_PUBLIC_INTERFACES_IMAGE_INFO_STRUCT_TRAITS_H_ #ifndef SKIA_PUBLIC_INTERFACES_IMAGE_INFO_STRUCT_TRAITS_H_
#define SKIA_PUBLIC_INTERFACES_IMAGE_INFO_STRUCT_TRAITS_H_ #define SKIA_PUBLIC_INTERFACES_IMAGE_INFO_STRUCT_TRAITS_H_
#include <vector>
#include "skia/public/interfaces/image_info.mojom.h" #include "skia/public/interfaces/image_info.mojom.h"
#include "third_party/skia/include/core/SkImageInfo.h" #include "third_party/skia/include/core/SkImageInfo.h"
...@@ -14,7 +16,7 @@ template <> ...@@ -14,7 +16,7 @@ template <>
struct StructTraits<skia::mojom::ImageInfoDataView, SkImageInfo> { struct StructTraits<skia::mojom::ImageInfoDataView, SkImageInfo> {
static skia::mojom::ColorType color_type(const SkImageInfo& info); static skia::mojom::ColorType color_type(const SkImageInfo& info);
static skia::mojom::AlphaType alpha_type(const SkImageInfo& info); static skia::mojom::AlphaType alpha_type(const SkImageInfo& info);
static skia::mojom::ColorProfileType profile_type(const SkImageInfo& info); static std::vector<uint8_t> serialized_color_space(const SkImageInfo& info);
static uint32_t width(const SkImageInfo& info); static uint32_t width(const SkImageInfo& info);
static uint32_t height(const SkImageInfo& info); static uint32_t height(const SkImageInfo& info);
static bool Read(skia::mojom::ImageInfoDataView data, SkImageInfo* info); static bool Read(skia::mojom::ImageInfoDataView data, SkImageInfo* info);
......
per-file struct_traits_unittest.cc=file://ipc/SECURITY_OWNERS
per-file *.mojom=set noparent per-file *.mojom=set noparent
per-file *.mojom=file://ipc/SECURITY_OWNERS per-file *.mojom=file://ipc/SECURITY_OWNERS
...@@ -52,36 +52,39 @@ class StructTraitsTest : public testing::Test, public mojom::TraitsTestService { ...@@ -52,36 +52,39 @@ class StructTraitsTest : public testing::Test, public mojom::TraitsTestService {
DISALLOW_COPY_AND_ASSIGN(StructTraitsTest); DISALLOW_COPY_AND_ASSIGN(StructTraitsTest);
}; };
static bool colorspace_srgb_gamma(SkColorSpace* cs) {
return cs && cs->gammaCloseToSRGB();
}
} // namespace } // namespace
TEST_F(StructTraitsTest, ImageInfo) { TEST_F(StructTraitsTest, ImageInfo) {
SkImageInfo input = SkImageInfo::Make( SkImageInfo input = SkImageInfo::Make(
34, 56, SkColorType::kGray_8_SkColorType, 34, 56, SkColorType::kGray_8_SkColorType,
SkAlphaType::kUnpremul_SkAlphaType, SkColorSpace::MakeSRGB()); SkAlphaType::kUnpremul_SkAlphaType,
SkColorSpace::MakeRGB(SkColorSpace::kSRGB_RenderTargetGamma,
SkColorSpace::kAdobeRGB_Gamut));
mojom::TraitsTestServicePtr proxy = GetTraitsTestProxy(); mojom::TraitsTestServicePtr proxy = GetTraitsTestProxy();
SkImageInfo output; SkImageInfo output;
proxy->EchoImageInfo(input, &output); proxy->EchoImageInfo(input, &output);
EXPECT_EQ(input, output); EXPECT_EQ(input, output);
SkImageInfo another_input_with_null_color_space =
SkImageInfo::Make(54, 43, SkColorType::kRGBA_8888_SkColorType,
SkAlphaType::kPremul_SkAlphaType, nullptr);
proxy->EchoImageInfo(another_input_with_null_color_space, &output);
EXPECT_FALSE(output.colorSpace());
EXPECT_EQ(another_input_with_null_color_space, output);
} }
TEST_F(StructTraitsTest, Bitmap) { TEST_F(StructTraitsTest, Bitmap) {
SkBitmap input; SkBitmap input;
input.allocN32Pixels(10, 5); input.allocPixels(SkImageInfo::MakeN32Premul(
10, 5,
SkColorSpace::MakeRGB(SkColorSpace::kLinear_RenderTargetGamma,
SkColorSpace::kRec2020_Gamut)));
input.eraseColor(SK_ColorYELLOW); input.eraseColor(SK_ColorYELLOW);
input.erase(SK_ColorTRANSPARENT, SkIRect::MakeXYWH(0, 1, 2, 3)); input.erase(SK_ColorTRANSPARENT, SkIRect::MakeXYWH(0, 1, 2, 3));
mojom::TraitsTestServicePtr proxy = GetTraitsTestProxy(); mojom::TraitsTestServicePtr proxy = GetTraitsTestProxy();
SkBitmap output; SkBitmap output;
proxy->EchoBitmap(input, &output); proxy->EchoBitmap(input, &output);
EXPECT_EQ(input.colorType(), output.colorType()); EXPECT_EQ(input.info(), output.info());
EXPECT_EQ(input.alphaType(), output.alphaType());
EXPECT_EQ(colorspace_srgb_gamma(input.colorSpace()),
colorspace_srgb_gamma(output.colorSpace()));
EXPECT_EQ(input.width(), output.width());
EXPECT_EQ(input.height(), output.height());
EXPECT_EQ(input.rowBytes(), output.rowBytes()); EXPECT_EQ(input.rowBytes(), output.rowBytes());
EXPECT_TRUE(gfx::BitmapsAreEqual(input, output)); EXPECT_TRUE(gfx::BitmapsAreEqual(input, output));
} }
...@@ -89,7 +92,8 @@ TEST_F(StructTraitsTest, Bitmap) { ...@@ -89,7 +92,8 @@ TEST_F(StructTraitsTest, Bitmap) {
TEST_F(StructTraitsTest, BitmapWithExtraRowBytes) { TEST_F(StructTraitsTest, BitmapWithExtraRowBytes) {
SkBitmap input; SkBitmap input;
// Ensure traits work with bitmaps containing additional bytes between rows. // Ensure traits work with bitmaps containing additional bytes between rows.
SkImageInfo info = SkImageInfo::MakeN32(8, 5, kPremul_SkAlphaType); SkImageInfo info =
SkImageInfo::MakeN32(8, 5, kPremul_SkAlphaType, SkColorSpace::MakeSRGB());
// Any extra bytes on each row must be a multiple of the row's pixel size to // Any extra bytes on each row must be a multiple of the row's pixel size to
// keep every row's pixels aligned. // keep every row's pixels aligned.
size_t extra = info.bytesPerPixel(); size_t extra = info.bytesPerPixel();
...@@ -99,12 +103,7 @@ TEST_F(StructTraitsTest, BitmapWithExtraRowBytes) { ...@@ -99,12 +103,7 @@ TEST_F(StructTraitsTest, BitmapWithExtraRowBytes) {
mojom::TraitsTestServicePtr proxy = GetTraitsTestProxy(); mojom::TraitsTestServicePtr proxy = GetTraitsTestProxy();
SkBitmap output; SkBitmap output;
proxy->EchoBitmap(input, &output); proxy->EchoBitmap(input, &output);
EXPECT_EQ(input.colorType(), output.colorType()); EXPECT_EQ(input.info(), output.info());
EXPECT_EQ(input.alphaType(), output.alphaType());
EXPECT_EQ(colorspace_srgb_gamma(input.colorSpace()),
colorspace_srgb_gamma(output.colorSpace()));
EXPECT_EQ(input.width(), output.width());
EXPECT_EQ(input.height(), output.height());
EXPECT_EQ(input.rowBytes(), output.rowBytes()); EXPECT_EQ(input.rowBytes(), output.rowBytes());
EXPECT_TRUE(gfx::BitmapsAreEqual(input, output)); EXPECT_TRUE(gfx::BitmapsAreEqual(input, output));
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment