blob: 2aa2786cb88392398151b6b5d09150bb129d6be7 [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()));
}
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 sk_sp<SkImage> newSkImageFromRaster(const SkImageInfo& info,
PassRefPtr<Uint8Array> imagePixels,
size_t imageRowBytes) {
SkPixmap pixmap(info, imagePixels->data(), imageRowBytes);
return 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 sk_sp<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 sk_sp<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 sk_sp<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());
}
sk_sp<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;
DCHECK(!frame->bitmap().isNull() && !frame->bitmap().empty());
return frame->finalizePixelsAndGetImage();
}
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()));
}
sk_sp<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) {
sk_sp<SkImage> croppedSkImage = 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(std::move(croppedSkImage));
}
sk_sp<SkSurface> surface = SkSurface::MakeRasterN32Premul(
parsedOptions.resizeWidth, parsedOptions.resizeHeight);
if (!surface)
return nullptr;
if (srcRect.isEmpty())
return StaticBitmapImage::create(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, drawSrcRect, drawDstRect,
&paint);
} else {
surface->getCanvas()->drawImage(skiaImage, dstLeft, dstTop);
}
skiaImage = surface->makeImageSnapshot();
if (parsedOptions.premultiplyAlpha) {
if (imageFormat == DontPremultiplyAlpha)
return StaticBitmapImage::create(
unPremulSkImageToPremul(skiaImage.get()));
return StaticBitmapImage::create(std::move(skiaImage));
}
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.
sk_sp<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, 0, 0);
m_image = StaticBitmapImage::create(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;
std::unique_ptr<ImageBuffer> buffer = ImageBuffer::create(
IntSize(parsedOptions.resizeWidth, parsedOptions.resizeHeight), NonOpaque,
DoNotInitializeImagePixels);
if (!buffer)
return;
IntPoint dstPoint =
IntPoint(-parsedOptions.cropRect.x(), -parsedOptions.cropRect.y());
if (parsedOptions.flipY) {
buffer->canvas()->translate(0, buffer->size().height());
buffer->canvas()->scale(1, -1);
}
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();
buffer->canvas()->scale(scaleRatioX, scaleRatioY);
paint.setFilterQuality(parsedOptions.resizeQuality);
}
buffer->canvas()->translate(dstPoint.x(), dstPoint.y());
video->paintCurrentFrame(
buffer->canvas(),
IntRect(IntPoint(), IntSize(video->videoWidth(), video->videoHeight())),
parsedOptions.shouldScaleInput ? &paint : nullptr);
sk_sp<SkImage> skiaImage =
buffer->newSkImageSnapshot(PreferNoAcceleration, SnapshotReasonUnknown);
if (!parsedOptions.premultiplyAlpha)
skiaImage = premulSkImageToUnPremul(skiaImage.get());
if (!skiaImage)
return;
m_image = StaticBitmapImage::create(std::move(skiaImage));
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(const void* pixelData,
uint32_t width,
uint32_t height,
bool isImageBitmapPremultiplied,
bool isImageBitmapOriginClean) {
SkImageInfo info = SkImageInfo::MakeN32(
width, height,
isImageBitmapPremultiplied ? kPremul_SkAlphaType : kUnpremul_SkAlphaType);
SkPixmap pixmap(info, pixelData, info.bytesPerPixel() * width);
m_image = StaticBitmapImage::create(SkImage::MakeRasterCopy(pixmap));
if (!m_image)
return;
m_image->setPremultiplied(isImageBitmapPremultiplied);
m_image->setOriginClean(isImageBitmapOriginClean);
}
static sk_sp<SkImage> scaleSkImage(sk_sp<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 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();
sk_sp<SkImage> skImage;
if (parsedOptions.cropRect == IntRect(IntPoint(), data->size())) {
swizzleImageData(srcAddr, data->size().height(), srcPixelBytesPerRow,
parsedOptions.flipY);
skImage =
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);
sk_sp<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, dstDrawRect, &paint);
skImage = surface->makeImageSnapshot();
}
m_image = StaticBitmapImage::create(std::move(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;
m_image->transfer();
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(std::move(image), cropRect, options);
}
ImageBitmap* ImageBitmap::create(PassRefPtr<StaticBitmapImage> image) {
return new ImageBitmap(std::move(image));
}
ImageBitmap* ImageBitmap::create(const void* pixelData,
uint32_t width,
uint32_t height,
bool isImageBitmapPremultiplied,
bool isImageBitmapOriginClean) {
return new ImageBitmap(pixelData, 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(std::move(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::isAccelerated() 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