mirror of
https://github.com/RGBCube/serenity
synced 2025-07-26 18:27:39 +00:00
LibWeb: Make 2D and 3D canvas rendering contexts GC-allocated
This commit is contained in:
parent
b8d485e6f0
commit
4452b5ca09
11 changed files with 107 additions and 61 deletions
|
@ -10,7 +10,7 @@
|
|||
#include <LibGfx/Painter.h>
|
||||
#include <LibGfx/Quad.h>
|
||||
#include <LibGfx/Rect.h>
|
||||
#include <LibWeb/Bindings/CanvasRenderingContext2DWrapper.h>
|
||||
#include <LibWeb/Bindings/CanvasRenderingContext2DPrototype.h>
|
||||
#include <LibWeb/DOM/ExceptionOr.h>
|
||||
#include <LibWeb/HTML/CanvasRenderingContext2D.h>
|
||||
#include <LibWeb/HTML/HTMLCanvasElement.h>
|
||||
|
@ -23,13 +23,26 @@
|
|||
|
||||
namespace Web::HTML {
|
||||
|
||||
CanvasRenderingContext2D::CanvasRenderingContext2D(HTMLCanvasElement& element)
|
||||
: m_element(JS::make_handle(element))
|
||||
JS::NonnullGCPtr<CanvasRenderingContext2D> CanvasRenderingContext2D::create(HTML::Window& window, HTMLCanvasElement& element)
|
||||
{
|
||||
return *window.heap().allocate<CanvasRenderingContext2D>(window.realm(), window, element);
|
||||
}
|
||||
|
||||
CanvasRenderingContext2D::CanvasRenderingContext2D(HTML::Window& window, HTMLCanvasElement& element)
|
||||
: PlatformObject(window.realm())
|
||||
, m_element(element)
|
||||
{
|
||||
set_prototype(&window.ensure_web_prototype<Bindings::CanvasRenderingContext2DPrototype>("CanvasRenderingContext2D"));
|
||||
}
|
||||
|
||||
CanvasRenderingContext2D::~CanvasRenderingContext2D() = default;
|
||||
|
||||
void CanvasRenderingContext2D::visit_edges(Cell::Visitor& visitor)
|
||||
{
|
||||
Base::visit_edges(visitor);
|
||||
visitor.visit(m_element.ptr());
|
||||
}
|
||||
|
||||
HTMLCanvasElement& CanvasRenderingContext2D::canvas_element()
|
||||
{
|
||||
return *m_element;
|
||||
|
@ -306,11 +319,7 @@ void CanvasRenderingContext2D::fill(Path2D& path, String const& fill_rule)
|
|||
|
||||
RefPtr<ImageData> CanvasRenderingContext2D::create_image_data(int width, int height) const
|
||||
{
|
||||
if (!wrapper()) {
|
||||
dbgln("Hmm! Attempted to create ImageData for wrapper-less CRC2D.");
|
||||
return {};
|
||||
}
|
||||
return ImageData::create_with_size(wrapper()->vm(), width, height);
|
||||
return ImageData::create_with_size(vm(), width, height);
|
||||
}
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/canvas.html#dom-context-2d-getimagedata
|
||||
|
@ -326,7 +335,7 @@ DOM::ExceptionOr<RefPtr<ImageData>> CanvasRenderingContext2D::get_image_data(int
|
|||
|
||||
// 3. Let imageData be a new ImageData object.
|
||||
// 4. Initialize imageData given sw, sh, settings set to settings, and defaultColorSpace set to this's color space.
|
||||
auto image_data = ImageData::create_with_size(wrapper()->vm(), width, height);
|
||||
auto image_data = ImageData::create_with_size(vm(), width, height);
|
||||
|
||||
// NOTE: We don't attempt to create the underlying bitmap here; if it doesn't exist, it's like copying only transparent black pixels (which is a no-op).
|
||||
if (!canvas_element().bitmap())
|
||||
|
|
|
@ -7,14 +7,13 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include <AK/RefCountForwarder.h>
|
||||
#include <AK/Variant.h>
|
||||
#include <LibGfx/AffineTransform.h>
|
||||
#include <LibGfx/Color.h>
|
||||
#include <LibGfx/Forward.h>
|
||||
#include <LibGfx/Painter.h>
|
||||
#include <LibGfx/Path.h>
|
||||
#include <LibWeb/Bindings/Wrappable.h>
|
||||
#include <LibWeb/Bindings/PlatformObject.h>
|
||||
#include <LibWeb/DOM/ExceptionOr.h>
|
||||
#include <LibWeb/HTML/Canvas/CanvasDrawImage.h>
|
||||
#include <LibWeb/HTML/Canvas/CanvasDrawPath.h>
|
||||
|
@ -37,8 +36,7 @@ namespace Web::HTML {
|
|||
using CanvasImageSource = Variant<JS::Handle<HTMLImageElement>, JS::Handle<HTMLCanvasElement>>;
|
||||
|
||||
class CanvasRenderingContext2D
|
||||
: public RefCounted<CanvasRenderingContext2D>
|
||||
, public Bindings::Wrappable
|
||||
: public Bindings::PlatformObject
|
||||
, public CanvasPath
|
||||
, public CanvasState
|
||||
, public CanvasTransform<CanvasRenderingContext2D>
|
||||
|
@ -50,14 +48,11 @@ class CanvasRenderingContext2D
|
|||
, public CanvasImageData
|
||||
, public CanvasPathDrawingStyles<CanvasRenderingContext2D> {
|
||||
|
||||
AK_MAKE_NONCOPYABLE(CanvasRenderingContext2D);
|
||||
AK_MAKE_NONMOVABLE(CanvasRenderingContext2D);
|
||||
WEB_PLATFORM_OBJECT(CanvasRenderingContext2D, Bindings::PlatformObject);
|
||||
|
||||
public:
|
||||
using WrapperType = Bindings::CanvasRenderingContext2DWrapper;
|
||||
|
||||
static NonnullRefPtr<CanvasRenderingContext2D> create(HTMLCanvasElement& element) { return adopt_ref(*new CanvasRenderingContext2D(element)); }
|
||||
~CanvasRenderingContext2D();
|
||||
static JS::NonnullGCPtr<CanvasRenderingContext2D> create(HTML::Window&, HTMLCanvasElement&);
|
||||
virtual ~CanvasRenderingContext2D() override;
|
||||
|
||||
virtual void fill_rect(float x, float y, float width, float height) override;
|
||||
virtual void stroke_rect(float x, float y, float width, float height) override;
|
||||
|
@ -88,7 +83,9 @@ public:
|
|||
virtual void clip() override;
|
||||
|
||||
private:
|
||||
explicit CanvasRenderingContext2D(HTMLCanvasElement&);
|
||||
explicit CanvasRenderingContext2D(HTML::Window&, HTMLCanvasElement&);
|
||||
|
||||
virtual void visit_edges(Cell::Visitor&) override;
|
||||
|
||||
struct PreparedTextGlyph {
|
||||
unsigned int c;
|
||||
|
@ -112,7 +109,7 @@ private:
|
|||
void stroke_internal(Gfx::Path const&);
|
||||
void fill_internal(Gfx::Path&, String const& fill_rule);
|
||||
|
||||
JS::Handle<HTMLCanvasElement> m_element;
|
||||
JS::NonnullGCPtr<HTMLCanvasElement> m_element;
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/canvas.html#concept-canvas-origin-clean
|
||||
bool m_origin_clean { true };
|
||||
|
@ -127,3 +124,5 @@ DOM::ExceptionOr<CanvasImageSourceUsability> check_usability_of_image(CanvasImag
|
|||
bool image_is_not_origin_clean(CanvasImageSource const&);
|
||||
|
||||
}
|
||||
|
||||
WRAPPER_HACK(CanvasRenderingContext2D, Web::HTML)
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
#include <AK/Checked.h>
|
||||
#include <LibGfx/Bitmap.h>
|
||||
#include <LibGfx/PNGWriter.h>
|
||||
#include <LibWeb/Bindings/HTMLCanvasElementPrototype.h>
|
||||
#include <LibWeb/CSS/StyleComputer.h>
|
||||
#include <LibWeb/DOM/Document.h>
|
||||
#include <LibWeb/HTML/CanvasRenderingContext2D.h>
|
||||
|
@ -21,10 +22,25 @@ static constexpr auto max_canvas_area = 16384 * 16384;
|
|||
HTMLCanvasElement::HTMLCanvasElement(DOM::Document& document, DOM::QualifiedName qualified_name)
|
||||
: HTMLElement(document, move(qualified_name))
|
||||
{
|
||||
set_prototype(&document.window().ensure_web_prototype<Bindings::HTMLCanvasElementPrototype>("HTMLCanvasElement"));
|
||||
}
|
||||
|
||||
HTMLCanvasElement::~HTMLCanvasElement() = default;
|
||||
|
||||
void HTMLCanvasElement::visit_edges(Cell::Visitor& visitor)
|
||||
{
|
||||
Base::visit_edges(visitor);
|
||||
m_context.visit(
|
||||
[&](JS::NonnullGCPtr<CanvasRenderingContext2D>& context) {
|
||||
visitor.visit(context.ptr());
|
||||
},
|
||||
[&](JS::NonnullGCPtr<WebGL::WebGLRenderingContext>& context) {
|
||||
visitor.visit(context.ptr());
|
||||
},
|
||||
[](Empty) {
|
||||
});
|
||||
}
|
||||
|
||||
unsigned HTMLCanvasElement::width() const
|
||||
{
|
||||
return attribute(HTML::AttributeNames::width).to_uint().value_or(300);
|
||||
|
@ -38,10 +54,10 @@ unsigned HTMLCanvasElement::height() const
|
|||
void HTMLCanvasElement::reset_context_to_default_state()
|
||||
{
|
||||
m_context.visit(
|
||||
[](NonnullRefPtr<CanvasRenderingContext2D>& context) {
|
||||
[](JS::NonnullGCPtr<CanvasRenderingContext2D>& context) {
|
||||
context->reset_to_default_state();
|
||||
},
|
||||
[](NonnullRefPtr<WebGL::WebGLRenderingContext>&) {
|
||||
[](JS::NonnullGCPtr<WebGL::WebGLRenderingContext>&) {
|
||||
TODO();
|
||||
},
|
||||
[](Empty) {
|
||||
|
@ -71,22 +87,22 @@ RefPtr<Layout::Node> HTMLCanvasElement::create_layout_node(NonnullRefPtr<CSS::St
|
|||
HTMLCanvasElement::HasOrCreatedContext HTMLCanvasElement::create_2d_context()
|
||||
{
|
||||
if (!m_context.has<Empty>())
|
||||
return m_context.has<NonnullRefPtr<CanvasRenderingContext2D>>() ? HasOrCreatedContext::Yes : HasOrCreatedContext::No;
|
||||
return m_context.has<JS::NonnullGCPtr<CanvasRenderingContext2D>>() ? HasOrCreatedContext::Yes : HasOrCreatedContext::No;
|
||||
|
||||
m_context = CanvasRenderingContext2D::create(*this);
|
||||
m_context = CanvasRenderingContext2D::create(window(), *this);
|
||||
return HasOrCreatedContext::Yes;
|
||||
}
|
||||
|
||||
JS::ThrowCompletionOr<HTMLCanvasElement::HasOrCreatedContext> HTMLCanvasElement::create_webgl_context(JS::Value options)
|
||||
{
|
||||
if (!m_context.has<Empty>())
|
||||
return m_context.has<NonnullRefPtr<WebGL::WebGLRenderingContext>>() ? HasOrCreatedContext::Yes : HasOrCreatedContext::No;
|
||||
return m_context.has<JS::NonnullGCPtr<WebGL::WebGLRenderingContext>>() ? HasOrCreatedContext::Yes : HasOrCreatedContext::No;
|
||||
|
||||
auto maybe_context = TRY(WebGL::WebGLRenderingContext::create(*this, options));
|
||||
auto maybe_context = TRY(WebGL::WebGLRenderingContext::create(window(), *this, options));
|
||||
if (!maybe_context)
|
||||
return HasOrCreatedContext::No;
|
||||
|
||||
m_context = maybe_context.release_nonnull();
|
||||
m_context = JS::NonnullGCPtr<WebGL::WebGLRenderingContext>(*maybe_context);
|
||||
return HasOrCreatedContext::Yes;
|
||||
}
|
||||
|
||||
|
@ -104,7 +120,7 @@ JS::ThrowCompletionOr<HTMLCanvasElement::RenderingContext> HTMLCanvasElement::ge
|
|||
// NOTE: See the spec for the full table.
|
||||
if (type == "2d"sv) {
|
||||
if (create_2d_context() == HasOrCreatedContext::Yes)
|
||||
return m_context;
|
||||
return JS::make_handle(*m_context.get<JS::NonnullGCPtr<HTML::CanvasRenderingContext2D>>());
|
||||
|
||||
return Empty {};
|
||||
}
|
||||
|
@ -112,7 +128,7 @@ JS::ThrowCompletionOr<HTMLCanvasElement::RenderingContext> HTMLCanvasElement::ge
|
|||
// NOTE: The WebGL spec says "experimental-webgl" is also acceptable and must be equivalent to "webgl". Other engines accept this, so we do too.
|
||||
if (type.is_one_of("webgl"sv, "experimental-webgl"sv)) {
|
||||
if (TRY(create_webgl_context(options)) == HasOrCreatedContext::Yes)
|
||||
return m_context;
|
||||
return JS::make_handle(*m_context.get<JS::NonnullGCPtr<WebGL::WebGLRenderingContext>>());
|
||||
|
||||
return Empty {};
|
||||
}
|
||||
|
@ -168,10 +184,10 @@ String HTMLCanvasElement::to_data_url(String const& type, [[maybe_unused]] Optio
|
|||
void HTMLCanvasElement::present()
|
||||
{
|
||||
m_context.visit(
|
||||
[](NonnullRefPtr<CanvasRenderingContext2D>&) {
|
||||
[](JS::NonnullGCPtr<CanvasRenderingContext2D>&) {
|
||||
// Do nothing, CRC2D writes directly to the canvas bitmap.
|
||||
},
|
||||
[](NonnullRefPtr<WebGL::WebGLRenderingContext>& context) {
|
||||
[](JS::NonnullGCPtr<WebGL::WebGLRenderingContext>& context) {
|
||||
context->present();
|
||||
},
|
||||
[](Empty) {
|
||||
|
|
|
@ -17,7 +17,7 @@ class HTMLCanvasElement final : public HTMLElement {
|
|||
WEB_PLATFORM_OBJECT(HTMLCanvasElement, HTMLElement);
|
||||
|
||||
public:
|
||||
using RenderingContext = Variant<NonnullRefPtr<CanvasRenderingContext2D>, NonnullRefPtr<WebGL::WebGLRenderingContext>, Empty>;
|
||||
using RenderingContext = Variant<JS::Handle<CanvasRenderingContext2D>, JS::Handle<WebGL::WebGLRenderingContext>, Empty>;
|
||||
|
||||
virtual ~HTMLCanvasElement() override;
|
||||
|
||||
|
@ -40,6 +40,8 @@ public:
|
|||
private:
|
||||
HTMLCanvasElement(DOM::Document&, DOM::QualifiedName);
|
||||
|
||||
virtual void visit_edges(Cell::Visitor&) override;
|
||||
|
||||
virtual RefPtr<Layout::Node> create_layout_node(NonnullRefPtr<CSS::StyleProperties>) override;
|
||||
|
||||
enum class HasOrCreatedContext {
|
||||
|
@ -52,7 +54,8 @@ private:
|
|||
void reset_context_to_default_state();
|
||||
|
||||
RefPtr<Gfx::Bitmap> m_bitmap;
|
||||
RenderingContext m_context;
|
||||
|
||||
Variant<JS::NonnullGCPtr<HTML::CanvasRenderingContext2D>, JS::NonnullGCPtr<WebGL::WebGLRenderingContext>, Empty> m_context;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue