#include "config.h"
#include "ImageBitmap.h"
#include "BitmapImage.h"
#include "Blob.h"
#include "CachedImage.h"
#include "ExceptionCode.h"
#include "ExceptionOr.h"
#include "FileReaderLoader.h"
#include "FileReaderLoaderClient.h"
#include "FrameView.h"
#include "GraphicsContext.h"
#include "HTMLCanvasElement.h"
#include "HTMLImageElement.h"
#include "HTMLVideoElement.h"
#include "ImageBitmapOptions.h"
#include "ImageBuffer.h"
#include "ImageData.h"
#include "IntRect.h"
#include "JSDOMPromiseDeferred.h"
#include "JSImageBitmap.h"
#include "LayoutSize.h"
#if ENABLE(OFFSCREEN_CANVAS)
#include "OffscreenCanvas.h"
#endif
#include "RenderElement.h"
#include "SharedBuffer.h"
#include "SuspendableTimer.h"
#include "TypedOMCSSImageValue.h"
#include <wtf/IsoMallocInlines.h>
#include <wtf/Optional.h>
#include <wtf/Scope.h>
#include <wtf/StdLibExtras.h>
#include <wtf/Variant.h>
namespace WebCore {
WTF_MAKE_ISO_ALLOCATED_IMPL(ImageBitmap);
#if USE(IOSURFACE_CANVAS_BACKING_STORE)
static RenderingMode bufferRenderingMode = RenderingMode::Accelerated;
#else
static RenderingMode bufferRenderingMode = RenderingMode::Unaccelerated;
#endif
Ref<ImageBitmap> ImageBitmap::create(ScriptExecutionContext& scriptExecutionContext, const IntSize& size)
{
return create({ createImageBuffer(scriptExecutionContext, size, bufferRenderingMode) });
}
Ref<ImageBitmap> ImageBitmap::create(Optional<ImageBitmapBacking>&& backingStore)
{
return adoptRef(*new ImageBitmap(WTFMove(backingStore)));
}
RefPtr<ImageBuffer> ImageBitmap::createImageBuffer(ScriptExecutionContext& scriptExecutionContext, const FloatSize& size, RenderingMode renderingMode, float resolutionScale)
{
if (scriptExecutionContext.isDocument()) {
auto& document = downcast<Document>(scriptExecutionContext);
if (document.view() && document.view()->root()) {
auto hostWindow = document.view()->root()->hostWindow();
return ImageBuffer::create(size, renderingMode, ShouldUseDisplayList::No, RenderingPurpose::Canvas, resolutionScale, ColorSpace::SRGB, PixelFormat::BGRA8, hostWindow);
}
}
return ImageBuffer::create(size, renderingMode, resolutionScale);
}
void ImageBitmap::createPromise(ScriptExecutionContext& scriptExecutionContext, ImageBitmap::Source&& source, ImageBitmapOptions&& options, ImageBitmap::Promise&& promise)
{
WTF::switchOn(source,
[&] (auto& specificSource) {
createPromise(scriptExecutionContext, specificSource, WTFMove(options), WTF::nullopt, WTFMove(promise));
}
);
}
Vector<Optional<ImageBitmapBacking>> ImageBitmap::detachBitmaps(Vector<RefPtr<ImageBitmap>>&& bitmaps)
{
Vector<Optional<ImageBitmapBacking>> buffers;
for (auto& bitmap : bitmaps)
buffers.append(bitmap->takeImageBitmapBacking());
return buffers;
}
void ImageBitmap::createPromise(ScriptExecutionContext& scriptExecutionContext, ImageBitmap::Source&& source, ImageBitmapOptions&& options, int sx, int sy, int sw, int sh, ImageBitmap::Promise&& promise)
{
if (!sw || !sh) {
promise.reject(RangeError, "Cannot create ImageBitmap with a width or height of 0");
return;
}
auto left = sw >= 0 ? sx : sx + sw;
auto top = sh >= 0 ? sy : sy + sh;
auto width = std::abs(sw);
auto height = std::abs(sh);
WTF::switchOn(source,
[&] (auto& specificSource) {
createPromise(scriptExecutionContext, specificSource, WTFMove(options), IntRect { left, top, width, height }, WTFMove(promise));
}
);
}
static bool taintsOrigin(CachedImage& cachedImage)
{
auto* image = cachedImage.image();
if (!image)
return false;
if (image->sourceURL().protocolIsData())
return false;
if (!image->hasSingleSecurityOrigin())
return true;
if (!cachedImage.isCORSSameOrigin())
return true;
return false;
}
#if ENABLE(VIDEO)
static bool taintsOrigin(SecurityOrigin* origin, HTMLVideoElement& video)
{
if (!video.hasSingleSecurityOrigin())
return true;
if (video.player()->didPassCORSAccessCheck())
return false;
auto url = video.currentSrc();
if (url.protocolIsData())
return false;
return !origin->canRequest(url);
}
#endif
static ExceptionOr<IntRect> croppedSourceRectangleWithFormatting(IntSize inputSize, ImageBitmapOptions& options, Optional<IntRect> rect)
{
if ((options.resizeWidth && options.resizeWidth.value() <= 0) || (options.resizeHeight && options.resizeHeight.value() <= 0))
return Exception { InvalidStateError, "Invalid resize dimensions" };
auto sourceRectangle = rect.valueOr(IntRect { 0, 0, inputSize.width(), inputSize.height() });
sourceRectangle.intersect(IntRect { 0, 0, inputSize.width(), inputSize.height() });
return { WTFMove(sourceRectangle) };
}
static IntSize outputSizeForSourceRectangle(IntRect sourceRectangle, ImageBitmapOptions& options)
{
auto outputWidth = [&] () -> int {
if (options.resizeWidth)
return options.resizeWidth.value();
if (options.resizeHeight)
return ceil(sourceRectangle.width() * static_cast<double>(options.resizeHeight.value()) / sourceRectangle.height());
return sourceRectangle.width();
}();
auto outputHeight = [&] () -> int {
if (options.resizeHeight)
return options.resizeHeight.value();
if (options.resizeWidth)
return ceil(sourceRectangle.height() * static_cast<double>(options.resizeWidth.value()) / sourceRectangle.width());
return sourceRectangle.height();
}();
return { outputWidth, outputHeight };
}
static InterpolationQuality interpolationQualityForResizeQuality(ImageBitmapOptions::ResizeQuality resizeQuality)
{
switch (resizeQuality) {
case ImageBitmapOptions::ResizeQuality::Pixelated:
return InterpolationQuality::DoNotInterpolate;
case ImageBitmapOptions::ResizeQuality::Low:
return InterpolationQuality::Default; case ImageBitmapOptions::ResizeQuality::Medium:
return InterpolationQuality::Medium;
case ImageBitmapOptions::ResizeQuality::High:
return InterpolationQuality::High;
}
ASSERT_NOT_REACHED();
return InterpolationQuality::Default;
}
static ImageOrientation imageOrientationForOrientation(ImageBitmapOptions::Orientation orientation)
{
if (orientation == ImageBitmapOptions::Orientation::FlipY)
return ImageOrientation(ImageOrientation::OriginBottomLeft);
return ImageOrientation();
}
static AlphaPremultiplication alphaPremultiplicationForPremultiplyAlpha(ImageBitmapOptions::PremultiplyAlpha premultiplyAlpha)
{
if (premultiplyAlpha == ImageBitmapOptions::PremultiplyAlpha::None)
return AlphaPremultiplication::Unpremultiplied;
return AlphaPremultiplication::Premultiplied;
}
void ImageBitmap::resolveWithBlankImageBuffer(ScriptExecutionContext& scriptExecutionContext, bool originClean, Promise&& promise)
{
auto bitmapData = createImageBuffer(scriptExecutionContext, FloatSize(1, 1), bufferRenderingMode);
OptionSet<SerializationState> serializationState;
if (originClean)
serializationState.add(SerializationState::OriginClean);
auto imageBitmap = create(ImageBitmapBacking(WTFMove(bitmapData), serializationState));
promise.resolve(WTFMove(imageBitmap));
}
void ImageBitmap::createPromise(ScriptExecutionContext& scriptExecutionContext, RefPtr<HTMLImageElement>& imageElement, ImageBitmapOptions&& options, Optional<IntRect> rect, ImageBitmap::Promise&& promise)
{
auto* cachedImage = imageElement->cachedImage();
if (!cachedImage || !imageElement->complete()) {
promise.reject(InvalidStateError, "Cannot create ImageBitmap that is not completely available");
return;
}
auto imageSize = cachedImage->imageSizeForRenderer(imageElement->renderer(), 1.0f);
if ((!imageSize.width() || !imageSize.height()) && (!options.resizeWidth || !options.resizeHeight)) {
promise.reject(InvalidStateError, "Cannot create ImageBitmap from a source with no intrinsic size without providing resize dimensions");
return;
}
if (!imageSize.width() && !imageSize.height()) {
imageSize.setWidth(options.resizeWidth.value());
imageSize.setHeight(options.resizeHeight.value());
}
if (!rect && (!imageSize.width() || !imageSize.height())) {
promise.reject(InvalidStateError, "Cannot create ImageBitmap from a source with no intrinsic size without providing dimensions");
return;
}
auto sourceRectangle = croppedSourceRectangleWithFormatting(roundedIntSize(imageSize), options, WTFMove(rect));
if (sourceRectangle.hasException()) {
promise.reject(sourceRectangle.releaseException());
return;
}
auto outputSize = outputSizeForSourceRectangle(sourceRectangle.returnValue(), options);
auto bitmapData = createImageBuffer(scriptExecutionContext, outputSize, bufferRenderingMode);
auto imageForRender = cachedImage->imageForRenderer(imageElement->renderer());
if (!imageForRender) {
promise.reject(InvalidStateError, "Cannot create ImageBitmap from image that can't be rendered");
return;
}
if (!bitmapData) {
resolveWithBlankImageBuffer(scriptExecutionContext, !taintsOrigin(*cachedImage), WTFMove(promise));
return;
}
FloatRect destRect(FloatPoint(), outputSize);
bitmapData->context().drawImage(*imageForRender, destRect, sourceRectangle.releaseReturnValue(), { interpolationQualityForResizeQuality(options.resizeQuality), imageOrientationForOrientation(options.imageOrientation) });
OptionSet<SerializationState> serializationState;
if (!taintsOrigin(*cachedImage))
serializationState.add(SerializationState::OriginClean);
if (alphaPremultiplicationForPremultiplyAlpha(options.premultiplyAlpha) == AlphaPremultiplication::Premultiplied)
serializationState.add(SerializationState::PremultiplyAlpha);
auto imageBitmap = create(ImageBitmapBacking(WTFMove(bitmapData), serializationState));
promise.resolve(WTFMove(imageBitmap));
}
void ImageBitmap::createPromise(ScriptExecutionContext& scriptExecutionContext, RefPtr<HTMLCanvasElement>& canvasElement, ImageBitmapOptions&& options, Optional<IntRect> rect, ImageBitmap::Promise&& promise)
{
createPromise(scriptExecutionContext, *canvasElement, WTFMove(options), WTFMove(rect), WTFMove(promise));
}
#if ENABLE(OFFSCREEN_CANVAS)
void ImageBitmap::createPromise(ScriptExecutionContext& scriptExecutionContext, RefPtr<OffscreenCanvas>& canvasElement, ImageBitmapOptions&& options, Optional<IntRect> rect, ImageBitmap::Promise&& promise)
{
createPromise(scriptExecutionContext, *canvasElement, WTFMove(options), WTFMove(rect), WTFMove(promise));
}
#endif
void ImageBitmap::createPromise(ScriptExecutionContext& scriptExecutionContext, CanvasBase& canvas, ImageBitmapOptions&& options, Optional<IntRect> rect, ImageBitmap::Promise&& promise)
{
auto size = canvas.size();
if (!size.width() || !size.height()) {
promise.reject(InvalidStateError, "Cannot create ImageBitmap from a canvas that has zero width or height");
return;
}
auto sourceRectangle = croppedSourceRectangleWithFormatting(size, options, WTFMove(rect));
if (sourceRectangle.hasException()) {
promise.reject(sourceRectangle.releaseException());
return;
}
auto outputSize = outputSizeForSourceRectangle(sourceRectangle.returnValue(), options);
auto bitmapData = createImageBuffer(scriptExecutionContext, outputSize, bufferRenderingMode);
auto imageForRender = canvas.copiedImage();
if (!imageForRender) {
promise.reject(InvalidStateError, "Cannot create ImageBitmap from canvas that can't be rendered");
return;
}
if (!bitmapData) {
resolveWithBlankImageBuffer(scriptExecutionContext, canvas.originClean(), WTFMove(promise));
return;
}
FloatRect destRect(FloatPoint(), outputSize);
bitmapData->context().drawImage(*imageForRender, destRect, sourceRectangle.releaseReturnValue(), { interpolationQualityForResizeQuality(options.resizeQuality), imageOrientationForOrientation(options.imageOrientation) });
OptionSet<SerializationState> serializationState;
if (canvas.originClean())
serializationState.add(SerializationState::OriginClean);
if (alphaPremultiplicationForPremultiplyAlpha(options.premultiplyAlpha) == AlphaPremultiplication::Premultiplied)
serializationState.add(SerializationState::PremultiplyAlpha);
auto imageBitmap = create(ImageBitmapBacking(WTFMove(bitmapData), serializationState));
promise.resolve(WTFMove(imageBitmap));
}
#if ENABLE(VIDEO)
void ImageBitmap::createPromise(ScriptExecutionContext& scriptExecutionContext, RefPtr<HTMLVideoElement>& video, ImageBitmapOptions&& options, Optional<IntRect> rect, ImageBitmap::Promise&& promise)
{
if (video->readyState() == HTMLMediaElement::HAVE_NOTHING || video->readyState() == HTMLMediaElement::HAVE_METADATA) {
promise.reject(InvalidStateError, "Cannot create ImageBitmap before the HTMLVideoElement has data");
return;
}
if (video->networkState() == HTMLMediaElement::NETWORK_EMPTY) {
promise.reject(InvalidStateError, "Cannot create ImageBitmap before the HTMLVideoElement has data");
return;
}
auto size = video->player() ? roundedIntSize(video->player()->naturalSize()) : IntSize();
auto maybeSourceRectangle = croppedSourceRectangleWithFormatting(size, options, WTFMove(rect));
if (maybeSourceRectangle.hasException()) {
promise.reject(maybeSourceRectangle.releaseException());
return;
}
auto sourceRectangle = maybeSourceRectangle.releaseReturnValue();
auto outputSize = outputSizeForSourceRectangle(sourceRectangle, options);
auto bitmapData = video->createBufferForPainting(outputSize, bufferRenderingMode);
if (!bitmapData) {
resolveWithBlankImageBuffer(scriptExecutionContext, !taintsOrigin(scriptExecutionContext.securityOrigin(), *video), WTFMove(promise));
return;
}
{
GraphicsContext& c = bitmapData->context();
GraphicsContextStateSaver stateSaver(c);
c.clip(FloatRect(FloatPoint(), outputSize));
auto scaleX = float(outputSize.width()) / float(sourceRectangle.width());
auto scaleY = float(outputSize.height()) / float(sourceRectangle.height());
if (options.imageOrientation == ImageBitmapOptions::Orientation::FlipY) {
c.scale(FloatSize(scaleX, -scaleY));
c.translate(IntPoint(-sourceRectangle.location().x(), sourceRectangle.location().y() - outputSize.height()));
} else {
c.scale(FloatSize(scaleX, scaleY));
c.translate(-sourceRectangle.location());
}
video->paintCurrentFrameInContext(c, FloatRect(FloatPoint(), size));
}
OptionSet<SerializationState> serializationState;
if (!taintsOrigin(scriptExecutionContext.securityOrigin(), *video))
serializationState.add(SerializationState::OriginClean);
if (alphaPremultiplicationForPremultiplyAlpha(options.premultiplyAlpha) == AlphaPremultiplication::Premultiplied)
serializationState.add(SerializationState::PremultiplyAlpha);
auto imageBitmap = create(ImageBitmapBacking(WTFMove(bitmapData), serializationState));
promise.resolve(WTFMove(imageBitmap));
}
#endif
#if ENABLE(CSS_TYPED_OM)
void ImageBitmap::createPromise(ScriptExecutionContext&, RefPtr<TypedOMCSSImageValue>&, ImageBitmapOptions&&, Optional<IntRect>, ImageBitmap::Promise&& promise)
{
promise.reject(InvalidStateError, "Not implemented");
}
#endif
void ImageBitmap::createPromise(ScriptExecutionContext& scriptExecutionContext, RefPtr<ImageBitmap>& existingImageBitmap, ImageBitmapOptions&& options, Optional<IntRect> rect, ImageBitmap::Promise&& promise)
{
if (existingImageBitmap->isDetached() || !existingImageBitmap->buffer()) {
promise.reject(InvalidStateError, "Cannot create ImageBitmap from a detached ImageBitmap");
return;
}
auto sourceRectangle = croppedSourceRectangleWithFormatting(existingImageBitmap->buffer()->logicalSize(), options, WTFMove(rect));
if (sourceRectangle.hasException()) {
promise.reject(sourceRectangle.releaseException());
return;
}
auto outputSize = outputSizeForSourceRectangle(sourceRectangle.returnValue(), options);
auto bitmapData = createImageBuffer(scriptExecutionContext, outputSize, bufferRenderingMode);
if (!bitmapData) {
resolveWithBlankImageBuffer(scriptExecutionContext, existingImageBitmap->originClean(), WTFMove(promise));
return;
}
auto imageForRender = existingImageBitmap->buffer()->copyImage();
FloatRect destRect(FloatPoint(), outputSize);
bitmapData->context().drawImage(*imageForRender, destRect, sourceRectangle.releaseReturnValue(), { interpolationQualityForResizeQuality(options.resizeQuality), imageOrientationForOrientation(options.imageOrientation) });
OptionSet<SerializationState> serializationState;
if (existingImageBitmap->originClean())
serializationState.add(SerializationState::OriginClean);
if (alphaPremultiplicationForPremultiplyAlpha(options.premultiplyAlpha) == AlphaPremultiplication::Premultiplied) {
serializationState.add(SerializationState::PremultiplyAlpha);
serializationState.add(SerializationState::ForciblyPremultiplyAlpha);
}
auto imageBitmap = create(ImageBitmapBacking(WTFMove(bitmapData), serializationState));
promise.resolve(WTFMove(imageBitmap));
}
class ImageBitmapImageObserver final : public RefCounted<ImageBitmapImageObserver>, public ImageObserver {
public:
static Ref<ImageBitmapImageObserver> create(String mimeType, long long expectedContentLength, const URL& sourceUrl)
{
return adoptRef(*new ImageBitmapImageObserver(mimeType, expectedContentLength, sourceUrl));
}
URL sourceUrl() const override { return m_sourceUrl; }
String mimeType() const override { return m_mimeType; }
long long expectedContentLength() const override { return m_expectedContentLength; }
void decodedSizeChanged(const Image&, long long) override { }
void didDraw(const Image&) override { }
bool canDestroyDecodedData(const Image&) override { return true; }
void imageFrameAvailable(const Image&, ImageAnimatingState, const IntRect* = nullptr, DecodingStatus = DecodingStatus::Invalid) override { }
void changedInRect(const Image&, const IntRect* = nullptr) override { }
void scheduleRenderingUpdate(const Image&) override { }
private:
ImageBitmapImageObserver(String mimeType, long long expectedContentLength, const URL& sourceUrl)
: m_mimeType(mimeType)
, m_expectedContentLength(expectedContentLength)
, m_sourceUrl(sourceUrl)
{ }
String m_mimeType;
long long m_expectedContentLength;
URL m_sourceUrl;
};
class PendingImageBitmap final : public ActiveDOMObject, public FileReaderLoaderClient {
WTF_MAKE_FAST_ALLOCATED;
public:
static void fetch(ScriptExecutionContext& scriptExecutionContext, RefPtr<Blob>&& blob, ImageBitmapOptions&& options, Optional<IntRect> rect, ImageBitmap::Promise&& promise)
{
if (scriptExecutionContext.activeDOMObjectsAreStopped())
return;
auto pendingImageBitmap = new PendingImageBitmap(scriptExecutionContext, WTFMove(blob), WTFMove(options), WTFMove(rect), WTFMove(promise));
pendingImageBitmap->start(scriptExecutionContext);
}
private:
PendingImageBitmap(ScriptExecutionContext& scriptExecutionContext, RefPtr<Blob>&& blob, ImageBitmapOptions&& options, Optional<IntRect> rect, ImageBitmap::Promise&& promise)
: ActiveDOMObject(&scriptExecutionContext)
, m_blobLoader(FileReaderLoader::ReadAsArrayBuffer, this)
, m_blob(WTFMove(blob))
, m_options(WTFMove(options))
, m_rect(WTFMove(rect))
, m_promise(WTFMove(promise))
, m_createImageBitmapTimer(&scriptExecutionContext, *this, &PendingImageBitmap::createImageBitmapAndResolvePromise)
{
suspendIfNeeded();
m_createImageBitmapTimer.suspendIfNeeded();
}
void start(ScriptExecutionContext& scriptExecutionContext)
{
m_blobLoader.start(&scriptExecutionContext, *m_blob);
}
const char* activeDOMObjectName() const final
{
return "PendingImageBitmap";
}
void stop() final
{
delete this;
}
void didStartLoading() override
{
}
void didReceiveData() override
{
}
void didFinishLoading() override
{
createImageBitmapAndResolvePromiseSoon(m_blobLoader.arrayBufferResult());
}
void didFail(ExceptionCode) override
{
createImageBitmapAndResolvePromiseSoon(nullptr);
}
void createImageBitmapAndResolvePromiseSoon(RefPtr<ArrayBuffer>&& arrayBuffer)
{
ASSERT(!m_createImageBitmapTimer.isActive());
m_arrayBufferToProcess = WTFMove(arrayBuffer);
m_createImageBitmapTimer.startOneShot(0_s);
}
void createImageBitmapAndResolvePromise()
{
auto destroyOnExit = makeScopeExit([this] {
delete this;
});
if (!m_arrayBufferToProcess) {
m_promise.reject(InvalidStateError, "An error occured reading the Blob argument to createImageBitmap");
return;
}
ImageBitmap::createFromBuffer(*scriptExecutionContext(), m_arrayBufferToProcess.releaseNonNull(), m_blob->type(), m_blob->size(), m_blobLoader.url(), WTFMove(m_options), WTFMove(m_rect), WTFMove(m_promise));
}
FileReaderLoader m_blobLoader;
RefPtr<Blob> m_blob;
ImageBitmapOptions m_options;
Optional<IntRect> m_rect;
ImageBitmap::Promise m_promise;
SuspendableTimer m_createImageBitmapTimer;
RefPtr<ArrayBuffer> m_arrayBufferToProcess;
};
void ImageBitmap::createFromBuffer(ScriptExecutionContext& scriptExecutionContext, Ref<ArrayBuffer>&& arrayBuffer, String mimeType, long long expectedContentLength, const URL& sourceURL, ImageBitmapOptions&& options, Optional<IntRect> rect, Promise&& promise)
{
if (!arrayBuffer->byteLength()) {
promise.reject(InvalidStateError, "Cannot create an ImageBitmap from an empty buffer");
return;
}
auto sharedBuffer = SharedBuffer::create(static_cast<const char*>(arrayBuffer->data()), arrayBuffer->byteLength());
auto observer = ImageBitmapImageObserver::create(mimeType, expectedContentLength, sourceURL);
auto image = Image::create(observer.get());
if (!image) {
promise.reject(InvalidStateError, "The type of the argument to createImageBitmap is not supported");
return;
}
auto result = image->setData(sharedBuffer.copyRef(), true);
if (result != EncodedDataStatus::Complete) {
promise.reject(InvalidStateError, "Cannot decode the data in the argument to createImageBitmap");
return;
}
auto sourceRectangle = croppedSourceRectangleWithFormatting(roundedIntSize(image->size()), options, rect);
if (sourceRectangle.hasException()) {
promise.reject(sourceRectangle.releaseException());
return;
}
auto outputSize = outputSizeForSourceRectangle(sourceRectangle.returnValue(), options);
auto bitmapData = createImageBuffer(scriptExecutionContext, outputSize, bufferRenderingMode);
if (!bitmapData) {
promise.reject(InvalidStateError, "Cannot create an image buffer from the argument to createImageBitmap");
return;
}
FloatRect destRect(FloatPoint(), outputSize);
bitmapData->context().drawImage(*image, destRect, sourceRectangle.releaseReturnValue(), { interpolationQualityForResizeQuality(options.resizeQuality), imageOrientationForOrientation(options.imageOrientation) });
OptionSet<SerializationState> serializationState = SerializationState::OriginClean;
if (alphaPremultiplicationForPremultiplyAlpha(options.premultiplyAlpha) == AlphaPremultiplication::Premultiplied)
serializationState.add(SerializationState::PremultiplyAlpha);
auto imageBitmap = create(ImageBitmapBacking(WTFMove(bitmapData), serializationState));
promise.resolve(WTFMove(imageBitmap));
}
void ImageBitmap::createPromise(ScriptExecutionContext& scriptExecutionContext, RefPtr<Blob>& blob, ImageBitmapOptions&& options, Optional<IntRect> rect, ImageBitmap::Promise&& promise)
{
PendingImageBitmap::fetch(scriptExecutionContext, WTFMove(blob), WTFMove(options), WTFMove(rect), WTFMove(promise));
}
void ImageBitmap::createPromise(ScriptExecutionContext& scriptExecutionContext, RefPtr<ImageData>& imageData, ImageBitmapOptions&& options, Optional<IntRect> rect, ImageBitmap::Promise&& promise)
{
if (imageData->data()->isDetached()) {
promise.reject(InvalidStateError, "ImageData's viewed buffer has been detached");
return;
}
auto sourceRectangle = croppedSourceRectangleWithFormatting(imageData->size(), options, WTFMove(rect));
if (sourceRectangle.hasException()) {
promise.reject(sourceRectangle.releaseException());
return;
}
auto outputSize = outputSizeForSourceRectangle(sourceRectangle.returnValue(), options);
auto bitmapData = createImageBuffer(scriptExecutionContext, outputSize, bufferRenderingMode);
if (!bitmapData) {
resolveWithBlankImageBuffer(scriptExecutionContext, true, WTFMove(promise));
return;
}
auto alphaPremultiplication = alphaPremultiplicationForPremultiplyAlpha(options.premultiplyAlpha);
if (sourceRectangle.returnValue().location().isZero() && sourceRectangle.returnValue().size() == imageData->size()
&& sourceRectangle.returnValue().size() == outputSize
&& options.imageOrientation == ImageBitmapOptions::Orientation::None) {
bitmapData->putImageData(AlphaPremultiplication::Unpremultiplied, *imageData, sourceRectangle.releaseReturnValue(), { }, alphaPremultiplication);
auto imageBitmap = create(ImageBitmapBacking(WTFMove(bitmapData)));
promise.resolve(WTFMove(imageBitmap));
return;
}
auto tempBitmapData = createImageBuffer(scriptExecutionContext, imageData->size(), bufferRenderingMode);
tempBitmapData->putImageData(AlphaPremultiplication::Unpremultiplied, *imageData, IntRect(0, 0, imageData->width(), imageData->height()), { }, alphaPremultiplication);
FloatRect destRect(FloatPoint(), outputSize);
bitmapData->context().drawImageBuffer(*tempBitmapData, destRect, sourceRectangle.releaseReturnValue(), { interpolationQualityForResizeQuality(options.resizeQuality), imageOrientationForOrientation(options.imageOrientation) });
auto imageBitmap = create({ WTFMove(bitmapData) });
promise.resolve(WTFMove(imageBitmap));
}
ImageBitmap::ImageBitmap(Optional<ImageBitmapBacking>&& backingStore)
: m_backingStore(WTFMove(backingStore))
{
ASSERT_IMPLIES(m_backingStore, m_backingStore->buffer());
}
ImageBitmap::~ImageBitmap() = default;
Optional<ImageBitmapBacking> ImageBitmap::takeImageBitmapBacking()
{
return std::exchange(m_backingStore, WTF::nullopt);
}
RefPtr<ImageBuffer> ImageBitmap::takeImageBuffer()
{
if (auto backingStore = takeImageBitmapBacking())
return backingStore->takeImageBuffer();
ASSERT(isDetached());
return nullptr;
}
}