/* * Copyright (c) 2025, Ladybird contributors * * SPDX-License-Identifier: BSD-2-Clause */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace Web::HTML { GC_DEFINE_ALLOCATOR(OffscreenCanvasRenderingContext2D); JS::ThrowCompletionOr> 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(realm, offscreen_canvas, context_attributes); } OffscreenCanvasRenderingContext2D::OffscreenCanvasRenderingContext2D(JS::Realm& realm, OffscreenCanvas& offscreen_canvas, CanvasRenderingContext2DSettings context_attributes) : PlatformObject(realm) , CanvasPath(static_cast(*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(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 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 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) { dbgln("(STUBBED) OffscreenCanvasRenderingContext2D::fill_text()"); } void OffscreenCanvasRenderingContext2D::stroke_text(StringView, float, float, Optional) { 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> OffscreenCanvasRenderingContext2D::create_image_data(int, int, Optional const&) const { return WebIDL::NotSupportedError::create(realm(), "(STUBBED) OffscreenCanvasRenderingContext2D::create_image_data(int, int)"_string); } WebIDL::ExceptionOr> OffscreenCanvasRenderingContext2D::create_image_data(ImageData const&) const { return WebIDL::NotSupportedError::create(realm(), "(STUBBED) OffscreenCanvasRenderingContext2D::create_image_data(ImageData&)"_string); } WebIDL::ExceptionOr> OffscreenCanvasRenderingContext2D::get_image_data(int, int, int, int, Optional 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 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; } // https://html.spec.whatwg.org/multipage/canvas.html#dom-context-2d-shadowoffsetx void OffscreenCanvasRenderingContext2D::set_shadow_offset_x(float offset_x) { // On setting, the attribute being set must be set to the new value, except if the value is infinite or NaN, // in which case the new value must be ignored. if (isinf(offset_x) || isnan(offset_x)) return; drawing_state().shadow_offset_x = offset_x; } float OffscreenCanvasRenderingContext2D::shadow_offset_y() const { return drawing_state().shadow_offset_y; } // https://html.spec.whatwg.org/multipage/canvas.html#dom-context-2d-shadowoffsety void OffscreenCanvasRenderingContext2D::set_shadow_offset_y(float offset_y) { // On setting, the attribute being set must be set to the new value, except if the value is infinite or NaN, // in which case the new value must be ignored. if (isinf(offset_y) || isnan(offset_y)) return; drawing_state().shadow_offset_y = offset_y; } 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({}, {}); // 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; } }