| 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 bb018a9d1a1b2348b78aba790d66a9c908f4d826..7d26541bd3169eb8691f61f4141638b21d9b1d07 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::storageFormatDataSize(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,40 +170,69 @@ 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;
 | 
| -  DOMArrayBufferView* byteArray =
 | 
| -      allocateAndValidateDataArray(4 * static_cast<unsigned>(size.width()) *
 | 
| -                                       static_cast<unsigned>(size.height()),
 | 
| -                                   kUint8ClampedArrayStorageFormat);
 | 
| -  return byteArray ? new ImageData(size, byteArray) : nullptr;
 | 
| +  DOMUint8ClampedArray* u8Array = const_cast<DOMUint8ClampedArray*>(
 | 
| +      static_cast<const DOMUint8ClampedArray*>(bufferView));
 | 
| +  DCHECK(u8Array);
 | 
| +  return u8Array;
 | 
|  }
 | 
|  
 | 
| -// This function accepts size (0, 0) and always returns the ImageData in
 | 
| -// "srgb" color space and "uint8" storage format.
 | 
| -ImageData* ImageData::createForTest(const IntSize& size) {
 | 
| -  CheckedNumeric<unsigned> dataSize = 4;
 | 
| -  dataSize *= size.width();
 | 
| -  dataSize *= size.height();
 | 
| -  if (!dataSize.IsValid())
 | 
| +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;
 | 
| +}
 | 
|  
 | 
| -  DOMUint8ClampedArray* byteArray =
 | 
| -      DOMUint8ClampedArray::createOrNull(dataSize.ValueOrDie());
 | 
| -  if (!byteArray)
 | 
| +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;
 | 
| +}
 | 
|  
 | 
| -  return new ImageData(size, byteArray);
 | 
| +ImageData* ImageData::create(const IntSize& size,
 | 
| +                             const ImageDataColorSettings* colorSettings) {
 | 
| +  if (!ImageData::validateConstructorArguments(kParamSize, &size, 0, 0, nullptr,
 | 
| +                                               colorSettings))
 | 
| +    return nullptr;
 | 
| +  ImageDataStorageFormat storageFormat = kUint8ClampedArrayStorageFormat;
 | 
| +  if (colorSettings) {
 | 
| +    storageFormat =
 | 
| +        ImageData::imageDataStorageFormat(colorSettings->storageFormat());
 | 
| +  }
 | 
| +  DOMArrayBufferView* dataArray =
 | 
| +      allocateAndValidateDataArray(4 * static_cast<unsigned>(size.width()) *
 | 
| +                                       static_cast<unsigned>(size.height()),
 | 
| +                                   storageFormat);
 | 
| +  return dataArray ? new ImageData(size, dataArray, colorSettings) : nullptr;
 | 
|  }
 | 
|  
 | 
|  ImageData* ImageData::create(const IntSize& size,
 | 
| -                             DOMUint8ClampedArray* byteArray) {
 | 
| +                             DOMArrayBufferView* dataArray,
 | 
| +                             const ImageDataColorSettings* colorSettings) {
 | 
|    if (!ImageData::validateConstructorArguments(kParamSize | kParamData, &size,
 | 
| -                                               0, 0, byteArray))
 | 
| +                                               0, 0, dataArray, colorSettings))
 | 
|      return nullptr;
 | 
| -
 | 
| -  return new ImageData(size, byteArray);
 | 
| +  return new ImageData(size, dataArray, colorSettings);
 | 
|  }
 | 
|  
 | 
|  ImageData* ImageData::create(unsigned width,
 | 
| @@ -218,7 +240,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 +251,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 +266,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,11 +279,11 @@ ImageData* ImageData::createImageData(
 | 
|      ExceptionState& exceptionState) {
 | 
|    if (!ImageData::validateConstructorArguments(kParamWidth | kParamHeight,
 | 
|                                                 nullptr, width, height, nullptr,
 | 
| -                                               &exceptionState))
 | 
| +                                               &colorSettings, &exceptionState))
 | 
|      return nullptr;
 | 
|  
 | 
|    ImageDataStorageFormat storageFormat =
 | 
| -      ImageData::getImageDataStorageFormat(colorSettings.storageFormat());
 | 
| +      ImageData::imageDataStorageFormat(colorSettings.storageFormat());
 | 
|    DOMArrayBufferView* bufferView = allocateAndValidateDataArray(
 | 
|        4 * width * height, storageFormat, &exceptionState);
 | 
|  
 | 
| @@ -270,29 +293,65 @@ ImageData* ImageData::createImageData(
 | 
|    return new ImageData(IntSize(width, height), bufferView, &colorSettings);
 | 
|  }
 | 
|  
 | 
| -ImageData* ImageData::createImageData(
 | 
| -    ImageDataArray& data,
 | 
| -    unsigned width,
 | 
| -    unsigned height,
 | 
| -    const ImageDataColorSettings& colorSettings,
 | 
| -    ExceptionState& exceptionState) {
 | 
| +ImageData* ImageData::createImageData(ImageDataArray& data,
 | 
| +                                      unsigned width,
 | 
| +                                      unsigned height,
 | 
| +                                      ImageDataColorSettings& colorSettings,
 | 
| +                                      ExceptionState& exceptionState) {
 | 
|    DOMArrayBufferView* bufferView = nullptr;
 | 
| -  if (data.isUint8ClampedArray())
 | 
| +  // When pixels data is provided, we need to override the storage format of
 | 
| +  // ImageDataColorSettings with the one that matches the data type of the
 | 
| +  // pixels.
 | 
| +  String storageFormatName;
 | 
| +
 | 
| +  if (data.isUint8ClampedArray()) {
 | 
|      bufferView = data.getAsUint8ClampedArray();
 | 
| -  else if (data.isUint16Array())
 | 
| +    storageFormatName = kUint8ClampedArrayStorageFormatName;
 | 
| +  } else if (data.isUint16Array()) {
 | 
|      bufferView = data.getAsUint16Array();
 | 
| -  else if (data.isFloat32Array())
 | 
| +    storageFormatName = kUint16ArrayStorageFormatName;
 | 
| +  } else if (data.isFloat32Array()) {
 | 
|      bufferView = data.getAsFloat32Array();
 | 
| -  else
 | 
| +    storageFormatName = kFloat32ArrayStorageFormatName;
 | 
| +  } else {
 | 
|      NOTREACHED();
 | 
| +  }
 | 
| +
 | 
| +  if (storageFormatName != colorSettings.storageFormat())
 | 
| +    colorSettings.setStorageFormat(storageFormatName);
 | 
|  
 | 
|    if (!ImageData::validateConstructorArguments(
 | 
|            kParamData | kParamWidth | kParamHeight, nullptr, width, height,
 | 
| -          bufferView, &exceptionState))
 | 
| +          bufferView, &colorSettings, &exceptionState))
 | 
| +    return nullptr;
 | 
| +
 | 
| +  return new ImageData(IntSize(width, height), bufferView, &colorSettings);
 | 
| +}
 | 
| +
 | 
| +// This function accepts size (0, 0) and always returns the ImageData in
 | 
| +// "srgb" color space and "uint8" storage format.
 | 
| +ImageData* ImageData::createForTest(const IntSize& size) {
 | 
| +  CheckedNumeric<unsigned> dataSize = 4;
 | 
| +  dataSize *= size.width();
 | 
| +  dataSize *= size.height();
 | 
| +  if (!dataSize.IsValid())
 | 
| +    return nullptr;
 | 
| +
 | 
| +  DOMUint8ClampedArray* byteArray =
 | 
| +      DOMUint8ClampedArray::createOrNull(dataSize.ValueOrDie());
 | 
| +  if (!byteArray)
 | 
|      return nullptr;
 | 
|  
 | 
| -  return new ImageData(IntSize(width, height), bufferView, &colorSettings,
 | 
| -                       kStorageFormatFromBufferType);
 | 
| +  return new ImageData(size, byteArray);
 | 
| +}
 | 
| +
 | 
| +// This function is called from unit tests, and all the parameters are supposed
 | 
| +// to be validated on the call site.
 | 
| +ImageData* ImageData::createForTest(
 | 
| +    const IntSize& size,
 | 
| +    DOMArrayBufferView* bufferView,
 | 
| +    const ImageDataColorSettings* colorSettings) {
 | 
| +  return new ImageData(size, bufferView, colorSettings);
 | 
|  }
 | 
|  
 | 
|  ScriptPromise ImageData::createImageBitmap(ScriptState* scriptState,
 | 
| @@ -351,7 +410,34 @@ DOMUint8ClampedArray* ImageData::data() {
 | 
|    return nullptr;
 | 
|  }
 | 
|  
 | 
| -ImageDataStorageFormat ImageData::getImageDataStorageFormat(
 | 
| +CanvasColorSpace ImageData::canvasColorSpace(const String& colorSpaceName) {
 | 
| +  if (colorSpaceName == kLegacyCanvasColorSpaceName)
 | 
| +    return kLegacyCanvasColorSpace;
 | 
| +  if (colorSpaceName == kSRGBCanvasColorSpaceName)
 | 
| +    return kSRGBCanvasColorSpace;
 | 
| +  if (colorSpaceName == kRec2020CanvasColorSpaceName)
 | 
| +    return kRec2020CanvasColorSpace;
 | 
| +  if (colorSpaceName == kP3CanvasColorSpaceName)
 | 
| +    return kP3CanvasColorSpace;
 | 
| +  NOTREACHED();
 | 
| +  return kSRGBCanvasColorSpace;
 | 
| +}
 | 
| +
 | 
| +String ImageData::canvasColorSpaceName(const CanvasColorSpace& colorSpace) {
 | 
| +  switch (colorSpace) {
 | 
| +    case kSRGBCanvasColorSpace:
 | 
| +      return kSRGBCanvasColorSpaceName;
 | 
| +    case kRec2020CanvasColorSpace:
 | 
| +      return kRec2020CanvasColorSpaceName;
 | 
| +    case kP3CanvasColorSpace:
 | 
| +      return kP3CanvasColorSpaceName;
 | 
| +    default:
 | 
| +      NOTREACHED();
 | 
| +  }
 | 
| +  return kSRGBCanvasColorSpaceName;
 | 
| +}
 | 
| +
 | 
| +ImageDataStorageFormat ImageData::imageDataStorageFormat(
 | 
|      const String& storageFormatName) {
 | 
|    if (storageFormatName == kUint8ClampedArrayStorageFormatName)
 | 
|      return kUint8ClampedArrayStorageFormat;
 | 
| @@ -363,9 +449,129 @@ ImageDataStorageFormat ImageData::getImageDataStorageFormat(
 | 
|    return kUint8ClampedArrayStorageFormat;
 | 
|  }
 | 
|  
 | 
| +unsigned ImageData::storageFormatDataSize(const String& storageFormatName) {
 | 
| +  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;
 | 
| +  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() / 4;
 | 
| +      switch (storageFormat) {
 | 
| +        case kUint8ClampedArrayStorageFormat:
 | 
| +          arrayBuffer = DOMArrayBuffer::create(content);
 | 
| +          return DOMUint8ClampedArray::create(arrayBuffer, 0,
 | 
| +                                              arrayBuffer->byteLength());
 | 
| +          break;
 | 
| +        case kFloat32ArrayStorageFormat:
 | 
| +          f32Array = allocateAndValidateFloat32Array(numPixels * 4);
 | 
| +          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() / 8;
 | 
| +      srcColorFormat = SkColorSpaceXform::ColorFormat::kRGBA_F16_ColorFormat;
 | 
| +
 | 
| +      switch (storageFormat) {
 | 
| +        case kUint8ClampedArrayStorageFormat:
 | 
| +          u8Array = allocateAndValidateUint8ClampedArray(numPixels * 4);
 | 
| +          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 * 4);
 | 
| +          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() {
 | 
| +sk_sp<SkColorSpace> ImageData::skColorSpace() {
 | 
|    if (!RuntimeEnabledFeatures::experimentalCanvasFeaturesEnabled() ||
 | 
|        !RuntimeEnabledFeatures::colorCorrectRenderingEnabled())
 | 
|      return nullptr;
 | 
| @@ -373,6 +579,124 @@ sk_sp<SkColorSpace> ImageData::getSkColorSpace() {
 | 
|    return SkColorSpace::MakeSRGB();
 | 
|  }
 | 
|  
 | 
| +// This function returns the proper SkColorSpace to color correct the pixels
 | 
| +// stored in ImageData before copying to the canvas. For now, it assumes that
 | 
| +// both ImageData and canvas use a linear gamma curve.
 | 
| +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::LINEAR))
 | 
| +          .ToSkColorSpace();
 | 
| +    case kP3CanvasColorSpace:
 | 
| +      return (gfx::ColorSpace(gfx::ColorSpace::PrimaryID::SMPTEST432_1,
 | 
| +                              gfx::ColorSpace::TransferID::LINEAR))
 | 
| +          .ToSkColorSpace();
 | 
| +  }
 | 
| +  NOTREACHED();
 | 
| +  return nullptr;
 | 
| +}
 | 
| +
 | 
| +sk_sp<SkColorSpace> ImageData::getSkColorSpaceForTest(
 | 
| +    const CanvasColorSpace& colorSpace,
 | 
| +    const CanvasPixelFormat& pixelFormat) {
 | 
| +  return getSkColorSpace(colorSpace, pixelFormat);
 | 
| +}
 | 
| +
 | 
| +bool ImageData::imageDataInCanvasColorSettings(
 | 
| +    const CanvasColorSpace& canvasColorSpace,
 | 
| +    const CanvasPixelFormat& canvasPixelFormat,
 | 
| +    std::unique_ptr<uint8_t[]>& convertedPixels) {
 | 
| +  if (!m_data && !m_dataU16 && !m_dataF32)
 | 
| +    return false;
 | 
| +
 | 
| +  // 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::canvasColorSpace(m_colorSettings.colorSpace());
 | 
| +  if (canvasPixelFormat == kRGBA8CanvasPixelFormat &&
 | 
| +      m_colorSettings.storageFormat() == kUint8ClampedArrayStorageFormatName) {
 | 
| +    if ((canvasColorSpace == kLegacyCanvasColorSpace ||
 | 
| +         canvasColorSpace == kSRGBCanvasColorSpace) &&
 | 
| +        (imageDataColorSpace == kLegacyCanvasColorSpace ||
 | 
| +         imageDataColorSpace == kSRGBCanvasColorSpace)) {
 | 
| +      memcpy(convertedPixels.get(), m_data->data(), m_data->length());
 | 
| +      return true;
 | 
| +    }
 | 
| +  }
 | 
| +
 | 
| +  // Otherwise, color convert the pixels.
 | 
| +  unsigned numPixels = m_size.width() * m_size.height();
 | 
| +  unsigned dataLength = numPixels * 4;
 | 
| +  void* srcData = nullptr;
 | 
| +  std::unique_ptr<uint16_t[]> leData;
 | 
| +  SkColorSpaceXform::ColorFormat srcColorFormat =
 | 
| +      SkColorSpaceXform::ColorFormat::kRGBA_8888_ColorFormat;
 | 
| +  if (m_data) {
 | 
| +    srcData = static_cast<void*>(m_data->data());
 | 
| +    DCHECK(srcData);
 | 
| +  } else if (m_dataU16) {
 | 
| +    srcData = static_cast<void*>(m_dataU16->data());
 | 
| +    DCHECK(srcData);
 | 
| +    // SkColorSpaceXform::apply expects U16 data to be in Big Endian byte
 | 
| +    // order, while srcData is always Little Endian. As we cannot consume
 | 
| +    // ImageData here, we change the byte order in a copy.
 | 
| +    leData.reset(new uint16_t[dataLength]());
 | 
| +    memcpy(leData.get(), srcData, dataLength * 2);
 | 
| +    uint16_t swapValue = 0;
 | 
| +    for (unsigned i = 0; i < dataLength; i++) {
 | 
| +      swapValue = leData[i];
 | 
| +      leData[i] = swapValue >> 8 | swapValue << 8;
 | 
| +    }
 | 
| +    srcData = static_cast<void*>(leData.get());
 | 
| +    DCHECK(srcData);
 | 
| +    srcColorFormat = SkColorSpaceXform::ColorFormat::kRGBA_U16_BE_ColorFormat;
 | 
| +  } else if (m_dataF32) {
 | 
| +    srcData = static_cast<void*>(m_dataF32->data());
 | 
| +    DCHECK(srcData);
 | 
| +    srcColorFormat = SkColorSpaceXform::ColorFormat::kRGBA_F32_ColorFormat;
 | 
| +  } else {
 | 
| +    NOTREACHED();
 | 
| +  }
 | 
| +
 | 
| +  sk_sp<SkColorSpace> srcColorSpace = nullptr;
 | 
| +  if (m_data) {
 | 
| +    srcColorSpace = ImageData::getSkColorSpace(imageDataColorSpace,
 | 
| +                                               kRGBA8CanvasPixelFormat);
 | 
| +  } else {
 | 
| +    srcColorSpace =
 | 
| +        ImageData::getSkColorSpace(imageDataColorSpace, kF16CanvasPixelFormat);
 | 
| +  }
 | 
| +
 | 
| +  sk_sp<SkColorSpace> dstColorSpace =
 | 
| +      ImageData::getSkColorSpace(canvasColorSpace, canvasPixelFormat);
 | 
| +
 | 
| +  SkColorSpaceXform::ColorFormat dstColorFormat =
 | 
| +      SkColorSpaceXform::ColorFormat::kRGBA_8888_ColorFormat;
 | 
| +  if (canvasPixelFormat == kF16CanvasPixelFormat)
 | 
| +    dstColorFormat = SkColorSpaceXform::ColorFormat::kRGBA_F16_ColorFormat;
 | 
| +
 | 
| +  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());
 | 
| +
 | 
| +  if (!xform->apply(dstColorFormat, convertedPixels.get(), srcColorFormat,
 | 
| +                    srcData, numPixels, SkAlphaType::kUnpremul_SkAlphaType))
 | 
| +    return false;
 | 
| +  return true;
 | 
| +}
 | 
| +
 | 
|  void ImageData::trace(Visitor* visitor) {
 | 
|    visitor->trace(m_data);
 | 
|    visitor->trace(m_dataU16);
 | 
| @@ -382,8 +706,7 @@ void ImageData::trace(Visitor* visitor) {
 | 
|  
 | 
|  ImageData::ImageData(const IntSize& size,
 | 
|                       DOMArrayBufferView* data,
 | 
| -                     const ImageDataColorSettings* colorSettings,
 | 
| -                     StorageFormatSource storageFormatSource)
 | 
| +                     const ImageDataColorSettings* colorSettings)
 | 
|      : m_size(size) {
 | 
|    DCHECK_GE(size.width(), 0);
 | 
|    DCHECK_GE(size.height(), 0);
 | 
| @@ -398,25 +721,8 @@ ImageData::ImageData(const IntSize& size,
 | 
|      m_colorSettings.setStorageFormat(colorSettings->storageFormat());
 | 
|    }
 | 
|  
 | 
| -  // if data is provided through the JS call, override the storage format.
 | 
| -  if (storageFormatSource == kStorageFormatFromBufferType) {
 | 
| -    switch (data->type()) {
 | 
| -      case DOMArrayBufferView::ViewType::TypeUint8Clamped:
 | 
| -        m_colorSettings.setStorageFormat(kUint8ClampedArrayStorageFormatName);
 | 
| -        break;
 | 
| -      case DOMArrayBufferView::ViewType::TypeUint16:
 | 
| -        m_colorSettings.setStorageFormat(kUint16ArrayStorageFormatName);
 | 
| -        break;
 | 
| -      case DOMArrayBufferView::ViewType::TypeFloat32:
 | 
| -        m_colorSettings.setStorageFormat(kFloat32ArrayStorageFormatName);
 | 
| -        break;
 | 
| -      default:
 | 
| -        NOTREACHED();
 | 
| -    }
 | 
| -  }
 | 
| -
 | 
|    ImageDataStorageFormat storageFormat =
 | 
| -      getImageDataStorageFormat(m_colorSettings.storageFormat());
 | 
| +      imageDataStorageFormat(m_colorSettings.storageFormat());
 | 
|  
 | 
|    switch (storageFormat) {
 | 
|      case kUint8ClampedArrayStorageFormat:
 | 
| 
 |