| Index: third_party/WebKit/Source/core/frame/ImageBitmap.cpp
|
| diff --git a/third_party/WebKit/Source/core/frame/ImageBitmap.cpp b/third_party/WebKit/Source/core/frame/ImageBitmap.cpp
|
| index 952843b6c13a31f0485499b1d6d5b7c64c223d1d..479c63850e5c1f9ab4ea0991c1678b90c09ba673 100644
|
| --- a/third_party/WebKit/Source/core/frame/ImageBitmap.cpp
|
| +++ b/third_party/WebKit/Source/core/frame/ImageBitmap.cpp
|
| @@ -9,6 +9,7 @@
|
| #include "core/html/HTMLVideoElement.h"
|
| #include "core/html/ImageData.h"
|
| #include "core/offscreencanvas/OffscreenCanvas.h"
|
| +#include "platform/graphics/CanvasColorParams.h"
|
| #include "platform/graphics/skia/SkiaUtils.h"
|
| #include "platform/image-decoders/ImageDecoder.h"
|
| #include "platform/wtf/CheckedNumeric.h"
|
| @@ -29,6 +30,8 @@ constexpr const char* kImageBitmapOptionResizeQualityMedium = "medium";
|
| constexpr const char* kImageBitmapOptionResizeQualityPixelated = "pixelated";
|
| constexpr const char* kSRGBImageBitmapColorSpaceConversion = "srgb";
|
| constexpr const char* kLinearRGBImageBitmapColorSpaceConversion = "linear-rgb";
|
| +constexpr const char* kP3ImageBitmapColorSpaceConversion = "p3";
|
| +constexpr const char* kRec2020ImageBitmapColorSpaceConversion = "rec2020";
|
|
|
| namespace {
|
|
|
| @@ -40,10 +43,9 @@ struct ParsedOptions {
|
| unsigned resize_height = 0;
|
| IntRect crop_rect;
|
| SkFilterQuality resize_quality = kLow_SkFilterQuality;
|
| - sk_sp<SkColorSpace> dst_color_space = nullptr;
|
| - sk_sp<SkColorSpace> latest_color_space = nullptr;
|
| - SkColorType dst_color_type = kN32_SkColorType;
|
| - SkColorType latest_color_type = kN32_SkColorType;
|
| + CanvasColorParams color_params;
|
| + bool color_correct_rendering_enabled = false;
|
| + bool use_global_target_color_space = false;
|
| };
|
|
|
| ParsedOptions DefaultOptions() {
|
| @@ -77,24 +79,32 @@ ParsedOptions ParseOptions(const ImageBitmapOptions& options,
|
| }
|
|
|
| if (options.colorSpaceConversion() != kImageBitmapOptionNone) {
|
| - if (!RuntimeEnabledFeatures::experimentalCanvasFeaturesEnabled() ||
|
| - !RuntimeEnabledFeatures::colorCorrectRenderingEnabled()) {
|
| + parsed_options.color_correct_rendering_enabled =
|
| + RuntimeEnabledFeatures::experimentalCanvasFeaturesEnabled() &&
|
| + RuntimeEnabledFeatures::colorCorrectRenderingEnabled();
|
| + if (!parsed_options.color_correct_rendering_enabled) {
|
| DCHECK_EQ(options.colorSpaceConversion(), kImageBitmapOptionDefault);
|
| if (RuntimeEnabledFeatures::colorCorrectRenderingDefaultModeEnabled()) {
|
| - parsed_options.dst_color_space =
|
| - ColorBehavior::GlobalTargetColorSpace().ToSkColorSpace();
|
| - parsed_options.dst_color_type = SkColorType::kN32_SkColorType;
|
| + parsed_options.use_global_target_color_space = true;
|
| }
|
| } else {
|
| if (options.colorSpaceConversion() == kImageBitmapOptionDefault ||
|
| options.colorSpaceConversion() ==
|
| kSRGBImageBitmapColorSpaceConversion) {
|
| - parsed_options.dst_color_space = SkColorSpace::MakeSRGB();
|
| - parsed_options.dst_color_type = SkColorType::kN32_SkColorType;
|
| + parsed_options.color_params.SetCanvasColorSpace(kSRGBCanvasColorSpace);
|
| } else if (options.colorSpaceConversion() ==
|
| kLinearRGBImageBitmapColorSpaceConversion) {
|
| - parsed_options.dst_color_space = SkColorSpace::MakeSRGBLinear();
|
| - parsed_options.dst_color_type = SkColorType::kRGBA_F16_SkColorType;
|
| + parsed_options.color_params.SetCanvasColorSpace(kSRGBCanvasColorSpace);
|
| + parsed_options.color_params.SetCanvasPixelFormat(kF16CanvasPixelFormat);
|
| + } else if (options.colorSpaceConversion() ==
|
| + kP3ImageBitmapColorSpaceConversion) {
|
| + parsed_options.color_params.SetCanvasColorSpace(kP3CanvasColorSpace);
|
| + parsed_options.color_params.SetCanvasPixelFormat(kF16CanvasPixelFormat);
|
| + } else if (options.colorSpaceConversion() ==
|
| + kRec2020ImageBitmapColorSpaceConversion) {
|
| + parsed_options.color_params.SetCanvasColorSpace(
|
| + kRec2020CanvasColorSpace);
|
| + parsed_options.color_params.SetCanvasPixelFormat(kF16CanvasPixelFormat);
|
| } else {
|
| NOTREACHED()
|
| << "Invalid ImageBitmap creation attribute colorSpaceConversion: "
|
| @@ -150,7 +160,8 @@ ParsedOptions ParseOptions(const ImageBitmapOptions& options,
|
| bool DstBufferSizeHasOverflow(const ParsedOptions& options) {
|
| CheckedNumeric<unsigned> total_bytes = options.crop_rect.Width();
|
| total_bytes *= options.crop_rect.Height();
|
| - total_bytes *= SkColorTypeBytesPerPixel(options.latest_color_type);
|
| + total_bytes *=
|
| + SkColorTypeBytesPerPixel(options.color_params.GetSkColorType());
|
| if (!total_bytes.IsValid())
|
| return true;
|
|
|
| @@ -158,7 +169,8 @@ bool DstBufferSizeHasOverflow(const ParsedOptions& options) {
|
| return false;
|
| total_bytes = options.resize_width;
|
| total_bytes *= options.resize_height;
|
| - total_bytes *= SkColorTypeBytesPerPixel(options.latest_color_type);
|
| + total_bytes *=
|
| + SkColorTypeBytesPerPixel(options.color_params.GetSkColorType());
|
| if (!total_bytes.IsValid())
|
| return true;
|
|
|
| @@ -245,8 +257,8 @@ static sk_sp<SkImage> FlipSkImageVertically(
|
| ? kPremul_SkAlphaType
|
| : kUnpremul_SkAlphaType;
|
| SkImageInfo info = SkImageInfo::Make(input->width(), input->height(),
|
| - options.latest_color_type, alpha_type,
|
| - options.latest_color_space);
|
| + options.color_params.GetSkColorType(),
|
| + alpha_type, input->refColorSpace());
|
|
|
| unsigned image_row_bytes = width * info.bytesPerPixel();
|
| RefPtr<Uint8Array> image_pixels = CopySkImageData(input, info);
|
| @@ -267,8 +279,8 @@ static sk_sp<SkImage> PremulSkImageToUnPremul(
|
| SkImage* input,
|
| const ParsedOptions& options = DefaultOptions()) {
|
| SkImageInfo info = SkImageInfo::Make(
|
| - input->width(), input->height(), options.latest_color_type,
|
| - kUnpremul_SkAlphaType, options.latest_color_space);
|
| + input->width(), input->height(), options.color_params.GetSkColorType(),
|
| + kUnpremul_SkAlphaType, input->refColorSpace());
|
|
|
| RefPtr<Uint8Array> dst_pixels = CopySkImageData(input, info);
|
| if (!dst_pixels)
|
| @@ -282,8 +294,8 @@ static sk_sp<SkImage> UnPremulSkImageToPremul(
|
| SkImage* input,
|
| const ParsedOptions& options = DefaultOptions()) {
|
| SkImageInfo info = SkImageInfo::Make(
|
| - input->width(), input->height(), options.latest_color_type,
|
| - kPremul_SkAlphaType, options.latest_color_space);
|
| + input->width(), input->height(), options.color_params.GetSkColorType(),
|
| + kPremul_SkAlphaType, input->refColorSpace());
|
|
|
| RefPtr<Uint8Array> dst_pixels = CopySkImageData(input, info);
|
| if (!dst_pixels)
|
| @@ -293,106 +305,63 @@ static sk_sp<SkImage> UnPremulSkImageToPremul(
|
| static_cast<unsigned>(input->width()) * info.bytesPerPixel());
|
| }
|
|
|
| -// This code is borrowed from third_party/skia/src/codec/SkCodecPriv.h
|
| -// If you need to change this, please check SkColorSpaceXform::ColorFormat
|
| -// and SkColorType for proper coverage.
|
| -static inline SkColorSpaceXform::ColorFormat GetXformFormat(
|
| - SkColorType color_type) {
|
| - switch (color_type) {
|
| - case kRGBA_8888_SkColorType:
|
| - return SkColorSpaceXform::kRGBA_8888_ColorFormat;
|
| - case kBGRA_8888_SkColorType:
|
| - return SkColorSpaceXform::kBGRA_8888_ColorFormat;
|
| - case kRGBA_F16_SkColorType:
|
| - return SkColorSpaceXform::kRGBA_F16_ColorFormat;
|
| - default:
|
| - NOTREACHED();
|
| - return SkColorSpaceXform::kRGBA_8888_ColorFormat;
|
| - }
|
| -}
|
| -
|
| -static inline void UpdateLatestColorInformation(ParsedOptions& options) {
|
| - options.latest_color_type = options.dst_color_type;
|
| - options.latest_color_space = options.dst_color_space;
|
| -}
|
| -
|
| -// TODO (zakrinasab). Rewrite this when SkImage::readPixels() respectes the
|
| -// color space of the passed SkImageInfo (crbug.com/skia/6021) and SkImage
|
| -// exposes SkColorSpace and SkColorType (crbug.com/skia/6022).
|
| static void ApplyColorSpaceConversion(sk_sp<SkImage>& image,
|
| ParsedOptions& options) {
|
| - if (!options.dst_color_space)
|
| + if (!options.color_correct_rendering_enabled &&
|
| + !options.use_global_target_color_space)
|
| return;
|
| - if (SkColorSpace::Equals(options.latest_color_space.get(),
|
| - options.dst_color_space.get()))
|
| +
|
| + sk_sp<SkColorSpace> dst_color_space = nullptr;
|
| + SkColorType dst_color_type = kN32_SkColorType;
|
| + if (options.use_global_target_color_space) {
|
| + dst_color_space = ColorBehavior::GlobalTargetColorSpace().ToSkColorSpace();
|
| + } else {
|
| + dst_color_space = options.color_params.GetSkColorSpace();
|
| + dst_color_type = options.color_params.GetSkColorType();
|
| + }
|
| + if (SkColorSpace::Equals(image->colorSpace(), dst_color_space.get()))
|
| return;
|
|
|
| - // If we have the color space information of the source image, we can use
|
| - // SkColorSpaceXform. Otherwise, we need to draw the image on a canvas and
|
| - // take a snapshot.
|
| - if (options.latest_color_space) {
|
| - SkImageInfo info = SkImageInfo::Make(
|
| - image->width(), image->height(), options.latest_color_type,
|
| - image->alphaType(), options.latest_color_space);
|
| - size_t size = image->width() * image->height() * info.bytesPerPixel();
|
| - sk_sp<SkData> src_data = SkData::MakeUninitialized(size);
|
| - if (src_data->size() != size)
|
| - return;
|
| - if (!image->readPixels(info, src_data->writable_data(),
|
| - image->width() * info.bytesPerPixel(), 0, 0)) {
|
| - return;
|
| - }
|
| - // Proceed with in-place color correction, if possible.
|
| - sk_sp<SkData> dst_data = src_data;
|
| - if (SkColorTypeBytesPerPixel(options.dst_color_type) !=
|
| - SkColorTypeBytesPerPixel(options.latest_color_type)) {
|
| - size = image->width() * image->height() *
|
| - SkColorTypeBytesPerPixel(options.dst_color_type);
|
| - dst_data = SkData::MakeUninitialized(size);
|
| - if (dst_data->size() != size)
|
| - return;
|
| - }
|
| - SkImageInfo dst_info = SkImageInfo::Make(
|
| - image->width(), image->height(), options.dst_color_type,
|
| - image->alphaType(), options.dst_color_space);
|
| - std::unique_ptr<SkColorSpaceXform> xform = SkColorSpaceXform::New(
|
| - options.latest_color_space.get(), options.dst_color_space.get());
|
| - if (xform->apply(
|
| - GetXformFormat(options.dst_color_type), dst_data->writable_data(),
|
| - GetXformFormat(options.latest_color_type), src_data->data(),
|
| - image->width() * image->height(), kUnpremul_SkAlphaType)) {
|
| - sk_sp<SkImage> colored_image = SkImage::MakeRasterData(
|
| - dst_info, dst_data, image->width() * dst_info.bytesPerPixel());
|
| - if (colored_image) {
|
| - UpdateLatestColorInformation(options);
|
| - image = colored_image;
|
| - return;
|
| - }
|
| - }
|
| + SkImageInfo dst_info =
|
| + SkImageInfo::Make(image->width(), image->height(), dst_color_type,
|
| + image->alphaType(), dst_color_space);
|
| +
|
| + size_t size = image->width() * image->height() * dst_info.bytesPerPixel();
|
| + sk_sp<SkData> dst_data = SkData::MakeUninitialized(size);
|
| + if (dst_data->size() != size)
|
| return;
|
| + sk_sp<SkImage> colored_image = nullptr;
|
| + if (image->readPixels(dst_info, dst_data->writable_data(),
|
| + image->width() * dst_info.bytesPerPixel(), 0, 0)) {
|
| + colored_image = SkImage::MakeRasterData(
|
| + dst_info, dst_data, image->width() * dst_info.bytesPerPixel());
|
| + } else {
|
| + // The desired way to apply color space conversion on a SkImage is to use
|
| + // SkImage::readPixels. However, if the SkImage is GPU-backed, readPixels
|
| + // still might not work properly. In this case, we fall back to drawing
|
| + // the SkImage to a canvas and reading back the result.
|
| + // Skia does not support drawing to unpremul surfaces/canvases.
|
| + sk_sp<SkImage> un_premul_image = nullptr;
|
| + if (image->alphaType() == kUnpremul_SkAlphaType) {
|
| + un_premul_image = UnPremulSkImageToPremul(image.get(), options);
|
| + dst_info = dst_info.makeAlphaType(kPremul_SkAlphaType);
|
| + }
|
| +
|
| + // If the color space of the source SkImage is null, the following code
|
| + // does not do any color conversion. This cannot be addressed here and
|
| + // the code that creates the SkImage must tag the SkImage with proper
|
| + // color space.
|
| + sk_sp<SkSurface> surface = SkSurface::MakeRaster(dst_info);
|
| + if (!surface)
|
| + return;
|
| + surface->getCanvas()->drawImage(
|
| + un_premul_image ? un_premul_image : sk_sp<SkImage>(image), 0, 0);
|
| + colored_image = surface->makeImageSnapshot();
|
| }
|
| - // Skia does not support drawing to unpremul surfaces/canvases.
|
| - sk_sp<SkImage> un_premul_image = nullptr;
|
| - if (image->alphaType() == kUnpremul_SkAlphaType)
|
| - un_premul_image = UnPremulSkImageToPremul(image.get(), options);
|
| -
|
| - // If the color space of the source SkImage is null, the following code
|
| - // does not do any color conversion even thought the new SkImage will be
|
| - // tagged by the new color space. If this is the case, the following code
|
| - // will tag a wrong color space to the SkImage. However, this cannot be
|
| - // addressed here and the code that creates the SkImage must tag the
|
| - // SkImage with proper color space.
|
| - SkImageInfo image_info = SkImageInfo::Make(
|
| - image->width(), image->height(), options.dst_color_type,
|
| - un_premul_image ? un_premul_image->alphaType() : image->alphaType(),
|
| - options.dst_color_space);
|
| - sk_sp<SkSurface> surface = SkSurface::MakeRaster(image_info);
|
| - surface->getCanvas()->drawImage(
|
| - un_premul_image ? un_premul_image : sk_sp<SkImage>(image), 0, 0);
|
| - sk_sp<SkImage> colored_image = surface->makeImageSnapshot();
|
| - UpdateLatestColorInformation(options);
|
| +
|
| + if (!colored_image)
|
| + return;
|
| image = colored_image;
|
| - return;
|
| }
|
|
|
| sk_sp<SkImage> ImageBitmap::GetSkImageFromDecoder(
|
| @@ -485,9 +454,12 @@ static PassRefPtr<StaticBitmapImage> CropImageAndApplyColorSpaceConversion(
|
| color_behavior));
|
| if (!decoder)
|
| return nullptr;
|
| + SkColorType color_type = parsed_options.color_params.GetSkColorType();
|
| + sk_sp<SkColorSpace> color_space =
|
| + parsed_options.color_params.GetSkColorSpace();
|
| skia_image = ImageBitmap::GetSkImageFromDecoder(
|
| - std::move(decoder), &parsed_options.latest_color_type,
|
| - &parsed_options.latest_color_space, kUpdateColorSpaceInformation);
|
| + std::move(decoder), &color_type, &color_space,
|
| + kUpdateColorSpaceInformation);
|
| if (!skia_image)
|
| return nullptr;
|
| }
|
| @@ -495,8 +467,7 @@ static PassRefPtr<StaticBitmapImage> CropImageAndApplyColorSpaceConversion(
|
| if (parsed_options.crop_rect == src_rect &&
|
| !parsed_options.should_scale_input) {
|
| sk_sp<SkImage> cropped_sk_image = skia_image->makeSubset(src_rect);
|
| - if (parsed_options.dst_color_space)
|
| - ApplyColorSpaceConversion(cropped_sk_image, parsed_options);
|
| + ApplyColorSpaceConversion(cropped_sk_image, parsed_options);
|
| if (parsed_options.flip_y) {
|
| return StaticBitmapImage::Create(
|
| FlipSkImageVertically(cropped_sk_image.get(),
|
| @@ -505,9 +476,10 @@ static PassRefPtr<StaticBitmapImage> CropImageAndApplyColorSpaceConversion(
|
| // Special case: The first parameter image is unpremul but we need to turn
|
| // it into premul.
|
| if (parsed_options.premultiply_alpha &&
|
| - image_format == kDontPremultiplyAlpha)
|
| + image_format == kDontPremultiplyAlpha) {
|
| return StaticBitmapImage::Create(
|
| UnPremulSkImageToPremul(cropped_sk_image.get()));
|
| + }
|
| return StaticBitmapImage::Create(std::move(cropped_sk_image));
|
| }
|
|
|
| @@ -542,8 +514,7 @@ static PassRefPtr<StaticBitmapImage> CropImageAndApplyColorSpaceConversion(
|
| surface->getCanvas()->drawImage(skia_image, dst_left, dst_top);
|
| }
|
| skia_image = surface->makeImageSnapshot();
|
| - if (parsed_options.dst_color_space)
|
| - ApplyColorSpaceConversion(skia_image, parsed_options);
|
| + ApplyColorSpaceConversion(skia_image, parsed_options);
|
|
|
| if (parsed_options.premultiply_alpha) {
|
| if (image_format == kDontPremultiplyAlpha)
|
| @@ -581,9 +552,18 @@ ImageBitmap::ImageBitmap(ImageElementBase* image,
|
| sk_sp<SkImage> sk_image = image_->ImageForCurrentFrame();
|
| SkPixmap pixmap;
|
| if (!sk_image->isTextureBacked() && !sk_image->peekPixels(&pixmap)) {
|
| - SkImageInfo image_info = SkImageInfo::Make(
|
| - sk_image->width(), sk_image->height(), parsed_options.dst_color_type,
|
| - kPremul_SkAlphaType, parsed_options.dst_color_space);
|
| + sk_sp<SkColorSpace> dst_color_space = nullptr;
|
| + SkColorType dst_color_type = kN32_SkColorType;
|
| + if (parsed_options.use_global_target_color_space) {
|
| + dst_color_space =
|
| + ColorBehavior::GlobalTargetColorSpace().ToSkColorSpace();
|
| + } else if (parsed_options.color_correct_rendering_enabled) {
|
| + dst_color_space = parsed_options.color_params.GetSkColorSpace();
|
| + dst_color_type = parsed_options.color_params.GetSkColorType();
|
| + }
|
| + SkImageInfo image_info =
|
| + SkImageInfo::Make(sk_image->width(), sk_image->height(), dst_color_type,
|
| + kPremul_SkAlphaType, dst_color_space);
|
| sk_sp<SkSurface> surface = SkSurface::MakeRaster(image_info);
|
| surface->getCanvas()->drawImage(sk_image, 0, 0);
|
| image_ = StaticBitmapImage::Create(surface->makeImageSnapshot());
|
| @@ -878,14 +858,12 @@ ImageBitmap::ImageBitmap(ImageData* data,
|
| }
|
| if (!sk_image)
|
| return;
|
| - if (data->GetSkColorSpace()) {
|
| - parsed_options.latest_color_space = data->GetSkColorSpace();
|
| - ApplyColorSpaceConversion(sk_image, parsed_options);
|
| - }
|
| + ApplyColorSpaceConversion(sk_image, parsed_options);
|
| if (parsed_options.should_scale_input) {
|
| image_ = StaticBitmapImage::Create(ScaleSkImage(
|
| sk_image, parsed_options.resize_width, parsed_options.resize_height,
|
| - parsed_options.resize_quality, parsed_options.latest_color_type,
|
| + parsed_options.resize_quality,
|
| + parsed_options.color_params.GetSkColorType(),
|
| data->GetSkColorSpace()));
|
| } else {
|
| image_ = StaticBitmapImage::Create(sk_image);
|
| @@ -951,10 +929,7 @@ ImageBitmap::ImageBitmap(ImageData* data,
|
| surface->getCanvas()->drawImageRect(sk_image, dst_draw_rect, &paint);
|
| sk_image = surface->makeImageSnapshot();
|
| }
|
| - if (data->GetSkColorSpace()) {
|
| - parsed_options.latest_color_space = data->GetSkColorSpace();
|
| - ApplyColorSpaceConversion(sk_image, parsed_options);
|
| - }
|
| + ApplyColorSpaceConversion(sk_image, parsed_options);
|
| image_ = StaticBitmapImage::Create(std::move(sk_image));
|
| }
|
|
|
|
|