mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-07-31 05:09:12 +00:00
LibWeb: Implement basics for OffscreenCanvas
This implements the basic interface, classes and functions for OffscreenCanvas. Many are still stubbed out and have many FIXMEs in them, but it is a basic skeleton.
This commit is contained in:
parent
193ab3757b
commit
2ad3ce5d37
Notes:
github-actions[bot]
2025-06-30 15:47:43 +00:00
Author: https://github.com/Totto16
Commit: 2ad3ce5d37
Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/3788
Reviewed-by: https://github.com/ADKaster ✅
Reviewed-by: https://github.com/tcl3
14 changed files with 1094 additions and 4 deletions
|
@ -491,6 +491,8 @@ set(SOURCES
|
|||
HTML/NavigatorBeacon.cpp
|
||||
HTML/NavigatorID.cpp
|
||||
HTML/Numbers.cpp
|
||||
HTML/OffscreenCanvas.cpp
|
||||
HTML/OffscreenCanvasRenderingContext2D.cpp
|
||||
HTML/PageTransitionEvent.cpp
|
||||
HTML/Parser/Entities.cpp
|
||||
HTML/Parser/HTMLEncodingDetection.cpp
|
||||
|
|
|
@ -81,6 +81,7 @@ enum class ImageSmoothingQuality : u8;
|
|||
enum class MediaDecodingType : u8;
|
||||
enum class MediaEncodingType : u8;
|
||||
enum class MediaKeysRequirement : u8;
|
||||
enum class OffscreenRenderingContextId : u8;
|
||||
enum class ReadableStreamReaderMode : u8;
|
||||
enum class ReferrerPolicy : u8;
|
||||
enum class RenderBlockingStatusType : u8;
|
||||
|
@ -620,6 +621,8 @@ class NavigationHistoryEntry;
|
|||
class NavigationObserver;
|
||||
class NavigationTransition;
|
||||
class Navigator;
|
||||
class OffscreenCanvas;
|
||||
class OffscreenCanvasRenderingContext2D;
|
||||
class PageTransitionEvent;
|
||||
class Path2D;
|
||||
class Plugin;
|
||||
|
|
|
@ -11,11 +11,14 @@
|
|||
#include <LibWeb/CSS/StyleValues/ShorthandStyleValue.h>
|
||||
#include <LibWeb/DOM/Document.h>
|
||||
#include <LibWeb/HTML/Canvas/CanvasState.h>
|
||||
#include <LibWeb/HTML/OffscreenCanvas.h>
|
||||
#include <LibWeb/HTML/Window.h>
|
||||
#include <LibWeb/HTML/WorkerGlobalScope.h>
|
||||
|
||||
namespace Web::HTML {
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/canvas.html#canvastextdrawingstyles
|
||||
template<typename IncludingClass>
|
||||
template<typename IncludingClass, typename CanvasType>
|
||||
class CanvasTextDrawingStyles {
|
||||
public:
|
||||
~CanvasTextDrawingStyles() = default;
|
||||
|
@ -40,6 +43,34 @@ public:
|
|||
font_family->to_string(CSS::SerializationMode::Normal));
|
||||
}
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/canvas.html#font-style-source-object
|
||||
Variant<DOM::Document*, HTML::WorkerGlobalScope*> get_font_source_for_font_style_source_object(CanvasType& font_style_source_object)
|
||||
{
|
||||
// Font resolution for the font style source object requires a font source. This is determined for a given object implementing CanvasTextDrawingStyles by the following steps: [CSSFONTLOAD]
|
||||
|
||||
if constexpr (SameAs<CanvasType, HTML::HTMLCanvasElement>) {
|
||||
// 1. If object's font style source object is a canvas element, return the element's node document.
|
||||
return &font_style_source_object.document();
|
||||
} else {
|
||||
// 2. Otherwise, object's font style source object is an OffscreenCanvas object:
|
||||
|
||||
// 1. Let global be object's relevant global object.
|
||||
auto& global_object = HTML::relevant_global_object(font_style_source_object);
|
||||
|
||||
// 2. If global is a Window object, then return global's associated Document.
|
||||
if (is<HTML::Window>(global_object)) {
|
||||
auto& window = as<HTML::Window>(global_object);
|
||||
return &(window.associated_document());
|
||||
}
|
||||
|
||||
// 3. Assert: global implements WorkerGlobalScope.
|
||||
VERIFY(is<HTML::WorkerGlobalScope>(global_object));
|
||||
|
||||
// 4. Return global.
|
||||
return &(as<HTML::WorkerGlobalScope>(global_object));
|
||||
};
|
||||
}
|
||||
|
||||
void set_font(StringView font)
|
||||
{
|
||||
// The font IDL attribute, on setting, must be parsed as a CSS <'font'> value (but without supporting property-independent style sheet syntax like 'inherit'),
|
||||
|
@ -59,13 +90,36 @@ public:
|
|||
// Load font with font style value properties
|
||||
auto const& font_style_value = my_drawing_state().font_style_value->as_shorthand();
|
||||
auto& canvas_element = reinterpret_cast<IncludingClass&>(*this).canvas_element();
|
||||
|
||||
auto& font_style = *font_style_value.longhand(CSS::PropertyID::FontStyle);
|
||||
auto& font_weight = *font_style_value.longhand(CSS::PropertyID::FontWeight);
|
||||
auto& font_width = *font_style_value.longhand(CSS::PropertyID::FontWidth);
|
||||
auto& font_size = *font_style_value.longhand(CSS::PropertyID::FontSize);
|
||||
auto& font_family = *font_style_value.longhand(CSS::PropertyID::FontFamily);
|
||||
auto font_list = canvas_element.document().style_computer().compute_font_for_style_values(&canvas_element, {}, font_family, font_size, font_style, font_weight, font_width);
|
||||
my_drawing_state().current_font_cascade_list = font_list;
|
||||
|
||||
// https://drafts.csswg.org/css-font-loading/#font-source
|
||||
auto font_source = get_font_source_for_font_style_source_object(canvas_element);
|
||||
|
||||
auto font_list = font_source.visit(
|
||||
[&](DOM::Document* document) -> RefPtr<Gfx::FontCascadeList const> {
|
||||
if constexpr (SameAs<CanvasType, HTML::HTMLCanvasElement>) {
|
||||
return document->style_computer().compute_font_for_style_values(&canvas_element, {}, font_family, font_size, font_style, font_weight, font_width);
|
||||
} else {
|
||||
return document->style_computer().compute_font_for_style_values(nullptr, {}, font_family, font_size, font_style, font_weight, font_width);
|
||||
}
|
||||
},
|
||||
[](HTML::WorkerGlobalScope*) -> RefPtr<Gfx::FontCascadeList const> {
|
||||
// FIXME: implement computing the font for HTML::WorkerGlobalScope
|
||||
return {};
|
||||
});
|
||||
|
||||
if (!font_list)
|
||||
return;
|
||||
|
||||
if (font_list->is_empty())
|
||||
return;
|
||||
|
||||
my_drawing_state().current_font_cascade_list = font_list->first();
|
||||
}
|
||||
|
||||
Bindings::CanvasTextAlign text_align() const { return my_drawing_state().text_align; }
|
||||
|
|
9
Libraries/LibWeb/HTML/Canvas/OffscreenCanvasBase.idl
Normal file
9
Libraries/LibWeb/HTML/Canvas/OffscreenCanvasBase.idl
Normal file
|
@ -0,0 +1,9 @@
|
|||
#import <DOM/EventTarget.idl>
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/canvas.html#offscreenrenderingcontext
|
||||
typedef (OffscreenCanvasRenderingContext2D or
|
||||
WebGLRenderingContext or
|
||||
//FIXME: ImageBitmapRenderingContext or
|
||||
WebGL2RenderingContext
|
||||
//FIXME: GPUCanvasContext
|
||||
) OffscreenRenderingContext;
|
|
@ -51,7 +51,7 @@ class CanvasRenderingContext2D
|
|||
, public CanvasCompositing
|
||||
, public CanvasSettings
|
||||
, public CanvasPathDrawingStyles<CanvasRenderingContext2D>
|
||||
, public CanvasTextDrawingStyles<CanvasRenderingContext2D> {
|
||||
, public CanvasTextDrawingStyles<CanvasRenderingContext2D, HTMLCanvasElement> {
|
||||
|
||||
WEB_PLATFORM_OBJECT(CanvasRenderingContext2D, Bindings::PlatformObject);
|
||||
GC_DECLARE_ALLOCATOR(CanvasRenderingContext2D);
|
||||
|
|
378
Libraries/LibWeb/HTML/OffscreenCanvas.cpp
Normal file
378
Libraries/LibWeb/HTML/OffscreenCanvas.cpp
Normal file
|
@ -0,0 +1,378 @@
|
|||
/*
|
||||
* Copyright (c) 2025, Ladybird contributors
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/Tuple.h>
|
||||
#include <LibWeb/Bindings/OffscreenCanvasPrototype.h>
|
||||
#include <LibWeb/HTML/Canvas/SerializeBitmap.h>
|
||||
#include <LibWeb/HTML/OffscreenCanvas.h>
|
||||
#include <LibWeb/HTML/OffscreenCanvasRenderingContext2D.h>
|
||||
#include <LibWeb/HTML/Scripting/TemporaryExecutionContext.h>
|
||||
#include <LibWeb/HTML/Window.h>
|
||||
#include <LibWeb/Platform/EventLoopPlugin.h>
|
||||
#include <LibWeb/WebGL/WebGL2RenderingContext.h>
|
||||
#include <LibWeb/WebGL/WebGLRenderingContext.h>
|
||||
|
||||
namespace Web::HTML {
|
||||
|
||||
GC_DEFINE_ALLOCATOR(OffscreenCanvas);
|
||||
|
||||
GC::Ref<OffscreenCanvas> OffscreenCanvas::create(JS::Realm& realm, WebIDL::UnsignedLong width,
|
||||
WebIDL::UnsignedLong height)
|
||||
{
|
||||
return MUST(OffscreenCanvas::construct_impl(realm, width, height));
|
||||
}
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/canvas.html#dom-offscreencanvas
|
||||
WebIDL::ExceptionOr<GC::Ref<OffscreenCanvas>> OffscreenCanvas::construct_impl(
|
||||
JS::Realm& realm,
|
||||
WebIDL::UnsignedLong width,
|
||||
WebIDL::UnsignedLong height)
|
||||
{
|
||||
|
||||
// The new OffscreenCanvas(width, height) constructor steps are:
|
||||
auto bitmap_or_error = Gfx::Bitmap::create(Gfx::BitmapFormat::RGBA8888, Gfx::IntSize { width, height });
|
||||
|
||||
if (bitmap_or_error.is_error()) {
|
||||
return WebIDL::InvalidStateError::create(realm, MUST(String::formatted("Error in allocating bitmap: {}", bitmap_or_error.error())));
|
||||
}
|
||||
|
||||
auto bitmap = bitmap_or_error.release_value();
|
||||
|
||||
// 1. Initialize the bitmap of this to a rectangular array of transparent black pixels of the dimensions specified by width and height.
|
||||
// noop, the pixel value to set is equal to 0x00000000, which the bitmap already contains
|
||||
|
||||
// 2. Initialize the width of this to width.
|
||||
// 3. Initialize the height of this to height.
|
||||
// noop, we use the height and width from the bitmap
|
||||
|
||||
// FIXME: 4. Set this's inherited language to explicitly unknown.
|
||||
|
||||
// FIXME: 5. Set this's inherited direction to "ltr".
|
||||
|
||||
// 6. Let global be the relevant global object of this.
|
||||
auto& global = realm.global_object();
|
||||
|
||||
// 7. If global is a Window object:
|
||||
if (is<HTML::Window>(global)) {
|
||||
auto& window = as<HTML::Window>(global);
|
||||
// 1.Let element be the document element of global's associated Document.
|
||||
auto* element = window.associated_document().document_element();
|
||||
// 2. If element is not null :
|
||||
if (element) {
|
||||
// FIXME: 1. Set the inherited language of this to element's language.
|
||||
// FIXME: 2. Set the inherited direction of this to element's directionality.
|
||||
}
|
||||
}
|
||||
|
||||
return realm.create<OffscreenCanvas>(realm, bitmap);
|
||||
}
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/canvas.html#dom-offscreencanvas
|
||||
OffscreenCanvas::OffscreenCanvas(JS::Realm& realm, NonnullRefPtr<Gfx::Bitmap> bitmap)
|
||||
: EventTarget(realm)
|
||||
, m_bitmap { move(bitmap) }
|
||||
{
|
||||
}
|
||||
|
||||
OffscreenCanvas::~OffscreenCanvas() = default;
|
||||
|
||||
WebIDL::ExceptionOr<void> OffscreenCanvas::transfer_steps(HTML::TransferDataHolder&)
|
||||
{
|
||||
// FIXME: Implement this
|
||||
dbgln("(STUBBED) OffscreenCanvas::transfer_steps(HTML::TransferDataHolder&)");
|
||||
return {};
|
||||
}
|
||||
|
||||
WebIDL::ExceptionOr<void> OffscreenCanvas::transfer_receiving_steps(HTML::TransferDataHolder&)
|
||||
{
|
||||
// FIXME: Implement this
|
||||
dbgln("(STUBBED) OffscreenCanvas::transfer_receiving_steps(HTML::TransferDataHolder&)");
|
||||
return {};
|
||||
}
|
||||
|
||||
HTML::TransferType OffscreenCanvas::primary_interface() const
|
||||
{
|
||||
// FIXME: Implement this
|
||||
dbgln("(STUBBED) OffscreenCanvas::primary_interface()");
|
||||
return {};
|
||||
}
|
||||
|
||||
WebIDL::UnsignedLong OffscreenCanvas::width() const
|
||||
{
|
||||
return m_bitmap->size().width();
|
||||
}
|
||||
|
||||
WebIDL::UnsignedLong OffscreenCanvas::height() const
|
||||
{
|
||||
return m_bitmap->size().height();
|
||||
}
|
||||
|
||||
void OffscreenCanvas::reset_context_to_default_state()
|
||||
{
|
||||
m_context.visit(
|
||||
[](GC::Ref<OffscreenCanvasRenderingContext2D>& context) {
|
||||
context->reset_to_default_state();
|
||||
},
|
||||
[](GC::Ref<WebGL::WebGLRenderingContext>& context) {
|
||||
context->reset_to_default_state();
|
||||
},
|
||||
[](GC::Ref<WebGL::WebGL2RenderingContext>& context) {
|
||||
context->reset_to_default_state();
|
||||
},
|
||||
[](Empty) {
|
||||
// Do nothing.
|
||||
});
|
||||
}
|
||||
|
||||
void OffscreenCanvas::set_new_bitmap_size(Gfx::IntSize new_size)
|
||||
{
|
||||
m_bitmap = MUST(Gfx::Bitmap::create(Gfx::BitmapFormat::RGBA8888, Gfx::IntSize { new_size.width(), new_size.height() }));
|
||||
|
||||
m_context.visit(
|
||||
[&](GC::Ref<OffscreenCanvasRenderingContext2D>& context) {
|
||||
context->set_size(new_size);
|
||||
},
|
||||
[&](GC::Ref<WebGL::WebGLRenderingContext>& context) {
|
||||
context->set_size(new_size);
|
||||
},
|
||||
[&](GC::Ref<WebGL::WebGL2RenderingContext>& context) {
|
||||
context->set_size(new_size);
|
||||
},
|
||||
[](Empty) {
|
||||
// Do nothing.
|
||||
});
|
||||
}
|
||||
NonnullRefPtr<Gfx::Bitmap> OffscreenCanvas::bitmap() const
|
||||
{
|
||||
return m_bitmap;
|
||||
}
|
||||
|
||||
WebIDL::ExceptionOr<void> OffscreenCanvas::set_width(WebIDL::UnsignedLong value)
|
||||
{
|
||||
Gfx::IntSize current_size = bitmap_size_for_canvas();
|
||||
current_size.set_width(value);
|
||||
|
||||
set_new_bitmap_size(current_size);
|
||||
reset_context_to_default_state();
|
||||
return {};
|
||||
}
|
||||
WebIDL::ExceptionOr<void> OffscreenCanvas::set_height(WebIDL::UnsignedLong value)
|
||||
{
|
||||
Gfx::IntSize current_size = bitmap_size_for_canvas();
|
||||
current_size.set_height(value);
|
||||
|
||||
set_new_bitmap_size(current_size);
|
||||
reset_context_to_default_state();
|
||||
return {};
|
||||
}
|
||||
|
||||
Gfx::IntSize OffscreenCanvas::bitmap_size_for_canvas() const
|
||||
{
|
||||
return m_bitmap->size();
|
||||
}
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/canvas.html#dom-offscreencanvas-getcontext
|
||||
JS::ThrowCompletionOr<OffscreenRenderingContext> OffscreenCanvas::get_context(Bindings::OffscreenRenderingContextId contextId, JS::Value options)
|
||||
{
|
||||
// 1. If options is not an object, then set options to null.
|
||||
if (!options.is_object())
|
||||
options = JS::js_null();
|
||||
|
||||
// 2. Set options to the result of converting options to a JavaScript value.
|
||||
// NOTE: No-op.
|
||||
|
||||
// 3. Run the steps in the cell of the following table whose column header matches this OffscreenCanvas object's context mode and whose row header matches contextId:
|
||||
// NOTE: See the spec for the full table.
|
||||
if (contextId == Bindings::OffscreenRenderingContextId::_2d) {
|
||||
if (TRY(create_2d_context(options)) == HasOrCreatedContext::Yes)
|
||||
return GC::make_root(*m_context.get<GC::Ref<HTML::OffscreenCanvasRenderingContext2D>>());
|
||||
|
||||
return Empty {};
|
||||
}
|
||||
|
||||
if (contextId == Bindings::OffscreenRenderingContextId::Webgl) {
|
||||
dbgln("(STUBBED) OffscreenCanvas::get_context(Webgl)");
|
||||
|
||||
return Empty {};
|
||||
}
|
||||
|
||||
if (contextId == Bindings::OffscreenRenderingContextId::Webgl2) {
|
||||
dbgln("(STUBBED) OffscreenCanvas::get_context(Webgl2)");
|
||||
|
||||
return Empty {};
|
||||
}
|
||||
|
||||
return Empty {};
|
||||
}
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/canvas.html#dom-offscreencanvas-transfertoimagebitmap
|
||||
WebIDL::ExceptionOr<GC::Ref<ImageBitmap>> OffscreenCanvas::transfer_to_image_bitmap()
|
||||
{
|
||||
// The transferToImageBitmap() method, when invoked, must run the following steps :
|
||||
|
||||
// FIXME: 1. If the value of this OffscreenCanvas object's [[Detached]] internal slot is set to true, then throw an "InvalidStateError" DOMException.
|
||||
|
||||
// 2. If this OffscreenCanvas object's context mode is set to none, then throw an "InvalidStateError" DOMException.
|
||||
if (m_context.has<Empty>()) {
|
||||
return WebIDL::InvalidStateError::create(realm(), "OffscreenCanvas has no context"_string);
|
||||
}
|
||||
|
||||
// 3. Let image be a newly created ImageBitmap object that references the same underlying bitmap data as this OffscreenCanvas object's bitmap.
|
||||
auto image = ImageBitmap::create(realm());
|
||||
image->set_bitmap(m_bitmap);
|
||||
|
||||
// 4. Set this OffscreenCanvas object 's bitmap to reference a newly created bitmap of the same dimensions and color space as the previous bitmap, and with its pixels initialized to transparent black, or opaque black if the rendering context' s alpha is false.
|
||||
// FIXME: implement the checking of the alpha from the context
|
||||
auto size = bitmap_size_for_canvas();
|
||||
m_bitmap = MUST(Gfx::Bitmap::create(Gfx::BitmapFormat::RGBA8888, size));
|
||||
|
||||
// 5. Return image.
|
||||
return image;
|
||||
}
|
||||
|
||||
static Tuple<FlyString, Optional<double>> options_convert_or_default(Optional<ImageEncodeOptions> options)
|
||||
{
|
||||
|
||||
if (!options.has_value()) {
|
||||
return { "image/png"_fly_string, {} };
|
||||
}
|
||||
|
||||
return { options->type, options->quality };
|
||||
}
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/canvas.html#dom-offscreencanvas-converttoblob
|
||||
GC::Ref<WebIDL::Promise> OffscreenCanvas::convert_to_blob(Optional<ImageEncodeOptions> maybe_options)
|
||||
{
|
||||
// The convertToBlob(options) method, when invoked, must run the following steps:
|
||||
|
||||
// FIXME: 1. If the value of this OffscreenCanvas object's [[Detached]] internal slot is set to true, then return a promise rejected with an "InvalidStateError" DOMException.
|
||||
|
||||
// FIXME 2. If this OffscreenCanvas object's context mode is 2d and the rendering context's output bitmap's origin-clean flag is set to false, then return a promise rejected with a "SecurityError" DOMException.
|
||||
|
||||
auto size = bitmap_size_for_canvas();
|
||||
|
||||
// 3. If this OffscreenCanvas object's bitmap has no pixels (i.e., either its horizontal dimension or its vertical dimension is zero) then return a promise rejected with an "IndexSizeError" DOMException.
|
||||
if (size.height() == 0 or size.width() == 0) {
|
||||
auto error = WebIDL::IndexSizeError::create(realm(), "OffscreenCanvas has invalid dimensions. The bitmap has no pixels"_string);
|
||||
|
||||
return WebIDL::create_rejected_promise_from_exception(realm(), error);
|
||||
}
|
||||
|
||||
// 4. Let bitmap be a copy of this OffscreenCanvas object's bitmap.
|
||||
auto bitmap = MUST(m_bitmap->clone());
|
||||
|
||||
// 5. Let result be a new promise object.
|
||||
auto result_promise = WebIDL::create_promise(realm());
|
||||
|
||||
// 6. Run these steps in parallel:
|
||||
Platform::EventLoopPlugin::the().deferred_invoke(GC::create_function(heap(), [this, result_promise, bitmap, maybe_options] {
|
||||
// 1. Let file be a serialization of bitmap as a file, with options's type and quality if present.
|
||||
Optional<SerializeBitmapResult> file_result {};
|
||||
auto options = options_convert_or_default(maybe_options);
|
||||
|
||||
if (auto result = serialize_bitmap(*bitmap, options.get<0>(), options.get<1>()); !result.is_error())
|
||||
file_result = result.release_value();
|
||||
|
||||
// 2. Queue an element task on the canvas blob serialization task source given the canvas element to run these steps:
|
||||
// FIXME: wait for spec bug to be resolve: https://github.com/whatwg/html/issues/11101
|
||||
|
||||
// AD-HOC: queue the task in an appropiate queue. This depends if the global object is a window or a worker
|
||||
Function<void()> task_to_queue = [this, result_promise, file_result = move(file_result)] -> void {
|
||||
HTML::TemporaryExecutionContext context(realm(), HTML::TemporaryExecutionContext::CallbacksEnabled::Yes);
|
||||
|
||||
// 1. If file is null, then reject result with an "EncodingError" DOMException.
|
||||
if (!file_result.has_value()) {
|
||||
auto error = WebIDL::EncodingError::create(realm(), "Failed to convert OffscreenCanvas to Blob"_string);
|
||||
|
||||
WebIDL::reject_promise(realm(), result_promise, error);
|
||||
} else {
|
||||
// 1. If result is non-null, resolve result with a new Blob object, created in the relevant realm of this OffscreenCanvas object, representing file. [FILEAPI]
|
||||
auto type = String::from_utf8(file_result->mime_type);
|
||||
if (type.is_error()) {
|
||||
auto error = WebIDL::EncodingError::create(realm(), MUST(String::formatted("OOM Error while converting string in OffscreenCanvas to blob: {}"_string, type.error())));
|
||||
WebIDL::reject_promise(realm(), result_promise, error);
|
||||
return;
|
||||
}
|
||||
|
||||
GC::Ptr<FileAPI::Blob> blob_result = FileAPI::Blob::create(realm(), file_result->buffer, type.release_value());
|
||||
WebIDL::resolve_promise(realm(), result_promise, blob_result);
|
||||
}
|
||||
};
|
||||
|
||||
auto& global_object = HTML::relevant_global_object(*this);
|
||||
|
||||
// AD-HOC: if the global_object is a window, queue an element task on the canvas blob serialization task source
|
||||
if (is<HTML::Window>(global_object)) {
|
||||
auto& window = as<HTML::Window>(global_object);
|
||||
window.associated_document().document_element()->queue_an_element_task(Task::Source::CanvasBlobSerializationTask, move(task_to_queue));
|
||||
return;
|
||||
}
|
||||
|
||||
// AD-HOC: the global object only can be a worker or a window
|
||||
VERIFY(is<HTML::WorkerGlobalScope>(global_object));
|
||||
|
||||
auto& worker = as<HTML::WorkerGlobalScope>(global_object);
|
||||
|
||||
// AD-HOC: if the global_object is a worker, queue a global task on the canvas blob serialization task source
|
||||
HTML::queue_global_task(Task::Source::CanvasBlobSerializationTask, worker, GC::create_function(heap(), move(task_to_queue)));
|
||||
}));
|
||||
|
||||
// 7. Return result.
|
||||
return result_promise;
|
||||
}
|
||||
void OffscreenCanvas::set_oncontextlost(GC::Ptr<WebIDL::CallbackType> event_handler)
|
||||
{
|
||||
set_event_handler_attribute(HTML::EventNames::contextlost, event_handler);
|
||||
}
|
||||
|
||||
GC::Ptr<WebIDL::CallbackType> OffscreenCanvas::oncontextlost()
|
||||
{
|
||||
return event_handler_attribute(HTML::EventNames::contextlost);
|
||||
}
|
||||
|
||||
void OffscreenCanvas::set_oncontextrestored(GC::Ptr<WebIDL::CallbackType> event_handler)
|
||||
{
|
||||
set_event_handler_attribute(HTML::EventNames::contextrestored, event_handler);
|
||||
}
|
||||
|
||||
GC::Ptr<WebIDL::CallbackType> OffscreenCanvas::oncontextrestored()
|
||||
{
|
||||
return event_handler_attribute(HTML::EventNames::contextrestored);
|
||||
}
|
||||
|
||||
void OffscreenCanvas::initialize(JS::Realm& realm)
|
||||
{
|
||||
Base::initialize(realm);
|
||||
WEB_SET_PROTOTYPE_FOR_INTERFACE(OffscreenCanvas);
|
||||
}
|
||||
|
||||
void OffscreenCanvas::visit_edges(Cell::Visitor& visitor)
|
||||
{
|
||||
Base::visit_edges(visitor);
|
||||
m_context.visit(
|
||||
[&](GC::Ref<OffscreenCanvasRenderingContext2D>& context) {
|
||||
visitor.visit(context);
|
||||
},
|
||||
[&](GC::Ref<WebGL::WebGLRenderingContext>& context) {
|
||||
visitor.visit(context);
|
||||
},
|
||||
[&](GC::Ref<WebGL::WebGL2RenderingContext>& context) {
|
||||
visitor.visit(context);
|
||||
},
|
||||
[](Empty) {
|
||||
});
|
||||
}
|
||||
|
||||
JS::ThrowCompletionOr<OffscreenCanvas::HasOrCreatedContext> OffscreenCanvas::create_2d_context(JS::Value options)
|
||||
{
|
||||
if (!m_context.has<Empty>())
|
||||
return m_context.has<GC::Ref<OffscreenCanvasRenderingContext2D>>() ? HasOrCreatedContext::Yes : HasOrCreatedContext::No;
|
||||
|
||||
m_context = TRY(OffscreenCanvasRenderingContext2D::create(realm(), *this, options));
|
||||
return HasOrCreatedContext::Yes;
|
||||
}
|
||||
|
||||
}
|
91
Libraries/LibWeb/HTML/OffscreenCanvas.h
Normal file
91
Libraries/LibWeb/HTML/OffscreenCanvas.h
Normal file
|
@ -0,0 +1,91 @@
|
|||
/*
|
||||
* Copyright (c) 2025, Ladybird contributors
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <LibGfx/Bitmap.h>
|
||||
#include <LibWeb/Bindings/PlatformObject.h>
|
||||
#include <LibWeb/Bindings/Transferable.h>
|
||||
#include <LibWeb/DOM/EventTarget.h>
|
||||
#include <LibWeb/Forward.h>
|
||||
#include <LibWeb/WebIDL/Types.h>
|
||||
|
||||
namespace Web::HTML {
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/canvas.html#offscreenrenderingcontext
|
||||
// NOTE: This is the Variant created by the IDL wrapper generator, and needs to be updated accordingly.
|
||||
using OffscreenRenderingContext = Variant<GC::Root<OffscreenCanvasRenderingContext2D>, GC::Root<WebGL::WebGLRenderingContext>, GC::Root<WebGL::WebGL2RenderingContext>, Empty>;
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/canvas.html#imageencodeoptions
|
||||
struct ImageEncodeOptions {
|
||||
FlyString type { "image/png"_fly_string };
|
||||
Optional<double> quality;
|
||||
};
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/canvas.html#offscreencanvas
|
||||
class OffscreenCanvas : public DOM::EventTarget
|
||||
, public Web::Bindings::Transferable {
|
||||
WEB_PLATFORM_OBJECT(OffscreenCanvas, DOM::EventTarget);
|
||||
GC_DECLARE_ALLOCATOR(OffscreenCanvas);
|
||||
|
||||
public:
|
||||
static GC::Ref<OffscreenCanvas> create(JS::Realm&, WebIDL::UnsignedLong width,
|
||||
WebIDL::UnsignedLong height);
|
||||
|
||||
static WebIDL::ExceptionOr<GC::Ref<OffscreenCanvas>> construct_impl(
|
||||
JS::Realm&,
|
||||
WebIDL::UnsignedLong width,
|
||||
WebIDL::UnsignedLong height);
|
||||
|
||||
virtual ~OffscreenCanvas() override;
|
||||
|
||||
// ^Web::Bindings::Transferable
|
||||
virtual WebIDL::ExceptionOr<void> transfer_steps(HTML::TransferDataHolder&) override;
|
||||
virtual WebIDL::ExceptionOr<void> transfer_receiving_steps(HTML::TransferDataHolder&) override;
|
||||
virtual HTML::TransferType primary_interface() const override;
|
||||
|
||||
WebIDL::UnsignedLong width() const;
|
||||
WebIDL::UnsignedLong height() const;
|
||||
|
||||
NonnullRefPtr<Gfx::Bitmap> bitmap() const;
|
||||
|
||||
WebIDL::ExceptionOr<void> set_width(WebIDL::UnsignedLong);
|
||||
WebIDL::ExceptionOr<void> set_height(WebIDL::UnsignedLong);
|
||||
|
||||
Gfx::IntSize bitmap_size_for_canvas() const;
|
||||
|
||||
JS::ThrowCompletionOr<OffscreenRenderingContext> get_context(Bindings::OffscreenRenderingContextId contextId, JS::Value options);
|
||||
|
||||
WebIDL::ExceptionOr<GC::Ref<ImageBitmap>> transfer_to_image_bitmap();
|
||||
|
||||
GC::Ref<WebIDL::Promise> convert_to_blob(Optional<ImageEncodeOptions> options);
|
||||
|
||||
void set_oncontextlost(GC::Ptr<WebIDL::CallbackType>);
|
||||
GC::Ptr<WebIDL::CallbackType> oncontextlost();
|
||||
void set_oncontextrestored(GC::Ptr<WebIDL::CallbackType>);
|
||||
GC::Ptr<WebIDL::CallbackType> oncontextrestored();
|
||||
|
||||
private:
|
||||
OffscreenCanvas(JS::Realm&, NonnullRefPtr<Gfx::Bitmap> bitmap);
|
||||
|
||||
virtual void initialize(JS::Realm&) override;
|
||||
virtual void visit_edges(Cell::Visitor&) override;
|
||||
|
||||
enum class HasOrCreatedContext {
|
||||
No,
|
||||
Yes,
|
||||
};
|
||||
JS::ThrowCompletionOr<HasOrCreatedContext> create_2d_context(JS::Value options);
|
||||
|
||||
void reset_context_to_default_state();
|
||||
void set_new_bitmap_size(Gfx::IntSize new_size);
|
||||
|
||||
Variant<GC::Ref<HTML::OffscreenCanvasRenderingContext2D>, GC::Ref<WebGL::WebGLRenderingContext>, GC::Ref<WebGL::WebGL2RenderingContext>, Empty> m_context;
|
||||
|
||||
NonnullRefPtr<Gfx::Bitmap> m_bitmap;
|
||||
};
|
||||
|
||||
}
|
32
Libraries/LibWeb/HTML/OffscreenCanvas.idl
Normal file
32
Libraries/LibWeb/HTML/OffscreenCanvas.idl
Normal file
|
@ -0,0 +1,32 @@
|
|||
#import <DOM/EventTarget.idl>
|
||||
#import <HTML/OffscreenCanvasRenderingContext2D.idl>
|
||||
#import <HTML/Canvas/OffscreenCanvasBase.idl>
|
||||
|
||||
|
||||
// FIXME: This should be in OffscreenCanvasBase.idl but then it is not exported
|
||||
// https://html.spec.whatwg.org/multipage/canvas.html#imageencodeoptions
|
||||
dictionary ImageEncodeOptions {
|
||||
DOMString type = "image/png";
|
||||
unrestricted double quality;
|
||||
};
|
||||
|
||||
// FIXME: This should be in OffscreenCanvasBase.idl but then it is not exported
|
||||
// https://html.spec.whatwg.org/multipage/canvas.html#offscreenrenderingcontextid
|
||||
enum OffscreenRenderingContextId { "2d", "bitmaprenderer", "webgl", "webgl2", "webgpu" };
|
||||
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/canvas.html#offscreencanvas
|
||||
[Exposed=(Window,Worker), Transferable]
|
||||
interface OffscreenCanvas : EventTarget {
|
||||
constructor([EnforceRange] unsigned long long width, [EnforceRange] unsigned long long height);
|
||||
|
||||
attribute unsigned long long width;
|
||||
attribute unsigned long long height;
|
||||
|
||||
OffscreenRenderingContext? getContext(OffscreenRenderingContextId contextId, optional any options = null);
|
||||
ImageBitmap transferToImageBitmap();
|
||||
Promise<Blob> convertToBlob(optional ImageEncodeOptions options = {});
|
||||
|
||||
attribute EventHandler oncontextlost;
|
||||
attribute EventHandler oncontextrestored;
|
||||
};
|
325
Libraries/LibWeb/HTML/OffscreenCanvasRenderingContext2D.cpp
Normal file
325
Libraries/LibWeb/HTML/OffscreenCanvasRenderingContext2D.cpp
Normal file
|
@ -0,0 +1,325 @@
|
|||
/*
|
||||
* Copyright (c) 2025, Ladybird contributors
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/OwnPtr.h>
|
||||
#include <LibGfx/CompositingAndBlendingOperator.h>
|
||||
#include <LibGfx/PainterSkia.h>
|
||||
#include <LibGfx/Rect.h>
|
||||
#include <LibUnicode/Segmenter.h>
|
||||
#include <LibWeb/Bindings/Intrinsics.h>
|
||||
#include <LibWeb/Bindings/OffscreenCanvasRenderingContext2DPrototype.h>
|
||||
#include <LibWeb/HTML/HTMLCanvasElement.h>
|
||||
#include <LibWeb/HTML/HTMLImageElement.h>
|
||||
#include <LibWeb/HTML/ImageBitmap.h>
|
||||
#include <LibWeb/HTML/ImageData.h>
|
||||
#include <LibWeb/HTML/OffscreenCanvas.h>
|
||||
#include <LibWeb/HTML/OffscreenCanvasRenderingContext2D.h>
|
||||
#include <LibWeb/HTML/Path2D.h>
|
||||
#include <LibWeb/HTML/TextMetrics.h>
|
||||
#include <LibWeb/HTML/TraversableNavigable.h>
|
||||
#include <LibWeb/Infra/CharacterTypes.h>
|
||||
#include <LibWeb/Layout/TextNode.h>
|
||||
#include <LibWeb/Painting/Paintable.h>
|
||||
#include <LibWeb/Platform/FontPlugin.h>
|
||||
#include <LibWeb/SVG/SVGImageElement.h>
|
||||
#include <LibWeb/WebIDL/ExceptionOr.h>
|
||||
|
||||
namespace Web::HTML {
|
||||
|
||||
GC_DEFINE_ALLOCATOR(OffscreenCanvasRenderingContext2D);
|
||||
|
||||
JS::ThrowCompletionOr<GC::Ref<OffscreenCanvasRenderingContext2D>> OffscreenCanvasRenderingContext2D::create(JS::Realm& realm, OffscreenCanvas& offscreen_canvas, JS::Value options)
|
||||
{
|
||||
auto context_attributes = TRY(CanvasRenderingContext2DSettings::from_js_value(realm.vm(), options));
|
||||
return realm.create<OffscreenCanvasRenderingContext2D>(realm, offscreen_canvas, context_attributes);
|
||||
}
|
||||
|
||||
OffscreenCanvasRenderingContext2D::OffscreenCanvasRenderingContext2D(JS::Realm& realm, OffscreenCanvas& offscreen_canvas, CanvasRenderingContext2DSettings context_attributes)
|
||||
: PlatformObject(realm)
|
||||
, CanvasPath(static_cast<Bindings::PlatformObject&>(*this), *this)
|
||||
, m_canvas(offscreen_canvas)
|
||||
, m_size(offscreen_canvas.bitmap_size_for_canvas())
|
||||
, m_context_attributes(context_attributes)
|
||||
{
|
||||
}
|
||||
|
||||
OffscreenCanvasRenderingContext2D::~OffscreenCanvasRenderingContext2D() = default;
|
||||
|
||||
void OffscreenCanvasRenderingContext2D::initialize(JS::Realm& realm)
|
||||
{
|
||||
Base::initialize(realm);
|
||||
set_prototype(&Bindings::ensure_web_prototype<Bindings::OffscreenCanvasRenderingContext2DPrototype>(realm, "OffscreenCanvasRenderingContext2D"_string));
|
||||
}
|
||||
|
||||
void OffscreenCanvasRenderingContext2D::visit_edges(Cell::Visitor& visitor)
|
||||
{
|
||||
Base::visit_edges(visitor);
|
||||
visitor.visit(m_canvas);
|
||||
}
|
||||
|
||||
void OffscreenCanvasRenderingContext2D::set_size(Gfx::IntSize const& size)
|
||||
{
|
||||
if (m_size == size)
|
||||
return;
|
||||
m_size = size;
|
||||
}
|
||||
|
||||
GC::Ref<OffscreenCanvas> OffscreenCanvasRenderingContext2D::canvas()
|
||||
{
|
||||
return m_canvas;
|
||||
}
|
||||
|
||||
OffscreenCanvas& OffscreenCanvasRenderingContext2D::canvas_element()
|
||||
{
|
||||
return *m_canvas;
|
||||
}
|
||||
|
||||
OffscreenCanvas const& OffscreenCanvasRenderingContext2D::canvas_element() const
|
||||
{
|
||||
|
||||
return *m_canvas;
|
||||
}
|
||||
|
||||
void OffscreenCanvasRenderingContext2D::fill_rect(float, float, float, float)
|
||||
{
|
||||
dbgln("(STUBBED) OffscreenCanvasRenderingContext2D::fill_rect()");
|
||||
}
|
||||
|
||||
void OffscreenCanvasRenderingContext2D::clear_rect(float, float, float, float)
|
||||
{
|
||||
dbgln("(STUBBED) OffscreenCanvasRenderingContext2D::clear_rect()");
|
||||
}
|
||||
|
||||
void OffscreenCanvasRenderingContext2D::stroke_rect(float, float, float, float)
|
||||
{
|
||||
dbgln("(STUBBED) OffscreenCanvasRenderingContext2D::stroke_rect()");
|
||||
}
|
||||
|
||||
WebIDL::ExceptionOr<void> OffscreenCanvasRenderingContext2D::draw_image_internal(CanvasImageSource const&, float, float, float, float, float, float, float, float)
|
||||
{
|
||||
dbgln("(STUBBED) OffscreenCanvasRenderingContext2D::draw_image_internal()");
|
||||
return {};
|
||||
}
|
||||
|
||||
void OffscreenCanvasRenderingContext2D::begin_path()
|
||||
{
|
||||
dbgln("(STUBBED) OffscreenCanvasRenderingContext2D::begin_path()");
|
||||
}
|
||||
|
||||
void OffscreenCanvasRenderingContext2D::stroke()
|
||||
{
|
||||
dbgln("(STUBBED) OffscreenCanvasRenderingContext2D::stroke()");
|
||||
}
|
||||
|
||||
void OffscreenCanvasRenderingContext2D::stroke(Path2D const&)
|
||||
{
|
||||
dbgln("(STUBBED) OffscreenCanvasRenderingContext2D::stroke(Path2D)");
|
||||
}
|
||||
|
||||
void OffscreenCanvasRenderingContext2D::fill_text(StringView, float, float, Optional<double>)
|
||||
{
|
||||
dbgln("(STUBBED) OffscreenCanvasRenderingContext2D::fill_text()");
|
||||
}
|
||||
|
||||
void OffscreenCanvasRenderingContext2D::stroke_text(StringView, float, float, Optional<double>)
|
||||
{
|
||||
dbgln("(STUBBED) OffscreenCanvasRenderingContext2D::stroke_text()");
|
||||
}
|
||||
|
||||
void OffscreenCanvasRenderingContext2D::fill(StringView)
|
||||
{
|
||||
dbgln("(STUBBED) OffscreenCanvasRenderingContext2D::fill(StringView)");
|
||||
}
|
||||
|
||||
void OffscreenCanvasRenderingContext2D::fill(Path2D&, StringView)
|
||||
{
|
||||
dbgln("(STUBBED) OffscreenCanvasRenderingContext2D::fill(Path2D&, StringView)");
|
||||
}
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/canvas.html#dom-context-2d-createimagedata
|
||||
WebIDL::ExceptionOr<GC::Ref<ImageData>> OffscreenCanvasRenderingContext2D::create_image_data(int, int, Optional<ImageDataSettings> const&) const
|
||||
{
|
||||
return WebIDL::NotSupportedError::create(realm(), "(STUBBED) OffscreenCanvasRenderingContext2D::create_image_data(int, int)"_string);
|
||||
}
|
||||
|
||||
WebIDL::ExceptionOr<GC::Ref<ImageData>> OffscreenCanvasRenderingContext2D::create_image_data(ImageData const&) const
|
||||
{
|
||||
return WebIDL::NotSupportedError::create(realm(), "(STUBBED) OffscreenCanvasRenderingContext2D::create_image_data(ImageData&)"_string);
|
||||
}
|
||||
|
||||
WebIDL::ExceptionOr<GC::Ptr<ImageData>> OffscreenCanvasRenderingContext2D::get_image_data(int, int, int, int, Optional<ImageDataSettings> const&) const
|
||||
{
|
||||
return WebIDL::NotSupportedError::create(realm(), "(STUBBED) OffscreenCanvasRenderingContext2D::get_image_data()"_string);
|
||||
}
|
||||
|
||||
void OffscreenCanvasRenderingContext2D::put_image_data(ImageData&, float, float)
|
||||
|
||||
{
|
||||
dbgln("(STUBBED) OffscreenCanvasRenderingContext2D::put_image_data()");
|
||||
}
|
||||
|
||||
void OffscreenCanvasRenderingContext2D::reset_to_default_state()
|
||||
{
|
||||
dbgln("(STUBBED) OffscreenCanvasRenderingContext2D::reset_to_default_state()");
|
||||
}
|
||||
|
||||
GC::Ref<TextMetrics> OffscreenCanvasRenderingContext2D::measure_text(StringView)
|
||||
{
|
||||
dbgln("(STUBBED) OffscreenCanvasRenderingContext2D::measure_text()");
|
||||
|
||||
auto metrics = TextMetrics::create(realm());
|
||||
return metrics;
|
||||
}
|
||||
|
||||
void OffscreenCanvasRenderingContext2D::clip(StringView)
|
||||
{
|
||||
dbgln("(STUBBED) OffscreenCanvasRenderingContext2D::clip(StringView)");
|
||||
}
|
||||
|
||||
void OffscreenCanvasRenderingContext2D::clip(Path2D&, StringView)
|
||||
{
|
||||
dbgln("(STUBBED) OffscreenCanvasRenderingContext2D::clip(Path2D&, StringView)");
|
||||
}
|
||||
|
||||
bool OffscreenCanvasRenderingContext2D::is_point_in_path(double, double, StringView)
|
||||
{
|
||||
dbgln("(STUBBED) OffscreenCanvasRenderingContext2D::is_point_in_path(double, double, StringView)");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool OffscreenCanvasRenderingContext2D::is_point_in_path(Path2D const&, double, double, StringView)
|
||||
{
|
||||
dbgln("(STUBBED) OffscreenCanvasRenderingContext2D::clip(Path2D const&, double, double, StringView)");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool OffscreenCanvasRenderingContext2D::image_smoothing_enabled() const
|
||||
{
|
||||
return drawing_state().image_smoothing_enabled;
|
||||
}
|
||||
|
||||
void OffscreenCanvasRenderingContext2D::set_image_smoothing_enabled(bool enabled)
|
||||
{
|
||||
drawing_state().image_smoothing_enabled = enabled;
|
||||
}
|
||||
|
||||
Bindings::ImageSmoothingQuality OffscreenCanvasRenderingContext2D::image_smoothing_quality() const
|
||||
{
|
||||
return drawing_state().image_smoothing_quality;
|
||||
}
|
||||
|
||||
void OffscreenCanvasRenderingContext2D::set_image_smoothing_quality(Bindings::ImageSmoothingQuality quality)
|
||||
{
|
||||
drawing_state().image_smoothing_quality = quality;
|
||||
}
|
||||
|
||||
String OffscreenCanvasRenderingContext2D::filter() const
|
||||
{
|
||||
dbgln("(STUBBED) OffscreenCanvasRenderingContext2D::filter()");
|
||||
return String::from_utf8_without_validation("none"sv.bytes());
|
||||
}
|
||||
|
||||
void OffscreenCanvasRenderingContext2D::set_filter(String)
|
||||
{
|
||||
dbgln("(STUBBED) OffscreenCanvasRenderingContext2D::set_filter()");
|
||||
}
|
||||
|
||||
float OffscreenCanvasRenderingContext2D::shadow_offset_x() const
|
||||
{
|
||||
return drawing_state().shadow_offset_x;
|
||||
}
|
||||
|
||||
void OffscreenCanvasRenderingContext2D::set_shadow_offset_x(float offsetX)
|
||||
{
|
||||
// https://html.spec.whatwg.org/multipage/canvas.html#dom-context-2d-shadowoffsetx
|
||||
drawing_state().shadow_offset_x = offsetX;
|
||||
}
|
||||
|
||||
float OffscreenCanvasRenderingContext2D::shadow_offset_y() const
|
||||
{
|
||||
return drawing_state().shadow_offset_y;
|
||||
}
|
||||
|
||||
void OffscreenCanvasRenderingContext2D::set_shadow_offset_y(float offsetY)
|
||||
{
|
||||
// https://html.spec.whatwg.org/multipage/canvas.html#dom-context-2d-shadowoffsety
|
||||
drawing_state().shadow_offset_y = offsetY;
|
||||
}
|
||||
|
||||
float OffscreenCanvasRenderingContext2D::shadow_blur() const
|
||||
{
|
||||
return drawing_state().shadow_blur;
|
||||
}
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/canvas.html#dom-context-2d-shadowblur
|
||||
void OffscreenCanvasRenderingContext2D::set_shadow_blur(float blur_radius)
|
||||
{
|
||||
// On setting, the attribute must be set to the new value,
|
||||
// except if the value is negative, infinite or NaN, in which case the new value must be ignored.
|
||||
if (blur_radius < 0 || isinf(blur_radius) || isnan(blur_radius))
|
||||
return;
|
||||
|
||||
drawing_state().shadow_blur = blur_radius;
|
||||
}
|
||||
|
||||
String OffscreenCanvasRenderingContext2D::shadow_color() const
|
||||
{
|
||||
// https://html.spec.whatwg.org/multipage/canvas.html#dom-context-2d-shadowcolor
|
||||
return drawing_state().shadow_color.to_string(Gfx::Color::HTMLCompatibleSerialization::Yes);
|
||||
}
|
||||
|
||||
void OffscreenCanvasRenderingContext2D::set_shadow_color(String color)
|
||||
{
|
||||
// 1. Let context be this's canvas attribute's value, if that is an element; otherwise null.
|
||||
|
||||
// 2. Let parsedValue be the result of parsing the given value with context if non-null.
|
||||
auto style_value = parse_css_value(CSS::Parser::ParsingParams(), color, CSS::PropertyID::Color);
|
||||
if (style_value && style_value->has_color()) {
|
||||
auto parsedValue = style_value->to_color(OptionalNone());
|
||||
|
||||
// 4. Set this's shadow color to parsedValue.
|
||||
drawing_state().shadow_color = parsedValue;
|
||||
} else {
|
||||
// 3. If parsedValue is failure, then return.
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
float OffscreenCanvasRenderingContext2D::global_alpha() const
|
||||
{
|
||||
return drawing_state().global_alpha;
|
||||
}
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/canvas.html#dom-context-2d-globalalpha
|
||||
void OffscreenCanvasRenderingContext2D::set_global_alpha(float alpha)
|
||||
{
|
||||
// 1. If the given value is either infinite, NaN, or not in the range 0.0 to 1.0, then return.
|
||||
if (!isfinite(alpha) || alpha < 0.0f || alpha > 1.0f) {
|
||||
return;
|
||||
}
|
||||
// 2. Otherwise, set this's global alpha to the given value.
|
||||
drawing_state().global_alpha = alpha;
|
||||
}
|
||||
|
||||
String OffscreenCanvasRenderingContext2D::global_composite_operation() const
|
||||
{
|
||||
dbgln("(STUBBED) OffscreenCanvasRenderingContext2D::global_composite_operation()");
|
||||
return String::from_utf8_without_validation(""sv.bytes());
|
||||
}
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/canvas.html#dom-context-2d-globalcompositeoperation
|
||||
void OffscreenCanvasRenderingContext2D::set_global_composite_operation(String)
|
||||
{
|
||||
dbgln("(STUBBED) OffscreenCanvasRenderingContext2D::set_global_composite_operation()");
|
||||
}
|
||||
|
||||
[[nodiscard]] Gfx::Painter* OffscreenCanvasRenderingContext2D::painter()
|
||||
{
|
||||
dbgln("(STUBBED) OffscreenCanvasRenderingContext2D::painter()");
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
}
|
149
Libraries/LibWeb/HTML/OffscreenCanvasRenderingContext2D.h
Normal file
149
Libraries/LibWeb/HTML/OffscreenCanvasRenderingContext2D.h
Normal file
|
@ -0,0 +1,149 @@
|
|||
/*
|
||||
* Copyright (c) 2025, Ladybird contributors
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/String.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/PlatformObject.h>
|
||||
#include <LibWeb/DOM/EventTarget.h>
|
||||
#include <LibWeb/HTML/Canvas/CanvasCompositing.h>
|
||||
#include <LibWeb/HTML/Canvas/CanvasDrawImage.h>
|
||||
#include <LibWeb/HTML/Canvas/CanvasDrawPath.h>
|
||||
#include <LibWeb/HTML/Canvas/CanvasFillStrokeStyles.h>
|
||||
#include <LibWeb/HTML/Canvas/CanvasFilters.h>
|
||||
#include <LibWeb/HTML/Canvas/CanvasImageData.h>
|
||||
#include <LibWeb/HTML/Canvas/CanvasImageSmoothing.h>
|
||||
#include <LibWeb/HTML/Canvas/CanvasPath.h>
|
||||
#include <LibWeb/HTML/Canvas/CanvasPathDrawingStyles.h>
|
||||
#include <LibWeb/HTML/Canvas/CanvasRect.h>
|
||||
#include <LibWeb/HTML/Canvas/CanvasSettings.h>
|
||||
#include <LibWeb/HTML/Canvas/CanvasShadowStyles.h>
|
||||
#include <LibWeb/HTML/Canvas/CanvasState.h>
|
||||
#include <LibWeb/HTML/Canvas/CanvasText.h>
|
||||
#include <LibWeb/HTML/Canvas/CanvasTextDrawingStyles.h>
|
||||
#include <LibWeb/HTML/Canvas/CanvasTransform.h>
|
||||
#include <LibWeb/HTML/CanvasGradient.h>
|
||||
#include <LibWeb/WebIDL/ExceptionOr.h>
|
||||
#include <LibWeb/WebIDL/Types.h>
|
||||
|
||||
namespace Web::HTML {
|
||||
|
||||
class OffscreenCanvasRenderingContext2D : public Bindings::PlatformObject
|
||||
, public CanvasState
|
||||
, public CanvasTransform<OffscreenCanvasRenderingContext2D>
|
||||
, public CanvasFillStrokeStyles<OffscreenCanvasRenderingContext2D>
|
||||
, public CanvasShadowStyles<OffscreenCanvasRenderingContext2D>
|
||||
, public CanvasFilters
|
||||
, public CanvasRect
|
||||
, public CanvasDrawPath
|
||||
, public CanvasText
|
||||
, public CanvasDrawImage
|
||||
, public CanvasImageData
|
||||
, public CanvasImageSmoothing
|
||||
, public CanvasCompositing
|
||||
, public CanvasSettings
|
||||
, public CanvasPathDrawingStyles<OffscreenCanvasRenderingContext2D>
|
||||
, public CanvasTextDrawingStyles<OffscreenCanvasRenderingContext2D, OffscreenCanvas>
|
||||
, public CanvasPath
|
||||
|
||||
{
|
||||
WEB_PLATFORM_OBJECT(OffscreenCanvasRenderingContext2D, Bindings::PlatformObject);
|
||||
GC_DECLARE_ALLOCATOR(OffscreenCanvasRenderingContext2D);
|
||||
|
||||
public:
|
||||
[[nodiscard]] static JS::ThrowCompletionOr<GC::Ref<OffscreenCanvasRenderingContext2D>> create(JS::Realm&, OffscreenCanvas&, JS::Value);
|
||||
virtual ~OffscreenCanvasRenderingContext2D() override;
|
||||
|
||||
GC::Ref<OffscreenCanvas> canvas();
|
||||
|
||||
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;
|
||||
virtual void clear_rect(float x, float y, float width, float height) override;
|
||||
|
||||
virtual WebIDL::ExceptionOr<void> draw_image_internal(CanvasImageSource const&, float source_x, float source_y, float source_width, float source_height, float destination_x, float destination_y, float destination_width, float destination_height) override;
|
||||
|
||||
virtual void begin_path() override;
|
||||
virtual void stroke() override;
|
||||
virtual void stroke(Path2D const& path) override;
|
||||
|
||||
virtual void fill_text(StringView, float x, float y, Optional<double> max_width) override;
|
||||
virtual void stroke_text(StringView, float x, float y, Optional<double> max_width) override;
|
||||
|
||||
virtual void fill(StringView fill_rule) override;
|
||||
virtual void fill(Path2D& path, StringView fill_rule) override;
|
||||
|
||||
virtual WebIDL::ExceptionOr<GC::Ref<ImageData>> create_image_data(int width, int height, Optional<ImageDataSettings> const& settings = {}) const override;
|
||||
virtual WebIDL::ExceptionOr<GC::Ref<ImageData>> create_image_data(ImageData const& image_data) const override;
|
||||
virtual WebIDL::ExceptionOr<GC::Ptr<ImageData>> get_image_data(int x, int y, int width, int height, Optional<ImageDataSettings> const& settings = {}) const override;
|
||||
virtual void put_image_data(ImageData&, float x, float y) override;
|
||||
|
||||
virtual void reset_to_default_state() override;
|
||||
|
||||
virtual CanvasRenderingContext2DSettings get_context_attributes() const override { return m_context_attributes; }
|
||||
|
||||
virtual GC::Ref<TextMetrics> measure_text(StringView text) override;
|
||||
|
||||
virtual void clip(StringView fill_rule) override;
|
||||
virtual void clip(Path2D& path, StringView fill_rule) override;
|
||||
|
||||
virtual bool is_point_in_path(double x, double y, StringView fill_rule) override;
|
||||
virtual bool is_point_in_path(Path2D const& path, double x, double y, StringView fill_rule) override;
|
||||
|
||||
virtual bool image_smoothing_enabled() const override;
|
||||
virtual void set_image_smoothing_enabled(bool) override;
|
||||
virtual Bindings::ImageSmoothingQuality image_smoothing_quality() const override;
|
||||
virtual void set_image_smoothing_quality(Bindings::ImageSmoothingQuality) override;
|
||||
|
||||
virtual float global_alpha() const override;
|
||||
virtual void set_global_alpha(float) override;
|
||||
|
||||
virtual String global_composite_operation() const override;
|
||||
virtual void set_global_composite_operation(String) override;
|
||||
|
||||
virtual String filter() const override;
|
||||
virtual void set_filter(String) override;
|
||||
|
||||
virtual float shadow_offset_x() const override;
|
||||
virtual void set_shadow_offset_x(float) override;
|
||||
virtual float shadow_offset_y() const override;
|
||||
virtual void set_shadow_offset_y(float) override;
|
||||
virtual float shadow_blur() const override;
|
||||
virtual void set_shadow_blur(float) override;
|
||||
virtual String shadow_color() const override;
|
||||
virtual void set_shadow_color(String) override;
|
||||
|
||||
OffscreenCanvas& canvas_element();
|
||||
OffscreenCanvas const& canvas_element() const;
|
||||
|
||||
[[nodiscard]] Gfx::Painter* painter();
|
||||
|
||||
void set_size(Gfx::IntSize const&);
|
||||
|
||||
private:
|
||||
explicit OffscreenCanvasRenderingContext2D(JS::Realm&, OffscreenCanvas&, CanvasRenderingContext2DSettings);
|
||||
|
||||
virtual void initialize(JS::Realm&) override;
|
||||
virtual void visit_edges(Cell::Visitor&) override;
|
||||
|
||||
virtual Gfx::Painter* painter_for_canvas_state() override
|
||||
{
|
||||
dbgln("(STUBBED) OffscreenCanvasRenderingContext2D::painter_for_canvas_state()");
|
||||
return nullptr;
|
||||
}
|
||||
virtual Gfx::Path& path_for_canvas_state() override { return path(); }
|
||||
|
||||
GC::Ref<OffscreenCanvas> m_canvas;
|
||||
Gfx::IntSize m_size;
|
||||
CanvasRenderingContext2DSettings m_context_attributes;
|
||||
};
|
||||
|
||||
}
|
41
Libraries/LibWeb/HTML/OffscreenCanvasRenderingContext2D.idl
Normal file
41
Libraries/LibWeb/HTML/OffscreenCanvasRenderingContext2D.idl
Normal file
|
@ -0,0 +1,41 @@
|
|||
#import <HTML/Canvas/CanvasCompositing.idl>
|
||||
#import <HTML/Canvas/OffscreenCanvasBase.idl>
|
||||
#import <HTML/CanvasRenderingContext2D.idl>
|
||||
#import <HTML/Canvas/CanvasDrawImage.idl>
|
||||
#import <HTML/Canvas/CanvasDrawPath.idl>
|
||||
#import <HTML/Canvas/CanvasFillStrokeStyles.idl>
|
||||
#import <HTML/Canvas/CanvasFilters.idl>
|
||||
#import <HTML/Canvas/CanvasImageData.idl>
|
||||
#import <HTML/Canvas/CanvasImageSmoothing.idl>
|
||||
#import <HTML/Canvas/CanvasPath.idl>
|
||||
#import <HTML/Canvas/CanvasPathDrawingStyles.idl>
|
||||
#import <HTML/Canvas/CanvasTextDrawingStyles.idl>
|
||||
#import <HTML/Canvas/CanvasRect.idl>
|
||||
#import <HTML/Canvas/CanvasShadowStyles.idl>
|
||||
#import <HTML/Canvas/CanvasState.idl>
|
||||
#import <HTML/Canvas/CanvasText.idl>
|
||||
#import <HTML/Canvas/CanvasTransform.idl>
|
||||
#import <HTML/Canvas/CanvasUserInterface.idl>
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/canvas.html#offscreencanvasrenderingcontext2d
|
||||
[Exposed=(Window,Worker)]
|
||||
interface OffscreenCanvasRenderingContext2D {
|
||||
readonly attribute OffscreenCanvas canvas;
|
||||
};
|
||||
|
||||
OffscreenCanvasRenderingContext2D includes CanvasSettings;
|
||||
OffscreenCanvasRenderingContext2D includes CanvasState;
|
||||
OffscreenCanvasRenderingContext2D includes CanvasTransform;
|
||||
OffscreenCanvasRenderingContext2D includes CanvasCompositing;
|
||||
OffscreenCanvasRenderingContext2D includes CanvasImageSmoothing;
|
||||
OffscreenCanvasRenderingContext2D includes CanvasFillStrokeStyles;
|
||||
OffscreenCanvasRenderingContext2D includes CanvasShadowStyles;
|
||||
OffscreenCanvasRenderingContext2D includes CanvasFilters;
|
||||
OffscreenCanvasRenderingContext2D includes CanvasRect;
|
||||
OffscreenCanvasRenderingContext2D includes CanvasDrawPath;
|
||||
OffscreenCanvasRenderingContext2D includes CanvasText;
|
||||
OffscreenCanvasRenderingContext2D includes CanvasDrawImage;
|
||||
OffscreenCanvasRenderingContext2D includes CanvasImageData;
|
||||
OffscreenCanvasRenderingContext2D includes CanvasPathDrawingStyles;
|
||||
OffscreenCanvasRenderingContext2D includes CanvasTextDrawingStyles;
|
||||
OffscreenCanvasRenderingContext2D includes CanvasPath;
|
|
@ -233,6 +233,8 @@ libweb_js_bindings(HTML/NavigationDestination)
|
|||
libweb_js_bindings(HTML/NavigationHistoryEntry)
|
||||
libweb_js_bindings(HTML/NavigationTransition)
|
||||
libweb_js_bindings(HTML/Navigator)
|
||||
libweb_js_bindings(HTML/OffscreenCanvas)
|
||||
libweb_js_bindings(HTML/OffscreenCanvasRenderingContext2D)
|
||||
libweb_js_bindings(HTML/PageTransitionEvent)
|
||||
libweb_js_bindings(HTML/Path2D)
|
||||
libweb_js_bindings(HTML/Plugin)
|
||||
|
|
|
@ -86,6 +86,8 @@ static bool is_platform_object(Type const& type)
|
|||
"NavigationDestination"sv,
|
||||
"NavigationHistoryEntry"sv,
|
||||
"Node"sv,
|
||||
"OffscreenCanvas"sv,
|
||||
"OffscreenCanvasRenderingContext2D"sv,
|
||||
"PasswordCredential"sv,
|
||||
"Path2D"sv,
|
||||
"PerformanceEntry"sv,
|
||||
|
|
|
@ -281,6 +281,8 @@ NodeList
|
|||
Number
|
||||
Object
|
||||
OfflineAudioContext
|
||||
OffscreenCanvas
|
||||
OffscreenCanvasRenderingContext2D
|
||||
Option
|
||||
OscillatorNode
|
||||
PageTransitionEvent
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue