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

Side by Side 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 unified diff | Download patch
« no previous file with comments | « third_party/WebKit/Source/core/html/ImageData.cpp ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2017 The Chromium Authors. All rights reserved. 1 // Copyright 2017 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "core/html/ImageData.h" 5 #include "core/html/ImageData.h"
6 6
7 #include "core/dom/ExceptionCode.h" 7 #include "core/dom/ExceptionCode.h"
8 #include "platform/geometry/IntSize.h" 8 #include "platform/geometry/IntSize.h"
9 #include "testing/gtest/include/gtest/gtest.h" 9 #include "testing/gtest/include/gtest/gtest.h"
10 #include "third_party/skia/include/core/SkColorSpaceXform.h"
10 11
11 namespace blink { 12 namespace blink {
12 namespace { 13 namespace {
13 14
14 class ImageDataTest : public ::testing::Test { 15 class ImageDataTest : public ::testing::Test {
15 protected: 16 protected:
16 ImageDataTest(){}; 17 virtual void SetUp() {
17 void TearDown(){}; 18 // Save the state of experimental canvas features and color correct
19 // rendering flags to restore them on teardown.
20 experimentalCanvasFeatures =
21 RuntimeEnabledFeatures::experimentalCanvasFeaturesEnabled();
22 colorCorrectRendering =
23 RuntimeEnabledFeatures::colorCorrectRenderingEnabled();
24 RuntimeEnabledFeatures::setExperimentalCanvasFeaturesEnabled(true);
25 RuntimeEnabledFeatures::setColorCorrectRenderingEnabled(true);
26 }
27
28 virtual void TearDown() {
29 RuntimeEnabledFeatures::setExperimentalCanvasFeaturesEnabled(
30 experimentalCanvasFeatures);
31 RuntimeEnabledFeatures::setColorCorrectRenderingEnabled(
32 colorCorrectRendering);
33 }
34
35 bool experimentalCanvasFeatures;
36 bool colorCorrectRendering;
18 }; 37 };
19 38
20 TEST_F(ImageDataTest, NegativeAndZeroIntSizeTest) { 39 TEST_F(ImageDataTest, NegativeAndZeroIntSizeTest) {
21 ImageData* imageData; 40 ImageData* imageData;
22 41
23 imageData = ImageData::create(IntSize(0, 10)); 42 imageData = ImageData::create(IntSize(0, 10));
24 EXPECT_EQ(imageData, nullptr); 43 EXPECT_EQ(imageData, nullptr);
25 44
26 imageData = ImageData::create(IntSize(10, 0)); 45 imageData = ImageData::create(IntSize(10, 0));
27 EXPECT_EQ(imageData, nullptr); 46 EXPECT_EQ(imageData, nullptr);
(...skipping 26 matching lines...) Expand all
54 // allocated to the ImageData, then an exception must raise. 73 // allocated to the ImageData, then an exception must raise.
55 TEST_F(ImageDataTest, MAYBE_CreateImageDataTooBig) { 74 TEST_F(ImageDataTest, MAYBE_CreateImageDataTooBig) {
56 DummyExceptionStateForTesting exceptionState; 75 DummyExceptionStateForTesting exceptionState;
57 ImageData* tooBigImageData = ImageData::create(32767, 32767, exceptionState); 76 ImageData* tooBigImageData = ImageData::create(32767, 32767, exceptionState);
58 if (!tooBigImageData) { 77 if (!tooBigImageData) {
59 EXPECT_TRUE(exceptionState.hadException()); 78 EXPECT_TRUE(exceptionState.hadException());
60 EXPECT_EQ(exceptionState.code(), V8RangeError); 79 EXPECT_EQ(exceptionState.code(), V8RangeError);
61 } 80 }
62 } 81 }
63 82
83 // Skia conversion does not guarantee to be exact, se we need to do approximate
84 // comparisons.
85 static inline bool IsNearlyTheSame(float f, float g) {
86 static const float epsilonScale = 0.01f;
87 return std::abs(f - g) <
88 epsilonScale *
89 std::max(std::max(std::abs(f), std::abs(g)), epsilonScale);
90 }
91
92 // This test verifies the correct behavior of ImageData member function used
93 // to convert pixels data from canvas pixel format to image data storage
94 // format. This function is used in BaseRenderingContext2D::getImageData.
95 TEST_F(ImageDataTest,
96 TestConvertPixelsFromCanvasPixelFormatToImageDataStorageFormat) {
97 // Source pixels in RGBA32
98 unsigned char rgba32Pixels[] = {255, 0, 0, 255, // Red
99 0, 0, 0, 0, // Transparent
100 255, 192, 128, 64, // Decreasing values
101 93, 117, 205, 11}; // Random values
102 const unsigned numColorComponents = 16;
103 // Source pixels in F16
104 unsigned char f16Pixels[numColorComponents * 2];
105
106 // Filling F16 source pixels
107 std::unique_ptr<SkColorSpaceXform> xform =
108 SkColorSpaceXform::New(SkColorSpace::MakeSRGBLinear().get(),
109 SkColorSpace::MakeSRGBLinear().get());
110 xform->apply(SkColorSpaceXform::ColorFormat::kRGBA_F16_ColorFormat, f16Pixels,
111 SkColorSpaceXform::ColorFormat::kRGBA_8888_ColorFormat,
112 rgba32Pixels, 4, SkAlphaType::kUnpremul_SkAlphaType);
113
114 // Creating ArrayBufferContents objects. We need two buffers for RGBA32 data
115 // because kRGBA8CanvasPixelFormat->kUint8ClampedArrayStorageFormat consumes
116 // the input data parameter.
117 WTF::ArrayBufferContents contentsRGBA32(
118 numColorComponents, 1, WTF::ArrayBufferContents::NotShared,
119 WTF::ArrayBufferContents::DontInitialize);
120 std::memcpy(contentsRGBA32.data(), rgba32Pixels, numColorComponents);
121
122 WTF::ArrayBufferContents contents2RGBA32(
123 numColorComponents, 1, WTF::ArrayBufferContents::NotShared,
124 WTF::ArrayBufferContents::DontInitialize);
125 std::memcpy(contents2RGBA32.data(), rgba32Pixels, numColorComponents);
126
127 WTF::ArrayBufferContents contentsF16(
128 numColorComponents * 2, 1, WTF::ArrayBufferContents::NotShared,
129 WTF::ArrayBufferContents::DontInitialize);
130 std::memcpy(contentsF16.data(), f16Pixels, numColorComponents * 2);
131
132 // Uint16 is not supported as the storage format for ImageData created from a
133 // canvas, so this conversion is neither implemented nor tested here.
134 bool testPassed = true;
135 DOMArrayBufferView* data = nullptr;
136 DOMUint8ClampedArray* dataU8 = nullptr;
137 DOMFloat32Array* dataF32 = nullptr;
138
139 // Testing kRGBA8CanvasPixelFormat -> kUint8ClampedArrayStorageFormat
140 data = ImageData::convertPixelsFromCanvasPixelFormatToImageDataStorageFormat(
141 contentsRGBA32, kRGBA8CanvasPixelFormat, kUint8ClampedArrayStorageFormat);
142 DCHECK(data->type() == DOMArrayBufferView::ViewType::TypeUint8Clamped);
143 dataU8 = const_cast<DOMUint8ClampedArray*>(
144 static_cast<const DOMUint8ClampedArray*>(data));
145 DCHECK(dataU8);
146 for (unsigned i = 0; i < numColorComponents; i++) {
147 if (dataU8->item(i) != rgba32Pixels[i]) {
148 testPassed = false;
149 break;
150 }
151 }
152 EXPECT_TRUE(testPassed);
153
154 // Testing kRGBA8CanvasPixelFormat -> kFloat32ArrayStorageFormat
155 data = ImageData::convertPixelsFromCanvasPixelFormatToImageDataStorageFormat(
156 contents2RGBA32, kRGBA8CanvasPixelFormat, kFloat32ArrayStorageFormat);
157 DCHECK(data->type() == DOMArrayBufferView::ViewType::TypeFloat32);
158 dataF32 =
159 const_cast<DOMFloat32Array*>(static_cast<const DOMFloat32Array*>(data));
160 DCHECK(dataF32);
161 for (unsigned i = 0; i < numColorComponents; i++) {
162 if (!IsNearlyTheSame(dataF32->item(i), rgba32Pixels[i] / 255.0)) {
163 testPassed = false;
164 break;
165 }
166 }
167 EXPECT_TRUE(testPassed);
168
169 // Testing kF16CanvasPixelFormat -> kUint8ClampedArrayStorageFormat
170 data = ImageData::convertPixelsFromCanvasPixelFormatToImageDataStorageFormat(
171 contentsF16, kF16CanvasPixelFormat, kUint8ClampedArrayStorageFormat);
172 DCHECK(data->type() == DOMArrayBufferView::ViewType::TypeUint8Clamped);
173 dataU8 = const_cast<DOMUint8ClampedArray*>(
174 static_cast<const DOMUint8ClampedArray*>(data));
175 DCHECK(dataU8);
176 for (unsigned i = 0; i < numColorComponents; i++) {
177 if (!IsNearlyTheSame(dataU8->item(i), rgba32Pixels[i])) {
178 testPassed = false;
179 break;
180 }
181 }
182 EXPECT_TRUE(testPassed);
183
184 // Testing kF16CanvasPixelFormat -> kFloat32ArrayStorageFormat
185 data = ImageData::convertPixelsFromCanvasPixelFormatToImageDataStorageFormat(
186 contentsF16, kF16CanvasPixelFormat, kFloat32ArrayStorageFormat);
187 DCHECK(data->type() == DOMArrayBufferView::ViewType::TypeFloat32);
188 dataF32 =
189 const_cast<DOMFloat32Array*>(static_cast<const DOMFloat32Array*>(data));
190 DCHECK(dataF32);
191 for (unsigned i = 0; i < numColorComponents; i++) {
192 if (!IsNearlyTheSame(dataF32->item(i), rgba32Pixels[i] / 255.0)) {
193 testPassed = false;
194 break;
195 }
196 }
197 EXPECT_TRUE(testPassed);
198 }
199
200 bool convertPixelsToColorSpaceAndPixelFormatForTest(
201 DOMArrayBufferView* dataArray,
202 CanvasColorSpace srcColorSpace,
203 CanvasColorSpace dstColorSpace,
204 CanvasPixelFormat dstPixelFormat,
205 std::unique_ptr<uint8_t[]>& convertedPixels) {
206 // Setting SkColorSpaceXform::apply parameters
207 SkColorSpaceXform::ColorFormat srcColorFormat =
208 SkColorSpaceXform::kRGBA_8888_ColorFormat;
209
210 unsigned dataLength = dataArray->byteLength() / dataArray->typeSize();
211 unsigned numPixels = dataLength / 4;
212 DOMUint8ClampedArray* u8Array = nullptr;
213 DOMUint16Array* u16Array = nullptr;
214 DOMFloat32Array* f32Array = nullptr;
215 void* srcData = nullptr;
216
217 switch (dataArray->type()) {
218 case ArrayBufferView::ViewType::TypeUint8Clamped:
219 u8Array = const_cast<DOMUint8ClampedArray*>(
220 static_cast<const DOMUint8ClampedArray*>(dataArray));
221 srcData = static_cast<void*>(u8Array->data());
222 break;
223
224 case ArrayBufferView::ViewType::TypeUint16:
225 u16Array = const_cast<DOMUint16Array*>(
226 static_cast<const DOMUint16Array*>(dataArray));
227 srcColorFormat = SkColorSpaceXform::ColorFormat::kRGBA_U16_BE_ColorFormat;
228 srcData = static_cast<void*>(u16Array->data());
229 break;
230
231 case ArrayBufferView::ViewType::TypeFloat32:
232 f32Array = const_cast<DOMFloat32Array*>(
233 static_cast<const DOMFloat32Array*>(dataArray));
234 srcColorFormat = SkColorSpaceXform::kRGBA_F32_ColorFormat;
235 srcData = static_cast<void*>(f32Array->data());
236 break;
237 default:
238 NOTREACHED();
239 return false;
240 }
241
242 SkColorSpaceXform::ColorFormat dstColorFormat =
243 SkColorSpaceXform::ColorFormat::kRGBA_8888_ColorFormat;
244 if (dstPixelFormat == kF16CanvasPixelFormat)
245 dstColorFormat = SkColorSpaceXform::ColorFormat::kRGBA_F16_ColorFormat;
246
247 sk_sp<SkColorSpace> srcSkColorSpace = nullptr;
248 if (u8Array) {
249 srcSkColorSpace = ImageData::getSkColorSpaceForTest(
250 srcColorSpace, kRGBA8CanvasPixelFormat);
251 } else {
252 srcSkColorSpace =
253 ImageData::getSkColorSpaceForTest(srcColorSpace, kF16CanvasPixelFormat);
254 }
255
256 sk_sp<SkColorSpace> dstSkColorSpace =
257 ImageData::getSkColorSpaceForTest(dstColorSpace, dstPixelFormat);
258
259 // When the input dataArray is in Uint16, we normally should convert the
260 // values from Little Endian to Big Endian before passing the buffer to
261 // SkColorSpaceXform::apply. However, in this test scenario we are creating
262 // the Uin16 dataArray by multiplying a Uint8Clamped array members by 257,
263 // hence the Big Endian and Little Endian representations are the same.
264
265 std::unique_ptr<SkColorSpaceXform> xform =
266 SkColorSpaceXform::New(srcSkColorSpace.get(), dstSkColorSpace.get());
267
268 if (!xform->apply(dstColorFormat, convertedPixels.get(), srcColorFormat,
269 srcData, numPixels, kUnpremul_SkAlphaType))
270 return false;
271 return true;
272 }
273
274 // This test verifies the correct behavior of ImageData member function used
275 // to convert image data from image data storage format to canvas pixel format.
276 // This function is used in BaseRenderingContext2D::putImageData.
277 TEST_F(ImageDataTest, TestGetImageDataInCanvasColorSettings) {
278 unsigned numImageDataColorSpaces = 3;
279 CanvasColorSpace imageDataColorSpaces[] = {
280 kSRGBCanvasColorSpace, kRec2020CanvasColorSpace, kP3CanvasColorSpace,
281 };
282
283 unsigned numImageDataStorageFormats = 3;
284 ImageDataStorageFormat imageDataStorageFormats[] = {
285 kUint8ClampedArrayStorageFormat, kUint16ArrayStorageFormat,
286 kFloat32ArrayStorageFormat,
287 };
288
289 unsigned numCanvasColorSettings = 4;
290 CanvasColorSpace canvasColorSpaces[] = {
291 kSRGBCanvasColorSpace, kSRGBCanvasColorSpace, kRec2020CanvasColorSpace,
292 kP3CanvasColorSpace,
293 };
294 CanvasPixelFormat canvasPixelFormats[] = {
295 kRGBA8CanvasPixelFormat, kF16CanvasPixelFormat, kF16CanvasPixelFormat,
296 kF16CanvasPixelFormat,
297 };
298
299 // As cross checking the output of Skia color space covnersion API is not in
300 // the scope of this unit test, we do turn-around tests here. To do so, we
301 // create an ImageData with the selected color space and storage format,
302 // convert it to the target canvas color space and pixel format by calling
303 // ImageData::imageDataInCanvasColorSettings(), and then convert it back
304 // to the source image data color space and Float32 storage format by calling
305 // ImageData::convertPixelsFromCanvasPixelFormatToImageDataStorageFormat().
306 // We expect to get the same image data as we started with.
307
308 // Source pixels in RGBA32
309 uint8_t u8Pixels[] = {255, 0, 0, 255, // Red
310 0, 0, 0, 0, // Transparent
311 255, 192, 128, 64, // Decreasing values
312 93, 117, 205, 11}; // Random values
313 unsigned dataLength = 16;
314
315 uint16_t* u16Pixels = new uint16_t[dataLength];
316 for (unsigned i = 0; i < dataLength; i++)
317 u16Pixels[i] = u8Pixels[i] * 257;
318
319 float* f32Pixels = new float[dataLength];
320 for (unsigned i = 0; i < dataLength; i++)
321 f32Pixels[i] = u8Pixels[i] / 255.0;
322
323 DOMArrayBufferView* dataArray = nullptr;
324
325 DOMUint8ClampedArray* dataU8 =
326 DOMUint8ClampedArray::create(u8Pixels, dataLength);
327 DCHECK(dataU8);
328 EXPECT_EQ(dataLength, dataU8->length());
329 DOMUint16Array* dataU16 = DOMUint16Array::create(u16Pixels, dataLength);
330 DCHECK(dataU16);
331 EXPECT_EQ(dataLength, dataU16->length());
332 DOMFloat32Array* dataF32 = DOMFloat32Array::create(f32Pixels, dataLength);
333 DCHECK(dataF32);
334 EXPECT_EQ(dataLength, dataF32->length());
335
336 ImageData* imageData = nullptr;
337 ImageDataColorSettings colorSettings;
338
339 // At most two bytes are needed for output per color component.
340 std::unique_ptr<uint8_t[]> pixelsConvertedManually(
341 new uint8_t[dataLength * 2]());
342 std::unique_ptr<uint8_t[]> pixelsConvertedInImageData(
343 new uint8_t[dataLength * 2]());
344
345 // Loop through different possible combinations of image data color space and
346 // storage formats and create the respective test image data objects.
347 for (unsigned i = 0; i < numImageDataColorSpaces; i++) {
348 colorSettings.setColorSpace(
349 ImageData::canvasColorSpaceName(imageDataColorSpaces[i]));
350
351 for (unsigned j = 0; j < numImageDataStorageFormats; j++) {
352 switch (imageDataStorageFormats[j]) {
353 case kUint8ClampedArrayStorageFormat:
354 dataArray = static_cast<DOMArrayBufferView*>(dataU8);
355 colorSettings.setStorageFormat(kUint8ClampedArrayStorageFormatName);
356 break;
357 case kUint16ArrayStorageFormat:
358 dataArray = static_cast<DOMArrayBufferView*>(dataU16);
359 colorSettings.setStorageFormat(kUint16ArrayStorageFormatName);
360 break;
361 case kFloat32ArrayStorageFormat:
362 dataArray = static_cast<DOMArrayBufferView*>(dataF32);
363 colorSettings.setStorageFormat(kFloat32ArrayStorageFormatName);
364 break;
365 default:
366 NOTREACHED();
367 }
368
369 imageData =
370 ImageData::createForTest(IntSize(2, 2), dataArray, &colorSettings);
371
372 for (unsigned k = 0; k < numCanvasColorSettings; k++) {
373 unsigned outputLength =
374 (canvasPixelFormats[k] == kRGBA8CanvasPixelFormat) ? dataLength
375 : dataLength * 2;
376 // Convert the original data used to create ImageData to the
377 // canvas color space and canvas pixel format.
378 EXPECT_TRUE(convertPixelsToColorSpaceAndPixelFormatForTest(
379 dataArray, imageDataColorSpaces[i], canvasColorSpaces[k],
380 canvasPixelFormats[k], pixelsConvertedManually));
381
382 // Convert the image data to the color settings of the canvas.
383 EXPECT_TRUE(imageData->imageDataInCanvasColorSettings(
384 canvasColorSpaces[k], canvasPixelFormats[k],
385 pixelsConvertedInImageData));
386
387 // Compare the converted pixels
388 EXPECT_EQ(0, memcmp(pixelsConvertedManually.get(),
389 pixelsConvertedInImageData.get(), outputLength));
390 }
391 }
392 }
393 delete[] u16Pixels;
394 delete[] f32Pixels;
395 }
396
64 } // namspace 397 } // namspace
65 } // namespace blink 398 } // namespace blink
OLDNEW
« 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