blob: 86600c029a2e977cc5a5404e250adb764c7fb184 [file] [log] [blame]
// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "core/frame/ImageBitmap.h"
#include "core/html/HTMLCanvasElement.h"
#include "core/html/HTMLVideoElement.h"
#include "core/html/ImageData.h"
#include "platform/graphics/skia/SkiaUtils.h"
#include "platform/image-decoders/ImageDecoder.h"
#include "third_party/skia/include/core/SkCanvas.h"
#include "third_party/skia/include/core/SkSurface.h"
#include "wtf/CheckedNumeric.h"
#include "wtf/PtrUtil.h"
#include "wtf/RefPtr.h"
#include <memory>
namespace blink {
static const char* imageOrientationFlipY = "flipY";
static const char* imageBitmapOptionNone = "none";
namespace {
struct ParsedOptions {
bool flipY = false;
bool premultiplyAlpha = true;
bool shouldScaleInput = false;
unsigned resizeWidth = 0;
unsigned resizeHeight = 0;
IntRect cropRect;
SkFilterQuality resizeQuality = kLow_SkFilterQuality;
// This value should be changed in the future when we support createImageBitmap with higher
// bit depth, in the parseOptions() function. For now, it is always 4.
int bytesPerPixel = 4;
};
// The following two functions are helpers used in cropImage
static inline IntRect normalizeRect(const IntRect& rect)
{
return IntRect(std::min(rect.x(), rect.maxX()),
std::min(rect.y(), rect.maxY()),
std::max(rect.width(), -rect.width()),
std::max(rect.height(), -rect.height()));
}
static bool frameIsValid(const SkBitmap& frameBitmap)
{
ASSERT(!frameBitmap.isNull() && !frameBitmap.empty() && frameBitmap.isImmutable());
return frameBitmap.colorType() == kN32_SkColorType;
}
ParsedOptions parseOptions(const ImageBitmapOptions& options, Optional<IntRect> cropRect, IntSize sourceSize)
{
ParsedOptions parsedOptions;
if (options.imageOrientation() == imageOrientationFlipY) {
parsedOptions.flipY = true;
} else {
parsedOptions.flipY = false;
DCHECK(options.imageOrientation() == imageBitmapOptionNone);
}
if (options.premultiplyAlpha() == imageBitmapOptionNone) {
parsedOptions.premultiplyAlpha = false;
} else {
parsedOptions.premultiplyAlpha = true;
DCHECK(options.premultiplyAlpha() == "default" || options.premultiplyAlpha() == "premultiply");
}
int sourceWidth = sourceSize.width();
int sourceHeight = sourceSize.height();
if (!cropRect) {
parsedOptions.cropRect = IntRect(0, 0, sourceWidth, sourceHeight);
} else {
parsedOptions.cropRect = normalizeRect(*cropRect);
}
if (!options.hasResizeWidth() && !options.hasResizeHeight()) {
parsedOptions.resizeWidth = parsedOptions.cropRect.width();
parsedOptions.resizeHeight = parsedOptions.cropRect.height();
} else if (options.hasResizeWidth() && options.hasResizeHeight()) {
parsedOptions.resizeWidth = options.resizeWidth();
parsedOptions.resizeHeight = options.resizeHeight();
} else if (options.hasResizeWidth() && !options.hasResizeHeight()) {
parsedOptions.resizeWidth = options.resizeWidth();
parsedOptions.resizeHeight = ceil(static_cast<float>(options.resizeWidth()) / parsedOptions.cropRect.width() * parsedOptions.cropRect.height());
} else {
parsedOptions.resizeHeight = options.resizeHeight();
parsedOptions.resizeWidth = ceil(static_cast<float>(options.resizeHeight()) / parsedOptions.cropRect.height() * parsedOptions.cropRect.width());
}
if (static_cast<int>(parsedOptions.resizeWidth) == parsedOptions.cropRect.width() && static_cast<int>(parsedOptions.resizeHeight) == parsedOptions.cropRect.height()) {
parsedOptions.shouldScaleInput = false;
return parsedOptions;
}
parsedOptions.shouldScaleInput = true;
if (options.resizeQuality() == "high")
parsedOptions.resizeQuality = kHigh_SkFilterQuality;
else if (options.resizeQuality() == "medium")
parsedOptions.resizeQuality = kMedium_SkFilterQuality;
else if (options.resizeQuality() == "pixelated")
parsedOptions.resizeQuality = kNone_SkFilterQuality;
else
parsedOptions.resizeQuality = kLow_SkFilterQuality;
return parsedOptions;
}
bool dstBufferSizeHasOverflow(ParsedOptions options)
{
CheckedNumeric<size_t> totalBytes = options.cropRect.width();
totalBytes *= options.cropRect.height();
totalBytes *= options.bytesPerPixel;
if (!totalBytes.IsValid())
return true;
if (!options.shouldScaleInput)
return false;
totalBytes = options.resizeWidth;
totalBytes *= options.resizeHeight;
totalBytes *= options.bytesPerPixel;
if (!totalBytes.IsValid())
return true;
return false;
}
} // namespace
static PassRefPtr<Uint8Array> copySkImageData(SkImage* input, const SkImageInfo& info)
{
// The function dstBufferSizeHasOverflow() is being called at the beginning of each
// ImageBitmap() constructor, which makes sure that doing width * height * bytesPerPixel
// will never overflow size_t.
size_t width = static_cast<size_t>(input->width());
RefPtr<ArrayBuffer> dstBuffer = ArrayBuffer::createOrNull(width * input->height(), info.bytesPerPixel());
if (!dstBuffer)
return nullptr;
RefPtr<Uint8Array> dstPixels = Uint8Array::create(dstBuffer, 0, dstBuffer->byteLength());
input->readPixels(info, dstPixels->data(), width * info.bytesPerPixel(), 0, 0);
return dstPixels;
}
static PassRefPtr<SkImage> newSkImageFromRaster(const SkImageInfo& info, PassRefPtr<Uint8Array> imagePixels, size_t imageRowBytes)
{
SkPixmap pixmap(info, imagePixels->data(), imageRowBytes);
return fromSkSp(SkImage::MakeFromRaster(pixmap, [](const void*, void* pixels)
{
static_cast<Uint8Array*>(pixels)->deref();
}, imagePixels.leakRef()));
}
static void swizzleImageData(unsigned char* srcAddr, size_t height, size_t bytesPerRow, bool flipY)
{
if (flipY) {
for (size_t i = 0; i < height / 2; i++) {
size_t topRowStartPosition = i * bytesPerRow;
size_t bottomRowStartPosition = (height - 1 - i) * bytesPerRow;
if (kN32_SkColorType == kBGRA_8888_SkColorType) { // needs to swizzle
for (size_t j = 0; j < bytesPerRow; j += 4) {
std::swap(srcAddr[topRowStartPosition + j], srcAddr[bottomRowStartPosition + j + 2]);
std::swap(srcAddr[topRowStartPosition + j + 1], srcAddr[bottomRowStartPosition + j + 1]);
std::swap(srcAddr[topRowStartPosition + j + 2], srcAddr[bottomRowStartPosition + j]);
std::swap(srcAddr[topRowStartPosition + j + 3], srcAddr[bottomRowStartPosition + j + 3]);
}
} else {
std::swap_ranges(srcAddr + topRowStartPosition, srcAddr + topRowStartPosition + bytesPerRow, srcAddr + bottomRowStartPosition);
}
}
} else {
if (kN32_SkColorType == kBGRA_8888_SkColorType) // needs to swizzle
for (size_t i = 0; i < height * bytesPerRow; i += 4)
std::swap(srcAddr[i], srcAddr[i + 2]);
}
}
static PassRefPtr<SkImage> flipSkImageVertically(SkImage* input, AlphaDisposition alphaOp)
{
size_t width = static_cast<size_t>(input->width());
size_t height = static_cast<size_t>(input->height());
SkImageInfo info = SkImageInfo::MakeN32(input->width(), input->height(), (alphaOp == PremultiplyAlpha) ? kPremul_SkAlphaType : kUnpremul_SkAlphaType);
size_t imageRowBytes = width * info.bytesPerPixel();
RefPtr<Uint8Array> imagePixels = copySkImageData(input, info);
if (!imagePixels)
return nullptr;
for (size_t i = 0; i < height / 2; i++) {
size_t topFirstElement = i * imageRowBytes;
size_t topLastElement = (i + 1) * imageRowBytes;
size_t bottomFirstElement = (height - 1 - i) * imageRowBytes;
std::swap_ranges(imagePixels->data() + topFirstElement, imagePixels->data() + topLastElement, imagePixels->data() + bottomFirstElement);
}
return newSkImageFromRaster(info, std::move(imagePixels), imageRowBytes);
}
static PassRefPtr<SkImage> premulSkImageToUnPremul(SkImage* input)
{
SkImageInfo info = SkImageInfo::Make(input->width(), input->height(), kN32_SkColorType, kUnpremul_SkAlphaType);
RefPtr<Uint8Array> dstPixels = copySkImageData(input, info);
if (!dstPixels)
return nullptr;
return newSkImageFromRaster(info, std::move(dstPixels), static_cast<size_t>(input->width()) * info.bytesPerPixel());
}
static PassRefPtr<SkImage> unPremulSkImageToPremul(SkImage* input)
{
SkImageInfo info = SkImageInfo::Make(input->width(), input->height(), kN32_SkColorType, kPremul_SkAlphaType);
RefPtr<Uint8Array> dstPixels = copySkImageData(input, info);
if (!dstPixels)
return nullptr;
return newSkImageFromRaster(info, std::move(dstPixels), static_cast<size_t>(input->width()) * info.bytesPerPixel());
}
PassRefPtr<SkImage> ImageBitmap::getSkImageFromDecoder(std::unique_ptr<ImageDecoder> decoder)
{
if (!decoder->frameCount())
return nullptr;
ImageFrame* frame = decoder->frameBufferAtIndex(0);
if (!frame || frame->getStatus() != ImageFrame::FrameComplete)
return nullptr;
SkBitmap bitmap = frame->bitmap();
if (!frameIsValid(bitmap))
return nullptr;
return fromSkSp(SkImage::MakeFromBitmap(bitmap));
}
bool ImageBitmap::isResizeOptionValid(const ImageBitmapOptions& options, ExceptionState& exceptionState)
{
if ((options.hasResizeWidth() && options.resizeWidth() == 0) || (options.hasResizeHeight() && options.resizeHeight() == 0)) {
exceptionState.throwDOMException(InvalidStateError, "The resizeWidth or/and resizeHeight is equal to 0.");
return false;
}
return true;
}
bool ImageBitmap::isSourceSizeValid(int sourceWidth, int sourceHeight, ExceptionState& exceptionState)
{
if (!sourceWidth || !sourceHeight) {
exceptionState.throwDOMException(IndexSizeError, String::format("The source %s provided is 0.", sourceWidth ? "height" : "width"));
return false;
}
return true;
}
// The parameter imageFormat indicates whether the first parameter "image" is unpremultiplied or not.
// imageFormat = PremultiplyAlpha means the image is in premuliplied format
// For example, if the image is already in unpremultiplied format and we want the created ImageBitmap
// in the same format, then we don't need to use the ImageDecoder to decode the image.
static PassRefPtr<StaticBitmapImage> cropImage(Image* image, const ParsedOptions& parsedOptions, AlphaDisposition imageFormat = PremultiplyAlpha, ImageDecoder::GammaAndColorProfileOption colorSpaceOp = ImageDecoder::GammaAndColorProfileApplied)
{
ASSERT(image);
IntRect imgRect(IntPoint(), IntSize(image->width(), image->height()));
const IntRect srcRect = intersection(imgRect, parsedOptions.cropRect);
// In the case when cropRect doesn't intersect the source image and it requires a umpremul image
// We immediately return a transparent black image with cropRect.size()
if (srcRect.isEmpty() && !parsedOptions.premultiplyAlpha) {
SkImageInfo info = SkImageInfo::Make(parsedOptions.resizeWidth, parsedOptions.resizeHeight, kN32_SkColorType, kUnpremul_SkAlphaType);
RefPtr<ArrayBuffer> dstBuffer = ArrayBuffer::createOrNull(static_cast<size_t>(info.width()) * info.height(), info.bytesPerPixel());
if (!dstBuffer)
return nullptr;
RefPtr<Uint8Array> dstPixels = Uint8Array::create(dstBuffer, 0, dstBuffer->byteLength());
return StaticBitmapImage::create(newSkImageFromRaster(info, std::move(dstPixels), static_cast<size_t>(info.width()) * info.bytesPerPixel()));
}
RefPtr<SkImage> skiaImage = image->imageForCurrentFrame();
// Attempt to get raw unpremultiplied image data, executed only when skiaImage is premultiplied.
if ((((!parsedOptions.premultiplyAlpha && !skiaImage->isOpaque()) || !skiaImage) && image->data() && imageFormat == PremultiplyAlpha) || colorSpaceOp == ImageDecoder::GammaAndColorProfileIgnored) {
std::unique_ptr<ImageDecoder> decoder(ImageDecoder::create(
image->data(), true,
parsedOptions.premultiplyAlpha ? ImageDecoder::AlphaPremultiplied : ImageDecoder::AlphaNotPremultiplied,
colorSpaceOp));
if (!decoder)
return nullptr;
skiaImage = ImageBitmap::getSkImageFromDecoder(std::move(decoder));
if (!skiaImage)
return nullptr;
}
if (parsedOptions.cropRect == srcRect && !parsedOptions.shouldScaleInput) {
RefPtr<SkImage> croppedSkImage = fromSkSp(skiaImage->makeSubset(srcRect));
if (parsedOptions.flipY)
return StaticBitmapImage::create(flipSkImageVertically(croppedSkImage.get(), parsedOptions.premultiplyAlpha ? PremultiplyAlpha : DontPremultiplyAlpha));
// Special case: The first parameter image is unpremul but we need to turn it into premul.
if (parsedOptions.premultiplyAlpha && imageFormat == DontPremultiplyAlpha)
return StaticBitmapImage::create(unPremulSkImageToPremul(croppedSkImage.get()));
// Call preroll to trigger image decoding.
croppedSkImage->preroll();
return StaticBitmapImage::create(croppedSkImage.release());
}
sk_sp<SkSurface> surface = SkSurface::MakeRasterN32Premul(parsedOptions.resizeWidth, parsedOptions.resizeHeight);
if (!surface)
return nullptr;
if (srcRect.isEmpty())
return StaticBitmapImage::create(fromSkSp(surface->makeImageSnapshot()));
SkScalar dstLeft = std::min(0, -parsedOptions.cropRect.x());
SkScalar dstTop = std::min(0, -parsedOptions.cropRect.y());
if (parsedOptions.cropRect.x() < 0)
dstLeft = -parsedOptions.cropRect.x();
if (parsedOptions.cropRect.y() < 0)
dstTop = -parsedOptions.cropRect.y();
if (parsedOptions.flipY) {
surface->getCanvas()->translate(0, surface->height());
surface->getCanvas()->scale(1, -1);
}
if (parsedOptions.shouldScaleInput) {
SkRect drawSrcRect = SkRect::MakeXYWH(parsedOptions.cropRect.x(), parsedOptions.cropRect.y(), parsedOptions.cropRect.width(), parsedOptions.cropRect.height());
SkRect drawDstRect = SkRect::MakeXYWH(0, 0, parsedOptions.resizeWidth, parsedOptions.resizeHeight);
SkPaint paint;
paint.setFilterQuality(parsedOptions.resizeQuality);
surface->getCanvas()->drawImageRect(skiaImage.get(), drawSrcRect, drawDstRect, &paint);
} else {
surface->getCanvas()->drawImage(skiaImage.get(), dstLeft, dstTop);
}
skiaImage = fromSkSp(surface->makeImageSnapshot());
if (parsedOptions.premultiplyAlpha) {
if (imageFormat == DontPremultiplyAlpha)
return StaticBitmapImage::create(unPremulSkImageToPremul(skiaImage.get()));
return StaticBitmapImage::create(skiaImage.release());
}
return StaticBitmapImage::create(premulSkImageToUnPremul(skiaImage.get()));
}
ImageBitmap::ImageBitmap(HTMLImageElement* image, Optional<IntRect> cropRect, Document* document, const ImageBitmapOptions& options)
{
RefPtr<Image> input = image->cachedImage()->getImage();
ParsedOptions parsedOptions = parseOptions(options, cropRect, image->bitmapSourceSize());
if (dstBufferSizeHasOverflow(parsedOptions))
return;
if (options.colorSpaceConversion() == "none")
m_image = cropImage(input.get(), parsedOptions, PremultiplyAlpha, ImageDecoder::GammaAndColorProfileIgnored);
else
m_image = cropImage(input.get(), parsedOptions, PremultiplyAlpha, ImageDecoder::GammaAndColorProfileApplied);
if (!m_image)
return;
// In the case where the source image is lazy-decoded, m_image may not be in
// a decoded state, we trigger it here.
RefPtr<SkImage> skImage = m_image->imageForCurrentFrame();
SkPixmap pixmap;
if (!skImage->isTextureBacked() && !skImage->peekPixels(&pixmap)) {
sk_sp<SkSurface> surface = SkSurface::MakeRasterN32Premul(skImage->width(), skImage->height());
surface->getCanvas()->drawImage(skImage.get(), 0, 0);
m_image = StaticBitmapImage::create(fromSkSp(surface->makeImageSnapshot()));
}
if (!m_image)
return;
m_image->setOriginClean(!image->wouldTaintOrigin(document->getSecurityOrigin()));
m_image->setPremultiplied(parsedOptions.premultiplyAlpha);
}
ImageBitmap::ImageBitmap(HTMLVideoElement* video, Optional<IntRect> cropRect, Document* document, const ImageBitmapOptions& options)
{
IntSize playerSize;
if (video->webMediaPlayer())
playerSize = video->webMediaPlayer()->naturalSize();
ParsedOptions parsedOptions = parseOptions(options, cropRect, video->bitmapSourceSize());
if (dstBufferSizeHasOverflow(parsedOptions))
return;
IntRect videoRect = IntRect(IntPoint(), playerSize);
IntRect srcRect = intersection(parsedOptions.cropRect, videoRect);
std::unique_ptr<ImageBuffer> buffer = ImageBuffer::create(IntSize(parsedOptions.resizeWidth, parsedOptions.resizeHeight), NonOpaque, DoNotInitializeImagePixels);
if (!buffer)
return;
if (parsedOptions.flipY) {
buffer->canvas()->translate(0, buffer->size().height());
buffer->canvas()->scale(1, -1);
}
IntPoint dstPoint = IntPoint(std::max(0, -parsedOptions.cropRect.x()), std::max(0, -parsedOptions.cropRect.y()));
IntSize dstSize = srcRect.size();
SkPaint paint;
if (parsedOptions.shouldScaleInput) {
float scaleRatioX = static_cast<float>(parsedOptions.resizeWidth) / parsedOptions.cropRect.width();
float scaleRatioY = static_cast<float>(parsedOptions.resizeHeight) / parsedOptions.cropRect.height();
dstPoint.scale(scaleRatioX, scaleRatioY);
paint.setFilterQuality(parsedOptions.resizeQuality);
dstSize.scale(scaleRatioX, scaleRatioY);
}
video->paintCurrentFrame(buffer->canvas(), IntRect(dstPoint, dstSize), parsedOptions.shouldScaleInput ? &paint : nullptr);
RefPtr<SkImage> skiaImage = buffer->newSkImageSnapshot(PreferNoAcceleration, SnapshotReasonUnknown);
if (!parsedOptions.premultiplyAlpha)
skiaImage = premulSkImageToUnPremul(skiaImage.get());
if (!skiaImage)
return;
m_image = StaticBitmapImage::create(skiaImage.release());
m_image->setOriginClean(!video->wouldTaintOrigin(document->getSecurityOrigin()));
m_image->setPremultiplied(parsedOptions.premultiplyAlpha);
}
ImageBitmap::ImageBitmap(HTMLCanvasElement* canvas, Optional<IntRect> cropRect, const ImageBitmapOptions& options)
{
ASSERT(canvas->isPaintable());
RefPtr<Image> input = canvas->copiedImage(BackBuffer, PreferAcceleration);
ParsedOptions parsedOptions = parseOptions(options, cropRect, canvas->bitmapSourceSize());
if (dstBufferSizeHasOverflow(parsedOptions))
return;
bool isPremultiplyAlphaReverted = false;
if (!parsedOptions.premultiplyAlpha) {
parsedOptions.premultiplyAlpha = true;
isPremultiplyAlphaReverted = true;
}
m_image = cropImage(input.get(), parsedOptions);
if (!m_image)
return;
if (isPremultiplyAlphaReverted) {
parsedOptions.premultiplyAlpha = false;
m_image = StaticBitmapImage::create(premulSkImageToUnPremul(m_image->imageForCurrentFrame().get()));
}
if (!m_image)
return;
m_image->setOriginClean(canvas->originClean());
m_image->setPremultiplied(parsedOptions.premultiplyAlpha);
}
ImageBitmap::ImageBitmap(std::unique_ptr<uint8_t[]> data, uint32_t width, uint32_t height, bool isImageBitmapPremultiplied, bool isImageBitmapOriginClean)
{
SkImageInfo info = SkImageInfo::MakeN32(width, height, isImageBitmapPremultiplied ? kPremul_SkAlphaType : kUnpremul_SkAlphaType);
m_image = StaticBitmapImage::create(fromSkSp(SkImage::MakeRasterCopy(SkPixmap(info, data.get(), info.bytesPerPixel() * width))));
if (!m_image)
return;
m_image->setPremultiplied(isImageBitmapPremultiplied);
m_image->setOriginClean(isImageBitmapOriginClean);
}
static PassRefPtr<SkImage> scaleSkImage(PassRefPtr<SkImage> skImage, unsigned resizeWidth, unsigned resizeHeight, SkFilterQuality resizeQuality)
{
SkImageInfo resizedInfo = SkImageInfo::Make(resizeWidth, resizeHeight, kN32_SkColorType, kUnpremul_SkAlphaType);
RefPtr<ArrayBuffer> dstBuffer = ArrayBuffer::createOrNull(resizeWidth * resizeHeight, resizedInfo.bytesPerPixel());
if (!dstBuffer)
return nullptr;
RefPtr<Uint8Array> resizedPixels = Uint8Array::create(dstBuffer, 0, dstBuffer->byteLength());
SkPixmap pixmap(resizedInfo, resizedPixels->data(), static_cast<size_t>(resizeWidth) * resizedInfo.bytesPerPixel());
skImage->scalePixels(pixmap, resizeQuality);
return fromSkSp(SkImage::MakeFromRaster(pixmap, [](const void*, void* pixels)
{
static_cast<Uint8Array*>(pixels)->deref();
}, resizedPixels.release().leakRef()));
}
ImageBitmap::ImageBitmap(ImageData* data, Optional<IntRect> cropRect, const ImageBitmapOptions& options)
{
// TODO(xidachen): implement the resize option
IntRect dataSrcRect = IntRect(IntPoint(), data->size());
ParsedOptions parsedOptions = parseOptions(options, cropRect, data->bitmapSourceSize());
if (dstBufferSizeHasOverflow(parsedOptions))
return;
IntRect srcRect = cropRect ? intersection(parsedOptions.cropRect, dataSrcRect) : dataSrcRect;
// treat non-premultiplyAlpha as a special case
if (!parsedOptions.premultiplyAlpha) {
unsigned char* srcAddr = data->data()->data();
// Using kN32 type, swizzle input if necessary.
SkImageInfo info = SkImageInfo::Make(parsedOptions.cropRect.width(), parsedOptions.cropRect.height(), kN32_SkColorType, kUnpremul_SkAlphaType);
size_t bytesPerPixel = static_cast<size_t>(info.bytesPerPixel());
size_t srcPixelBytesPerRow = bytesPerPixel * data->size().width();
size_t dstPixelBytesPerRow = bytesPerPixel * parsedOptions.cropRect.width();
RefPtr<SkImage> skImage;
if (parsedOptions.cropRect == IntRect(IntPoint(), data->size())) {
swizzleImageData(srcAddr, data->size().height(), srcPixelBytesPerRow, parsedOptions.flipY);
skImage = fromSkSp(SkImage::MakeRasterCopy(SkPixmap(info, srcAddr, dstPixelBytesPerRow)));
// restore the original ImageData
swizzleImageData(srcAddr, data->size().height(), srcPixelBytesPerRow, parsedOptions.flipY);
} else {
RefPtr<ArrayBuffer> dstBuffer = ArrayBuffer::createOrNull(static_cast<size_t>(parsedOptions.cropRect.height()) * parsedOptions.cropRect.width(), bytesPerPixel);
if (!dstBuffer)
return;
RefPtr<Uint8Array> copiedDataBuffer = Uint8Array::create(dstBuffer, 0, dstBuffer->byteLength());
if (!srcRect.isEmpty()) {
IntPoint srcPoint = IntPoint((parsedOptions.cropRect.x() > 0) ? parsedOptions.cropRect.x() : 0, (parsedOptions.cropRect.y() > 0) ? parsedOptions.cropRect.y() : 0);
IntPoint dstPoint = IntPoint((parsedOptions.cropRect.x() >= 0) ? 0 : -parsedOptions.cropRect.x(), (parsedOptions.cropRect.y() >= 0) ? 0 : -parsedOptions.cropRect.y());
int copyHeight = data->size().height() - srcPoint.y();
if (parsedOptions.cropRect.height() < copyHeight)
copyHeight = parsedOptions.cropRect.height();
int copyWidth = data->size().width() - srcPoint.x();
if (parsedOptions.cropRect.width() < copyWidth)
copyWidth = parsedOptions.cropRect.width();
for (int i = 0; i < copyHeight; i++) {
size_t srcStartCopyPosition = (i + srcPoint.y()) * srcPixelBytesPerRow + srcPoint.x() * bytesPerPixel;
size_t srcEndCopyPosition = srcStartCopyPosition + copyWidth * bytesPerPixel;
size_t dstStartCopyPosition;
if (parsedOptions.flipY)
dstStartCopyPosition = (parsedOptions.cropRect.height() -1 - dstPoint.y() - i) * dstPixelBytesPerRow + dstPoint.x() * bytesPerPixel;
else
dstStartCopyPosition = (dstPoint.y() + i) * dstPixelBytesPerRow + dstPoint.x() * bytesPerPixel;
for (size_t j = 0; j < srcEndCopyPosition - srcStartCopyPosition; j++) {
// swizzle when necessary
if (kN32_SkColorType == kBGRA_8888_SkColorType) {
if (j % 4 == 0)
copiedDataBuffer->data()[dstStartCopyPosition + j] = srcAddr[srcStartCopyPosition + j + 2];
else if (j % 4 == 2)
copiedDataBuffer->data()[dstStartCopyPosition + j] = srcAddr[srcStartCopyPosition + j - 2];
else
copiedDataBuffer->data()[dstStartCopyPosition + j] = srcAddr[srcStartCopyPosition + j];
} else {
copiedDataBuffer->data()[dstStartCopyPosition + j] = srcAddr[srcStartCopyPosition + j];
}
}
}
}
skImage = newSkImageFromRaster(info, std::move(copiedDataBuffer), dstPixelBytesPerRow);
}
if (!skImage)
return;
if (parsedOptions.shouldScaleInput)
m_image = StaticBitmapImage::create(scaleSkImage(skImage, parsedOptions.resizeWidth, parsedOptions.resizeHeight, parsedOptions.resizeQuality));
else
m_image = StaticBitmapImage::create(skImage);
if (!m_image)
return;
m_image->setPremultiplied(parsedOptions.premultiplyAlpha);
return;
}
std::unique_ptr<ImageBuffer> buffer = ImageBuffer::create(parsedOptions.cropRect.size(), NonOpaque, DoNotInitializeImagePixels);
if (!buffer)
return;
if (srcRect.isEmpty()) {
m_image = StaticBitmapImage::create(buffer->newSkImageSnapshot(PreferNoAcceleration, SnapshotReasonUnknown));
return;
}
IntPoint dstPoint = IntPoint(std::min(0, -parsedOptions.cropRect.x()), std::min(0, -parsedOptions.cropRect.y()));
if (parsedOptions.cropRect.x() < 0)
dstPoint.setX(-parsedOptions.cropRect.x());
if (parsedOptions.cropRect.y() < 0)
dstPoint.setY(-parsedOptions.cropRect.y());
buffer->putByteArray(Unmultiplied, data->data()->data(), data->size(), srcRect, dstPoint);
RefPtr<SkImage> skImage = buffer->newSkImageSnapshot(PreferNoAcceleration, SnapshotReasonUnknown);
if (parsedOptions.flipY)
skImage = flipSkImageVertically(skImage.get(), PremultiplyAlpha);
if (!skImage)
return;
if (parsedOptions.shouldScaleInput) {
sk_sp<SkSurface> surface = SkSurface::MakeRasterN32Premul(parsedOptions.resizeWidth, parsedOptions.resizeHeight);
if (!surface)
return;
SkPaint paint;
paint.setFilterQuality(parsedOptions.resizeQuality);
SkRect dstDrawRect = SkRect::MakeWH(parsedOptions.resizeWidth, parsedOptions.resizeHeight);
surface->getCanvas()->drawImageRect(skImage.get(), dstDrawRect, &paint);
skImage = fromSkSp(surface->makeImageSnapshot());
}
m_image = StaticBitmapImage::create(skImage);
}
ImageBitmap::ImageBitmap(ImageBitmap* bitmap, Optional<IntRect> cropRect, const ImageBitmapOptions& options)
{
RefPtr<Image> input = bitmap->bitmapImage();
if (!input)
return;
ParsedOptions parsedOptions = parseOptions(options, cropRect, input->size());
if (dstBufferSizeHasOverflow(parsedOptions))
return;
m_image = cropImage(input.get(), parsedOptions, bitmap->isPremultiplied() ? PremultiplyAlpha : DontPremultiplyAlpha);
if (!m_image)
return;
m_image->setOriginClean(bitmap->originClean());
m_image->setPremultiplied(parsedOptions.premultiplyAlpha);
}
ImageBitmap::ImageBitmap(PassRefPtr<StaticBitmapImage> image, Optional<IntRect> cropRect, const ImageBitmapOptions& options)
{
bool originClean = image->originClean();
RefPtr<Image> input = image;
ParsedOptions parsedOptions = parseOptions(options, cropRect, input->size());
if (dstBufferSizeHasOverflow(parsedOptions))
return;
m_image = cropImage(input.get(), parsedOptions);
if (!m_image)
return;
m_image->setOriginClean(originClean);
m_image->setPremultiplied(parsedOptions.premultiplyAlpha);
}
ImageBitmap::ImageBitmap(PassRefPtr<StaticBitmapImage> image)
{
m_image = image;
}
PassRefPtr<StaticBitmapImage> ImageBitmap::transfer()
{
ASSERT(!isNeutered());
m_isNeutered = true;
return m_image.release();
}
ImageBitmap::~ImageBitmap()
{
}
ImageBitmap* ImageBitmap::create(HTMLImageElement* image, Optional<IntRect> cropRect, Document* document, const ImageBitmapOptions& options)
{
return new ImageBitmap(image, cropRect, document, options);
}
ImageBitmap* ImageBitmap::create(HTMLVideoElement* video, Optional<IntRect> cropRect, Document* document, const ImageBitmapOptions& options)
{
return new ImageBitmap(video, cropRect, document, options);
}
ImageBitmap* ImageBitmap::create(HTMLCanvasElement* canvas, Optional<IntRect> cropRect, const ImageBitmapOptions& options)
{
return new ImageBitmap(canvas, cropRect, options);
}
ImageBitmap* ImageBitmap::create(ImageData* data, Optional<IntRect> cropRect, const ImageBitmapOptions& options)
{
return new ImageBitmap(data, cropRect, options);
}
ImageBitmap* ImageBitmap::create(ImageBitmap* bitmap, Optional<IntRect> cropRect, const ImageBitmapOptions& options)
{
return new ImageBitmap(bitmap, cropRect, options);
}
ImageBitmap* ImageBitmap::create(PassRefPtr<StaticBitmapImage> image, Optional<IntRect> cropRect, const ImageBitmapOptions& options)
{
return new ImageBitmap(image, cropRect, options);
}
ImageBitmap* ImageBitmap::create(PassRefPtr<StaticBitmapImage> image)
{
return new ImageBitmap(image);
}
ImageBitmap* ImageBitmap::create(std::unique_ptr<uint8_t[]> data, uint32_t width, uint32_t height, bool isImageBitmapPremultiplied, bool isImageBitmapOriginClean)
{
return new ImageBitmap(std::move(data), width, height, isImageBitmapPremultiplied, isImageBitmapOriginClean);
}
void ImageBitmap::close()
{
if (!m_image || m_isNeutered)
return;
m_image.clear();
m_isNeutered = true;
}
// static
ImageBitmap* ImageBitmap::take(ScriptPromiseResolver*, sk_sp<SkImage> image)
{
return ImageBitmap::create(StaticBitmapImage::create(fromSkSp(image)));
}
PassRefPtr<Uint8Array> ImageBitmap::copyBitmapData(AlphaDisposition alphaOp, DataColorFormat format)
{
SkImageInfo info = SkImageInfo::Make(width(), height(), (format == RGBAColorType) ? kRGBA_8888_SkColorType : kN32_SkColorType, (alphaOp == PremultiplyAlpha) ? kPremul_SkAlphaType : kUnpremul_SkAlphaType);
RefPtr<Uint8Array> dstPixels = copySkImageData(m_image->imageForCurrentFrame().get(), info);
return dstPixels.release();
}
unsigned long ImageBitmap::width() const
{
if (!m_image)
return 0;
ASSERT(m_image->width() > 0);
return m_image->width();
}
unsigned long ImageBitmap::height() const
{
if (!m_image)
return 0;
ASSERT(m_image->height() > 0);
return m_image->height();
}
bool ImageBitmap::isTextureBacked() const
{
return m_image && (m_image->isTextureBacked() || m_image->hasMailbox());
}
IntSize ImageBitmap::size() const
{
if (!m_image)
return IntSize();
ASSERT(m_image->width() > 0 && m_image->height() > 0);
return IntSize(m_image->width(), m_image->height());
}
ScriptPromise ImageBitmap::createImageBitmap(ScriptState* scriptState, EventTarget& eventTarget, Optional<IntRect> cropRect, const ImageBitmapOptions& options, ExceptionState& exceptionState)
{
if ((cropRect && !isSourceSizeValid(cropRect->width(), cropRect->height(), exceptionState))
|| !isSourceSizeValid(width(), height(), exceptionState))
return ScriptPromise();
if (!isResizeOptionValid(options, exceptionState))
return ScriptPromise();
return ImageBitmapSource::fulfillImageBitmap(scriptState, create(this, cropRect, options));
}
PassRefPtr<Image> ImageBitmap::getSourceImageForCanvas(SourceImageStatus* status, AccelerationHint, SnapshotReason, const FloatSize&) const
{
*status = NormalSourceImageStatus;
return m_image ? m_image : nullptr;
}
void ImageBitmap::adjustDrawRects(FloatRect* srcRect, FloatRect* dstRect) const
{
}
FloatSize ImageBitmap::elementSize(const FloatSize&) const
{
return FloatSize(width(), height());
}
DEFINE_TRACE(ImageBitmap)
{
}
} // namespace blink