| 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
|
|
|