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

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

Issue 2771813003: Prepare ImageData for color managed BaseRenderingContext2D::create/put/get-ImageData (Closed)
Patch Set: Unit test added Created 3 years, 9 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
Index: third_party/WebKit/Source/core/html/ImageData.cpp
diff --git a/third_party/WebKit/Source/core/html/ImageData.cpp b/third_party/WebKit/Source/core/html/ImageData.cpp
index 6e6f3eb28dd897992d7304b9c0bceeee2dd997e3..4498c2a13db7f2c389f6d42c0d03415ee8ba6ba3 100644
--- a/third_party/WebKit/Source/core/html/ImageData.cpp
+++ b/third_party/WebKit/Source/core/html/ImageData.cpp
@@ -35,6 +35,7 @@
#include "core/imagebitmap/ImageBitmapOptions.h"
#include "platform/RuntimeEnabledFeatures.h"
#include "platform/graphics/ColorBehavior.h"
+#include "third_party/skia/include/core/SkColorSpaceXform.h"
namespace blink {
@@ -46,12 +47,14 @@ bool RaiseDOMExceptionAndReturnFalse(ExceptionState* exceptionState,
return false;
}
-bool ImageData::validateConstructorArguments(const unsigned& paramFlags,
- const IntSize* size,
- const unsigned& width,
- const unsigned& height,
- const DOMArrayBufferView* data,
- ExceptionState* exceptionState) {
+bool ImageData::validateConstructorArguments(
+ const unsigned& paramFlags,
+ const IntSize* size,
+ const unsigned& width,
+ const unsigned& height,
+ const DOMArrayBufferView* data,
+ const ImageDataColorSettings* colorSettings,
+ ExceptionState* exceptionState) {
// We accept all the combinations of colorSpace and storageFormat in an
// ImageDataColorSettings to be stored in an ImageData. Therefore, we don't
// check the color settings in this function.
@@ -70,6 +73,10 @@ bool ImageData::validateConstructorArguments(const unsigned& paramFlags,
if (paramFlags & (kParamWidth | kParamHeight)) {
CheckedNumeric<unsigned> dataSize = 4;
+ if (colorSettings) {
+ dataSize *=
+ ImageData::getStorageFormatDataSize(colorSettings->storageFormat());
+ }
dataSize *= width;
dataSize *= height;
if (!dataSize.IsValid())
@@ -81,27 +88,20 @@ bool ImageData::validateConstructorArguments(const unsigned& paramFlags,
unsigned dataLength = 0;
if (paramFlags & kParamData) {
DCHECK(data);
- switch (data->type()) {
- case DOMArrayBufferView::ViewType::TypeUint8Clamped:
- dataLength = data->view()->byteLength();
- break;
- case DOMArrayBufferView::ViewType::TypeUint16:
- dataLength = data->view()->byteLength() / 2;
- break;
- case DOMArrayBufferView::ViewType::TypeFloat32:
- dataLength = data->view()->byteLength() / 4;
- break;
- default:
- return RaiseDOMExceptionAndReturnFalse(
- exceptionState, NotSupportedError,
- "The input data type is not supported.");
+ if (data->type() != DOMArrayBufferView::ViewType::TypeUint8Clamped &&
+ data->type() != DOMArrayBufferView::ViewType::TypeUint16 &&
+ data->type() != DOMArrayBufferView::ViewType::TypeFloat32) {
+ return RaiseDOMExceptionAndReturnFalse(
+ exceptionState, NotSupportedError,
+ "The input data type is not supported.");
}
- if (!dataLength) {
+ if (!data->byteLength()) {
return RaiseDOMExceptionAndReturnFalse(
exceptionState, IndexSizeError, "The input data has zero elements.");
}
+ dataLength = data->byteLength() / data->typeSize();
if (dataLength % 4) {
return RaiseDOMExceptionAndReturnFalse(
exceptionState, IndexSizeError,
@@ -146,28 +146,21 @@ DOMArrayBufferView* ImageData::allocateAndValidateDataArray(
return nullptr;
DOMArrayBufferView* dataArray = nullptr;
- unsigned dataLength = 0;
- unsigned dataItemLength = 1;
switch (storageFormat) {
case kUint8ClampedArrayStorageFormat:
dataArray = DOMUint8ClampedArray::createOrNull(length);
break;
case kUint16ArrayStorageFormat:
dataArray = DOMUint16Array::createOrNull(length);
- dataItemLength = 2;
break;
case kFloat32ArrayStorageFormat:
dataArray = DOMFloat32Array::createOrNull(length);
- dataItemLength = 4;
break;
default:
NOTREACHED();
}
- if (dataArray)
- dataLength = dataArray->view()->byteLength() / dataItemLength;
-
- if (!dataArray || length != dataLength) {
+ if (!dataArray || length != dataArray->byteLength() / dataArray->typeSize()) {
if (exceptionState)
exceptionState->throwDOMException(V8RangeError,
"Out of memory at ImageData creation");
@@ -177,14 +170,60 @@ DOMArrayBufferView* ImageData::allocateAndValidateDataArray(
return dataArray;
}
-ImageData* ImageData::create(const IntSize& size) {
- if (!ImageData::validateConstructorArguments(kParamSize, &size))
+DOMUint8ClampedArray* ImageData::allocateAndValidateUint8ClampedArray(
+ const unsigned& length,
+ ExceptionState* exceptionState) {
+ DOMArrayBufferView* bufferView = allocateAndValidateDataArray(
+ length, kUint8ClampedArrayStorageFormat, exceptionState);
+ if (!bufferView)
+ return nullptr;
+ DOMUint8ClampedArray* u8Array = const_cast<DOMUint8ClampedArray*>(
+ static_cast<const DOMUint8ClampedArray*>(bufferView));
+ DCHECK(u8Array);
+ return u8Array;
+}
+
+DOMUint16Array* ImageData::allocateAndValidateUint16Array(
+ const unsigned& length,
+ ExceptionState* exceptionState) {
+ DOMArrayBufferView* bufferView = allocateAndValidateDataArray(
+ length, kUint16ArrayStorageFormat, exceptionState);
+ if (!bufferView)
+ return nullptr;
+ DOMUint16Array* u16Array = const_cast<DOMUint16Array*>(
+ static_cast<const DOMUint16Array*>(bufferView));
+ DCHECK(u16Array);
+ return u16Array;
+}
+
+DOMFloat32Array* ImageData::allocateAndValidateFloat32Array(
+ const unsigned& length,
+ ExceptionState* exceptionState) {
+ DOMArrayBufferView* bufferView = allocateAndValidateDataArray(
+ length, kFloat32ArrayStorageFormat, exceptionState);
+ if (!bufferView)
+ return nullptr;
+ DOMFloat32Array* f32Array = const_cast<DOMFloat32Array*>(
+ static_cast<const DOMFloat32Array*>(bufferView));
+ DCHECK(f32Array);
+ return f32Array;
+}
+
+ImageData* ImageData::create(const IntSize& size,
+ const ImageDataColorSettings* colorSettings) {
+ if (!ImageData::validateConstructorArguments(kParamSize, &size, 0, 0, nullptr,
+ colorSettings))
return nullptr;
- DOMArrayBufferView* byteArray =
+ ImageDataStorageFormat storageFormat = kUint8ClampedArrayStorageFormat;
+ if (colorSettings) {
+ storageFormat =
+ ImageData::getImageDataStorageFormat(colorSettings->storageFormat());
+ }
+ DOMArrayBufferView* dataArray =
allocateAndValidateDataArray(4 * static_cast<unsigned>(size.width()) *
static_cast<unsigned>(size.height()),
kUint8ClampedArrayStorageFormat);
- return byteArray ? new ImageData(size, byteArray) : nullptr;
+ return dataArray ? new ImageData(size, dataArray) : nullptr;
}
// This function accepts size (0, 0) and always returns the ImageData in
@@ -205,12 +244,15 @@ ImageData* ImageData::createForTest(const IntSize& size) {
}
ImageData* ImageData::create(const IntSize& size,
- DOMUint8ClampedArray* byteArray) {
+ DOMArrayBufferView* dataArray,
+ const ImageDataColorSettings* colorSettings) {
+ LOG(ERROR) << "HERE";
if (!ImageData::validateConstructorArguments(kParamSize | kParamData, &size,
- 0, 0, byteArray))
+ 0, 0, dataArray, colorSettings))
return nullptr;
- return new ImageData(size, byteArray);
+ LOG(ERROR) << "HERE";
+ return new ImageData(size, dataArray, colorSettings);
}
ImageData* ImageData::create(unsigned width,
@@ -218,7 +260,7 @@ ImageData* ImageData::create(unsigned width,
ExceptionState& exceptionState) {
if (!ImageData::validateConstructorArguments(kParamWidth | kParamHeight,
nullptr, width, height, nullptr,
- &exceptionState))
+ nullptr, &exceptionState))
return nullptr;
DOMArrayBufferView* byteArray = allocateAndValidateDataArray(
@@ -229,8 +271,9 @@ ImageData* ImageData::create(unsigned width,
ImageData* ImageData::create(DOMUint8ClampedArray* data,
unsigned width,
ExceptionState& exceptionState) {
- if (!ImageData::validateConstructorArguments(
- kParamData | kParamWidth, nullptr, width, 0, data, &exceptionState))
+ if (!ImageData::validateConstructorArguments(kParamData | kParamWidth,
+ nullptr, width, 0, data, nullptr,
+ &exceptionState))
return nullptr;
unsigned height = data->length() / (width * 4);
@@ -243,7 +286,7 @@ ImageData* ImageData::create(DOMUint8ClampedArray* data,
ExceptionState& exceptionState) {
if (!ImageData::validateConstructorArguments(
kParamData | kParamWidth | kParamHeight, nullptr, width, height, data,
- &exceptionState))
+ nullptr, &exceptionState))
return nullptr;
return new ImageData(IntSize(width, height), data);
@@ -256,7 +299,7 @@ ImageData* ImageData::createImageData(
ExceptionState& exceptionState) {
if (!ImageData::validateConstructorArguments(kParamWidth | kParamHeight,
nullptr, width, height, nullptr,
- &exceptionState))
+ &colorSettings, &exceptionState))
return nullptr;
ImageDataStorageFormat storageFormat =
@@ -288,7 +331,7 @@ ImageData* ImageData::createImageData(
if (!ImageData::validateConstructorArguments(
kParamData | kParamWidth | kParamHeight, nullptr, width, height,
- bufferView, &exceptionState))
+ bufferView, &colorSettings, &exceptionState))
return nullptr;
return new ImageData(IntSize(width, height), bufferView, &colorSettings,
@@ -351,6 +394,19 @@ DOMUint8ClampedArray* ImageData::data() {
return nullptr;
}
+CanvasColorSpace ImageData::getCanvasColorSpace(const String& colorSpaceName) {
Justin Novosad 2017/03/27 19:43:48 In blink, don't use the "get" prefix in method nam
zakerinasab 2017/03/30 17:56:11 Done.
+ if (colorSpaceName == kLegacyCanvasColorSpaceName)
+ return kLegacyCanvasColorSpace;
+ if (colorSpaceName == kSRGBCanvasColorSpaceName)
+ return kSRGBCanvasColorSpace;
+ if (colorSpaceName == kRec2020CanvasColorSpaceName)
+ return kRec2020CanvasColorSpace;
+ if (colorSpaceName == kP3CanvasColorSpaceName)
+ return kP3CanvasColorSpace;
+ NOTREACHED();
+ return kSRGBCanvasColorSpace;
+}
+
ImageDataStorageFormat ImageData::getImageDataStorageFormat(
const String& storageFormatName) {
if (storageFormatName == kUint8ClampedArrayStorageFormatName)
@@ -363,6 +419,129 @@ ImageDataStorageFormat ImageData::getImageDataStorageFormat(
return kUint8ClampedArrayStorageFormat;
}
+unsigned ImageData::getStorageFormatDataSize(const String& storageFormatName) {
Justin Novosad 2017/03/27 19:43:48 same here.
zakerinasab 2017/03/30 17:56:11 Done.
+ if (storageFormatName == kUint8ClampedArrayStorageFormatName)
+ return 1;
+ if (storageFormatName == kUint16ArrayStorageFormatName)
+ return 2;
+ if (storageFormatName == kFloat32ArrayStorageFormatName)
+ return 4;
+ NOTREACHED();
+ return 1;
+}
+
+DOMFloat32Array* ImageData::convertFloat16ArrayToFloat32Array(
+ const uint16_t* f16Array,
+ unsigned arrayLength) {
+ if (!f16Array || arrayLength <= 0)
+ return nullptr;
+
+ DOMFloat32Array* f32Array = allocateAndValidateFloat32Array(arrayLength);
+ if (!f32Array)
+ return nullptr;
+
+ std::unique_ptr<SkColorSpaceXform> xform =
+ SkColorSpaceXform::New(SkColorSpace::MakeSRGBLinear().get(),
+ SkColorSpace::MakeSRGBLinear().get());
+ xform->apply(SkColorSpaceXform::ColorFormat::kRGBA_F32_ColorFormat,
+ f32Array->data(),
+ SkColorSpaceXform::ColorFormat::kRGBA_F16_ColorFormat, f16Array,
+ arrayLength, SkAlphaType::kUnpremul_SkAlphaType);
+ return f32Array;
+}
+
+DOMArrayBufferView*
+ImageData::convertPixelsFromCanvasPixelFormatToImageDataStorageFormat(
+ WTF::ArrayBufferContents& content,
+ CanvasPixelFormat pixelFormat,
+ ImageDataStorageFormat storageFormat) {
+ if (!content.sizeInBytes())
+ return nullptr;
+
+ // Uint16 is not supported as the storage format for ImageData created from a
+ // canvas
+ if (storageFormat == kUint16ArrayStorageFormat)
+ return nullptr;
+
+ unsigned numPixels = 0;
+ uint8_t* dataU8 = nullptr;
+ DOMArrayBuffer* arrayBuffer = nullptr;
+ DOMUint8ClampedArray* u8Array = nullptr;
+ DOMFloat32Array* f32Array = nullptr;
+
+ SkColorSpaceXform::ColorFormat srcColorFormat =
+ SkColorSpaceXform::ColorFormat::kRGBA_8888_ColorFormat;
+ SkColorSpaceXform::ColorFormat dstColorFormat =
+ SkColorSpaceXform::ColorFormat::kRGBA_F32_ColorFormat;
+ std::unique_ptr<SkColorSpaceXform> xform =
+ SkColorSpaceXform::New(SkColorSpace::MakeSRGBLinear().get(),
+ SkColorSpace::MakeSRGBLinear().get());
+
+ // To speed up the conversion process, we use SkColorSpaceXform::apply()
+ // wherever appropriate.
+ switch (pixelFormat) {
+ case kRGBA8CanvasPixelFormat:
+ numPixels = content.sizeInBytes();
+ dataU8 = static_cast<uint8_t*>(content.data());
+ DCHECK(dataU8);
+ switch (storageFormat) {
+ case kUint8ClampedArrayStorageFormat:
+ arrayBuffer = DOMArrayBuffer::create(content);
+ return DOMUint8ClampedArray::create(arrayBuffer, 0,
+ arrayBuffer->byteLength());
+ break;
+ case kFloat32ArrayStorageFormat:
+ f32Array = allocateAndValidateFloat32Array(numPixels);
+ if (!f32Array)
+ return nullptr;
+ xform->apply(dstColorFormat, f32Array->data(), srcColorFormat,
+ content.data(), numPixels,
+ SkAlphaType::kUnpremul_SkAlphaType);
+ return f32Array;
+ break;
+ default:
+ NOTREACHED();
+ }
+ break;
+
+ case kF16CanvasPixelFormat:
+ numPixels = content.sizeInBytes() / 2;
+ srcColorFormat = SkColorSpaceXform::ColorFormat::kRGBA_F16_ColorFormat;
+
+ switch (storageFormat) {
+ case kUint8ClampedArrayStorageFormat:
+ u8Array = allocateAndValidateUint8ClampedArray(numPixels);
+ if (!u8Array)
+ return nullptr;
+ dstColorFormat =
+ SkColorSpaceXform::ColorFormat::kRGBA_8888_ColorFormat;
+ xform->apply(dstColorFormat, u8Array->data(), srcColorFormat,
+ content.data(), numPixels,
+ SkAlphaType::kUnpremul_SkAlphaType);
+ return u8Array;
+ break;
+ case kFloat32ArrayStorageFormat:
+ f32Array = allocateAndValidateFloat32Array(numPixels);
+ if (!f32Array)
+ return nullptr;
+ dstColorFormat =
+ SkColorSpaceXform::ColorFormat::kRGBA_F32_ColorFormat;
+ xform->apply(dstColorFormat, f32Array->data(), srcColorFormat,
+ content.data(), numPixels,
+ SkAlphaType::kUnpremul_SkAlphaType);
+ return f32Array;
+ break;
+ default:
+ NOTREACHED();
+ }
+ break;
+
+ default:
+ NOTREACHED();
+ }
+ return nullptr;
+}
+
// For ImageData, the color space is only specified by color settings.
// It cannot have a SkColorSpace. This doesn't mean anything. Fix this.
sk_sp<SkColorSpace> ImageData::getSkColorSpace() {
@@ -373,6 +552,100 @@ sk_sp<SkColorSpace> ImageData::getSkColorSpace() {
return SkColorSpace::MakeSRGB();
}
+sk_sp<SkColorSpace> ImageData::getSkColorSpace(
+ const CanvasColorSpace& colorSpace,
+ const CanvasPixelFormat& pixelFormat) {
+ switch (colorSpace) {
+ case kLegacyCanvasColorSpace:
+ return (gfx::ColorSpace::CreateSRGB()).ToSkColorSpace();
+ case kSRGBCanvasColorSpace:
+ if (pixelFormat == kF16CanvasPixelFormat)
+ return (gfx::ColorSpace::CreateSCRGBLinear()).ToSkColorSpace();
+ return (gfx::ColorSpace::CreateSRGB()).ToSkColorSpace();
+ case kRec2020CanvasColorSpace:
+ return (gfx::ColorSpace(gfx::ColorSpace::PrimaryID::BT2020,
+ gfx::ColorSpace::TransferID::IEC61966_2_1))
+ .ToSkColorSpace();
+ case kP3CanvasColorSpace:
+ return (gfx::ColorSpace(gfx::ColorSpace::PrimaryID::SMPTEST432_1,
+ gfx::ColorSpace::TransferID::IEC61966_2_1))
+ .ToSkColorSpace();
+ }
+ NOTREACHED();
+ return nullptr;
+}
+
+unsigned char* ImageData::getImageDataInCanvasColorSettings(
+ const CanvasColorSpace& canvasColorSpace,
+ const CanvasPixelFormat& canvasPixelFormat) {
+ if (!m_data && !m_dataU16 && !m_dataF32)
+ return nullptr;
+
+ // If canvas and image data are both in the same color space and pixel format
+ // is 8-8-8-8, just return the embedded data.
+ CanvasColorSpace imageDataColorSpace =
+ ImageData::getCanvasColorSpace(m_colorSettings.colorSpace());
+ if (canvasPixelFormat == kRGBA8CanvasPixelFormat &&
+ m_colorSettings.storageFormat() == kUint8ClampedArrayStorageFormatName) {
+ if ((canvasColorSpace == kLegacyCanvasColorSpace ||
+ canvasColorSpace == kSRGBCanvasColorSpace) &&
+ (imageDataColorSpace == kLegacyCanvasColorSpace ||
+ imageDataColorSpace == kSRGBCanvasColorSpace)) {
+ return m_data->data();
+ }
+ }
+
+ // Otherwise, color convert the pixels.
+ sk_sp<SkColorSpace> srcColorSpace =
+ ImageData::getSkColorSpace(imageDataColorSpace, canvasPixelFormat);
+
+ unsigned dataLength = 0;
+ void* srcData = nullptr;
+ SkColorSpaceXform::ColorFormat srcColorFormat =
+ SkColorSpaceXform::ColorFormat::kRGBA_8888_ColorFormat;
+ if (m_data) {
+ dataLength = m_data->length();
+ srcData = static_cast<void*>(m_data->data());
+ DCHECK(srcData);
+ } else if (m_dataU16) {
+ dataLength = m_dataU16->length();
+ srcData = static_cast<void*>(m_dataU16->data());
+ DCHECK(srcData);
+ srcColorFormat = SkColorSpaceXform::ColorFormat::kRGBA_U16_BE_ColorFormat;
+ } else if (m_dataF32) {
+ dataLength = m_dataF32->length();
+ srcData = static_cast<void*>(m_dataF32->data());
+ DCHECK(srcData);
+ srcColorFormat = SkColorSpaceXform::ColorFormat::kRGBA_F32_ColorFormat;
+ }
+
+ sk_sp<SkColorSpace> dstColorSpace =
+ ImageData::getSkColorSpace(canvasColorSpace, canvasPixelFormat);
+
+ void* dstData = nullptr;
+ SkColorSpaceXform::ColorFormat dstColorFormat =
+ SkColorSpaceXform::ColorFormat::kRGBA_8888_ColorFormat;
+ if (canvasPixelFormat == kRGBA8CanvasPixelFormat) {
+ dstData = new unsigned char[dataLength];
+ } else {
+ dstData = new unsigned char[dataLength * 2];
+ dstColorFormat = SkColorSpaceXform::ColorFormat::kRGBA_F16_ColorFormat;
+ }
+ if (!dstData)
+ return nullptr;
+
+ if (SkColorSpace::Equals(srcColorSpace.get(), dstColorSpace.get()) &&
+ srcColorFormat == dstColorFormat)
+ return static_cast<unsigned char*>(srcData);
+
+ std::unique_ptr<SkColorSpaceXform> xform =
+ SkColorSpaceXform::New(srcColorSpace.get(), dstColorSpace.get());
+ xform->apply(dstColorFormat, dstData, srcColorFormat, srcData, dataLength,
+ SkAlphaType::kUnpremul_SkAlphaType);
+
+ return static_cast<unsigned char*>(dstData);
+}
+
void ImageData::trace(Visitor* visitor) {
visitor->trace(m_data);
visitor->trace(m_dataU16);

Powered by Google App Engine
This is Rietveld 408576698