mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-08-11 02:29:21 +00:00
LibWeb: Add CanvasRenderingContext2D context attribute parsing
Makes `context.getContextAttributes()` work.
This commit is contained in:
parent
4d70f6ce1c
commit
fac0f82031
Notes:
github-actions[bot]
2025-04-29 11:52:34 +00:00
Author: https://github.com/gmta
Commit: fac0f82031
Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/4506
Reviewed-by: https://github.com/shannonbooth
6 changed files with 81 additions and 17 deletions
|
@ -4,6 +4,7 @@
|
||||||
* Copyright (c) 2023, MacDue <macdue@dueutil.tech>
|
* Copyright (c) 2023, MacDue <macdue@dueutil.tech>
|
||||||
* Copyright (c) 2024, Aliaksandr Kalenik <kalenik.aliaksandr@gmail.com>
|
* Copyright (c) 2024, Aliaksandr Kalenik <kalenik.aliaksandr@gmail.com>
|
||||||
* Copyright (c) 2024, Lucien Fiorini <lucienfiorini@gmail.com>
|
* Copyright (c) 2024, Lucien Fiorini <lucienfiorini@gmail.com>
|
||||||
|
* Copyright (c) 2025, Jelle Raaijmakers <jelle@ladybird.org>
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
*/
|
*/
|
||||||
|
@ -26,7 +27,6 @@
|
||||||
#include <LibWeb/Infra/CharacterTypes.h>
|
#include <LibWeb/Infra/CharacterTypes.h>
|
||||||
#include <LibWeb/Layout/TextNode.h>
|
#include <LibWeb/Layout/TextNode.h>
|
||||||
#include <LibWeb/Painting/Paintable.h>
|
#include <LibWeb/Painting/Paintable.h>
|
||||||
#include <LibWeb/Platform/FontPlugin.h>
|
|
||||||
#include <LibWeb/SVG/SVGImageElement.h>
|
#include <LibWeb/SVG/SVGImageElement.h>
|
||||||
#include <LibWeb/WebIDL/ExceptionOr.h>
|
#include <LibWeb/WebIDL/ExceptionOr.h>
|
||||||
|
|
||||||
|
@ -34,16 +34,18 @@ namespace Web::HTML {
|
||||||
|
|
||||||
GC_DEFINE_ALLOCATOR(CanvasRenderingContext2D);
|
GC_DEFINE_ALLOCATOR(CanvasRenderingContext2D);
|
||||||
|
|
||||||
GC::Ref<CanvasRenderingContext2D> CanvasRenderingContext2D::create(JS::Realm& realm, HTMLCanvasElement& element)
|
JS::ThrowCompletionOr<GC::Ref<CanvasRenderingContext2D>> CanvasRenderingContext2D::create(JS::Realm& realm, HTMLCanvasElement& element, JS::Value options)
|
||||||
{
|
{
|
||||||
return realm.create<CanvasRenderingContext2D>(realm, element);
|
auto context_attributes = TRY(context_attributes_from_options(realm.vm(), options));
|
||||||
|
return realm.create<CanvasRenderingContext2D>(realm, element, context_attributes);
|
||||||
}
|
}
|
||||||
|
|
||||||
CanvasRenderingContext2D::CanvasRenderingContext2D(JS::Realm& realm, HTMLCanvasElement& element)
|
CanvasRenderingContext2D::CanvasRenderingContext2D(JS::Realm& realm, HTMLCanvasElement& element, CanvasRenderingContext2DSettings context_attributes)
|
||||||
: PlatformObject(realm)
|
: PlatformObject(realm)
|
||||||
, CanvasPath(static_cast<Bindings::PlatformObject&>(*this), *this)
|
, CanvasPath(static_cast<Bindings::PlatformObject&>(*this), *this)
|
||||||
, m_element(element)
|
, m_element(element)
|
||||||
, m_size(element.bitmap_size_for_canvas())
|
, m_size(element.bitmap_size_for_canvas())
|
||||||
|
, m_context_attributes(move(context_attributes))
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -61,6 +63,52 @@ void CanvasRenderingContext2D::visit_edges(Cell::Visitor& visitor)
|
||||||
visitor.visit(m_element);
|
visitor.visit(m_element);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// https://html.spec.whatwg.org/multipage/canvas.html#canvasrenderingcontext2dsettings
|
||||||
|
JS::ThrowCompletionOr<CanvasRenderingContext2DSettings> CanvasRenderingContext2D::context_attributes_from_options(JS::VM& vm, JS::Value value)
|
||||||
|
{
|
||||||
|
if (!value.is_nullish() && !value.is_object())
|
||||||
|
return vm.throw_completion<JS::TypeError>(JS::ErrorType::NotAnObjectOfType, "CanvasRenderingContext2DSettings");
|
||||||
|
|
||||||
|
CanvasRenderingContext2DSettings settings;
|
||||||
|
if (value.is_nullish())
|
||||||
|
return settings;
|
||||||
|
|
||||||
|
auto& value_object = value.as_object();
|
||||||
|
|
||||||
|
JS::Value alpha = TRY(value_object.get("alpha"_fly_string));
|
||||||
|
settings.alpha = alpha.is_undefined() ? true : alpha.to_boolean();
|
||||||
|
|
||||||
|
JS::Value desynchronized = TRY(value_object.get("desynchronized"_fly_string));
|
||||||
|
settings.desynchronized = desynchronized.is_undefined() ? false : desynchronized.to_boolean();
|
||||||
|
|
||||||
|
JS::Value color_space = TRY(value_object.get("colorSpace"_fly_string));
|
||||||
|
if (!color_space.is_undefined()) {
|
||||||
|
auto color_space_string = TRY(color_space.to_string(vm));
|
||||||
|
if (color_space_string == "srgb"sv)
|
||||||
|
settings.color_space = Bindings::PredefinedColorSpace::Srgb;
|
||||||
|
else if (color_space_string == "display-p3"sv)
|
||||||
|
settings.color_space = Bindings::PredefinedColorSpace::DisplayP3;
|
||||||
|
else
|
||||||
|
return vm.throw_completion<JS::TypeError>(JS::ErrorType::InvalidEnumerationValue, color_space_string, "colorSpace");
|
||||||
|
}
|
||||||
|
|
||||||
|
JS::Value color_type = TRY(value_object.get("colorType"_fly_string));
|
||||||
|
if (!color_type.is_undefined()) {
|
||||||
|
auto color_type_string = TRY(color_type.to_string(vm));
|
||||||
|
if (color_type_string == "unorm8"sv)
|
||||||
|
settings.color_type = Bindings::CanvasColorType::Unorm8;
|
||||||
|
else if (color_type_string == "float16"sv)
|
||||||
|
settings.color_type = Bindings::CanvasColorType::Float16;
|
||||||
|
else
|
||||||
|
return vm.throw_completion<JS::TypeError>(JS::ErrorType::InvalidEnumerationValue, color_type_string, "colorType");
|
||||||
|
}
|
||||||
|
|
||||||
|
JS::Value will_read_frequently = TRY(value_object.get("willReadFrequently"_fly_string));
|
||||||
|
settings.will_read_frequently = will_read_frequently.is_undefined() ? false : will_read_frequently.to_boolean();
|
||||||
|
|
||||||
|
return settings;
|
||||||
|
}
|
||||||
|
|
||||||
HTMLCanvasElement& CanvasRenderingContext2D::canvas_element()
|
HTMLCanvasElement& CanvasRenderingContext2D::canvas_element()
|
||||||
{
|
{
|
||||||
return *m_element;
|
return *m_element;
|
||||||
|
@ -221,6 +269,13 @@ void CanvasRenderingContext2D::allocate_painting_surface_if_needed()
|
||||||
{
|
{
|
||||||
if (m_surface || m_size.is_empty())
|
if (m_surface || m_size.is_empty())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
// FIXME: implement context attribute .alpha
|
||||||
|
// FIXME: implement context attribute .color_space
|
||||||
|
// FIXME: implement context attribute .color_type
|
||||||
|
// FIXME: implement context attribute .desynchronized
|
||||||
|
// FIXME: implement context attribute .will_read_frequently
|
||||||
|
|
||||||
auto skia_backend_context = canvas_element().navigable()->traversable_navigable()->skia_backend_context();
|
auto skia_backend_context = canvas_element().navigable()->traversable_navigable()->skia_backend_context();
|
||||||
m_surface = Gfx::PaintingSurface::create_with_size(skia_backend_context, canvas_element().bitmap_size_for_canvas(), Gfx::BitmapFormat::BGRA8888, Gfx::AlphaType::Premultiplied);
|
m_surface = Gfx::PaintingSurface::create_with_size(skia_backend_context, canvas_element().bitmap_size_for_canvas(), Gfx::BitmapFormat::BGRA8888, Gfx::AlphaType::Premultiplied);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
/*
|
/*
|
||||||
* Copyright (c) 2020-2024, Andreas Kling <andreas@ladybird.org>
|
* Copyright (c) 2020-2024, Andreas Kling <andreas@ladybird.org>
|
||||||
* Copyright (c) 2021-2022, Linus Groh <linusg@serenityos.org>
|
* Copyright (c) 2021-2022, Linus Groh <linusg@serenityos.org>
|
||||||
|
* Copyright (c) 2025, Jelle Raaijmakers <jelle@ladybird.org>
|
||||||
*
|
*
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
*/
|
*/
|
||||||
|
@ -8,9 +9,6 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <AK/String.h>
|
#include <AK/String.h>
|
||||||
#include <AK/Variant.h>
|
|
||||||
#include <LibGfx/AffineTransform.h>
|
|
||||||
#include <LibGfx/Color.h>
|
|
||||||
#include <LibGfx/Forward.h>
|
#include <LibGfx/Forward.h>
|
||||||
#include <LibGfx/Painter.h>
|
#include <LibGfx/Painter.h>
|
||||||
#include <LibGfx/Path.h>
|
#include <LibGfx/Path.h>
|
||||||
|
@ -31,11 +29,18 @@
|
||||||
#include <LibWeb/HTML/Canvas/CanvasText.h>
|
#include <LibWeb/HTML/Canvas/CanvasText.h>
|
||||||
#include <LibWeb/HTML/Canvas/CanvasTextDrawingStyles.h>
|
#include <LibWeb/HTML/Canvas/CanvasTextDrawingStyles.h>
|
||||||
#include <LibWeb/HTML/Canvas/CanvasTransform.h>
|
#include <LibWeb/HTML/Canvas/CanvasTransform.h>
|
||||||
#include <LibWeb/HTML/CanvasGradient.h>
|
|
||||||
#include <LibWeb/WebIDL/ExceptionOr.h>
|
#include <LibWeb/WebIDL/ExceptionOr.h>
|
||||||
|
|
||||||
namespace Web::HTML {
|
namespace Web::HTML {
|
||||||
|
|
||||||
|
struct CanvasRenderingContext2DSettings {
|
||||||
|
bool alpha { true };
|
||||||
|
bool desynchronized { false };
|
||||||
|
Bindings::PredefinedColorSpace color_space { Bindings::PredefinedColorSpace::Srgb };
|
||||||
|
Bindings::CanvasColorType color_type { Bindings::CanvasColorType::Unorm8 };
|
||||||
|
bool will_read_frequently { false };
|
||||||
|
};
|
||||||
|
|
||||||
class CanvasRenderingContext2D
|
class CanvasRenderingContext2D
|
||||||
: public Bindings::PlatformObject
|
: public Bindings::PlatformObject
|
||||||
, public CanvasPath
|
, public CanvasPath
|
||||||
|
@ -58,7 +63,7 @@ class CanvasRenderingContext2D
|
||||||
GC_DECLARE_ALLOCATOR(CanvasRenderingContext2D);
|
GC_DECLARE_ALLOCATOR(CanvasRenderingContext2D);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
[[nodiscard]] static GC::Ref<CanvasRenderingContext2D> create(JS::Realm&, HTMLCanvasElement&);
|
static JS::ThrowCompletionOr<GC::Ref<CanvasRenderingContext2D>> create(JS::Realm&, HTMLCanvasElement&, JS::Value options);
|
||||||
virtual ~CanvasRenderingContext2D() override;
|
virtual ~CanvasRenderingContext2D() override;
|
||||||
|
|
||||||
virtual void fill_rect(float x, float y, float width, float height) override;
|
virtual void fill_rect(float x, float y, float width, float height) override;
|
||||||
|
@ -86,6 +91,8 @@ public:
|
||||||
|
|
||||||
GC::Ref<HTMLCanvasElement> canvas_for_binding() const;
|
GC::Ref<HTMLCanvasElement> canvas_for_binding() const;
|
||||||
|
|
||||||
|
CanvasRenderingContext2DSettings get_context_attributes() const { return m_context_attributes; }
|
||||||
|
|
||||||
virtual GC::Ref<TextMetrics> measure_text(StringView text) override;
|
virtual GC::Ref<TextMetrics> measure_text(StringView text) override;
|
||||||
|
|
||||||
virtual void clip(StringView fill_rule) override;
|
virtual void clip(StringView fill_rule) override;
|
||||||
|
@ -128,11 +135,13 @@ public:
|
||||||
void allocate_painting_surface_if_needed();
|
void allocate_painting_surface_if_needed();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
explicit CanvasRenderingContext2D(JS::Realm&, HTMLCanvasElement&);
|
CanvasRenderingContext2D(JS::Realm&, HTMLCanvasElement&, CanvasRenderingContext2DSettings);
|
||||||
|
|
||||||
virtual void initialize(JS::Realm&) override;
|
virtual void initialize(JS::Realm&) override;
|
||||||
virtual void visit_edges(Cell::Visitor&) override;
|
virtual void visit_edges(Cell::Visitor&) override;
|
||||||
|
|
||||||
|
static JS::ThrowCompletionOr<CanvasRenderingContext2DSettings> context_attributes_from_options(JS::VM&, JS::Value);
|
||||||
|
|
||||||
virtual Gfx::Painter* painter_for_canvas_state() override { return painter(); }
|
virtual Gfx::Painter* painter_for_canvas_state() override { return painter(); }
|
||||||
virtual Gfx::Path& path_for_canvas_state() override { return path(); }
|
virtual Gfx::Path& path_for_canvas_state() override { return path(); }
|
||||||
|
|
||||||
|
@ -165,6 +174,7 @@ private:
|
||||||
|
|
||||||
Gfx::IntSize m_size;
|
Gfx::IntSize m_size;
|
||||||
RefPtr<Gfx::PaintingSurface> m_surface;
|
RefPtr<Gfx::PaintingSurface> m_surface;
|
||||||
|
CanvasRenderingContext2DSettings m_context_attributes;
|
||||||
};
|
};
|
||||||
|
|
||||||
enum class CanvasImageSourceUsability {
|
enum class CanvasImageSourceUsability {
|
||||||
|
|
|
@ -64,5 +64,5 @@ CanvasRenderingContext2D includes CanvasPath;
|
||||||
// https://html.spec.whatwg.org/multipage/canvas.html#canvassettings
|
// https://html.spec.whatwg.org/multipage/canvas.html#canvassettings
|
||||||
interface mixin CanvasSettings {
|
interface mixin CanvasSettings {
|
||||||
// settings
|
// settings
|
||||||
[FIXME] CanvasRenderingContext2DSettings getContextAttributes();
|
CanvasRenderingContext2DSettings getContextAttributes();
|
||||||
};
|
};
|
|
@ -208,12 +208,12 @@ void HTMLCanvasElement::adjust_computed_style(CSS::ComputedProperties& style)
|
||||||
style.set_property(CSS::PropertyID::Display, CSS::DisplayStyleValue::create(CSS::Display::from_short(CSS::Display::Short::None)));
|
style.set_property(CSS::PropertyID::Display, CSS::DisplayStyleValue::create(CSS::Display::from_short(CSS::Display::Short::None)));
|
||||||
}
|
}
|
||||||
|
|
||||||
HTMLCanvasElement::HasOrCreatedContext HTMLCanvasElement::create_2d_context()
|
JS::ThrowCompletionOr<HTMLCanvasElement::HasOrCreatedContext> HTMLCanvasElement::create_2d_context(JS::Value options)
|
||||||
{
|
{
|
||||||
if (!m_context.has<Empty>())
|
if (!m_context.has<Empty>())
|
||||||
return m_context.has<GC::Ref<CanvasRenderingContext2D>>() ? HasOrCreatedContext::Yes : HasOrCreatedContext::No;
|
return m_context.has<GC::Ref<CanvasRenderingContext2D>>() ? HasOrCreatedContext::Yes : HasOrCreatedContext::No;
|
||||||
|
|
||||||
m_context = CanvasRenderingContext2D::create(realm(), *this);
|
m_context = TRY(CanvasRenderingContext2D::create(realm(), *this, options));
|
||||||
return HasOrCreatedContext::Yes;
|
return HasOrCreatedContext::Yes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -244,7 +244,7 @@ JS::ThrowCompletionOr<HTMLCanvasElement::RenderingContext> HTMLCanvasElement::ge
|
||||||
// 3. Run the steps in the cell of the following table whose column header matches this canvas element's canvas context mode and whose row header matches contextId:
|
// 3. Run the steps in the cell of the following table whose column header matches this canvas element's canvas context mode and whose row header matches contextId:
|
||||||
// NOTE: See the spec for the full table.
|
// NOTE: See the spec for the full table.
|
||||||
if (type == "2d"sv) {
|
if (type == "2d"sv) {
|
||||||
if (create_2d_context() == HasOrCreatedContext::Yes)
|
if (TRY(create_2d_context(options)) == HasOrCreatedContext::Yes)
|
||||||
return GC::make_root(*m_context.get<GC::Ref<HTML::CanvasRenderingContext2D>>());
|
return GC::make_root(*m_context.get<GC::Ref<HTML::CanvasRenderingContext2D>>());
|
||||||
|
|
||||||
return Empty {};
|
return Empty {};
|
||||||
|
|
|
@ -6,7 +6,6 @@
|
||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <AK/ByteBuffer.h>
|
|
||||||
#include <LibGfx/Forward.h>
|
#include <LibGfx/Forward.h>
|
||||||
#include <LibGfx/PaintingSurface.h>
|
#include <LibGfx/PaintingSurface.h>
|
||||||
#include <LibWeb/HTML/HTMLElement.h>
|
#include <LibWeb/HTML/HTMLElement.h>
|
||||||
|
@ -30,7 +29,7 @@ public:
|
||||||
No,
|
No,
|
||||||
Yes,
|
Yes,
|
||||||
};
|
};
|
||||||
HasOrCreatedContext create_2d_context();
|
JS::ThrowCompletionOr<HasOrCreatedContext> create_2d_context(JS::Value options);
|
||||||
|
|
||||||
WebIDL::UnsignedLong width() const;
|
WebIDL::UnsignedLong width() const;
|
||||||
WebIDL::UnsignedLong height() const;
|
WebIDL::UnsignedLong height() const;
|
||||||
|
|
|
@ -43,7 +43,7 @@ ErrorOr<GC::Ref<HTML::HTMLCanvasElement>, WebDriver::Error> draw_bounding_box_fr
|
||||||
MUST(canvas.set_height(paint_height));
|
MUST(canvas.set_height(paint_height));
|
||||||
|
|
||||||
// FIXME: 5. Let context, a canvas context mode, be the result of invoking the 2D context creation algorithm given canvas as the target.
|
// FIXME: 5. Let context, a canvas context mode, be the result of invoking the 2D context creation algorithm given canvas as the target.
|
||||||
canvas.create_2d_context();
|
MUST(canvas.create_2d_context({}));
|
||||||
canvas.allocate_painting_surface_if_needed();
|
canvas.allocate_painting_surface_if_needed();
|
||||||
if (!canvas.surface())
|
if (!canvas.surface())
|
||||||
return Error::from_code(ErrorCode::UnableToCaptureScreen, "Failed to allocate painting surface"sv);
|
return Error::from_code(ErrorCode::UnableToCaptureScreen, "Failed to allocate painting surface"sv);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue