Skip to content
Permalink
Browse files
Add ImageBitmapRenderingContext support to OffscreenCanvas
https://bugs.webkit.org/show_bug.cgi?id=197421

Reviewed by Chris Lord.

* LayoutTests/imported/w3c/web-platform-tests/html/canvas/offscreen/manual/the-offscreen-canvas/offscreencanvas.transfer.to.imagebitmap.nocrash-expected.txt:
* LayoutTests/platform/glib/TestExpectations:
* LayoutTests/platform/glib/imported/w3c/web-platform-tests/imagebitmap-renderingcontext/context-creation-offscreen-expected.txt: Added.
* LayoutTests/platform/glib/imported/w3c/web-platform-tests/imagebitmap-renderingcontext/context-creation-offscreen-with-alpha-expected.txt: Added.
* LayoutTests/platform/glib/imported/w3c/web-platform-tests/imagebitmap-renderingcontext/toBlob-origin-clean-offscreen.sub-expected.txt:
* LayoutTests/platform/glib/imported/w3c/web-platform-tests/imagebitmap-renderingcontext/tranferFromImageBitmap-ToBlob-offscreen-expected.txt: Added.
* LayoutTests/platform/glib/imported/w3c/web-platform-tests/imagebitmap-renderingcontext/tranferFromImageBitmap-TransferToImageBitmap-offscreen-expected.txt: Added.
* LayoutTests/platform/glib/imported/w3c/web-platform-tests/imagebitmap-renderingcontext/tranferFromImageBitmap-null-offscreen-expected.txt: Added.
* Source/WebCore/html/CanvasBase.h:
(WebCore::CanvasBase::setImageBufferAndMarkDirty):
* Source/WebCore/html/HTMLCanvasElement.h:
* Source/WebCore/html/OffscreenCanvas.cpp:
(WebCore::OffscreenCanvas::getContext):
(WebCore::OffscreenCanvas::transferToImageBitmap):
(WebCore::OffscreenCanvas::setImageBufferAndMarkDirty):
* Source/WebCore/html/OffscreenCanvas.h:
* Source/WebCore/html/OffscreenCanvas.idl:
* Source/WebCore/html/canvas/ImageBitmapRenderingContext.cpp:
(WebCore::ImageBitmapRenderingContext::canvas):
(WebCore::ImageBitmapRenderingContext::setOutputBitmap):
(WebCore::ImageBitmapRenderingContext::canvas const): Deleted.
* Source/WebCore/html/canvas/ImageBitmapRenderingContext.h:
* Source/WebCore/html/canvas/ImageBitmapRenderingContext.idl:

Canonical link: https://commits.webkit.org/254697@main
  • Loading branch information
mattwoodrow committed Sep 20, 2022
1 parent 47fb3d6 commit b861c55669ed7191403b900d55ab30147ba41735
Show file tree
Hide file tree
Showing 16 changed files with 98 additions and 31 deletions.
@@ -1,5 +1,5 @@
Tests that an ImageBitmap in Offscreen without content does not crash.


FAIL offscreencanvas Argument 1 ('contextType') to OffscreenCanvas.getContext must be one of: "2d", "webgl", "webgl2"
PASS offscreencanvas

@@ -1060,14 +1060,6 @@ webkit.org/b/209144 imported/w3c/web-platform-tests/html/canvas/offscreen/shadow
webkit.org/b/209144 imported/w3c/web-platform-tests/html/canvas/offscreen/shadows/2d.shadow.pattern.transparent.1.html [ DumpJSConsoleLogInStdErr ]
webkit.org/b/209144 imported/w3c/web-platform-tests/html/canvas/offscreen/shadows/2d.shadow.pattern.transparent.2.html [ DumpJSConsoleLogInStdErr ]

# Tests requiring bitmaprenderer context support in OffscreenCanvas.
# Might need glib-specific baselines when actually fixed as the root expectations fail with missing OffscreenCanvas support.
webkit.org/b/197421 imported/w3c/web-platform-tests/imagebitmap-renderingcontext/context-creation-offscreen-with-alpha.html [ Failure ]
webkit.org/b/197421 imported/w3c/web-platform-tests/imagebitmap-renderingcontext/context-creation-offscreen.html [ Failure ]
webkit.org/b/197421 imported/w3c/web-platform-tests/imagebitmap-renderingcontext/tranferFromImageBitmap-ToBlob-offscreen.html [ Failure ]
webkit.org/b/197421 imported/w3c/web-platform-tests/imagebitmap-renderingcontext/tranferFromImageBitmap-TransferToImageBitmap-offscreen.html [ Failure ]
webkit.org/b/197421 imported/w3c/web-platform-tests/imagebitmap-renderingcontext/tranferFromImageBitmap-null-offscreen.html [ Failure ]

webkit.org/b/221311 [ Debug ] imported/w3c/web-platform-tests/html/canvas/offscreen/filter/offscreencanvas.filter.w.html [ Slow ]
webkit.org/b/221311 [ Debug ] imported/w3c/web-platform-tests/html/canvas/offscreen/manual/filter/offscreencanvas.filter.w.html [ Slow ]

@@ -0,0 +1,3 @@

PASS Test that canvas.getContext('bitmaprenderer') returns an instance of ImageBitmapRenderingContext and ctx.canvas on a ImageBitmapRenderingContext returns the original OffscreenCanvas

@@ -0,0 +1,5 @@

PASS Test that an ImageBitmapRenderingContext with alpha disabled makes the canvas opaque
PASS Test that an ImageBitmapRenderingContext with alpha enabled preserves the alpha
PASS Test that the 'alpha' context creation attribute is true by default

@@ -1,3 +1,3 @@

FAIL Test that call convertToBlob on a tainted OffscreenCanvas throws exception Argument 1 ('contextType') to OffscreenCanvas.getContext must be one of: "2d", "webgl", "webgl2"
PASS Test that call convertToBlob on a tainted OffscreenCanvas throws exception

@@ -0,0 +1,3 @@

PASS Test that convertToBlob works and produce the expected image

@@ -0,0 +1,3 @@

PASS Test that transferToImageBitmap works and that resets the imagebitmap to black

@@ -0,0 +1,3 @@

PASS Test that transferFromImageBitmap(null) discards the previously transferred image

@@ -80,6 +80,8 @@ class CanvasBase {

ImageBuffer* buffer() const;

virtual void setImageBufferAndMarkDirty(RefPtr<ImageBuffer>&&) { }

virtual AffineTransform baseTransform() const;

void makeRenderingResultsAvailable();
@@ -121,7 +121,7 @@ class HTMLCanvasElement final : public HTMLElement, public CanvasBase, public Ac

// FIXME: Only some canvas rendering contexts need an ImageBuffer.
// It would be better to have the contexts own the buffers.
void setImageBufferAndMarkDirty(RefPtr<ImageBuffer>&&);
void setImageBufferAndMarkDirty(RefPtr<ImageBuffer>&&) final;

WEBCORE_EXPORT static void setMaxPixelMemoryForTesting(std::optional<size_t>);
WEBCORE_EXPORT static void setMaxCanvasAreaForTesting(std::optional<size_t>);
@@ -34,6 +34,7 @@
#include "Document.h"
#include "HTMLCanvasElement.h"
#include "ImageBitmap.h"
#include "ImageBitmapRenderingContext.h"
#include "ImageData.h"
#include "JSBlob.h"
#include "JSDOMPromiseDeferred.h"
@@ -234,6 +235,22 @@ ExceptionOr<std::optional<OffscreenRenderingContext>> OffscreenCanvas::getContex
return { { std::nullopt } };

return { { RefPtr<OffscreenCanvasRenderingContext2D> { &downcast<OffscreenCanvasRenderingContext2D>(*m_context) } } };
} else if (contextType == RenderingContextType::Bitmaprenderer) {
if (m_context) {
if (!is<ImageBitmapRenderingContext>(*m_context))
return { { std::nullopt } };
return { { RefPtr<ImageBitmapRenderingContext> { &downcast<ImageBitmapRenderingContext>(*m_context) } } };
}

auto scope = DECLARE_THROW_SCOPE(state.vm());
auto settings = convert<IDLDictionary<ImageBitmapRenderingContextSettings>>(state, arguments.isEmpty() ? JSC::jsUndefined() : (arguments[0].isObject() ? arguments[0].get() : JSC::jsNull()));
RETURN_IF_EXCEPTION(scope, Exception { ExistingExceptionError });

m_context = ImageBitmapRenderingContext::create(*this, WTFMove(settings));
if (!m_context)
return { { std::nullopt } };

return { { RefPtr<ImageBitmapRenderingContext> { &downcast<ImageBitmapRenderingContext>(*m_context) } } };
}
#if ENABLE(WEBGL)
else {
@@ -271,7 +288,7 @@ ExceptionOr<RefPtr<ImageBitmap>> OffscreenCanvas::transferToImageBitmap()
if (m_detached || !m_context)
return Exception { InvalidStateError };

if (is<OffscreenCanvasRenderingContext2D>(*m_context)) {
if (is<OffscreenCanvasRenderingContext2D>(*m_context) || is<ImageBitmapRenderingContext>(*m_context)) {
if (!width() || !height())
return { RefPtr<ImageBitmap> { nullptr } };

@@ -280,14 +297,23 @@ ExceptionOr<RefPtr<ImageBitmap>> OffscreenCanvas::transferToImageBitmap()
return { ImageBitmap::create(ImageBitmapBacking(WTFMove(buffer))) };
}

// As the canvas context state is stored in GraphicsContext, which is owned
// by buffer(), to avoid resetting the context state, we have to make a copy and
// clear the original buffer rather than returning the original buffer.
auto bufferCopy = buffer()->clone();
downcast<OffscreenCanvasRenderingContext2D>(*m_context).clearCanvas();
RefPtr<ImageBuffer> bitmap;
if (is<OffscreenCanvasRenderingContext2D>(*m_context)) {
// As the canvas context state is stored in GraphicsContext, which is owned
// by buffer(), to avoid resetting the context state, we have to make a copy and
// clear the original buffer rather than returning the original buffer.
bitmap = buffer()->clone();
downcast<OffscreenCanvasRenderingContext2D>(*m_context).clearCanvas();
} else {
// ImageBitmapRenderingContext doesn't use the context state, so we can just take its
// buffer, and then call transferFromImageBitmap(nullptr) which will trigger it to allocate
// a new blank bitmap.
bitmap = buffer();
downcast<ImageBitmapRenderingContext>(*m_context).transferFromImageBitmap(nullptr);
}
clearCopiedImage();

return { ImageBitmap::create(ImageBitmapBacking(WTFMove(bufferCopy), originClean() ? SerializationState::OriginClean : SerializationState())) };
return { ImageBitmap::create(ImageBitmapBacking(WTFMove(bitmap), originClean() ? SerializationState::OriginClean : SerializationState())) };
}

#if ENABLE(WEBGL)
@@ -497,6 +523,14 @@ void OffscreenCanvas::createImageBuffer() const
setImageBuffer(ImageBitmap::createImageBuffer(*canvasBaseScriptExecutionContext(), size(), RenderingMode::Unaccelerated, colorSpace));
}

void OffscreenCanvas::setImageBufferAndMarkDirty(RefPtr<ImageBuffer>&& buffer)
{
m_hasCreatedImageBuffer = true;
setImageBuffer(WTFMove(buffer));

didDraw(FloatRect(FloatPoint(), size()));
}

RefPtr<ImageBuffer> OffscreenCanvas::takeImageBuffer() const
{
ASSERT(m_detached);
@@ -55,6 +55,7 @@ class CSSValuePool;
class DeferredPromise;
class HTMLCanvasElement;
class ImageBitmap;
class ImageBitmapRenderingContext;
class ImageData;
class OffscreenCanvasRenderingContext2D;
class WebGL2RenderingContext;
@@ -68,6 +69,7 @@ using OffscreenRenderingContext = std::variant<
#if ENABLE(WEBGL2)
RefPtr<WebGL2RenderingContext>,
#endif
RefPtr<ImageBitmapRenderingContext>,
RefPtr<OffscreenCanvasRenderingContext2D>
>;

@@ -110,7 +112,8 @@ class OffscreenCanvas final : public RefCounted<OffscreenCanvas>, public CanvasB
enum class RenderingContextType {
_2d,
Webgl,
Webgl2
Webgl2,
Bitmaprenderer
};

static bool enabledForContext(ScriptExecutionContext&);
@@ -125,6 +128,8 @@ class OffscreenCanvas final : public RefCounted<OffscreenCanvas>, public CanvasB
void setWidth(unsigned);
void setHeight(unsigned);

void setImageBufferAndMarkDirty(RefPtr<ImageBuffer>&&) final;

CanvasRenderingContext* renderingContext() const final { return m_context.get(); }

ExceptionOr<std::optional<OffscreenRenderingContext>> getContext(JSC::JSGlobalObject&, RenderingContextType, FixedVector<JSC::Strong<JSC::Unknown>>&& arguments);
@@ -30,6 +30,7 @@ typedef (
#if defined(ENABLE_WEBGL2) && ENABLE_WEBGL2
WebGL2RenderingContext or
#endif
ImageBitmapRenderingContext or
OffscreenCanvasRenderingContext2D) OffscreenRenderingContext;

dictionary ImageEncodeOptions
@@ -42,7 +43,8 @@ enum OffscreenRenderingContextType
{
"2d",
"webgl",
"webgl2"
"webgl2",
"bitmaprenderer"
};

[
@@ -30,6 +30,7 @@
#include "ImageBitmap.h"
#include "ImageBuffer.h"
#include "InspectorInstrumentation.h"
#include "OffscreenCanvas.h"
#include <wtf/IsoMallocInlines.h>

namespace WebCore {
@@ -54,11 +55,13 @@ ImageBitmapRenderingContext::ImageBitmapRenderingContext(CanvasBase& canvas, Ima

ImageBitmapRenderingContext::~ImageBitmapRenderingContext() = default;

HTMLCanvasElement* ImageBitmapRenderingContext::canvas() const
ImageBitmapCanvas ImageBitmapRenderingContext::canvas()
{
auto& base = canvasBase();
if (!is<HTMLCanvasElement>(base))
return nullptr;
#if ENABLE(OFFSCREEN_CANVAS)
if (is<OffscreenCanvas>(base))
return &downcast<OffscreenCanvas>(base);
#endif
return &downcast<HTMLCanvasElement>(base);
}

@@ -89,12 +92,12 @@ void ImageBitmapRenderingContext::setOutputBitmap(RefPtr<ImageBitmap> imageBitma
// only reason I can think of is toDataURL(), but that doesn't seem like
// a good enough argument to waste memory.

auto buffer = ImageBuffer::create(FloatSize(canvas()->width(), canvas()->height()), RenderingPurpose::Unspecified, 1, DestinationColorSpace::SRGB(), PixelFormat::BGRA8, bufferOptionsForRendingMode(RenderingMode::Unaccelerated));
canvas()->setImageBufferAndMarkDirty(WTFMove(buffer));
auto buffer = ImageBuffer::create(FloatSize(canvasBase().width(), canvasBase().height()), RenderingPurpose::Unspecified, 1, DestinationColorSpace::SRGB(), PixelFormat::BGRA8, bufferOptionsForRendingMode(RenderingMode::Unaccelerated));
canvasBase().setImageBufferAndMarkDirty(WTFMove(buffer));

// 1.4. Set the output bitmap's origin-clean flag to true.

canvas()->setOriginClean();
canvasBase().setOriginClean();
return;
}

@@ -110,10 +113,10 @@ void ImageBitmapRenderingContext::setOutputBitmap(RefPtr<ImageBitmap> imageBitma
// bitmap data to be referenced by context's output bitmap.

if (imageBitmap->originClean())
canvas()->setOriginClean();
canvasBase().setOriginClean();
else
canvas()->setOriginTainted();
canvas()->setImageBufferAndMarkDirty(imageBitmap->takeImageBuffer());
canvasBase().setOriginTainted();
canvasBase().setImageBufferAndMarkDirty(imageBitmap->takeImageBuffer());
}

ExceptionOr<void> ImageBitmapRenderingContext::transferFromImageBitmap(RefPtr<ImageBitmap> imageBitmap)
@@ -37,6 +37,12 @@ namespace WebCore {
class ImageBitmap;
class ImageBuffer;

#if ENABLE(OFFSCREEN_CANVAS)
using ImageBitmapCanvas = std::variant<RefPtr<HTMLCanvasElement>, RefPtr<OffscreenCanvas>>;
#else
using ImageBitmapCanvas = std::variant<RefPtr<HTMLCanvasElement>>;
#endif

class ImageBitmapRenderingContext final : public CanvasRenderingContext {
WTF_MAKE_ISO_ALLOCATED(ImageBitmapRenderingContext);
public:
@@ -49,7 +55,7 @@ class ImageBitmapRenderingContext final : public CanvasRenderingContext {

~ImageBitmapRenderingContext();

HTMLCanvasElement* canvas() const;
ImageBitmapCanvas canvas();

ExceptionOr<void> transferFromImageBitmap(RefPtr<ImageBitmap>);

@@ -23,12 +23,18 @@
* THE POSSIBILITY OF SUCH DAMAGE.
*/

#if defined(ENABLE_OFFSCREEN_CANVAS)
typedef (HTMLCanvasElement or OffscreenCanvas) ImageBitmapCanvas;
#else
typedef (HTMLCanvasElement) ImageBitmapCanvas;
#endif

[
EnabledByDeprecatedGlobalSetting=ImageBitmapEnabled,
Exposed=(Window,Worker),
CallTracer=InspectorCanvasCallTracer,
] interface ImageBitmapRenderingContext {
readonly attribute HTMLCanvasElement canvas;
readonly attribute ImageBitmapCanvas canvas;

undefined transferFromImageBitmap(ImageBitmap? bitmap);
};

0 comments on commit b861c55

Please sign in to comment.