Commit ec67cae3 authored by Reza.Zakerinasab's avatar Reza.Zakerinasab Committed by Commit Bot

With {premultiplyAlpha: "none"} ImageBitmap should avoid premul code path

When premultiplyAlpha is set to "none" in ImageBitmapOptions, we should never pass through premul
format in color managed ImageBitmap code.

Bug: 781908, 785313
Change-Id: I3a2fc1664dd57530bdc31694c5f9d61e95a9501b
Reviewed-on: https://chromium-review.googlesource.com/759116
Commit-Queue: Mohammad Reza Zakerinasab <zakerinasab@chromium.org>
Reviewed-by: default avatarJustin Novosad <junov@chromium.org>
Reviewed-by: default avatarFernando Serboncini <fserb@chromium.org>
Cr-Commit-Position: refs/heads/master@{#517858}
parent 0f874ace
...@@ -744,7 +744,7 @@ void ImageData::SwizzleIfNeeded(DataU8ColorType u8_color_type, ...@@ -744,7 +744,7 @@ void ImageData::SwizzleIfNeeded(DataU8ColorType u8_color_type,
bool ImageData::ImageDataInCanvasColorSettings( bool ImageData::ImageDataInCanvasColorSettings(
CanvasColorSpace canvas_color_space, CanvasColorSpace canvas_color_space,
CanvasPixelFormat canvas_pixel_format, CanvasPixelFormat canvas_pixel_format,
std::unique_ptr<uint8_t[]>& converted_pixels, unsigned char* converted_pixels,
DataU8ColorType u8_color_type, DataU8ColorType u8_color_type,
const IntRect* src_rect) { const IntRect* src_rect) {
if (!data_ && !data_u16_ && !data_f32_) if (!data_ && !data_u16_ && !data_f32_)
...@@ -768,8 +768,7 @@ bool ImageData::ImageDataInCanvasColorSettings( ...@@ -768,8 +768,7 @@ bool ImageData::ImageDataInCanvasColorSettings(
if (crop_rect) { if (crop_rect) {
unsigned char* src_data = unsigned char* src_data =
static_cast<unsigned char*>(BufferBase()->Data()); static_cast<unsigned char*>(BufferBase()->Data());
unsigned char* dst_data = unsigned char* dst_data = static_cast<unsigned char*>(converted_pixels);
static_cast<unsigned char*>(converted_pixels.get());
int src_index = (crop_rect->X() + crop_rect->Y() * width()) * 4; int src_index = (crop_rect->X() + crop_rect->Y() * width()) * 4;
int dst_index = 0; int dst_index = 0;
int src_row_stride = width() * 4; int src_row_stride = width() * 4;
...@@ -780,7 +779,7 @@ bool ImageData::ImageDataInCanvasColorSettings( ...@@ -780,7 +779,7 @@ bool ImageData::ImageDataInCanvasColorSettings(
dst_index += dst_row_stride; dst_index += dst_row_stride;
} }
} else { } else {
memcpy(converted_pixels.get(), data_->Data(), data_->length()); memcpy(converted_pixels, data_->Data(), data_->length());
} }
SwizzleIfNeeded(u8_color_type, crop_rect); SwizzleIfNeeded(u8_color_type, crop_rect);
return true; return true;
...@@ -815,8 +814,7 @@ bool ImageData::ImageDataInCanvasColorSettings( ...@@ -815,8 +814,7 @@ bool ImageData::ImageDataInCanvasColorSettings(
if (crop_rect) { if (crop_rect) {
unsigned char* src_data = static_cast<unsigned char*>(BufferBase()->Data()); unsigned char* src_data = static_cast<unsigned char*>(BufferBase()->Data());
unsigned char* dst_data = unsigned char* dst_data = static_cast<unsigned char*>(converted_pixels);
static_cast<unsigned char*>(converted_pixels.get());
int src_data_type_size = int src_data_type_size =
ImageData::StorageFormatDataSize(color_settings_.storageFormat()); ImageData::StorageFormatDataSize(color_settings_.storageFormat());
int dst_pixel_size = dst_color_params.BytesPerPixel(); int dst_pixel_size = dst_color_params.BytesPerPixel();
...@@ -837,7 +835,7 @@ bool ImageData::ImageDataInCanvasColorSettings( ...@@ -837,7 +835,7 @@ bool ImageData::ImageDataInCanvasColorSettings(
dst_index += dst_row_stride; dst_index += dst_row_stride;
} }
} else { } else {
conversion_result = xform->apply(dst_color_format, converted_pixels.get(), conversion_result = xform->apply(dst_color_format, converted_pixels,
src_color_format, src_data, size_.Area(), src_color_format, src_data, size_.Area(),
SkAlphaType::kUnpremul_SkAlphaType); SkAlphaType::kUnpremul_SkAlphaType);
} }
......
...@@ -140,7 +140,7 @@ class CORE_EXPORT ImageData final : public ScriptWrappable, ...@@ -140,7 +140,7 @@ class CORE_EXPORT ImageData final : public ScriptWrappable,
// used to create an ImageBitmap, kN32ColorType should be used. // used to create an ImageBitmap, kN32ColorType should be used.
bool ImageDataInCanvasColorSettings(CanvasColorSpace, bool ImageDataInCanvasColorSettings(CanvasColorSpace,
CanvasPixelFormat, CanvasPixelFormat,
std::unique_ptr<uint8_t[]>&, unsigned char* converted_pixels,
DataU8ColorType, DataU8ColorType,
const IntRect* = nullptr); const IntRect* = nullptr);
......
...@@ -245,7 +245,7 @@ TEST_F(ImageDataTest, TestGetImageDataInCanvasColorSettings) { ...@@ -245,7 +245,7 @@ TEST_F(ImageDataTest, TestGetImageDataInCanvasColorSettings) {
// Convert the image data to the color settings of the canvas. // Convert the image data to the color settings of the canvas.
EXPECT_TRUE(image_data->ImageDataInCanvasColorSettings( EXPECT_TRUE(image_data->ImageDataInCanvasColorSettings(
canvas_color_spaces[k], canvas_pixel_formats[k], canvas_color_spaces[k], canvas_pixel_formats[k],
pixels_converted_in_image_data, kRGBAColorType)); pixels_converted_in_image_data.get(), kRGBAColorType));
// Compare the converted pixels // Compare the converted pixels
ColorCorrectionTestUtils::CompareColorCorrectedPixels( ColorCorrectionTestUtils::CompareColorCorrectedPixels(
......
...@@ -23,6 +23,7 @@ ...@@ -23,6 +23,7 @@
#include "third_party/skia/include/core/SkImageInfo.h" #include "third_party/skia/include/core/SkImageInfo.h"
#include "third_party/skia/include/core/SkSurface.h" #include "third_party/skia/include/core/SkSurface.h"
#include "third_party/skia/include/core/SkSwizzle.h" #include "third_party/skia/include/core/SkSwizzle.h"
#include "third_party/skia/include/effects/SkColorFilterImageFilter.h"
namespace blink { namespace blink {
...@@ -156,8 +157,7 @@ bool DstBufferSizeHasOverflow(const ImageBitmap::ParsedOptions& options) { ...@@ -156,8 +157,7 @@ bool DstBufferSizeHasOverflow(const ImageBitmap::ParsedOptions& options) {
return false; return false;
} }
SkImageInfo GetSkImageInfo(const scoped_refptr<StaticBitmapImage>& image) { SkImageInfo GetSkImageInfo(sk_sp<SkImage> skia_image) {
sk_sp<SkImage> skia_image = image->PaintImageForCurrentFrame().GetSkImage();
SkColorType color_type = kN32_SkColorType; SkColorType color_type = kN32_SkColorType;
if (skia_image->colorSpace() && skia_image->colorSpace()->gammaIsLinear()) if (skia_image->colorSpace() && skia_image->colorSpace()->gammaIsLinear())
color_type = kRGBA_F16_SkColorType; color_type = kRGBA_F16_SkColorType;
...@@ -166,6 +166,11 @@ SkImageInfo GetSkImageInfo(const scoped_refptr<StaticBitmapImage>& image) { ...@@ -166,6 +166,11 @@ SkImageInfo GetSkImageInfo(const scoped_refptr<StaticBitmapImage>& image) {
skia_image->refColorSpace()); skia_image->refColorSpace());
} }
SkImageInfo GetSkImageInfo(const scoped_refptr<StaticBitmapImage>& image) {
return GetSkImageInfo(image->PaintImageForCurrentFrame().GetSkImage());
}
// This function results in a readback due to using SkImage::readPixels().
scoped_refptr<Uint8Array> CopyImageData( scoped_refptr<Uint8Array> CopyImageData(
const scoped_refptr<StaticBitmapImage>& input, const scoped_refptr<StaticBitmapImage>& input,
const SkImageInfo& info) { const SkImageInfo& info) {
...@@ -217,27 +222,24 @@ scoped_refptr<StaticBitmapImage> NewImageFromRaster( ...@@ -217,27 +222,24 @@ scoped_refptr<StaticBitmapImage> NewImageFromRaster(
SkImage::MakeFromRaster(pixmap, freePixels, pixels)); SkImage::MakeFromRaster(pixmap, freePixels, pixels));
} }
scoped_refptr<StaticBitmapImage> NewImageFromRaster( static inline bool ShouldAvoidPremul(
const SkImageInfo& info, const ImageBitmap::ParsedOptions& options) {
std::unique_ptr<uint8_t[]>& image_pixels) { return options.source_is_unpremul && !options.premultiply_alpha;
SkPixmap pixmap(info, image_pixels.get(), info.minRowBytes());
return StaticBitmapImage::Create(SkImage::MakeRasterCopy(pixmap));
} }
scoped_refptr<StaticBitmapImage> FlipImageVertically( scoped_refptr<StaticBitmapImage> FlipImageVertically(
scoped_refptr<StaticBitmapImage> input, scoped_refptr<StaticBitmapImage> input,
const ImageBitmap::ParsedOptions& parsed_options) { const ImageBitmap::ParsedOptions& parsed_options) {
sk_sp<SkImage> image = input->PaintImageForCurrentFrame().GetSkImage(); sk_sp<SkImage> image = input->PaintImageForCurrentFrame().GetSkImage();
// If image is unpremul and premultiply alpha is none, we have to avoid
// SkSurface code path, which only supports premul. This code path may result if (ShouldAvoidPremul(parsed_options)) {
// in a GPU readback if |input| is texture backed since CopyImageData() uses // Unpremul code path may result in a GPU readback if |input| is texture
// SkImage::readPixels() to extract the pixels from SkImage. // backed since CopyImageData() uses SkImage::readPixels() to extract the
if (image->alphaType() == kUnpremul_SkAlphaType && // pixels from SkImage.
!parsed_options.premultiply_alpha) {
scoped_refptr<Uint8Array> image_pixels = CopyImageData(input); scoped_refptr<Uint8Array> image_pixels = CopyImageData(input);
if (!image_pixels) if (!image_pixels)
return nullptr; return nullptr;
SkImageInfo info = GetSkImageInfo(input.get()); SkImageInfo info = GetSkImageInfo(input);
unsigned image_row_bytes = info.width() * info.bytesPerPixel(); unsigned image_row_bytes = info.width() * info.bytesPerPixel();
for (int i = 0; i < info.height() / 2; i++) { for (int i = 0; i < info.height() / 2; i++) {
unsigned top_first_element = i * image_row_bytes; unsigned top_first_element = i * image_row_bytes;
...@@ -250,7 +252,8 @@ scoped_refptr<StaticBitmapImage> FlipImageVertically( ...@@ -250,7 +252,8 @@ scoped_refptr<StaticBitmapImage> FlipImageVertically(
return NewImageFromRaster(info, std::move(image_pixels)); return NewImageFromRaster(info, std::move(image_pixels));
} }
// Otherwise, we can use Skia to flip the image by drawing it on a surface. // Since we are allowed to premul the input image if needed, we can use Skia
// to flip the image by drawing it on a surface.
sk_sp<SkSurface> surface = SkSurface::MakeRaster(GetSkImageInfo(input)); sk_sp<SkSurface> surface = SkSurface::MakeRaster(GetSkImageInfo(input));
if (!surface) if (!surface)
return nullptr; return nullptr;
...@@ -258,69 +261,207 @@ scoped_refptr<StaticBitmapImage> FlipImageVertically( ...@@ -258,69 +261,207 @@ scoped_refptr<StaticBitmapImage> FlipImageVertically(
canvas->scale(1, -1); canvas->scale(1, -1);
canvas->translate(0, -input->height()); canvas->translate(0, -input->height());
canvas->drawImage(image.get(), 0, 0); canvas->drawImage(image.get(), 0, 0);
return StaticBitmapImage::Create(surface->makeImageSnapshot()); return StaticBitmapImage::Create(surface->makeImageSnapshot(),
input->ContextProviderWrapper());
} }
scoped_refptr<StaticBitmapImage> GetImageWithAlphaDisposition( scoped_refptr<StaticBitmapImage> GetImageWithAlphaDisposition(
scoped_refptr<StaticBitmapImage>&& image, scoped_refptr<StaticBitmapImage>&& image,
AlphaDisposition alpha_disposition) { AlphaDisposition alpha_disposition) {
SkAlphaType alpha_type = kPremul_SkAlphaType; SkAlphaType alpha_type = (alpha_disposition == kPremultiplyAlpha)
if (alpha_disposition == kDontPremultiplyAlpha) ? kPremul_SkAlphaType
alpha_type = kUnpremul_SkAlphaType; : kUnpremul_SkAlphaType;
sk_sp<SkImage> skia_image = image->PaintImageForCurrentFrame().GetSkImage(); sk_sp<SkImage> skia_image = image->PaintImageForCurrentFrame().GetSkImage();
if (skia_image->alphaType() == alpha_type) if (skia_image->alphaType() == alpha_type)
return image; return image;
SkImageInfo info = GetSkImageInfo(image.get()); SkImageInfo info = GetSkImageInfo(image.get());
info = info.makeAlphaType(alpha_type); info = info.makeAlphaType(alpha_type);
scoped_refptr<Uint8Array> dst_pixels =
CopyImageData(image, info.makeColorSpace(nullptr));
if (!dst_pixels)
return nullptr;
return NewImageFromRaster(info, std::move(dst_pixels));
}
scoped_refptr<StaticBitmapImage> ScaleImage( // For premul to unpremul, we have to readback the pixels.
scoped_refptr<StaticBitmapImage>&& image, if (alpha_type == kUnpremul_SkAlphaType ||
unsigned resize_width, (image->width() * image->height() < 16384)) {
unsigned resize_height, // Set the color space of the ImageInfo to nullptr to unpremul in gamma
SkFilterQuality resize_quality) { // encoded space
auto sk_image = image->PaintImageForCurrentFrame().GetSkImage(); scoped_refptr<Uint8Array> dst_pixels =
// SkImage::scalePixels() only works in premul. If the input is unpremul, we CopyImageData(image, info.makeColorSpace(nullptr));
// convert it to premul. if (!dst_pixels)
bool converted_to_premul = false;
if (sk_image->alphaType() == kUnpremul_SkAlphaType) {
image = GetImageWithAlphaDisposition(std::move(image), kPremultiplyAlpha);
sk_image = image->PaintImageForCurrentFrame().GetSkImage();
if (!sk_image.get())
return nullptr; return nullptr;
converted_to_premul = true; return NewImageFromRaster(info, std::move(dst_pixels));
} }
// For unpremul to premul, we can either readback the pixels or draw onto a
// surface. As shown in
// https://fiddle.skia.org/c/1ec3c61ed08f7863d43b9f49ab120a0a, drawing on a
// surface and getting a snapshot is slower if the image is small. Therefore,
// for small images (< 128x128 pixels), we still do read back.
// Draw on a surface. Avoid sRGB gamma transfer curve.
if (SkColorSpace::Equals(info.colorSpace(), SkColorSpace::MakeSRGB().get()))
info = info.makeColorSpace(nullptr);
sk_sp<SkSurface> surface = SkSurface::MakeRaster(info);
if (!surface)
return nullptr;
SkPaint paint;
paint.setBlendMode(SkBlendMode::kSrc);
surface->getCanvas()->drawImage(skia_image.get(), 0, 0, &paint);
return StaticBitmapImage::Create(surface->makeImageSnapshot(),
image->ContextProviderWrapper());
}
// Resizes an SkImage using scalePixels(). This code path should not be used if
// source image is not premul and premul is not allowed and the requested filter
// quality is high.
sk_sp<SkImage> ScaleSkImage(sk_sp<SkImage> image,
const ImageBitmap::ParsedOptions& parsed_options,
AlphaDisposition alpha_disposition) {
auto image_info = GetSkImageInfo(image); auto image_info = GetSkImageInfo(image);
DCHECK(image_info.alphaType() == kPremul_SkAlphaType ||
!ShouldAvoidPremul(parsed_options) ||
parsed_options.resize_quality != kHigh_SkFilterQuality);
// Avoid sRGB transfer function by setting the color space to nullptr. // Avoid sRGB transfer function by setting the color space to nullptr.
if (SkColorSpace::Equals(image_info.colorSpace(), if (SkColorSpace::Equals(image_info.colorSpace(),
SkColorSpace::MakeSRGB().get())) SkColorSpace::MakeSRGB().get()))
image_info = image_info.makeColorSpace(nullptr); image_info = image_info.makeColorSpace(nullptr);
SkImageInfo resized_info = image_info.makeWH(resize_width, resize_height);
auto resized_data = // Premul if needed
SkData::MakeUninitialized(resized_info.computeMinByteSize()); if (alpha_disposition == kPremultiplyAlpha &&
if (!resized_data) image_info.alphaType() == kUnpremul_SkAlphaType) {
image_info = image_info.makeAlphaType(kPremul_SkAlphaType);
sk_sp<SkSurface> surface = SkSurface::MakeRaster(image_info);
if (!surface)
return nullptr;
SkPaint paint;
paint.setBlendMode(SkBlendMode::kSrc);
surface->getCanvas()->drawImage(image.get(), 0, 0, &paint);
image = surface->makeImageSnapshot();
}
SkImageInfo resized_info = image_info.makeWH(parsed_options.resize_width,
parsed_options.resize_height);
scoped_refptr<ArrayBuffer> resized_buffer =
ArrayBuffer::CreateOrNull(resized_info.computeMinByteSize(), 1);
if (!resized_buffer)
return nullptr;
scoped_refptr<Uint8Array> resized_pixels = Uint8Array::Create(
std::move(resized_buffer), 0, resized_info.computeMinByteSize());
if (!resized_pixels)
return nullptr; return nullptr;
SkPixmap resized_pixmap(resized_info, resized_data->writable_data(), SkPixmap resized_pixmap(resized_info, resized_pixels->Data(),
resized_info.minRowBytes()); resized_info.minRowBytes());
sk_image->scalePixels(resized_pixmap, resize_quality); image->scalePixels(resized_pixmap, parsed_options.resize_quality);
// Tag the resized Pixmap with the correct color space. // Tag the resized Pixmap with the correct color space.
resized_pixmap.setColorSpace(GetSkImageInfo(image).refColorSpace()); resized_pixmap.setColorSpace(GetSkImageInfo(image).refColorSpace());
auto resized_sk_image = SkImage::MakeRasterCopy(resized_pixmap);
scoped_refptr<StaticBitmapImage> resized_image = StaticBitmapImage::Create( Uint8Array* pixels = resized_pixels.get();
resized_sk_image, image->ContextProviderWrapper()); if (pixels) {
pixels->AddRef();
// If the source image was unpremul, unpremul the resized image. resized_pixels = nullptr;
if (converted_to_premul) {
resized_image = GetImageWithAlphaDisposition(std::move(resized_image),
kDontPremultiplyAlpha);
} }
return resized_image; return SkImage::MakeFromRaster(resized_pixmap, freePixels, pixels);
}
scoped_refptr<StaticBitmapImage> ScaleImage(
scoped_refptr<StaticBitmapImage>&& image,
const ImageBitmap::ParsedOptions& parsed_options) {
// Use ScaleSkImage() to resize the image unless the image is unpremul and
// premul code path is not allowed and the filter quality is high.
auto image_info = GetSkImageInfo(image);
if (image_info.alphaType() == kPremul_SkAlphaType ||
!ShouldAvoidPremul(parsed_options) ||
parsed_options.resize_quality != kHigh_SkFilterQuality) {
auto sk_image = image->PaintImageForCurrentFrame().GetSkImage();
AlphaDisposition alpha_disposition = kDontPremultiplyAlpha;
if (image_info.alphaType() == kUnpremul_SkAlphaType &&
!ShouldAvoidPremul(parsed_options))
alpha_disposition = kPremultiplyAlpha;
auto resized_sk_image =
ScaleSkImage(sk_image, parsed_options, alpha_disposition);
return StaticBitmapImage::Create(resized_sk_image,
image->ContextProviderWrapper());
}
// If source image is unpremul, premul code path is not allowed, and the
// filter quality is high, we cannot use SkImage::scalePixels(), and thus
// ScaleSkImage(), as Skia clamps color channels to alpha in this case.
// Instead, we scale color channels and alpha channel separately: RGBA ->
// RGB/255 and A/255,255,255, scale, merge.
SkScalar set_alpha_255[] = {1, 0, 0, 0, 0, // copy red channel
0, 1, 0, 0, 0, // copy green channel
0, 0, 1, 0, 0, // copy blue channel
0, 0, 0, 0, 255}; // set alpha to 255
auto color_filter_set_alpha_255 =
SkColorFilter::MakeMatrixFilterRowMajor255(set_alpha_255);
auto image_filter_set_alpha_255 = SkColorFilterImageFilter::Make(
std::move(color_filter_set_alpha_255), nullptr, nullptr);
SkScalar copy_alpha_to_red[] = {0, 0, 0, 1, 0, // copy alpha to red
0, 0, 0, 0, 0, // set green to zero
0, 0, 0, 0, 0, // set blue to zero
0, 0, 0, 0, 255}; // set alpha to 255
auto color_filter_copy_alpha_to_red =
SkColorFilter::MakeMatrixFilterRowMajor255(copy_alpha_to_red);
auto image_filter_copy_alpha_to_red = SkColorFilterImageFilter::Make(
std::move(color_filter_copy_alpha_to_red), nullptr, nullptr);
// separate RGBA to RGB/255 and A,0,0/255
SkIRect subset;
SkIPoint offset;
auto sk_image = image->PaintImageForCurrentFrame().GetSkImage();
auto rgb_image = sk_image->makeWithFilter(
image_filter_set_alpha_255.get(), sk_image->bounds(), sk_image->bounds(),
&subset, &offset);
auto alpha_image = sk_image->makeWithFilter(
image_filter_copy_alpha_to_red.get(), sk_image->bounds(),
sk_image->bounds(), &subset, &offset);
// resize
auto resized_rgb_image =
ScaleSkImage(rgb_image, parsed_options, kDontPremultiplyAlpha);
auto resized_alpha_image =
ScaleSkImage(alpha_image, parsed_options, kDontPremultiplyAlpha);
// Merge two resized rgb and alpha SkImages together.
// A better solution would be using SkImageFilter and SkBlendMode to merge
// the images: convert RGB/255 to RGB/0, convert A,0,0/255 to 0,0,0/A, merge
// using kSrc and kLighten blend modes. Unfortunately, this doesn't work as
// SkImageFilter clamps color channels to zero when setting alpha to zero.
// Therefore, we use a pixmap here.
scoped_refptr<Uint8Array> rgb_data =
CopyImageData(StaticBitmapImage::Create(resized_rgb_image));
scoped_refptr<Uint8Array> alpha_data =
CopyImageData(StaticBitmapImage::Create(resized_alpha_image));
SkImageInfo resized_image_info = GetSkImageInfo(resized_rgb_image);
if (resized_image_info.colorType() == kRGBA_F16_SkColorType) {
uint16_t* rgb_data_iter = static_cast<uint16_t*>((void*)(rgb_data->Data()));
uint16_t* alpha_data_iter =
static_cast<uint16_t*>((void*)(alpha_data->Data()));
for (int i = 0;
i < resized_image_info.width() * resized_image_info.height(); i++)
*(rgb_data_iter + i * 4 + 3) = *(alpha_data_iter + i * 4);
} else {
uint8_t* rgb_data_iter = static_cast<uint8_t*>(rgb_data->Data());
uint8_t* alpha_data_iter = static_cast<uint8_t*>(alpha_data->Data());
int red_channel_locator =
(kN32_SkColorType == kRGBA_8888_SkColorType) ? 0 : 2;
for (int i = 0;
i < resized_image_info.width() * resized_image_info.height(); i++) {
*(rgb_data_iter + i * 4 + 3) =
*(alpha_data_iter + i * 4 + red_channel_locator);
}
}
SkImageInfo resized_unpremul_info =
resized_image_info.makeAlphaType(kUnpremul_SkAlphaType);
SkPixmap pixmap(resized_unpremul_info, rgb_data->Data(),
resized_unpremul_info.minRowBytes());
Uint8Array* pixels = rgb_data.get();
if (pixels) {
pixels->AddRef();
rgb_data = nullptr;
}
return StaticBitmapImage::Create(
SkImage::MakeFromRaster(pixmap, freePixels, pixels));
} }
scoped_refptr<StaticBitmapImage> ApplyColorSpaceConversion( scoped_refptr<StaticBitmapImage> ApplyColorSpaceConversion(
...@@ -399,6 +540,17 @@ static scoped_refptr<StaticBitmapImage> CropImageAndApplyColorSpaceConversion( ...@@ -399,6 +540,17 @@ static scoped_refptr<StaticBitmapImage> CropImageAndApplyColorSpaceConversion(
skia_image = ImageBitmap::GetSkImageFromDecoder(std::move(decoder)); skia_image = ImageBitmap::GetSkImageFromDecoder(std::move(decoder));
if (!skia_image) if (!skia_image)
return nullptr; return nullptr;
// In the case where the source image is lazy-decoded, image_ may not be in
// a decoded state, we trigger it here.
SkPixmap pixmap;
if (!skia_image->isTextureBacked() && !skia_image->peekPixels(&pixmap)) {
sk_sp<SkSurface> surface =
SkSurface::MakeRaster(GetSkImageInfo(StaticBitmapImage::Create(
skia_image, image->ContextProviderWrapper())));
surface->getCanvas()->drawImage(skia_image.get(), 0, 0);
skia_image = surface->makeImageSnapshot();
}
} }
if (src_rect != img_rect) if (src_rect != img_rect)
...@@ -413,32 +565,41 @@ static scoped_refptr<StaticBitmapImage> CropImageAndApplyColorSpaceConversion( ...@@ -413,32 +565,41 @@ static scoped_refptr<StaticBitmapImage> CropImageAndApplyColorSpaceConversion(
(parsed_options.resize_width * parsed_options.resize_height < (parsed_options.resize_width * parsed_options.resize_height <
result->Size().Area()); result->Size().Area());
bool up_scaling = parsed_options.should_scale_input && !down_scaling; bool up_scaling = parsed_options.should_scale_input && !down_scaling;
// resize if down-scaling // resize if down-scaling
if (down_scaling) { if (down_scaling) {
result = result = ScaleImage(std::move(result), parsed_options);
ScaleImage(std::move(result), parsed_options.resize_width, if (!result)
parsed_options.resize_height, parsed_options.resize_quality); return nullptr;
} }
// flip if needed // flip if needed
if (parsed_options.flip_y) if (parsed_options.flip_y) {
result = FlipImageVertically(std::move(result), parsed_options); result = FlipImageVertically(std::move(result), parsed_options);
if (!result)
return nullptr;
}
// color convert if needed // color convert if needed
if (parsed_options.has_color_space_conversion) if (parsed_options.has_color_space_conversion) {
result = ApplyColorSpaceConversion(std::move(result), parsed_options); result = ApplyColorSpaceConversion(std::move(result), parsed_options);
if (!result)
return nullptr;
}
// resize if up-scaling
if (up_scaling) {
result = ScaleImage(std::move(result), parsed_options);
if (!result)
return nullptr;
}
// premultiply / unpremultiply if needed // premultiply / unpremultiply if needed
result = GetImageWithAlphaDisposition(std::move(result), result = GetImageWithAlphaDisposition(std::move(result),
parsed_options.premultiply_alpha parsed_options.premultiply_alpha
? kPremultiplyAlpha ? kPremultiplyAlpha
: kDontPremultiplyAlpha); : kDontPremultiplyAlpha);
// resize if up-scaling
if (up_scaling) {
result =
ScaleImage(std::move(result), parsed_options.resize_width,
parsed_options.resize_height, parsed_options.resize_quality);
}
return result; return result;
} }
...@@ -449,6 +610,9 @@ ImageBitmap::ImageBitmap(ImageElementBase* image, ...@@ -449,6 +610,9 @@ ImageBitmap::ImageBitmap(ImageElementBase* image,
scoped_refptr<Image> input = image->CachedImage()->GetImage(); scoped_refptr<Image> input = image->CachedImage()->GetImage();
ParsedOptions parsed_options = ParsedOptions parsed_options =
ParseOptions(options, crop_rect, image->BitmapSourceSize()); ParseOptions(options, crop_rect, image->BitmapSourceSize());
parsed_options.source_is_unpremul =
(input->PaintImageForCurrentFrame().GetSkImage()->alphaType() ==
kUnpremul_SkAlphaType);
if (DstBufferSizeHasOverflow(parsed_options)) if (DstBufferSizeHasOverflow(parsed_options))
return; return;
...@@ -457,27 +621,6 @@ ImageBitmap::ImageBitmap(ImageElementBase* image, ...@@ -457,27 +621,6 @@ ImageBitmap::ImageBitmap(ImageElementBase* image,
if (!image_) if (!image_)
return; return;
// In the case where the source image is lazy-decoded, m_image may not be in
// a decoded state, we trigger it here.
sk_sp<SkImage> sk_image = image_->PaintImageForCurrentFrame().GetSkImage();
SkPixmap pixmap;
if (!sk_image->isTextureBacked() && !sk_image->peekPixels(&pixmap)) {
sk_sp<SkColorSpace> dst_color_space =
parsed_options.color_params.GetSkColorSpace();
SkColorType 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_ = ApplyColorSpaceConversion(
StaticBitmapImage::Create(surface->makeImageSnapshot(),
input->ContextProviderWrapper()),
parsed_options);
}
if (!image_)
return;
image_->SetOriginClean( image_->SetOriginClean(
!image->WouldTaintOrigin(document->GetSecurityOrigin())); !image->WouldTaintOrigin(document->GetSecurityOrigin()));
} }
...@@ -586,6 +729,8 @@ ImageBitmap::ImageBitmap(ImageData* data, ...@@ -586,6 +729,8 @@ ImageBitmap::ImageBitmap(ImageData* data,
const ImageBitmapOptions& options) { const ImageBitmapOptions& options) {
ParsedOptions parsed_options = ParsedOptions parsed_options =
ParseOptions(options, crop_rect, data->BitmapSourceSize()); ParseOptions(options, crop_rect, data->BitmapSourceSize());
// ImageData is always unpremul.
parsed_options.source_is_unpremul = true;
if (DstBufferSizeHasOverflow(parsed_options)) if (DstBufferSizeHasOverflow(parsed_options))
return; return;
...@@ -601,19 +746,43 @@ ImageBitmap::ImageBitmap(ImageData* data, ...@@ -601,19 +746,43 @@ ImageBitmap::ImageBitmap(ImageData* data,
return; return;
} }
int byte_length = // Copy / color convert the pixels
src_rect.Size().Area() * parsed_options.color_params.BytesPerPixel(); scoped_refptr<ArrayBuffer> pixels_buffer = ArrayBuffer::CreateOrNull(
std::unique_ptr<uint8_t[]> image_pixels(new uint8_t[byte_length]); src_rect.Size().Area(), parsed_options.color_params.BytesPerPixel());
if (!pixels_buffer)
return;
unsigned byte_length = pixels_buffer->ByteLength();
scoped_refptr<Uint8Array> image_pixels =
Uint8Array::Create(std::move(pixels_buffer), 0, byte_length);
if (!image_pixels)
return;
if (!data->ImageDataInCanvasColorSettings( if (!data->ImageDataInCanvasColorSettings(
parsed_options.color_params.ColorSpace(), parsed_options.color_params.ColorSpace(),
parsed_options.color_params.PixelFormat(), image_pixels, parsed_options.color_params.PixelFormat(), image_pixels->Data(),
kN32ColorType, &src_rect)) kN32ColorType, &src_rect))
return; return;
// Create Image object
SkImageInfo info = SkImageInfo::Make( SkImageInfo info = SkImageInfo::Make(
src_rect.Width(), src_rect.Height(), src_rect.Width(), src_rect.Height(),
parsed_options.color_params.GetSkColorType(), kUnpremul_SkAlphaType, parsed_options.color_params.GetSkColorType(), kUnpremul_SkAlphaType,
parsed_options.color_params.GetSkColorSpaceForSkSurfaces()); parsed_options.color_params.GetSkColorSpaceForSkSurfaces());
image_ = NewImageFromRaster(info, image_pixels); image_ = NewImageFromRaster(info, std::move(image_pixels));
if (!image_)
return;
// down-scaling has higher priority than other tasks, up-scaling has lower.
bool down_scaling =
parsed_options.should_scale_input &&
(parsed_options.resize_width * parsed_options.resize_height <
image_->Size().Area());
bool up_scaling = parsed_options.should_scale_input && !down_scaling;
// resize if down-scaling
if (down_scaling)
image_ = ScaleImage(std::move(image_), parsed_options);
if (!image_)
return;
// premultiply if needed // premultiply if needed
if (parsed_options.premultiply_alpha) if (parsed_options.premultiply_alpha)
...@@ -624,15 +793,12 @@ ImageBitmap::ImageBitmap(ImageData* data, ...@@ -624,15 +793,12 @@ ImageBitmap::ImageBitmap(ImageData* data,
// flip if needed // flip if needed
if (parsed_options.flip_y) if (parsed_options.flip_y)
image_ = FlipImageVertically(std::move(image_), parsed_options); image_ = FlipImageVertically(std::move(image_), parsed_options);
if (!image_)
return;
// resize if needed // resize if up-scaling
if (parsed_options.should_scale_input) { if (up_scaling)
image_ = image_ = ScaleImage(std::move(image_), parsed_options);
ScaleImage(std::move(image_), parsed_options.resize_width,
parsed_options.resize_height, parsed_options.resize_quality);
if (!image_)
return;
}
} }
ImageBitmap::ImageBitmap(ImageBitmap* bitmap, ImageBitmap::ImageBitmap(ImageBitmap* bitmap,
...@@ -643,6 +809,9 @@ ImageBitmap::ImageBitmap(ImageBitmap* bitmap, ...@@ -643,6 +809,9 @@ ImageBitmap::ImageBitmap(ImageBitmap* bitmap,
return; return;
ParsedOptions parsed_options = ParsedOptions parsed_options =
ParseOptions(options, crop_rect, input->Size()); ParseOptions(options, crop_rect, input->Size());
parsed_options.source_is_unpremul =
(input->PaintImageForCurrentFrame().GetSkImage()->alphaType() ==
kUnpremul_SkAlphaType);
if (DstBufferSizeHasOverflow(parsed_options)) if (DstBufferSizeHasOverflow(parsed_options))
return; return;
...@@ -660,6 +829,9 @@ ImageBitmap::ImageBitmap(scoped_refptr<StaticBitmapImage> image, ...@@ -660,6 +829,9 @@ ImageBitmap::ImageBitmap(scoped_refptr<StaticBitmapImage> image,
bool origin_clean = image->OriginClean(); bool origin_clean = image->OriginClean();
ParsedOptions parsed_options = ParsedOptions parsed_options =
ParseOptions(options, crop_rect, image->Size()); ParseOptions(options, crop_rect, image->Size());
parsed_options.source_is_unpremul =
(image->PaintImageForCurrentFrame().GetSkImage()->alphaType() ==
kUnpremul_SkAlphaType);
if (DstBufferSizeHasOverflow(parsed_options)) if (DstBufferSizeHasOverflow(parsed_options))
return; return;
......
...@@ -134,6 +134,7 @@ class CORE_EXPORT ImageBitmap final : public ScriptWrappable, ...@@ -134,6 +134,7 @@ class CORE_EXPORT ImageBitmap final : public ScriptWrappable,
bool premultiply_alpha = true; bool premultiply_alpha = true;
bool should_scale_input = false; bool should_scale_input = false;
bool has_color_space_conversion = false; bool has_color_space_conversion = false;
bool source_is_unpremul = false;
unsigned resize_width = 0; unsigned resize_width = 0;
unsigned resize_height = 0; unsigned resize_height = 0;
IntRect crop_rect; IntRect crop_rect;
......
...@@ -1757,8 +1757,9 @@ void BaseRenderingContext2D::putImageData(ImageData* data, ...@@ -1757,8 +1757,9 @@ void BaseRenderingContext2D::putImageData(ImageData* data,
unsigned data_length = unsigned data_length =
data->Size().Area() * context_color_params.BytesPerPixel(); data->Size().Area() * context_color_params.BytesPerPixel();
std::unique_ptr<uint8_t[]> converted_pixels(new uint8_t[data_length]); std::unique_ptr<uint8_t[]> converted_pixels(new uint8_t[data_length]);
if (data->ImageDataInCanvasColorSettings( if (data->ImageDataInCanvasColorSettings(ColorSpace(), PixelFormat(),
ColorSpace(), PixelFormat(), converted_pixels, kRGBAColorType)) { converted_pixels.get(),
kRGBAColorType)) {
buffer->PutByteArray(converted_pixels.get(), buffer->PutByteArray(converted_pixels.get(),
IntSize(data->width(), data->height()), source_rect, IntSize(data->width(), data->height()), source_rect,
IntPoint(dest_offset)); IntPoint(dest_offset));
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment