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

Unified Diff: third_party/WebKit/Source/core/html/ImageDataTest.cpp

Issue 2771813003: Prepare ImageData for color managed BaseRenderingContext2D::create/put/get-ImageData (Closed)
Patch Set: Fixing issues 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
« no previous file with comments | « third_party/WebKit/Source/core/html/ImageData.cpp ('k') | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: third_party/WebKit/Source/core/html/ImageDataTest.cpp
diff --git a/third_party/WebKit/Source/core/html/ImageDataTest.cpp b/third_party/WebKit/Source/core/html/ImageDataTest.cpp
index 906f56299ab0e3b8a2064811dec5ecc284618241..82d9a8a4a17978e127508ccd7142e856518e887a 100644
--- a/third_party/WebKit/Source/core/html/ImageDataTest.cpp
+++ b/third_party/WebKit/Source/core/html/ImageDataTest.cpp
@@ -7,14 +7,33 @@
#include "core/dom/ExceptionCode.h"
#include "platform/geometry/IntSize.h"
#include "testing/gtest/include/gtest/gtest.h"
+#include "third_party/skia/include/core/SkColorSpaceXform.h"
namespace blink {
namespace {
class ImageDataTest : public ::testing::Test {
protected:
- ImageDataTest(){};
- void TearDown(){};
+ virtual void SetUp() {
+ // Save the state of experimental canvas features and color correct
+ // rendering flags to restore them on teardown.
+ experimentalCanvasFeatures =
+ RuntimeEnabledFeatures::experimentalCanvasFeaturesEnabled();
+ colorCorrectRendering =
+ RuntimeEnabledFeatures::colorCorrectRenderingEnabled();
+ RuntimeEnabledFeatures::setExperimentalCanvasFeaturesEnabled(true);
+ RuntimeEnabledFeatures::setColorCorrectRenderingEnabled(true);
+ }
+
+ virtual void TearDown() {
+ RuntimeEnabledFeatures::setExperimentalCanvasFeaturesEnabled(
+ experimentalCanvasFeatures);
+ RuntimeEnabledFeatures::setColorCorrectRenderingEnabled(
+ colorCorrectRendering);
+ }
+
+ bool experimentalCanvasFeatures;
+ bool colorCorrectRendering;
};
TEST_F(ImageDataTest, NegativeAndZeroIntSizeTest) {
@@ -61,5 +80,319 @@ TEST_F(ImageDataTest, MAYBE_CreateImageDataTooBig) {
}
}
+// Skia conversion does not guarantee to be exact, se we need to do approximate
+// comparisons.
+static inline bool IsNearlyTheSame(float f, float g) {
+ static const float epsilonScale = 0.01f;
+ return std::abs(f - g) <
+ epsilonScale *
+ std::max(std::max(std::abs(f), std::abs(g)), epsilonScale);
+}
+
+// This test verifies the correct behavior of ImageData member function used
+// to convert pixels data from canvas pixel format to image data storage
+// format. This function is used in BaseRenderingContext2D::getImageData.
+TEST_F(ImageDataTest,
+ TestConvertPixelsFromCanvasPixelFormatToImageDataStorageFormat) {
+ // Source pixels in RGBA32
+ unsigned char rgba32Pixels[] = {255, 0, 0, 255, // Red
+ 0, 0, 0, 0, // Transparent
+ 255, 192, 128, 64, // Decreasing values
+ 93, 117, 205, 11}; // Random values
+ const unsigned numColorComponents = 16;
+ // Source pixels in F16
+ unsigned char f16Pixels[numColorComponents * 2];
+
+ // Filling F16 source pixels
+ std::unique_ptr<SkColorSpaceXform> xform =
+ SkColorSpaceXform::New(SkColorSpace::MakeSRGBLinear().get(),
+ SkColorSpace::MakeSRGBLinear().get());
+ xform->apply(SkColorSpaceXform::ColorFormat::kRGBA_F16_ColorFormat, f16Pixels,
+ SkColorSpaceXform::ColorFormat::kRGBA_8888_ColorFormat,
+ rgba32Pixels, 4, SkAlphaType::kUnpremul_SkAlphaType);
+
+ // Creating ArrayBufferContents objects. We need two buffers for RGBA32 data
+ // because kRGBA8CanvasPixelFormat->kUint8ClampedArrayStorageFormat consumes
+ // the input data parameter.
+ WTF::ArrayBufferContents contentsRGBA32(
+ numColorComponents, 1, WTF::ArrayBufferContents::NotShared,
+ WTF::ArrayBufferContents::DontInitialize);
+ std::memcpy(contentsRGBA32.data(), rgba32Pixels, numColorComponents);
+
+ WTF::ArrayBufferContents contents2RGBA32(
+ numColorComponents, 1, WTF::ArrayBufferContents::NotShared,
+ WTF::ArrayBufferContents::DontInitialize);
+ std::memcpy(contents2RGBA32.data(), rgba32Pixels, numColorComponents);
+
+ WTF::ArrayBufferContents contentsF16(
+ numColorComponents * 2, 1, WTF::ArrayBufferContents::NotShared,
+ WTF::ArrayBufferContents::DontInitialize);
+ std::memcpy(contentsF16.data(), f16Pixels, numColorComponents * 2);
+
+ // Uint16 is not supported as the storage format for ImageData created from a
+ // canvas, so this conversion is neither implemented nor tested here.
+ bool testPassed = true;
+ DOMArrayBufferView* data = nullptr;
+ DOMUint8ClampedArray* dataU8 = nullptr;
+ DOMFloat32Array* dataF32 = nullptr;
+
+ // Testing kRGBA8CanvasPixelFormat -> kUint8ClampedArrayStorageFormat
+ data = ImageData::convertPixelsFromCanvasPixelFormatToImageDataStorageFormat(
+ contentsRGBA32, kRGBA8CanvasPixelFormat, kUint8ClampedArrayStorageFormat);
+ DCHECK(data->type() == DOMArrayBufferView::ViewType::TypeUint8Clamped);
+ dataU8 = const_cast<DOMUint8ClampedArray*>(
+ static_cast<const DOMUint8ClampedArray*>(data));
+ DCHECK(dataU8);
+ for (unsigned i = 0; i < numColorComponents; i++) {
+ if (dataU8->item(i) != rgba32Pixels[i]) {
+ testPassed = false;
+ break;
+ }
+ }
+ EXPECT_TRUE(testPassed);
+
+ // Testing kRGBA8CanvasPixelFormat -> kFloat32ArrayStorageFormat
+ data = ImageData::convertPixelsFromCanvasPixelFormatToImageDataStorageFormat(
+ contents2RGBA32, kRGBA8CanvasPixelFormat, kFloat32ArrayStorageFormat);
+ DCHECK(data->type() == DOMArrayBufferView::ViewType::TypeFloat32);
+ dataF32 =
+ const_cast<DOMFloat32Array*>(static_cast<const DOMFloat32Array*>(data));
+ DCHECK(dataF32);
+ for (unsigned i = 0; i < numColorComponents; i++) {
+ if (!IsNearlyTheSame(dataF32->item(i), rgba32Pixels[i] / 255.0)) {
+ testPassed = false;
+ break;
+ }
+ }
+ EXPECT_TRUE(testPassed);
+
+ // Testing kF16CanvasPixelFormat -> kUint8ClampedArrayStorageFormat
+ data = ImageData::convertPixelsFromCanvasPixelFormatToImageDataStorageFormat(
+ contentsF16, kF16CanvasPixelFormat, kUint8ClampedArrayStorageFormat);
+ DCHECK(data->type() == DOMArrayBufferView::ViewType::TypeUint8Clamped);
+ dataU8 = const_cast<DOMUint8ClampedArray*>(
+ static_cast<const DOMUint8ClampedArray*>(data));
+ DCHECK(dataU8);
+ for (unsigned i = 0; i < numColorComponents; i++) {
+ if (!IsNearlyTheSame(dataU8->item(i), rgba32Pixels[i])) {
+ testPassed = false;
+ break;
+ }
+ }
+ EXPECT_TRUE(testPassed);
+
+ // Testing kF16CanvasPixelFormat -> kFloat32ArrayStorageFormat
+ data = ImageData::convertPixelsFromCanvasPixelFormatToImageDataStorageFormat(
+ contentsF16, kF16CanvasPixelFormat, kFloat32ArrayStorageFormat);
+ DCHECK(data->type() == DOMArrayBufferView::ViewType::TypeFloat32);
+ dataF32 =
+ const_cast<DOMFloat32Array*>(static_cast<const DOMFloat32Array*>(data));
+ DCHECK(dataF32);
+ for (unsigned i = 0; i < numColorComponents; i++) {
+ if (!IsNearlyTheSame(dataF32->item(i), rgba32Pixels[i] / 255.0)) {
+ testPassed = false;
+ break;
+ }
+ }
+ EXPECT_TRUE(testPassed);
+}
+
+bool convertPixelsToColorSpaceAndPixelFormatForTest(
+ DOMArrayBufferView* dataArray,
+ CanvasColorSpace srcColorSpace,
+ CanvasColorSpace dstColorSpace,
+ CanvasPixelFormat dstPixelFormat,
+ std::unique_ptr<uint8_t[]>& convertedPixels) {
+ // Setting SkColorSpaceXform::apply parameters
+ SkColorSpaceXform::ColorFormat srcColorFormat =
+ SkColorSpaceXform::kRGBA_8888_ColorFormat;
+
+ unsigned dataLength = dataArray->byteLength() / dataArray->typeSize();
+ unsigned numPixels = dataLength / 4;
+ DOMUint8ClampedArray* u8Array = nullptr;
+ DOMUint16Array* u16Array = nullptr;
+ DOMFloat32Array* f32Array = nullptr;
+ void* srcData = nullptr;
+
+ switch (dataArray->type()) {
+ case ArrayBufferView::ViewType::TypeUint8Clamped:
+ u8Array = const_cast<DOMUint8ClampedArray*>(
+ static_cast<const DOMUint8ClampedArray*>(dataArray));
+ srcData = static_cast<void*>(u8Array->data());
+ break;
+
+ case ArrayBufferView::ViewType::TypeUint16:
+ u16Array = const_cast<DOMUint16Array*>(
+ static_cast<const DOMUint16Array*>(dataArray));
+ srcColorFormat = SkColorSpaceXform::ColorFormat::kRGBA_U16_BE_ColorFormat;
+ srcData = static_cast<void*>(u16Array->data());
+ break;
+
+ case ArrayBufferView::ViewType::TypeFloat32:
+ f32Array = const_cast<DOMFloat32Array*>(
+ static_cast<const DOMFloat32Array*>(dataArray));
+ srcColorFormat = SkColorSpaceXform::kRGBA_F32_ColorFormat;
+ srcData = static_cast<void*>(f32Array->data());
+ break;
+ default:
+ NOTREACHED();
+ return false;
+ }
+
+ SkColorSpaceXform::ColorFormat dstColorFormat =
+ SkColorSpaceXform::ColorFormat::kRGBA_8888_ColorFormat;
+ if (dstPixelFormat == kF16CanvasPixelFormat)
+ dstColorFormat = SkColorSpaceXform::ColorFormat::kRGBA_F16_ColorFormat;
+
+ sk_sp<SkColorSpace> srcSkColorSpace = nullptr;
+ if (u8Array) {
+ srcSkColorSpace = ImageData::getSkColorSpaceForTest(
+ srcColorSpace, kRGBA8CanvasPixelFormat);
+ } else {
+ srcSkColorSpace =
+ ImageData::getSkColorSpaceForTest(srcColorSpace, kF16CanvasPixelFormat);
+ }
+
+ sk_sp<SkColorSpace> dstSkColorSpace =
+ ImageData::getSkColorSpaceForTest(dstColorSpace, dstPixelFormat);
+
+ // 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(srcSkColorSpace.get(), dstSkColorSpace.get());
+
+ if (!xform->apply(dstColorFormat, convertedPixels.get(), srcColorFormat,
+ srcData, numPixels, kUnpremul_SkAlphaType))
+ return false;
+ return true;
+}
+
+// This test verifies the correct behavior of ImageData member function used
+// to convert image data from image data storage format to canvas pixel format.
+// This function is used in BaseRenderingContext2D::putImageData.
+TEST_F(ImageDataTest, TestGetImageDataInCanvasColorSettings) {
+ unsigned numImageDataColorSpaces = 3;
+ CanvasColorSpace imageDataColorSpaces[] = {
+ kSRGBCanvasColorSpace, kRec2020CanvasColorSpace, kP3CanvasColorSpace,
+ };
+
+ unsigned numImageDataStorageFormats = 3;
+ ImageDataStorageFormat imageDataStorageFormats[] = {
+ kUint8ClampedArrayStorageFormat, kUint16ArrayStorageFormat,
+ kFloat32ArrayStorageFormat,
+ };
+
+ unsigned numCanvasColorSettings = 4;
+ CanvasColorSpace canvasColorSpaces[] = {
+ kSRGBCanvasColorSpace, kSRGBCanvasColorSpace, kRec2020CanvasColorSpace,
+ kP3CanvasColorSpace,
+ };
+ CanvasPixelFormat canvasPixelFormats[] = {
+ kRGBA8CanvasPixelFormat, kF16CanvasPixelFormat, kF16CanvasPixelFormat,
+ kF16CanvasPixelFormat,
+ };
+
+ // As cross checking the output of Skia color space covnersion API is not in
+ // the scope of this unit test, we do turn-around tests here. To do so, we
+ // create an ImageData with the selected color space and storage format,
+ // convert it to the target canvas color space and pixel format by calling
+ // ImageData::imageDataInCanvasColorSettings(), and then convert it back
+ // to the source image data color space and Float32 storage format by calling
+ // ImageData::convertPixelsFromCanvasPixelFormatToImageDataStorageFormat().
+ // We expect to get the same image data as we started with.
+
+ // Source pixels in RGBA32
+ uint8_t u8Pixels[] = {255, 0, 0, 255, // Red
+ 0, 0, 0, 0, // Transparent
+ 255, 192, 128, 64, // Decreasing values
+ 93, 117, 205, 11}; // Random values
+ unsigned dataLength = 16;
+
+ uint16_t* u16Pixels = new uint16_t[dataLength];
+ for (unsigned i = 0; i < dataLength; i++)
+ u16Pixels[i] = u8Pixels[i] * 257;
+
+ float* f32Pixels = new float[dataLength];
+ for (unsigned i = 0; i < dataLength; i++)
+ f32Pixels[i] = u8Pixels[i] / 255.0;
+
+ DOMArrayBufferView* dataArray = nullptr;
+
+ DOMUint8ClampedArray* dataU8 =
+ DOMUint8ClampedArray::create(u8Pixels, dataLength);
+ DCHECK(dataU8);
+ EXPECT_EQ(dataLength, dataU8->length());
+ DOMUint16Array* dataU16 = DOMUint16Array::create(u16Pixels, dataLength);
+ DCHECK(dataU16);
+ EXPECT_EQ(dataLength, dataU16->length());
+ DOMFloat32Array* dataF32 = DOMFloat32Array::create(f32Pixels, dataLength);
+ DCHECK(dataF32);
+ EXPECT_EQ(dataLength, dataF32->length());
+
+ ImageData* imageData = nullptr;
+ ImageDataColorSettings colorSettings;
+
+ // At most two bytes are needed for output per color component.
+ std::unique_ptr<uint8_t[]> pixelsConvertedManually(
+ new uint8_t[dataLength * 2]());
+ std::unique_ptr<uint8_t[]> pixelsConvertedInImageData(
+ new uint8_t[dataLength * 2]());
+
+ // 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 < numImageDataColorSpaces; i++) {
+ colorSettings.setColorSpace(
+ ImageData::canvasColorSpaceName(imageDataColorSpaces[i]));
+
+ for (unsigned j = 0; j < numImageDataStorageFormats; j++) {
+ switch (imageDataStorageFormats[j]) {
+ case kUint8ClampedArrayStorageFormat:
+ dataArray = static_cast<DOMArrayBufferView*>(dataU8);
+ colorSettings.setStorageFormat(kUint8ClampedArrayStorageFormatName);
+ break;
+ case kUint16ArrayStorageFormat:
+ dataArray = static_cast<DOMArrayBufferView*>(dataU16);
+ colorSettings.setStorageFormat(kUint16ArrayStorageFormatName);
+ break;
+ case kFloat32ArrayStorageFormat:
+ dataArray = static_cast<DOMArrayBufferView*>(dataF32);
+ colorSettings.setStorageFormat(kFloat32ArrayStorageFormatName);
+ break;
+ default:
+ NOTREACHED();
+ }
+
+ imageData =
+ ImageData::createForTest(IntSize(2, 2), dataArray, &colorSettings);
+
+ for (unsigned k = 0; k < numCanvasColorSettings; k++) {
+ unsigned outputLength =
+ (canvasPixelFormats[k] == kRGBA8CanvasPixelFormat) ? dataLength
+ : dataLength * 2;
+ // Convert the original data used to create ImageData to the
+ // canvas color space and canvas pixel format.
+ EXPECT_TRUE(convertPixelsToColorSpaceAndPixelFormatForTest(
+ dataArray, imageDataColorSpaces[i], canvasColorSpaces[k],
+ canvasPixelFormats[k], pixelsConvertedManually));
+
+ // Convert the image data to the color settings of the canvas.
+ EXPECT_TRUE(imageData->imageDataInCanvasColorSettings(
+ canvasColorSpaces[k], canvasPixelFormats[k],
+ pixelsConvertedInImageData));
+
+ // Compare the converted pixels
+ EXPECT_EQ(0, memcmp(pixelsConvertedManually.get(),
+ pixelsConvertedInImageData.get(), outputLength));
+ }
+ }
+ }
+ delete[] u16Pixels;
+ delete[] f32Pixels;
+}
+
} // namspace
} // namespace blink
« no previous file with comments | « third_party/WebKit/Source/core/html/ImageData.cpp ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698