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