Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(538)

Unified Diff: third_party/WebKit/Source/modules/canvas2d/CanvasRenderingContext2DTest.cpp

Issue 2797213002: Fix BaseRenderingContext2D create/put/get-ImageData() for color managed canvas (Closed)
Patch Set: Rebaseline Created 3 years, 8 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: third_party/WebKit/Source/modules/canvas2d/CanvasRenderingContext2DTest.cpp
diff --git a/third_party/WebKit/Source/modules/canvas2d/CanvasRenderingContext2DTest.cpp b/third_party/WebKit/Source/modules/canvas2d/CanvasRenderingContext2DTest.cpp
index cfc096c739c577f714387f413a39d2aa2eb1acdd..3489a30ddffadac68c908151690635341696ddfe 100644
--- a/third_party/WebKit/Source/modules/canvas2d/CanvasRenderingContext2DTest.cpp
+++ b/third_party/WebKit/Source/modules/canvas2d/CanvasRenderingContext2DTest.cpp
@@ -37,6 +37,7 @@
#include "third_party/skia/include/core/SkColorSpaceXform.h"
#include "third_party/skia/include/core/SkImage.h"
#include "third_party/skia/include/core/SkSurface.h"
+#include "third_party/skia/include/core/SkSwizzle.h"
using ::testing::Mock;
@@ -1342,6 +1343,291 @@ TEST_F(CanvasRenderingContext2DTest, ImageBitmapColorSpaceConversion) {
color_correct_rendering_default_mode_runtime_flag);
}
+bool ConvertPixelsToColorSpaceAndPixelFormatForTest(
+ DOMArrayBufferView* data_array,
+ CanvasColorSpace src_color_space,
+ CanvasColorSpace dst_color_space,
+ CanvasPixelFormat dst_pixel_format,
+ std::unique_ptr<uint8_t[]>& converted_pixels) {
+ // Setting SkColorSpaceXform::apply parameters
+ SkColorSpaceXform::ColorFormat src_color_format =
+ SkColorSpaceXform::kRGBA_8888_ColorFormat;
+
+ unsigned data_length = data_array->byteLength() / data_array->TypeSize();
+ unsigned num_pixels = data_length / 4;
+ DOMUint8ClampedArray* u8_array = nullptr;
+ DOMUint16Array* u16_array = nullptr;
+ DOMFloat32Array* f32_array = nullptr;
+ void* src_data = nullptr;
+
+ switch (data_array->GetType()) {
+ case ArrayBufferView::ViewType::kTypeUint8Clamped:
+ u8_array = const_cast<DOMUint8ClampedArray*>(
+ static_cast<const DOMUint8ClampedArray*>(data_array));
+ src_data = static_cast<void*>(u8_array->Data());
+ break;
+
+ case ArrayBufferView::ViewType::kTypeUint16:
+ u16_array = const_cast<DOMUint16Array*>(
+ static_cast<const DOMUint16Array*>(data_array));
+ src_color_format =
+ SkColorSpaceXform::ColorFormat::kRGBA_U16_BE_ColorFormat;
+ src_data = static_cast<void*>(u16_array->Data());
+ break;
+
+ case ArrayBufferView::ViewType::kTypeFloat32:
+ f32_array = const_cast<DOMFloat32Array*>(
+ static_cast<const DOMFloat32Array*>(data_array));
+ src_color_format = SkColorSpaceXform::kRGBA_F32_ColorFormat;
+ src_data = static_cast<void*>(f32_array->Data());
+ break;
+ default:
+ NOTREACHED();
+ return false;
+ }
+
+ SkColorSpaceXform::ColorFormat dst_color_format =
+ SkColorSpaceXform::ColorFormat::kRGBA_8888_ColorFormat;
+ if (dst_pixel_format == kF16CanvasPixelFormat)
+ dst_color_format = SkColorSpaceXform::ColorFormat::kRGBA_F32_ColorFormat;
+
+ sk_sp<SkColorSpace> src_sk_color_space = nullptr;
+ if (u8_array) {
+ src_sk_color_space = ImageData::GetSkColorSpaceForTest(
+ src_color_space, kRGBA8CanvasPixelFormat);
+ } else {
+ src_sk_color_space = ImageData::GetSkColorSpaceForTest(
+ src_color_space, kF16CanvasPixelFormat);
+ }
+
+ sk_sp<SkColorSpace> dst_sk_color_space =
+ ImageData::GetSkColorSpaceForTest(dst_color_space, dst_pixel_format);
+
+ // When the input dataArray is in Uint16, we normally should convert the
+ // values from Little Endian to Big Endian before passing the buffer to
+ // SkColorSpaceXform::apply. However, in this test scenario we are creating
+ // the Uin16 dataArray by multiplying a Uint8Clamped array members by 257,
+ // hence the Big Endian and Little Endian representations are the same.
+
+ std::unique_ptr<SkColorSpaceXform> xform = SkColorSpaceXform::New(
+ src_sk_color_space.get(), dst_sk_color_space.get());
+
+ if (!xform->apply(dst_color_format, converted_pixels.get(), src_color_format,
+ src_data, num_pixels, kUnpremul_SkAlphaType))
+ return false;
+ return true;
+}
+
+// The color settings of the surface of the canvas always remaines loyal to the
+// first created context 2D. Therefore, we have to test different canvas color
+// space settings for CanvasRenderingContext2D::putImageData() in different
+// tests.
+enum class CanvasColorSpaceSettings : uint8_t {
+ CANVAS_SRGB = 0,
+ CANVAS_LINEARSRGB = 1,
+ CANVAS_REC2020 = 2,
+ CANVAS_P3 = 3,
+
+ LAST = CANVAS_P3
+};
+
+// This test verifies the correct behavior of putImageData member function in
+// color managed mode.
+void TestPutImageDataOnCanvasWithColorSpaceSettings(
+ HTMLCanvasElement& canvas_element,
+ CanvasColorSpaceSettings canvas_colorspace_setting,
+ float color_tolerance) {
+ bool experimental_canvas_features_runtime_flag =
+ RuntimeEnabledFeatures::experimentalCanvasFeaturesEnabled();
+ bool color_correct_rendering_runtime_flag =
+ RuntimeEnabledFeatures::colorCorrectRenderingEnabled();
+ RuntimeEnabledFeatures::setExperimentalCanvasFeaturesEnabled(true);
+ RuntimeEnabledFeatures::setColorCorrectRenderingEnabled(true);
+
+ bool test_passed = true;
+ unsigned num_image_data_color_spaces = 3;
+ CanvasColorSpace image_data_color_spaces[] = {
+ kSRGBCanvasColorSpace, kRec2020CanvasColorSpace, kP3CanvasColorSpace,
+ };
+
+ unsigned num_image_data_storage_formats = 3;
+ ImageDataStorageFormat image_data_storage_formats[] = {
+ kUint8ClampedArrayStorageFormat, kUint16ArrayStorageFormat,
+ kFloat32ArrayStorageFormat,
+ };
+
+ CanvasColorSpace canvas_color_spaces[] = {
+ kSRGBCanvasColorSpace, kSRGBCanvasColorSpace, kRec2020CanvasColorSpace,
+ kP3CanvasColorSpace,
+ };
+
+ String canvas_color_space_names[] = {
+ kSRGBCanvasColorSpaceName, kSRGBCanvasColorSpaceName,
+ kRec2020CanvasColorSpaceName, kP3CanvasColorSpaceName};
+
+ CanvasPixelFormat canvas_pixel_formats[] = {
+ kRGBA8CanvasPixelFormat, kF16CanvasPixelFormat, kF16CanvasPixelFormat,
+ kF16CanvasPixelFormat,
+ };
+
+ String canvas_pixel_format_names[] = {
+ kRGBA8CanvasPixelFormatName, kF16CanvasPixelFormatName,
+ kF16CanvasPixelFormatName, kF16CanvasPixelFormatName};
+
+ // Source pixels in RGBA32
+ uint8_t u8_pixels[] = {255, 0, 0, 255, // Red
+ 0, 0, 0, 0, // Transparent
+ 255, 192, 128, 64, // Decreasing values
+ 93, 117, 205, 11}; // Random values
+ unsigned data_length = 16;
+
+ uint16_t* u16_pixels = new uint16_t[data_length];
+ for (unsigned i = 0; i < data_length; i++)
+ u16_pixels[i] = u8_pixels[i] * 257;
+
+ float* f32_pixels = new float[data_length];
+ for (unsigned i = 0; i < data_length; i++)
+ f32_pixels[i] = u8_pixels[i] / 255.0;
+
+ DOMArrayBufferView* data_array = nullptr;
+
+ DOMUint8ClampedArray* data_u8 =
+ DOMUint8ClampedArray::Create(u8_pixels, data_length);
+ DCHECK(data_u8);
+ EXPECT_EQ(data_length, data_u8->length());
+ DOMUint16Array* data_u16 = DOMUint16Array::Create(u16_pixels, data_length);
+ DCHECK(data_u16);
+ EXPECT_EQ(data_length, data_u16->length());
+ DOMFloat32Array* data_f32 = DOMFloat32Array::Create(f32_pixels, data_length);
+ DCHECK(data_f32);
+ EXPECT_EQ(data_length, data_f32->length());
+
+ ImageData* image_data = nullptr;
+ ImageDataColorSettings color_settings;
+
+ // At most four bytes are needed for Float32 output per color component.
+ std::unique_ptr<uint8_t[]> pixels_converted_manually(
+ new uint8_t[data_length * 4]());
+
+ // Loop through different possible combinations of image data color space and
+ // storage formats and create the respective test image data objects.
+ for (unsigned i = 0; i < num_image_data_color_spaces; i++) {
+ color_settings.setColorSpace(
+ ImageData::CanvasColorSpaceName(image_data_color_spaces[i]));
+
+ for (unsigned j = 0; j < num_image_data_storage_formats; j++) {
+ switch (image_data_storage_formats[j]) {
+ case kUint8ClampedArrayStorageFormat:
+ data_array = static_cast<DOMArrayBufferView*>(data_u8);
+ color_settings.setStorageFormat(kUint8ClampedArrayStorageFormatName);
+ break;
+ case kUint16ArrayStorageFormat:
+ data_array = static_cast<DOMArrayBufferView*>(data_u16);
+ color_settings.setStorageFormat(kUint16ArrayStorageFormatName);
+ break;
+ case kFloat32ArrayStorageFormat:
+ data_array = static_cast<DOMArrayBufferView*>(data_f32);
+ color_settings.setStorageFormat(kFloat32ArrayStorageFormatName);
+ break;
+ default:
+ NOTREACHED();
+ }
+
+ image_data =
+ ImageData::CreateForTest(IntSize(2, 2), data_array, &color_settings);
+
+ unsigned k = (unsigned)(canvas_colorspace_setting);
+ // Convert the original data used to create ImageData to the
+ // canvas color space and canvas pixel format.
+ EXPECT_TRUE(ConvertPixelsToColorSpaceAndPixelFormatForTest(
+ data_array, image_data_color_spaces[i], canvas_color_spaces[k],
+ canvas_pixel_formats[k], pixels_converted_manually));
+
+ // Create a canvas and call putImageData and getImageData to make sure
+ // the conversion is done correctly.
+ CanvasContextCreationAttributes attributes;
+ attributes.setAlpha(true);
+ attributes.setColorSpace(canvas_color_space_names[k]);
+ attributes.setPixelFormat(canvas_pixel_format_names[k]);
+ CanvasRenderingContext2D* context =
+ static_cast<CanvasRenderingContext2D*>(
+ canvas_element.GetCanvasRenderingContext("2d", attributes));
+ NonThrowableExceptionState exception_state;
+ context->putImageData(image_data, 0, 0, exception_state);
+
+ void* pixels_from_get_image_data = nullptr;
+ if (canvas_pixel_formats[k] == kRGBA8CanvasPixelFormat) {
+ pixels_from_get_image_data =
+ context->getImageData(0, 0, 2, 2, exception_state)->data()->Data();
+ // Swizzle if needed
+ if (kN32_SkColorType == kBGRA_8888_SkColorType) {
+ SkSwapRB(static_cast<uint32_t*>(pixels_from_get_image_data),
+ static_cast<uint32_t*>(pixels_from_get_image_data),
+ data_length / 4);
+ }
+
+ unsigned char* cpixels1 =
+ static_cast<unsigned char*>(pixels_converted_manually.get());
+ unsigned char* cpixels2 =
+ static_cast<unsigned char*>(pixels_from_get_image_data);
+ for (unsigned m = 0; m < data_length; m++) {
+ if (abs(cpixels1[m] - cpixels2[m]) > color_tolerance)
+ test_passed = false;
+ }
+ } else {
+ pixels_from_get_image_data =
+ context->getImageData(0, 0, 2, 2, exception_state)
+ ->dataUnion()
+ .getAsFloat32Array()
+ .View()
+ ->Data();
+ float* fpixels1 = nullptr;
+ float* fpixels2 = nullptr;
+ void* vpointer = pixels_converted_manually.get();
+ fpixels1 = static_cast<float*>(vpointer);
+ fpixels2 = static_cast<float*>(pixels_from_get_image_data);
+ for (unsigned m = 0; m < data_length; m++) {
+ if (fpixels1[m] < 0)
+ fpixels1[m] = 0;
+ if (fabs(fpixels1[m] - fpixels2[m]) > color_tolerance) {
+ test_passed = false;
+ }
+ }
+
+ ASSERT_TRUE(test_passed);
+ }
+ }
+ }
+ delete[] u16_pixels;
+ delete[] f32_pixels;
+
+ RuntimeEnabledFeatures::setExperimentalCanvasFeaturesEnabled(
+ experimental_canvas_features_runtime_flag);
+ RuntimeEnabledFeatures::setColorCorrectRenderingEnabled(
+ color_correct_rendering_runtime_flag);
+}
+
+TEST_F(CanvasRenderingContext2DTest, ColorManagedPutImageDataOnSRGBCanvas) {
+ TestPutImageDataOnCanvasWithColorSpaceSettings(
+ CanvasElement(), CanvasColorSpaceSettings::CANVAS_SRGB, 0);
+}
+
+TEST_F(CanvasRenderingContext2DTest,
+ ColorManagedPutImageDataOnLinearSRGBCanvas) {
+ TestPutImageDataOnCanvasWithColorSpaceSettings(
+ CanvasElement(), CanvasColorSpaceSettings::CANVAS_LINEARSRGB, 0.15);
+}
+
+TEST_F(CanvasRenderingContext2DTest, ColorManagedPutImageDataOnRec2020Canvas) {
+ TestPutImageDataOnCanvasWithColorSpaceSettings(
+ CanvasElement(), CanvasColorSpaceSettings::CANVAS_REC2020, 0.1);
+}
+
+TEST_F(CanvasRenderingContext2DTest, ColorManagedPutImageDataOnP3Canvas) {
+ TestPutImageDataOnCanvasWithColorSpaceSettings(
+ CanvasElement(), CanvasColorSpaceSettings::CANVAS_P3, 0.1);
+}
+
void OverrideScriptEnabled(Settings& settings) {
// Simulate that we allow scripts, so that HTMLCanvasElement uses
// LayoutHTMLCanvas.

Powered by Google App Engine
This is Rietveld 408576698