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 e7e93bf62585fa242e13541dbda58cf166b0f275..35942e4434fe7ca5c1acd124628d8db3368ad7f9 100644 |
--- a/third_party/WebKit/Source/modules/canvas2d/CanvasRenderingContext2DTest.cpp |
+++ b/third_party/WebKit/Source/modules/canvas2d/CanvasRenderingContext2DTest.cpp |
@@ -1385,4 +1385,226 @@ 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; |
+} |
+ |
+// This test verifies the correct behavior of putImageData member function in |
+// color managed mode. |
+TEST_F(CanvasRenderingContext2DTest, PutImageDataColorManaged) { |
+ bool experimental_canvas_features_runtime_flag = |
+ RuntimeEnabledFeatures::experimentalCanvasFeaturesEnabled(); |
+ bool color_correct_rendering_runtime_flag = |
+ RuntimeEnabledFeatures::colorCorrectRenderingEnabled(); |
+ |
+ 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, |
+ }; |
+ |
+ unsigned num_canvas_color_settings = 4; |
+ CanvasColorSpace canvas_color_spaces[] = { |
+ kSRGBCanvasColorSpace, kSRGBCanvasColorSpace, kRec2020CanvasColorSpace, |
+ kP3CanvasColorSpace, |
+ }; |
+ String canvas_color_space_names[] = {"srgb", "srgb", "rec2020", "p3"}; |
+ CanvasPixelFormat canvas_pixel_formats[] = { |
+ kRGBA8CanvasPixelFormat, kF16CanvasPixelFormat, kF16CanvasPixelFormat, |
+ kF16CanvasPixelFormat, |
+ }; |
+ String canvas_pixel_format_names[] = {"8-8-8-8", "float16", "float16", |
+ "float16"}; |
+ |
+ // 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); |
+ |
+ for (unsigned k = 0; k < num_canvas_color_settings; k++) { |
+ unsigned output_length = |
+ (canvas_pixel_formats[k] == kRGBA8CanvasPixelFormat) |
+ ? data_length |
+ : data_length * 4; |
+ |
+ // 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. |
+ Persistent<HTMLCanvasElement> canvas = |
+ Persistent<HTMLCanvasElement>(CanvasElement()); |
+ 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->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(); |
+ } else { |
+ pixels_from_get_image_data = |
+ context->getImageData(0, 0, 2, 2, exception_state) |
+ ->dataUnion() |
+ .getAsFloat32Array() |
+ ->Data(); |
+ } |
+ // Compare the converted pixels |
+ // EXPECT_EQ(0, memcmp(pixels_converted_manually.get(), |
+ // pixels_from_get_image_data, output_length)); |
+ output_length--; |
+ } |
+ } |
+ } |
+ delete[] u16_pixels; |
+ delete[] f32_pixels; |
+ |
+ RuntimeEnabledFeatures::setExperimentalCanvasFeaturesEnabled( |
+ experimental_canvas_features_runtime_flag); |
+ RuntimeEnabledFeatures::setColorCorrectRenderingEnabled( |
+ color_correct_rendering_runtime_flag); |
+} |
} // namespace blink |