| OLD | NEW | 
|    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  Loading... | 
|   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 | 
| OLD | NEW |