mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-09-03 08:08:43 +00:00
Everywhere: Hoist the Libraries folder to the top-level
This commit is contained in:
parent
950e819ee7
commit
93712b24bf
Notes:
github-actions[bot]
2024-11-10 11:51:52 +00:00
Author: https://github.com/trflynn89
Commit: 93712b24bf
Pull-request: https://github.com/LadybirdBrowser/ladybird/pull/2256
Reviewed-by: https://github.com/sideshowbarker
4547 changed files with 104 additions and 113 deletions
25
Libraries/LibWeb/HTML/Canvas/CanvasCompositing.h
Normal file
25
Libraries/LibWeb/HTML/Canvas/CanvasCompositing.h
Normal file
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
* Copyright (c) 2023, MacDue <macdue@dueutil.tech>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <LibWeb/HTML/ImageData.h>
|
||||
|
||||
namespace Web::HTML {
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/canvas.html#canvascompositing
|
||||
class CanvasCompositing {
|
||||
public:
|
||||
virtual ~CanvasCompositing() = default;
|
||||
|
||||
virtual float global_alpha() const = 0;
|
||||
virtual void set_global_alpha(float) = 0;
|
||||
|
||||
protected:
|
||||
CanvasCompositing() = default;
|
||||
};
|
||||
|
||||
}
|
6
Libraries/LibWeb/HTML/Canvas/CanvasCompositing.idl
Normal file
6
Libraries/LibWeb/HTML/Canvas/CanvasCompositing.idl
Normal file
|
@ -0,0 +1,6 @@
|
|||
// https://html.spec.whatwg.org/multipage/canvas.html#canvascompositing
|
||||
interface mixin CanvasCompositing {
|
||||
// compositing
|
||||
attribute unrestricted double globalAlpha; // (default 1.0)
|
||||
[FIXME] attribute DOMString globalCompositeOperation; // (default "source-over")
|
||||
};
|
92
Libraries/LibWeb/HTML/Canvas/CanvasDrawImage.cpp
Normal file
92
Libraries/LibWeb/HTML/Canvas/CanvasDrawImage.cpp
Normal file
|
@ -0,0 +1,92 @@
|
|||
/*
|
||||
* Copyright (c) 2022, Sam Atkins <atkinssj@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <LibWeb/HTML/Canvas/CanvasDrawImage.h>
|
||||
#include <LibWeb/HTML/ImageBitmap.h>
|
||||
#include <LibWeb/SVG/SVGImageElement.h>
|
||||
|
||||
namespace Web::HTML {
|
||||
|
||||
static void default_source_size(CanvasImageSource const& image, float& source_width, float& source_height)
|
||||
{
|
||||
image.visit(
|
||||
[&source_width, &source_height](JS::Handle<HTMLImageElement> const& source) {
|
||||
if (source->immutable_bitmap()) {
|
||||
source_width = source->immutable_bitmap()->width();
|
||||
source_height = source->immutable_bitmap()->height();
|
||||
} else {
|
||||
// FIXME: This is very janky and not correct.
|
||||
source_width = source->width();
|
||||
source_height = source->height();
|
||||
}
|
||||
},
|
||||
[&source_width, &source_height](JS::Handle<SVG::SVGImageElement> const& source) {
|
||||
if (source->current_image_bitmap()) {
|
||||
source_width = source->current_image_bitmap()->width();
|
||||
source_height = source->current_image_bitmap()->height();
|
||||
} else {
|
||||
// FIXME: This is very janky and not correct.
|
||||
source_width = source->width()->anim_val()->value();
|
||||
source_height = source->height()->anim_val()->value();
|
||||
}
|
||||
},
|
||||
[&source_width, &source_height](JS::Handle<HTML::HTMLVideoElement> const& source) {
|
||||
if (auto const bitmap = source->bitmap(); bitmap) {
|
||||
source_width = bitmap->width();
|
||||
source_height = bitmap->height();
|
||||
} else {
|
||||
source_width = source->video_width();
|
||||
source_height = source->video_height();
|
||||
}
|
||||
},
|
||||
[&source_width, &source_height](JS::Handle<HTMLCanvasElement> const& source) {
|
||||
if (source->surface()) {
|
||||
source_width = source->surface()->size().width();
|
||||
source_height = source->surface()->size().height();
|
||||
} else {
|
||||
source_width = source->width();
|
||||
source_height = source->height();
|
||||
}
|
||||
},
|
||||
[&source_width, &source_height](auto const& source) {
|
||||
if (source->bitmap()) {
|
||||
source_width = source->bitmap()->width();
|
||||
source_height = source->bitmap()->height();
|
||||
} else {
|
||||
source_width = source->width();
|
||||
source_height = source->height();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
WebIDL::ExceptionOr<void> CanvasDrawImage::draw_image(Web::HTML::CanvasImageSource const& image, float destination_x, float destination_y)
|
||||
{
|
||||
// If not specified, the dw and dh arguments must default to the values of sw and sh, interpreted such that one CSS pixel in the image is treated as one unit in the output bitmap's coordinate space.
|
||||
// If the sx, sy, sw, and sh arguments are omitted, then they must default to 0, 0, the image's intrinsic width in image pixels, and the image's intrinsic height in image pixels, respectively.
|
||||
// If the image has no intrinsic dimensions, then the concrete object size must be used instead, as determined using the CSS "Concrete Object Size Resolution" algorithm, with the specified size having
|
||||
// neither a definite width nor height, nor any additional constraints, the object's intrinsic properties being those of the image argument, and the default object size being the size of the output bitmap.
|
||||
float source_width;
|
||||
float source_height;
|
||||
default_source_size(image, source_width, source_height);
|
||||
return draw_image_internal(image, 0, 0, source_width, source_height, destination_x, destination_y, source_width, source_height);
|
||||
}
|
||||
|
||||
WebIDL::ExceptionOr<void> CanvasDrawImage::draw_image(Web::HTML::CanvasImageSource const& image, float destination_x, float destination_y, float destination_width, float destination_height)
|
||||
{
|
||||
// If the sx, sy, sw, and sh arguments are omitted, then they must default to 0, 0, the image's intrinsic width in image pixels, and the image's intrinsic height in image pixels, respectively.
|
||||
// If the image has no intrinsic dimensions, then the concrete object size must be used instead, as determined using the CSS "Concrete Object Size Resolution" algorithm, with the specified size having
|
||||
// neither a definite width nor height, nor any additional constraints, the object's intrinsic properties being those of the image argument, and the default object size being the size of the output bitmap.
|
||||
float source_width;
|
||||
float source_height;
|
||||
default_source_size(image, source_width, source_height);
|
||||
return draw_image_internal(image, 0, 0, source_width, source_height, destination_x, destination_y, destination_width, destination_height);
|
||||
}
|
||||
|
||||
WebIDL::ExceptionOr<void> CanvasDrawImage::draw_image(Web::HTML::CanvasImageSource const& image, float source_x, float source_y, float source_width, float source_height, float destination_x, float destination_y, float destination_width, float destination_height)
|
||||
{
|
||||
return draw_image_internal(image, source_x, source_y, source_width, source_height, destination_x, destination_y, destination_width, destination_height);
|
||||
}
|
||||
}
|
36
Libraries/LibWeb/HTML/Canvas/CanvasDrawImage.h
Normal file
36
Libraries/LibWeb/HTML/Canvas/CanvasDrawImage.h
Normal file
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* Copyright (c) 2022, Sam Atkins <atkinssj@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <LibWeb/Forward.h>
|
||||
#include <LibWeb/HTML/HTMLCanvasElement.h>
|
||||
#include <LibWeb/HTML/HTMLImageElement.h>
|
||||
#include <LibWeb/HTML/HTMLVideoElement.h>
|
||||
#include <LibWeb/WebIDL/ExceptionOr.h>
|
||||
|
||||
namespace Web::HTML {
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/canvas.html#canvasimagesource
|
||||
// NOTE: This is the Variant created by the IDL wrapper generator, and needs to be updated accordingly.
|
||||
using CanvasImageSource = Variant<JS::Handle<HTMLImageElement>, JS::Handle<SVG::SVGImageElement>, JS::Handle<HTMLCanvasElement>, JS::Handle<ImageBitmap>, JS::Handle<HTMLVideoElement>>;
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/canvas.html#canvasdrawimage
|
||||
class CanvasDrawImage {
|
||||
public:
|
||||
virtual ~CanvasDrawImage() = default;
|
||||
|
||||
WebIDL::ExceptionOr<void> draw_image(CanvasImageSource const&, float destination_x, float destination_y);
|
||||
WebIDL::ExceptionOr<void> draw_image(CanvasImageSource const&, float destination_x, float destination_y, float destination_width, float destination_height);
|
||||
WebIDL::ExceptionOr<void> draw_image(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);
|
||||
|
||||
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) = 0;
|
||||
|
||||
protected:
|
||||
CanvasDrawImage() = default;
|
||||
};
|
||||
|
||||
}
|
22
Libraries/LibWeb/HTML/Canvas/CanvasDrawImage.idl
Normal file
22
Libraries/LibWeb/HTML/Canvas/CanvasDrawImage.idl
Normal file
|
@ -0,0 +1,22 @@
|
|||
#import <HTML/HTMLCanvasElement.idl>
|
||||
#import <HTML/HTMLImageElement.idl>
|
||||
#import <HTML/HTMLVideoElement.idl>
|
||||
#import <HTML/ImageBitmap.idl>
|
||||
#import <SVG/SVGImageElement.idl>
|
||||
|
||||
typedef (HTMLImageElement or
|
||||
SVGImageElement or
|
||||
// FIXME: We should use HTMLOrSVGImageElement instead of HTMLImageElement
|
||||
HTMLVideoElement or
|
||||
HTMLCanvasElement or
|
||||
ImageBitmap
|
||||
// FIXME: OffscreenCanvas
|
||||
// FIXME: VideoFrame
|
||||
) CanvasImageSource;
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/canvas.html#canvasdrawimage
|
||||
interface mixin CanvasDrawImage {
|
||||
undefined drawImage(CanvasImageSource image, unrestricted double dx, unrestricted double dy);
|
||||
undefined drawImage(CanvasImageSource image, unrestricted double dx, unrestricted double dy, unrestricted double dw, unrestricted double dh);
|
||||
undefined drawImage(CanvasImageSource image, unrestricted double sx, unrestricted double sy, unrestricted double sw, unrestricted double sh, unrestricted double dx, unrestricted double dy, unrestricted double dw, unrestricted double dh);
|
||||
};
|
37
Libraries/LibWeb/HTML/Canvas/CanvasDrawPath.h
Normal file
37
Libraries/LibWeb/HTML/Canvas/CanvasDrawPath.h
Normal file
|
@ -0,0 +1,37 @@
|
|||
/*
|
||||
* Copyright (c) 2022, Sam Atkins <atkinssj@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/ByteString.h>
|
||||
#include <LibWeb/HTML/Path2D.h>
|
||||
|
||||
namespace Web::HTML {
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/canvas.html#canvasdrawpath
|
||||
class CanvasDrawPath {
|
||||
public:
|
||||
virtual ~CanvasDrawPath() = default;
|
||||
|
||||
virtual void begin_path() = 0;
|
||||
|
||||
virtual void fill(StringView fill_rule) = 0;
|
||||
virtual void fill(Path2D& path, StringView fill_rule) = 0;
|
||||
|
||||
virtual void stroke() = 0;
|
||||
virtual void stroke(Path2D const& path) = 0;
|
||||
|
||||
virtual void clip(StringView fill_rule) = 0;
|
||||
virtual void clip(Path2D& path, StringView fill_rule) = 0;
|
||||
|
||||
virtual bool is_point_in_path(double x, double y, StringView fill_rule) = 0;
|
||||
virtual bool is_point_in_path(Path2D const& path, double x, double y, StringView fill_rule) = 0;
|
||||
|
||||
protected:
|
||||
CanvasDrawPath() = default;
|
||||
};
|
||||
|
||||
}
|
27
Libraries/LibWeb/HTML/Canvas/CanvasDrawPath.idl
Normal file
27
Libraries/LibWeb/HTML/Canvas/CanvasDrawPath.idl
Normal file
|
@ -0,0 +1,27 @@
|
|||
#import <HTML/Path2D.idl>
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/canvas.html#canvasfillrule
|
||||
enum CanvasFillRule { "nonzero", "evenodd" };
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/canvas.html#canvasdrawpath
|
||||
interface mixin CanvasDrawPath {
|
||||
undefined beginPath();
|
||||
// FIXME: `DOMString` should be `CanvasFillRule`
|
||||
undefined fill(optional DOMString fillRule = "nonzero");
|
||||
// FIXME: `DOMString` should be `CanvasFillRule`
|
||||
undefined fill(Path2D path, optional DOMString fillRule = "nonzero");
|
||||
undefined stroke();
|
||||
undefined stroke(Path2D path);
|
||||
|
||||
// FIXME: `DOMString` should be `CanvasFillRule`
|
||||
undefined clip(optional DOMString fillRule = "nonzero");
|
||||
// FIXME: `DOMString` should be `CanvasFillRule`
|
||||
undefined clip(Path2D path, optional DOMString fillRule = "nonzero");
|
||||
|
||||
// FIXME: `DOMString` should be `CanvasFillRule`
|
||||
boolean isPointInPath(unrestricted double x, unrestricted double y, optional DOMString fillRule = "nonzero");
|
||||
// FIXME: `DOMString` should be `CanvasFillRule`
|
||||
boolean isPointInPath(Path2D path, unrestricted double x, unrestricted double y, optional DOMString fillRule = "nonzero");
|
||||
[FIXME] boolean isPointInStroke(unrestricted double x, unrestricted double y);
|
||||
[FIXME] boolean isPointInStroke(Path2D path, unrestricted double x, unrestricted double y);
|
||||
};
|
142
Libraries/LibWeb/HTML/Canvas/CanvasFillStrokeStyles.h
Normal file
142
Libraries/LibWeb/HTML/Canvas/CanvasFillStrokeStyles.h
Normal file
|
@ -0,0 +1,142 @@
|
|||
/*
|
||||
* Copyright (c) 2020-2022, Andreas Kling <andreas@ladybird.org>
|
||||
* Copyright (c) 2021-2022, Linus Groh <linusg@serenityos.org>
|
||||
* Copyright (c) 2022, Sam Atkins <atkinssj@serenityos.org>
|
||||
* Copyright (c) 2023, MacDue <macdue@dueutil.tech>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/String.h>
|
||||
#include <LibWeb/CSS/Parser/Parser.h>
|
||||
#include <LibWeb/HTML/Canvas/CanvasState.h>
|
||||
#include <LibWeb/HTML/CanvasGradient.h>
|
||||
#include <LibWeb/HTML/CanvasPattern.h>
|
||||
|
||||
namespace Web::HTML {
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/canvas.html#canvasfillstrokestyles
|
||||
template<typename IncludingClass>
|
||||
class CanvasFillStrokeStyles {
|
||||
public:
|
||||
~CanvasFillStrokeStyles() = default;
|
||||
using FillOrStrokeStyleVariant = Variant<String, JS::Handle<CanvasGradient>, JS::Handle<CanvasPattern>>;
|
||||
|
||||
void set_fill_style(FillOrStrokeStyleVariant style)
|
||||
{
|
||||
auto& realm = static_cast<IncludingClass&>(*this).realm();
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/canvas.html#dom-context-2d-fillstyle
|
||||
style.visit(
|
||||
// 1. If the given value is a string, then:
|
||||
[&](String const& string) {
|
||||
// 1. Let context be this's canvas attribute's value, if that is an element; otherwise null.
|
||||
auto parser = CSS::Parser::Parser::create(CSS::Parser::ParsingContext(realm), string);
|
||||
|
||||
// 2. Let parsedValue be the result of parsing the given value with context if non-null.
|
||||
// FIXME: Parse a color value
|
||||
// https://drafts.csswg.org/css-color/#parse-a-css-color-value
|
||||
auto style_value = parser.parse_as_css_value(CSS::PropertyID::Color);
|
||||
if (style_value && style_value->has_color()) {
|
||||
auto parsedValue = style_value->to_color(OptionalNone());
|
||||
|
||||
// 4. Set this's fill style to parsedValue.
|
||||
my_drawing_state().fill_style = parsedValue;
|
||||
} else {
|
||||
// 3. If parsedValue is failure, then return.
|
||||
return;
|
||||
}
|
||||
|
||||
// 5. Return.
|
||||
return;
|
||||
},
|
||||
[&](auto fill_or_stroke_style) {
|
||||
// FIXME: 2. If the given value is a CanvasPattern object that is marked as not origin-clean, then set this's origin-clean flag to false.
|
||||
|
||||
// 3. Set this's fill style to the given value.
|
||||
my_drawing_state().fill_style = fill_or_stroke_style;
|
||||
});
|
||||
}
|
||||
|
||||
FillOrStrokeStyleVariant fill_style() const
|
||||
{
|
||||
return my_drawing_state().fill_style.to_js_fill_or_stroke_style();
|
||||
}
|
||||
|
||||
void set_stroke_style(FillOrStrokeStyleVariant style)
|
||||
{
|
||||
auto& realm = static_cast<IncludingClass&>(*this).realm();
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/canvas.html#dom-context-2d-strokestyle
|
||||
|
||||
style.visit(
|
||||
// 1. If the given value is a string, then:
|
||||
[&](String const& string) {
|
||||
// 1. Let context be this's canvas attribute's value, if that is an element; otherwise null.
|
||||
auto parser = CSS::Parser::Parser::create(CSS::Parser::ParsingContext(realm), string);
|
||||
|
||||
// 2. Let parsedValue be the result of parsing the given value with context if non-null.
|
||||
// FIXME: Parse a color value
|
||||
// https://drafts.csswg.org/css-color/#parse-a-css-color-value
|
||||
auto style_value = parser.parse_as_css_value(CSS::PropertyID::Color);
|
||||
if (style_value && style_value->has_color()) {
|
||||
auto parsedValue = style_value->to_color(OptionalNone());
|
||||
|
||||
// 4. Set this's stroke style to parsedValue.
|
||||
my_drawing_state().stroke_style = parsedValue;
|
||||
} else {
|
||||
// 3. If parsedValue is failure, then return.
|
||||
return;
|
||||
}
|
||||
|
||||
// 5. Return.
|
||||
return;
|
||||
},
|
||||
[&](auto fill_or_stroke_style) {
|
||||
// FIXME: 2. If the given value is a CanvasPattern object that is marked as not origin-clean, then set this's origin-clean flag to false.
|
||||
|
||||
// 3. Set this's stroke style to the given value.
|
||||
my_drawing_state().fill_style = fill_or_stroke_style;
|
||||
});
|
||||
}
|
||||
|
||||
FillOrStrokeStyleVariant stroke_style() const
|
||||
{
|
||||
return my_drawing_state().stroke_style.to_js_fill_or_stroke_style();
|
||||
}
|
||||
|
||||
WebIDL::ExceptionOr<JS::NonnullGCPtr<CanvasGradient>> create_radial_gradient(double x0, double y0, double r0, double x1, double y1, double r1)
|
||||
{
|
||||
auto& realm = static_cast<IncludingClass&>(*this).realm();
|
||||
return CanvasGradient::create_radial(realm, x0, y0, r0, x1, y1, r1);
|
||||
}
|
||||
|
||||
JS::NonnullGCPtr<CanvasGradient> create_linear_gradient(double x0, double y0, double x1, double y1)
|
||||
{
|
||||
auto& realm = static_cast<IncludingClass&>(*this).realm();
|
||||
return CanvasGradient::create_linear(realm, x0, y0, x1, y1).release_value_but_fixme_should_propagate_errors();
|
||||
}
|
||||
|
||||
JS::NonnullGCPtr<CanvasGradient> create_conic_gradient(double start_angle, double x, double y)
|
||||
{
|
||||
auto& realm = static_cast<IncludingClass&>(*this).realm();
|
||||
return CanvasGradient::create_conic(realm, start_angle, x, y).release_value_but_fixme_should_propagate_errors();
|
||||
}
|
||||
|
||||
WebIDL::ExceptionOr<JS::GCPtr<CanvasPattern>> create_pattern(CanvasImageSource const& image, StringView repetition)
|
||||
{
|
||||
auto& realm = static_cast<IncludingClass&>(*this).realm();
|
||||
return CanvasPattern::create(realm, image, repetition);
|
||||
}
|
||||
|
||||
protected:
|
||||
CanvasFillStrokeStyles() = default;
|
||||
|
||||
private:
|
||||
CanvasState::DrawingState& my_drawing_state() { return reinterpret_cast<IncludingClass&>(*this).drawing_state(); }
|
||||
CanvasState::DrawingState const& my_drawing_state() const { return reinterpret_cast<IncludingClass const&>(*this).drawing_state(); }
|
||||
};
|
||||
|
||||
}
|
14
Libraries/LibWeb/HTML/Canvas/CanvasFillStrokeStyles.idl
Normal file
14
Libraries/LibWeb/HTML/Canvas/CanvasFillStrokeStyles.idl
Normal file
|
@ -0,0 +1,14 @@
|
|||
#import <HTML/CanvasGradient.idl>
|
||||
#import <HTML/CanvasPattern.idl>
|
||||
#import <HTML/HTMLCanvasElement.idl>
|
||||
#import <HTML/HTMLImageElement.idl>
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/canvas.html#canvasfillstrokestyles
|
||||
interface mixin CanvasFillStrokeStyles {
|
||||
attribute (DOMString or CanvasGradient or CanvasPattern) strokeStyle;
|
||||
attribute (DOMString or CanvasGradient or CanvasPattern) fillStyle;
|
||||
CanvasGradient createLinearGradient(double x0, double y0, double x1, double y1);
|
||||
CanvasGradient createRadialGradient(double x0, double y0, double r0, double x1, double y1, double r1);
|
||||
CanvasGradient createConicGradient(double startAngle, double x, double y);
|
||||
CanvasPattern? createPattern(CanvasImageSource image, [LegacyNullToEmptyString] DOMString repetition);
|
||||
};
|
5
Libraries/LibWeb/HTML/Canvas/CanvasFilters.idl
Normal file
5
Libraries/LibWeb/HTML/Canvas/CanvasFilters.idl
Normal file
|
@ -0,0 +1,5 @@
|
|||
// https://html.spec.whatwg.org/multipage/canvas.html#canvasfilters
|
||||
interface mixin CanvasFilters {
|
||||
// filters
|
||||
[FIXME] attribute DOMString filter; // (default "none")
|
||||
};
|
27
Libraries/LibWeb/HTML/Canvas/CanvasImageData.h
Normal file
27
Libraries/LibWeb/HTML/Canvas/CanvasImageData.h
Normal file
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
* Copyright (c) 2022, Sam Atkins <atkinssj@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <LibWeb/HTML/ImageData.h>
|
||||
|
||||
namespace Web::HTML {
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/canvas.html#canvasimagedata
|
||||
class CanvasImageData {
|
||||
public:
|
||||
virtual ~CanvasImageData() = default;
|
||||
|
||||
virtual WebIDL::ExceptionOr<JS::NonnullGCPtr<ImageData>> create_image_data(int width, int height, Optional<ImageDataSettings> const& settings = {}) const = 0;
|
||||
virtual WebIDL::ExceptionOr<JS::NonnullGCPtr<ImageData>> create_image_data(ImageData const&) const = 0;
|
||||
virtual WebIDL::ExceptionOr<JS::GCPtr<ImageData>> get_image_data(int x, int y, int width, int height, Optional<ImageDataSettings> const& settings = {}) const = 0;
|
||||
virtual void put_image_data(ImageData const&, float x, float y) = 0;
|
||||
|
||||
protected:
|
||||
CanvasImageData() = default;
|
||||
};
|
||||
|
||||
}
|
12
Libraries/LibWeb/HTML/Canvas/CanvasImageData.idl
Normal file
12
Libraries/LibWeb/HTML/Canvas/CanvasImageData.idl
Normal file
|
@ -0,0 +1,12 @@
|
|||
#import <HTML/ImageData.idl>
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/canvas.html#canvasimagedata
|
||||
interface mixin CanvasImageData {
|
||||
ImageData createImageData([EnforceRange] long sw, [EnforceRange] long sh, optional ImageDataSettings settings = {});
|
||||
ImageData createImageData(ImageData imagedata);
|
||||
|
||||
ImageData getImageData([EnforceRange] long sx, [EnforceRange] long sy, [EnforceRange] long sw, [EnforceRange] long sh, optional ImageDataSettings settings = {});
|
||||
|
||||
undefined putImageData(ImageData imagedata, [EnforceRange] long dx, [EnforceRange] long dy);
|
||||
[FIXME] undefined putImageData(ImageData imagedata, [EnforceRange] long dx, [EnforceRange] long dy, [EnforceRange] long dirtyX, [EnforceRange] long dirtyY, [EnforceRange] long dirtyWidth, [EnforceRange] long dirtyHeight);
|
||||
};
|
27
Libraries/LibWeb/HTML/Canvas/CanvasImageSmoothing.h
Normal file
27
Libraries/LibWeb/HTML/Canvas/CanvasImageSmoothing.h
Normal file
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
* Copyright (c) 2023, Andreas Kling <andreas@ladybird.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <LibWeb/HTML/ImageData.h>
|
||||
|
||||
namespace Web::HTML {
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/canvas.html#canvasimagesmoothing
|
||||
class CanvasImageSmoothing {
|
||||
public:
|
||||
virtual ~CanvasImageSmoothing() = default;
|
||||
|
||||
virtual bool image_smoothing_enabled() const = 0;
|
||||
virtual void set_image_smoothing_enabled(bool) = 0;
|
||||
virtual Bindings::ImageSmoothingQuality image_smoothing_quality() const = 0;
|
||||
virtual void set_image_smoothing_quality(Bindings::ImageSmoothingQuality) = 0;
|
||||
|
||||
protected:
|
||||
CanvasImageSmoothing() = default;
|
||||
};
|
||||
|
||||
}
|
8
Libraries/LibWeb/HTML/Canvas/CanvasImageSmoothing.idl
Normal file
8
Libraries/LibWeb/HTML/Canvas/CanvasImageSmoothing.idl
Normal file
|
@ -0,0 +1,8 @@
|
|||
#import <HTML/CanvasRenderingContext2D.idl>
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/canvas.html#canvasimagesmoothing
|
||||
interface mixin CanvasImageSmoothing {
|
||||
// image smoothing
|
||||
attribute boolean imageSmoothingEnabled; // (default true)
|
||||
attribute ImageSmoothingQuality imageSmoothingQuality; // (default low)
|
||||
};
|
441
Libraries/LibWeb/HTML/Canvas/CanvasPath.cpp
Normal file
441
Libraries/LibWeb/HTML/Canvas/CanvasPath.cpp
Normal file
|
@ -0,0 +1,441 @@
|
|||
/*
|
||||
* Copyright (c) 2020-2022, Andreas Kling <andreas@ladybird.org>
|
||||
* Copyright (c) 2022, Sam Atkins <atkinssj@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <LibGfx/Vector2.h>
|
||||
#include <LibWeb/HTML/Canvas/CanvasPath.h>
|
||||
|
||||
namespace Web::HTML {
|
||||
|
||||
Gfx::AffineTransform CanvasPath::active_transform() const
|
||||
{
|
||||
if (m_canvas_state.has_value())
|
||||
return m_canvas_state->drawing_state().transform;
|
||||
return {};
|
||||
}
|
||||
|
||||
void CanvasPath::ensure_subpath(float x, float y)
|
||||
{
|
||||
if (m_path.is_empty())
|
||||
m_path.move_to(Gfx::FloatPoint { x, y });
|
||||
}
|
||||
|
||||
void CanvasPath::close_path()
|
||||
{
|
||||
m_path.close();
|
||||
}
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/canvas.html#dom-context-2d-moveto
|
||||
void CanvasPath::move_to(float x, float y)
|
||||
{
|
||||
// 1. If either of the arguments are infinite or NaN, then return.
|
||||
if (!isfinite(x) || !isfinite(y))
|
||||
return;
|
||||
|
||||
// 2. Create a new subpath with the specified point as its first (and only) point.
|
||||
m_path.move_to(Gfx::FloatPoint { x, y });
|
||||
}
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/canvas.html#dom-context-2d-lineto
|
||||
void CanvasPath::line_to(float x, float y)
|
||||
{
|
||||
// 1. If either of the arguments are infinite or NaN, then return.
|
||||
if (!isfinite(x) || !isfinite(y))
|
||||
return;
|
||||
|
||||
if (m_path.is_empty()) {
|
||||
// 2. If the object's path has no subpaths, then ensure there is a subpath for (x, y).
|
||||
ensure_subpath(x, y);
|
||||
} else {
|
||||
// 3. Otherwise, connect the last point in the subpath to the given point (x, y) using a straight line,
|
||||
// and then add the given point (x, y) to the subpath.
|
||||
m_path.line_to(Gfx::FloatPoint { x, y });
|
||||
}
|
||||
}
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/canvas.html#dom-context-2d-quadraticcurveto
|
||||
void CanvasPath::quadratic_curve_to(float cpx, float cpy, float x, float y)
|
||||
{
|
||||
// 1. If any of the arguments are infinite or NaN, then return.
|
||||
if (!isfinite(cpx) || !isfinite(cpy) || !isfinite(x) || !isfinite(y))
|
||||
return;
|
||||
|
||||
// 2. Ensure there is a subpath for (cpx, cpy)
|
||||
ensure_subpath(cpx, cpy);
|
||||
|
||||
// 3. Connect the last point in the subpath to the given point (x, y) using a quadratic Bézier curve with control point (cpx, cpy).
|
||||
// 4. Add the given point (x, y) to the subpath.
|
||||
m_path.quadratic_bezier_curve_to(Gfx::FloatPoint { cpx, cpy }, Gfx::FloatPoint { x, y });
|
||||
}
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/canvas.html#dom-context-2d-beziercurveto
|
||||
void CanvasPath::bezier_curve_to(double cp1x, double cp1y, double cp2x, double cp2y, double x, double y)
|
||||
{
|
||||
// 1. If any of the arguments are infinite or NaN, then return.
|
||||
if (!isfinite(cp1x) || !isfinite(cp1y) || !isfinite(cp2x) || !isfinite(cp2y) || !isfinite(x) || !isfinite(y))
|
||||
return;
|
||||
|
||||
// 2. Ensure there is a subpath for (cp1x, cp1y)
|
||||
ensure_subpath(cp1x, cp1y);
|
||||
|
||||
// 3. Connect the last point in the subpath to the given point (x, y) using a cubic Bézier curve with control poits (cp1x, cp1y) and (cp2x, cp2y).
|
||||
// 4. Add the point (x, y) to the subpath.
|
||||
m_path.cubic_bezier_curve_to(
|
||||
Gfx::FloatPoint { cp1x, cp1y }, Gfx::FloatPoint { cp2x, cp2y }, Gfx::FloatPoint { x, y });
|
||||
}
|
||||
|
||||
WebIDL::ExceptionOr<void> CanvasPath::arc(float x, float y, float radius, float start_angle, float end_angle, bool counter_clockwise)
|
||||
{
|
||||
if (radius < 0)
|
||||
return WebIDL::IndexSizeError::create(m_self->realm(), MUST(String::formatted("The radius provided ({}) is negative.", radius)));
|
||||
return ellipse(x, y, radius, radius, 0, start_angle, end_angle, counter_clockwise);
|
||||
}
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/canvas.html#dom-context-2d-ellipse
|
||||
WebIDL::ExceptionOr<void> CanvasPath::ellipse(float x, float y, float radius_x, float radius_y, float rotation, float start_angle, float end_angle, bool counter_clockwise)
|
||||
{
|
||||
// 1. If any of the arguments are infinite or NaN, then return.
|
||||
if (!isfinite(x) || !isfinite(y) || !isfinite(radius_x) || !isfinite(radius_y) || !isfinite(rotation) || !isfinite(start_angle) || !isfinite(end_angle))
|
||||
return {};
|
||||
|
||||
// 2. If either radiusX or radiusY are negative, then throw an "IndexSizeError" DOMException.
|
||||
if (radius_x < 0)
|
||||
return WebIDL::IndexSizeError::create(m_self->realm(), MUST(String::formatted("The major-axis radius provided ({}) is negative.", radius_x)));
|
||||
if (radius_y < 0)
|
||||
return WebIDL::IndexSizeError::create(m_self->realm(), MUST(String::formatted("The minor-axis radius provided ({}) is negative.", radius_y)));
|
||||
|
||||
// "If counterclockwise is false and endAngle − startAngle is greater than or equal to 2π,
|
||||
// or, if counterclockwise is true and startAngle − endAngle is greater than or equal to 2π,
|
||||
// then the arc is the whole circumference of this ellipse"
|
||||
// Also draw the full ellipse if making a non-zero whole number of turns.
|
||||
if (constexpr float tau = M_PI * 2; (!counter_clockwise && (end_angle - start_angle) >= tau)
|
||||
|| (counter_clockwise && (start_angle - end_angle) >= tau)
|
||||
|| (start_angle != end_angle && fmodf(start_angle - end_angle, tau) == 0)) {
|
||||
start_angle = 0;
|
||||
// FIXME: elliptical_arc_to() incorrectly handles the case where the start/end points are very close.
|
||||
// So we slightly fudge the numbers here to correct for that.
|
||||
end_angle = tau * 0.9999f;
|
||||
counter_clockwise = false;
|
||||
} else {
|
||||
start_angle = fmodf(start_angle, tau);
|
||||
end_angle = fmodf(end_angle, tau);
|
||||
}
|
||||
|
||||
// Then, figure out where the ends of the arc are.
|
||||
// To do so, we can pretend that the center of this ellipse is at (0, 0),
|
||||
// and the whole coordinate system is rotated `rotation` radians around the x axis, centered on `center`.
|
||||
// The sign of the resulting relative positions is just whether our angle is on one of the left quadrants.
|
||||
float sin_rotation;
|
||||
float cos_rotation;
|
||||
AK::sincos(rotation, sin_rotation, cos_rotation);
|
||||
|
||||
auto resolve_point_with_angle = [&](float angle) {
|
||||
auto tan_relative = tanf(angle);
|
||||
auto tan2 = tan_relative * tan_relative;
|
||||
|
||||
auto ab = radius_x * radius_y;
|
||||
auto a2 = radius_x * radius_x;
|
||||
auto b2 = radius_y * radius_y;
|
||||
auto sqrt = sqrtf(b2 + a2 * tan2);
|
||||
|
||||
auto relative_x_position = ab / sqrt;
|
||||
auto relative_y_position = ab * tan_relative / sqrt;
|
||||
|
||||
// Make sure to set the correct sign
|
||||
// -1 if 0 ≤ θ < 90° or 270°< θ ≤ 360°
|
||||
// 1 if 90° < θ< 270°
|
||||
float sn = cosf(angle) >= 0 ? 1 : -1;
|
||||
relative_x_position *= sn;
|
||||
relative_y_position *= sn;
|
||||
|
||||
// Now rotate it (back) around the center point by 'rotation' radians, then move it back to our actual origin.
|
||||
auto relative_rotated_x_position = relative_x_position * cos_rotation - relative_y_position * sin_rotation;
|
||||
auto relative_rotated_y_position = relative_x_position * sin_rotation + relative_y_position * cos_rotation;
|
||||
return Gfx::FloatPoint { relative_rotated_x_position + x, relative_rotated_y_position + y };
|
||||
};
|
||||
|
||||
auto start_point = resolve_point_with_angle(start_angle);
|
||||
auto end_point = resolve_point_with_angle(end_angle);
|
||||
|
||||
float delta_theta;
|
||||
if (counter_clockwise) {
|
||||
delta_theta = start_angle - end_angle;
|
||||
} else {
|
||||
delta_theta = end_angle - start_angle;
|
||||
}
|
||||
|
||||
if (delta_theta < 0)
|
||||
delta_theta += AK::Pi<float> * 2;
|
||||
|
||||
// 3. If canvasPath's path has any subpaths, then add a straight line from the last point in the subpath to the start point of the arc.
|
||||
if (!m_path.is_empty())
|
||||
m_path.line_to(start_point);
|
||||
else
|
||||
m_path.move_to(start_point);
|
||||
|
||||
// 4. Add the start and end points of the arc to the subpath, and connect them with an arc.
|
||||
m_path.elliptical_arc_to(
|
||||
Gfx::FloatPoint { end_point },
|
||||
Gfx::FloatSize { radius_x, radius_y },
|
||||
rotation,
|
||||
delta_theta > AK::Pi<float>, !counter_clockwise);
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/canvas.html#dom-context-2d-arcto
|
||||
WebIDL::ExceptionOr<void> CanvasPath::arc_to(double x1, double y1, double x2, double y2, double radius)
|
||||
{
|
||||
// 1. If any of the arguments are infinite or NaN, then return.
|
||||
if (!isfinite(x1) || !isfinite(y1) || !isfinite(x2) || !isfinite(y2) || !isfinite(radius))
|
||||
return {};
|
||||
|
||||
// 2. Ensure there is a subpath for (x1, y1).
|
||||
ensure_subpath(x1, y1);
|
||||
|
||||
// 3. If radius is negative, then throw an "IndexSizeError" DOMException.
|
||||
if (radius < 0)
|
||||
return WebIDL::IndexSizeError::create(m_self->realm(), MUST(String::formatted("The radius provided ({}) is negative.", radius)));
|
||||
|
||||
auto transform = active_transform();
|
||||
|
||||
// 4. Let the point (x0, y0) be the last point in the subpath,
|
||||
// transformed by the inverse of the current transformation matrix
|
||||
// (so that it is in the same coordinate system as the points passed to the method).
|
||||
// Point (x0, y0)
|
||||
auto p0 = transform.inverse().value_or(Gfx::AffineTransform()).map(m_path.last_point());
|
||||
// Point (x1, y1)
|
||||
auto p1 = Gfx::FloatPoint { x1, y1 };
|
||||
// Point (x2, y2)
|
||||
auto p2 = Gfx::FloatPoint { x2, y2 };
|
||||
|
||||
// 5. If the point (x0, y0) is equal to the point (x1, y1),
|
||||
// or if the point (x1, y1) is equal to the point (x2, y2),
|
||||
// or if radius is zero, then add the point (x1, y1) to the subpath,
|
||||
// and connect that point to the previous point (x0, y0) by a straight line.
|
||||
if (p0 == p1 || p1 == p2 || radius == 0) {
|
||||
m_path.line_to(p1);
|
||||
return {};
|
||||
}
|
||||
|
||||
auto v1 = Gfx::FloatVector2 { p0.x() - p1.x(), p0.y() - p1.y() };
|
||||
auto v2 = Gfx::FloatVector2 { p2.x() - p1.x(), p2.y() - p1.y() };
|
||||
auto cos_theta = v1.dot(v2) / (v1.length() * v2.length());
|
||||
// 6. Otherwise, if the points (x0, y0), (x1, y1), and (x2, y2) all lie on a single straight line,
|
||||
// then add the point (x1, y1) to the subpath,
|
||||
// and connect that point to the previous point (x0, y0) by a straight line.
|
||||
if (-1 == cos_theta || 1 == cos_theta) {
|
||||
m_path.line_to(p1);
|
||||
return {};
|
||||
}
|
||||
|
||||
// 7. Otherwise, let The Arc be the shortest arc given by circumference of the circle that has radius radius,
|
||||
// and that has one point tangent to the half-infinite line that crosses the point (x0, y0) and ends at the point (x1, y1),
|
||||
// and that has a different point tangent to the half-infinite line that ends at the point (x1, y1) and crosses the point (x2, y2).
|
||||
// The points at which this circle touches these two lines are called the start and end tangent points respectively.
|
||||
auto adjacent = radius / static_cast<double>(tan(acos(cos_theta) / 2));
|
||||
auto factor1 = adjacent / static_cast<double>(v1.length());
|
||||
auto x3 = static_cast<double>(p1.x()) + factor1 * static_cast<double>(p0.x() - p1.x());
|
||||
auto y3 = static_cast<double>(p1.y()) + factor1 * static_cast<double>(p0.y() - p1.y());
|
||||
auto start_tangent = Gfx::FloatPoint { x3, y3 };
|
||||
|
||||
auto factor2 = adjacent / static_cast<double>(v2.length());
|
||||
auto x4 = static_cast<double>(p1.x()) + factor2 * static_cast<double>(p2.x() - p1.x());
|
||||
auto y4 = static_cast<double>(p1.y()) + factor2 * static_cast<double>(p2.y() - p1.y());
|
||||
auto end_tangent = Gfx::FloatPoint { x4, y4 };
|
||||
|
||||
// Connect the point (x0, y0) to the start tangent point by a straight line, adding the start tangent point to the subpath.
|
||||
m_path.line_to(start_tangent);
|
||||
|
||||
bool const large_arc = false; // always small since tangent points define arc endpoints and lines meet at (x1, y1)
|
||||
auto cross_product = v1.x() * v2.y() - v1.y() * v2.x();
|
||||
bool const sweep = cross_product < 0; // right-hand rule, true means clockwise
|
||||
|
||||
// and then connect the start tangent point to the end tangent point by The Arc, adding the end tangent point to the subpath.
|
||||
m_path.arc_to(end_tangent, radius, large_arc, sweep);
|
||||
return {};
|
||||
}
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/canvas.html#dom-context-2d-rect
|
||||
void CanvasPath::rect(double x, double y, double w, double h)
|
||||
{
|
||||
// 1. If any of the arguments are infinite or NaN, then return.
|
||||
if (!isfinite(x) || !isfinite(y) || !isfinite(w) || !isfinite(h))
|
||||
return;
|
||||
|
||||
// 2. Create a new subpath containing just the four points (x, y), (x+w, y), (x+w, y+h), (x, y+h), in that order, with those four points connected by straight lines.
|
||||
m_path.move_to(Gfx::FloatPoint { x, y });
|
||||
m_path.line_to(Gfx::FloatPoint { x + w, y });
|
||||
m_path.line_to(Gfx::FloatPoint { x + w, y + h });
|
||||
m_path.line_to(Gfx::FloatPoint { x, y + h });
|
||||
|
||||
// 3. Mark the subpath as closed.
|
||||
m_path.close();
|
||||
|
||||
// 4. Create a new subpath with the point (x, y) as the only point in the subpath.
|
||||
m_path.move_to(Gfx::FloatPoint { x, y });
|
||||
}
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/canvas.html#dom-context-2d-roundrect
|
||||
WebIDL::ExceptionOr<void> CanvasPath::round_rect(double x, double y, double w, double h, Variant<double, Geometry::DOMPointInit, Vector<Variant<double, Geometry::DOMPointInit>>> radii)
|
||||
{
|
||||
using Radius = Variant<double, Geometry::DOMPointInit>;
|
||||
|
||||
// 1. If any of x, y, w, or h are infinite or NaN, then return.
|
||||
if (!isfinite(x) || !isfinite(y) || !isfinite(w) || !isfinite(h))
|
||||
return {};
|
||||
|
||||
// 2. If radii is an unrestricted double or DOMPointInit, then set radii to « radii ».
|
||||
if (radii.has<double>() || radii.has<Geometry::DOMPointInit>()) {
|
||||
Vector<Radius> radii_list;
|
||||
if (radii.has<double>())
|
||||
radii_list.append(radii.get<double>());
|
||||
else
|
||||
radii_list.append(radii.get<Geometry::DOMPointInit>());
|
||||
radii = radii_list;
|
||||
}
|
||||
|
||||
// 3. If radii is not a list of size one, two, three, or four, then throw a RangeError.
|
||||
if (radii.get<Vector<Radius>>().is_empty() || radii.get<Vector<Radius>>().size() > 4)
|
||||
return WebIDL::SimpleException { WebIDL::SimpleExceptionType::RangeError, "roundRect: Can have between 1 and 4 radii"sv };
|
||||
|
||||
// 4. Let normalizedRadii be an empty list.
|
||||
Vector<Geometry::DOMPointInit> normalized_radii;
|
||||
|
||||
// 5. For each radius of radii:
|
||||
for (auto const& radius : radii.get<Vector<Radius>>()) {
|
||||
// 5.1. If radius is a DOMPointInit:
|
||||
if (radius.has<Geometry::DOMPointInit>()) {
|
||||
auto const& radius_as_dom_point = radius.get<Geometry::DOMPointInit>();
|
||||
|
||||
// 5.1.1. If radius["x"] or radius["y"] is infinite or NaN, then return.
|
||||
if (!isfinite(radius_as_dom_point.x) || !isfinite(radius_as_dom_point.y))
|
||||
return {};
|
||||
|
||||
// 5.1.2. If radius["x"] or radius["y"] is negative, then throw a RangeError.
|
||||
if (radius_as_dom_point.x < 0 || radius_as_dom_point.y < 0)
|
||||
return WebIDL::SimpleException { WebIDL::SimpleExceptionType::RangeError, "roundRect: Radius can't be negative"sv };
|
||||
|
||||
// 5.1.3. Otherwise, append radius to normalizedRadii.
|
||||
normalized_radii.append(radius_as_dom_point);
|
||||
}
|
||||
|
||||
// 5.2. If radius is a unrestricted double:
|
||||
if (radius.has<double>()) {
|
||||
auto radius_as_double = radius.get<double>();
|
||||
|
||||
// 5.2.1. If radius is infinite or NaN, then return.
|
||||
if (!isfinite(radius_as_double))
|
||||
return {};
|
||||
|
||||
// 5.2.2. If radius is negative, then throw a RangeError.
|
||||
if (radius_as_double < 0)
|
||||
return WebIDL::SimpleException { WebIDL::SimpleExceptionType::RangeError, "roundRect: Radius can't be negative"sv };
|
||||
|
||||
// 5.2.3. Otherwise append «[ "x" → radius, "y" → radius ]» to normalizedRadii.
|
||||
normalized_radii.append(Geometry::DOMPointInit { radius_as_double, radius_as_double });
|
||||
}
|
||||
}
|
||||
|
||||
// 6. Let upperLeft, upperRight, lowerRight, and lowerLeft be null.
|
||||
Geometry::DOMPointInit upper_left {};
|
||||
Geometry::DOMPointInit upper_right {};
|
||||
Geometry::DOMPointInit lower_right {};
|
||||
Geometry::DOMPointInit lower_left {};
|
||||
|
||||
// 7. If normalizedRadii's size is 4, then set upperLeft to normalizedRadii[0], set upperRight to normalizedRadii[1], set lowerRight to normalizedRadii[2], and set lowerLeft to normalizedRadii[3].
|
||||
if (normalized_radii.size() == 4) {
|
||||
upper_left = normalized_radii.at(0);
|
||||
upper_right = normalized_radii.at(1);
|
||||
lower_right = normalized_radii.at(2);
|
||||
lower_left = normalized_radii.at(3);
|
||||
}
|
||||
|
||||
// 8. If normalizedRadii's size is 3, then set upperLeft to normalizedRadii[0], set upperRight and lowerLeft to normalizedRadii[1], and set lowerRight to normalizedRadii[2].
|
||||
if (normalized_radii.size() == 3) {
|
||||
upper_left = normalized_radii.at(0);
|
||||
upper_right = lower_left = normalized_radii.at(1);
|
||||
lower_right = normalized_radii.at(2);
|
||||
}
|
||||
|
||||
// 9. If normalizedRadii's size is 2, then set upperLeft and lowerRight to normalizedRadii[0] and set upperRight and lowerLeft to normalizedRadii[1].
|
||||
if (normalized_radii.size() == 2) {
|
||||
upper_left = lower_right = normalized_radii.at(0);
|
||||
upper_right = lower_left = normalized_radii.at(1);
|
||||
}
|
||||
|
||||
// 10. If normalizedRadii's size is 1, then set upperLeft, upperRight, lowerRight, and lowerLeft to normalizedRadii[0].
|
||||
if (normalized_radii.size() == 1)
|
||||
upper_left = upper_right = lower_right = lower_left = normalized_radii.at(0);
|
||||
|
||||
// 11. Corner curves must not overlap. Scale all radii to prevent this:
|
||||
// 11.1. Let top be upperLeft["x"] + upperRight["x"].
|
||||
double top = upper_left.x + upper_right.x;
|
||||
|
||||
// 11.2. Let right be upperRight["y"] + lowerRight["y"].
|
||||
double right = upper_right.y + lower_right.y;
|
||||
|
||||
// 11.3. Let bottom be lowerRight["x"] + lowerLeft["x"].
|
||||
double bottom = lower_right.x + lower_left.x;
|
||||
|
||||
// 11.4. Let left be upperLeft["y"] + lowerLeft["y"].
|
||||
double left = upper_left.y + lower_left.y;
|
||||
|
||||
// 11.5. Let scale be the minimum value of the ratios w / top, h / right, w / bottom, h / left.
|
||||
double scale = AK::min(AK::min(w / top, h / right), AK::min(w / bottom, h / left));
|
||||
|
||||
// 11.6. If scale is less than 1, then set the x and y members of upperLeft, upperRight, lowerLeft, and lowerRight to their current values multiplied by scale.
|
||||
if (scale < 1) {
|
||||
upper_left.x *= scale;
|
||||
upper_left.y *= scale;
|
||||
upper_right.x *= scale;
|
||||
upper_right.y *= scale;
|
||||
lower_left.x *= scale;
|
||||
lower_left.y *= scale;
|
||||
lower_right.x *= scale;
|
||||
lower_right.y *= scale;
|
||||
}
|
||||
|
||||
// 12. Create a new subpath:
|
||||
bool large_arc = false;
|
||||
bool sweep = true;
|
||||
|
||||
// 12.1. Move to the point (x + upperLeft["x"], y).
|
||||
m_path.move_to(Gfx::FloatPoint { x + upper_left.x, y });
|
||||
|
||||
// 12.2. Draw a straight line to the point (x + w − upperRight["x"], y).
|
||||
m_path.line_to(Gfx::FloatPoint { x + w - upper_right.x, y });
|
||||
|
||||
// 12.3. Draw an arc to the point (x + w, y + upperRight["y"]).
|
||||
m_path.elliptical_arc_to(Gfx::FloatPoint { x + w, y + upper_right.y }, { upper_right.x, upper_right.y }, 0, large_arc, sweep);
|
||||
|
||||
// 12.4. Draw a straight line to the point (x + w, y + h − lowerRight["y"]).
|
||||
m_path.line_to(Gfx::FloatPoint { x + w, y + h - lower_right.y });
|
||||
|
||||
// 12.5. Draw an arc to the point (x + w − lowerRight["x"], y + h).
|
||||
m_path.elliptical_arc_to(Gfx::FloatPoint { x + w - lower_right.x, y + h }, { lower_right.x, lower_right.y }, 0, large_arc, sweep);
|
||||
|
||||
// 12.6. Draw a straight line to the point (x + lowerLeft["x"], y + h).
|
||||
m_path.line_to(Gfx::FloatPoint { x + lower_left.x, y + h });
|
||||
|
||||
// 12.7. Draw an arc to the point (x, y + h − lowerLeft["y"]).
|
||||
m_path.elliptical_arc_to(Gfx::FloatPoint { x, y + h - lower_left.y }, { lower_left.x, lower_left.y }, 0, large_arc, sweep);
|
||||
|
||||
// 12.8. Draw a straight line to the point (x, y + upperLeft["y"]).
|
||||
m_path.line_to(Gfx::FloatPoint { x, y + upper_left.y });
|
||||
|
||||
// 12.9. Draw an arc to the point (x + upperLeft["x"], y).
|
||||
m_path.elliptical_arc_to(Gfx::FloatPoint { x + upper_left.x, y }, { upper_left.x, upper_left.y }, 0, large_arc, sweep);
|
||||
|
||||
// 13. Mark the subpath as closed.
|
||||
m_path.close();
|
||||
|
||||
// 14. Create a new subpath with the point (x, y) as the only point in the subpath.
|
||||
m_path.move_to(Gfx::FloatPoint { x, y });
|
||||
return {};
|
||||
}
|
||||
|
||||
}
|
58
Libraries/LibWeb/HTML/Canvas/CanvasPath.h
Normal file
58
Libraries/LibWeb/HTML/Canvas/CanvasPath.h
Normal file
|
@ -0,0 +1,58 @@
|
|||
/*
|
||||
* Copyright (c) 2022, Sam Atkins <atkinssj@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <LibGfx/Path.h>
|
||||
#include <LibWeb/Geometry/DOMPointReadOnly.h>
|
||||
#include <LibWeb/HTML/Canvas/CanvasState.h>
|
||||
#include <LibWeb/WebIDL/ExceptionOr.h>
|
||||
|
||||
namespace Web::HTML {
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/canvas.html#canvaspath
|
||||
class CanvasPath {
|
||||
public:
|
||||
~CanvasPath() = default;
|
||||
|
||||
void close_path();
|
||||
|
||||
void move_to(float x, float y);
|
||||
void line_to(float x, float y);
|
||||
void quadratic_curve_to(float cx, float cy, float x, float y);
|
||||
void bezier_curve_to(double cp1x, double cp1y, double cp2x, double cp2y, double x, double y);
|
||||
WebIDL::ExceptionOr<void> arc_to(double x1, double y1, double x2, double y2, double radius);
|
||||
void rect(double x, double y, double w, double h);
|
||||
WebIDL::ExceptionOr<void> round_rect(double x, double y, double w, double h, Variant<double, Geometry::DOMPointInit, Vector<Variant<double, Geometry::DOMPointInit>>> radii = { 0 });
|
||||
WebIDL::ExceptionOr<void> arc(float x, float y, float radius, float start_angle, float end_angle, bool counter_clockwise);
|
||||
WebIDL::ExceptionOr<void> ellipse(float x, float y, float radius_x, float radius_y, float rotation, float start_angle, float end_angle, bool counter_clockwise);
|
||||
|
||||
Gfx::Path& path() { return m_path; }
|
||||
Gfx::Path const& path() const { return m_path; }
|
||||
|
||||
protected:
|
||||
explicit CanvasPath(Bindings::PlatformObject& self)
|
||||
: m_self(self)
|
||||
{
|
||||
}
|
||||
|
||||
explicit CanvasPath(Bindings::PlatformObject& self, CanvasState const& canvas_state)
|
||||
: m_self(self)
|
||||
, m_canvas_state(canvas_state)
|
||||
{
|
||||
}
|
||||
|
||||
private:
|
||||
Gfx::AffineTransform active_transform() const;
|
||||
|
||||
void ensure_subpath(float x, float y);
|
||||
|
||||
JS::NonnullGCPtr<Bindings::PlatformObject> m_self;
|
||||
Optional<CanvasState const&> m_canvas_state;
|
||||
Gfx::Path m_path;
|
||||
};
|
||||
|
||||
}
|
15
Libraries/LibWeb/HTML/Canvas/CanvasPath.idl
Normal file
15
Libraries/LibWeb/HTML/Canvas/CanvasPath.idl
Normal file
|
@ -0,0 +1,15 @@
|
|||
#import <Geometry/DOMPointReadOnly.idl>
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/canvas.html#canvaspath
|
||||
interface mixin CanvasPath {
|
||||
undefined closePath();
|
||||
undefined moveTo(unrestricted double x, unrestricted double y);
|
||||
undefined lineTo(unrestricted double x, unrestricted double y);
|
||||
undefined quadraticCurveTo(unrestricted double cpx, unrestricted double cpy, unrestricted double x, unrestricted double y);
|
||||
undefined bezierCurveTo(unrestricted double cp1x, unrestricted double cp1y, unrestricted double cp2x, unrestricted double cp2y, unrestricted double x, unrestricted double y);
|
||||
undefined arcTo(unrestricted double x1, unrestricted double y1, unrestricted double x2, unrestricted double y2, unrestricted double radius);
|
||||
undefined rect(unrestricted double x, unrestricted double y, unrestricted double w, unrestricted double h);
|
||||
undefined roundRect(unrestricted double x, unrestricted double y, unrestricted double w, unrestricted double h, optional (unrestricted double or DOMPointInit or sequence<(unrestricted double or DOMPointInit)>) radii = 0);
|
||||
undefined arc(unrestricted double x, unrestricted double y, unrestricted double radius, unrestricted double startAngle, unrestricted double endAngle, optional boolean counterclockwise = false);
|
||||
undefined ellipse(unrestricted double x, unrestricted double y, unrestricted double radiusX, unrestricted double radiusY, unrestricted double rotation, unrestricted double startAngle, unrestricted double endAngle, optional boolean counterclockwise = false);
|
||||
};
|
123
Libraries/LibWeb/HTML/Canvas/CanvasPathDrawingStyles.h
Normal file
123
Libraries/LibWeb/HTML/Canvas/CanvasPathDrawingStyles.h
Normal file
|
@ -0,0 +1,123 @@
|
|||
/*
|
||||
* Copyright (c) 2022, Sam Atkins <atkinssj@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <LibWeb/HTML/Canvas/CanvasState.h>
|
||||
|
||||
namespace Web::HTML {
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/canvas.html#canvaspathdrawingstyles
|
||||
template<typename IncludingClass>
|
||||
class CanvasPathDrawingStyles {
|
||||
public:
|
||||
~CanvasPathDrawingStyles() = default;
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/canvas.html#dom-context-2d-linewidth
|
||||
void set_line_width(float line_width)
|
||||
{
|
||||
// On setting, zero, negative, infinite, and NaN values must be ignored, leaving the value unchanged;
|
||||
if (line_width <= 0 || !isfinite(line_width))
|
||||
return;
|
||||
// other values must change the current value to the new value.
|
||||
my_drawing_state().line_width = line_width;
|
||||
}
|
||||
float line_width() const
|
||||
{
|
||||
// On getting, it must return the current value.
|
||||
return my_drawing_state().line_width;
|
||||
}
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/canvas.html#dom-context-2d-linecap
|
||||
void set_line_cap(Bindings::CanvasLineCap line_cap)
|
||||
{
|
||||
// On setting, the current value must be changed to the new value.
|
||||
my_drawing_state().line_cap = line_cap;
|
||||
}
|
||||
Bindings::CanvasLineCap line_cap() const
|
||||
{
|
||||
// On getting, it must return the current value.
|
||||
return my_drawing_state().line_cap;
|
||||
}
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/canvas.html#dom-context-2d-linejoin
|
||||
void set_line_join(Bindings::CanvasLineJoin line_join)
|
||||
{
|
||||
// On setting, the current value must be changed to the new value.
|
||||
my_drawing_state().line_join = line_join;
|
||||
}
|
||||
Bindings::CanvasLineJoin line_join() const
|
||||
{
|
||||
// On getting, it must return the current value.
|
||||
return my_drawing_state().line_join;
|
||||
}
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/canvas.html#dom-context-2d-miterlimit
|
||||
void set_miter_limit(float miter_limit)
|
||||
{
|
||||
// On setting, zero, negative, infinite, and NaN values must be ignored, leaving the value unchanged;
|
||||
if (miter_limit <= 0 || !isfinite(miter_limit))
|
||||
return;
|
||||
// other values must change the current value to the new value.
|
||||
my_drawing_state().miter_limit = miter_limit;
|
||||
}
|
||||
float miter_limit() const
|
||||
{
|
||||
// On getting, it must return the current value.
|
||||
return my_drawing_state().miter_limit;
|
||||
}
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/canvas.html#dom-context-2d-setlinedash
|
||||
void set_line_dash(Vector<double> segments)
|
||||
{
|
||||
// The setLineDash(segments) method, when invoked, must run these steps:
|
||||
|
||||
// 1. If any value in segments is not finite (e.g. an Infinity or a NaN value), or if any value is negative (less than zero), then return
|
||||
// (without throwing an exception; user agents could show a message on a developer console, though, as that would be helpful for debugging).
|
||||
for (auto const& segment : segments) {
|
||||
if (!isfinite(segment) || segment < 0)
|
||||
return;
|
||||
}
|
||||
|
||||
// 2. If the number of elements in segments is odd, then let segments be the concatenation of two copies of segments.
|
||||
if (segments.size() % 2 == 1)
|
||||
segments.extend(segments);
|
||||
|
||||
// 3. Let the object's dash list be segments.
|
||||
my_drawing_state().dash_list = segments;
|
||||
}
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/canvas.html#dom-context-2d-getlinedash
|
||||
Vector<double> get_line_dash()
|
||||
{
|
||||
// When the getLineDash() method is invoked, it must return a sequence whose values are the values of the object's dash list, in the same order.
|
||||
return my_drawing_state().dash_list;
|
||||
}
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/canvas.html#dom-context-2d-linedashoffset
|
||||
void set_line_dash_offset(float line_dash_offset)
|
||||
{
|
||||
// On setting, infinite and NaN values must be ignored, leaving the value unchanged;
|
||||
if (!isfinite(line_dash_offset))
|
||||
return;
|
||||
// other values must change the current value to the new value.
|
||||
my_drawing_state().line_dash_offset = line_dash_offset;
|
||||
}
|
||||
float line_dash_offset() const
|
||||
{
|
||||
// On getting, it must return the current value.
|
||||
return my_drawing_state().line_dash_offset;
|
||||
}
|
||||
|
||||
protected:
|
||||
CanvasPathDrawingStyles() = default;
|
||||
|
||||
private:
|
||||
CanvasState::DrawingState& my_drawing_state() { return reinterpret_cast<IncludingClass&>(*this).drawing_state(); }
|
||||
CanvasState::DrawingState const& my_drawing_state() const { return reinterpret_cast<IncludingClass const&>(*this).drawing_state(); }
|
||||
};
|
||||
|
||||
}
|
15
Libraries/LibWeb/HTML/Canvas/CanvasPathDrawingStyles.idl
Normal file
15
Libraries/LibWeb/HTML/Canvas/CanvasPathDrawingStyles.idl
Normal file
|
@ -0,0 +1,15 @@
|
|||
// https://html.spec.whatwg.org/multipage/canvas.html#canvaslinecap
|
||||
// enum CanvasLineCap { "butt", "round", "square" };
|
||||
// enum CanvasLineJoin { "round", "bevel", "miter" };
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/canvas.html#canvaspathdrawingstyles
|
||||
interface mixin CanvasPathDrawingStyles {
|
||||
attribute unrestricted double lineWidth;
|
||||
attribute CanvasLineCap lineCap;
|
||||
attribute CanvasLineJoin lineJoin;
|
||||
attribute unrestricted double miterLimit;
|
||||
|
||||
undefined setLineDash(sequence<unrestricted double> segments);
|
||||
sequence<unrestricted double> getLineDash();
|
||||
attribute unrestricted double lineDashOffset;
|
||||
};
|
24
Libraries/LibWeb/HTML/Canvas/CanvasRect.h
Normal file
24
Libraries/LibWeb/HTML/Canvas/CanvasRect.h
Normal file
|
@ -0,0 +1,24 @@
|
|||
/*
|
||||
* Copyright (c) 2022, Sam Atkins <atkinssj@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
namespace Web::HTML {
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/canvas.html#canvasrect
|
||||
class CanvasRect {
|
||||
public:
|
||||
virtual ~CanvasRect() = default;
|
||||
|
||||
virtual void fill_rect(float x, float y, float width, float height) = 0;
|
||||
virtual void stroke_rect(float x, float y, float width, float height) = 0;
|
||||
virtual void clear_rect(float x, float y, float width, float height) = 0;
|
||||
|
||||
protected:
|
||||
CanvasRect() = default;
|
||||
};
|
||||
|
||||
}
|
6
Libraries/LibWeb/HTML/Canvas/CanvasRect.idl
Normal file
6
Libraries/LibWeb/HTML/Canvas/CanvasRect.idl
Normal file
|
@ -0,0 +1,6 @@
|
|||
// https://html.spec.whatwg.org/multipage/canvas.html#canvasrect
|
||||
interface mixin CanvasRect {
|
||||
undefined clearRect(unrestricted double x, unrestricted double y, unrestricted double w, unrestricted double h);
|
||||
undefined fillRect(unrestricted double x, unrestricted double y, unrestricted double w, unrestricted double h);
|
||||
undefined strokeRect(unrestricted double x, unrestricted double y, unrestricted double w, unrestricted double h);
|
||||
};
|
40
Libraries/LibWeb/HTML/Canvas/CanvasShadowStyles.h
Normal file
40
Libraries/LibWeb/HTML/Canvas/CanvasShadowStyles.h
Normal file
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* Copyright (c) 2024, İbrahim UYSAL <uysalibov@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/String.h>
|
||||
#include <LibWeb/CSS/Parser/Parser.h>
|
||||
#include <LibWeb/HTML/Canvas/CanvasState.h>
|
||||
#include <LibWeb/HTML/CanvasGradient.h>
|
||||
#include <LibWeb/HTML/CanvasPattern.h>
|
||||
|
||||
namespace Web::HTML {
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/canvas.html#canvasshadowstyles
|
||||
template<typename IncludingClass>
|
||||
class CanvasShadowStyles {
|
||||
public:
|
||||
~CanvasShadowStyles() = default;
|
||||
|
||||
virtual float shadow_offset_x() const = 0;
|
||||
virtual void set_shadow_offset_x(float offsetX) = 0;
|
||||
|
||||
virtual float shadow_offset_y() const = 0;
|
||||
virtual void set_shadow_offset_y(float offsetY) = 0;
|
||||
|
||||
virtual String shadow_color() const = 0;
|
||||
virtual void set_shadow_color(String color) = 0;
|
||||
|
||||
protected:
|
||||
CanvasShadowStyles() = default;
|
||||
|
||||
private:
|
||||
CanvasState::DrawingState& my_drawing_state() { return reinterpret_cast<IncludingClass&>(*this).drawing_state(); }
|
||||
CanvasState::DrawingState const& my_drawing_state() const { return reinterpret_cast<IncludingClass const&>(*this).drawing_state(); }
|
||||
};
|
||||
|
||||
}
|
8
Libraries/LibWeb/HTML/Canvas/CanvasShadowStyles.idl
Normal file
8
Libraries/LibWeb/HTML/Canvas/CanvasShadowStyles.idl
Normal file
|
@ -0,0 +1,8 @@
|
|||
// https://html.spec.whatwg.org/multipage/canvas.html#canvasshadowstyles
|
||||
interface mixin CanvasShadowStyles {
|
||||
// shadows
|
||||
attribute unrestricted double shadowOffsetX; // (default 0)
|
||||
attribute unrestricted double shadowOffsetY; // (default 0)
|
||||
[FIXME] attribute unrestricted double shadowBlur; // (default 0)
|
||||
attribute DOMString shadowColor; // (default transparent black)
|
||||
};
|
75
Libraries/LibWeb/HTML/Canvas/CanvasState.cpp
Normal file
75
Libraries/LibWeb/HTML/Canvas/CanvasState.cpp
Normal file
|
@ -0,0 +1,75 @@
|
|||
/*
|
||||
* Copyright (c) 2021-2022, Linus Groh <linusg@serenityos.org>
|
||||
* Copyright (c) 2022, Sam Atkins <atkinssj@serenityos.org>
|
||||
* Copyright (c) 2023, MacDue <macdue@dueutil.tech>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <LibGfx/Painter.h>
|
||||
#include <LibWeb/HTML/Canvas/CanvasState.h>
|
||||
|
||||
namespace Web::HTML {
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/canvas.html#dom-context-2d-save
|
||||
void CanvasState::save()
|
||||
{
|
||||
// The save() method steps are to push a copy of the current drawing state onto the drawing state stack.
|
||||
m_drawing_state_stack.append(m_drawing_state);
|
||||
|
||||
if (auto* painter = painter_for_canvas_state())
|
||||
painter->save();
|
||||
}
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/canvas.html#dom-context-2d-restore
|
||||
void CanvasState::restore()
|
||||
{
|
||||
// The restore() method steps are to pop the top entry in the drawing state stack, and reset the drawing state it describes. If there is no saved state, then the method must do nothing.
|
||||
if (m_drawing_state_stack.is_empty())
|
||||
return;
|
||||
m_drawing_state = m_drawing_state_stack.take_last();
|
||||
|
||||
if (auto* painter = painter_for_canvas_state())
|
||||
painter->restore();
|
||||
}
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/canvas.html#dom-context-2d-reset
|
||||
void CanvasState::reset()
|
||||
{
|
||||
// The reset() method steps are to reset the rendering context to its default state.
|
||||
reset_to_default_state();
|
||||
}
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/canvas.html#dom-context-2d-iscontextlost
|
||||
bool CanvasState::is_context_lost()
|
||||
{
|
||||
// The isContextLost() method steps are to return this's context lost.
|
||||
return m_context_lost;
|
||||
}
|
||||
|
||||
NonnullRefPtr<Gfx::PaintStyle> CanvasState::FillOrStrokeStyle::to_gfx_paint_style()
|
||||
{
|
||||
return m_fill_or_stroke_style.visit(
|
||||
[&](Gfx::Color color) -> NonnullRefPtr<Gfx::PaintStyle> {
|
||||
if (!m_color_paint_style)
|
||||
m_color_paint_style = Gfx::SolidColorPaintStyle::create(color).release_value_but_fixme_should_propagate_errors();
|
||||
return m_color_paint_style.release_nonnull();
|
||||
},
|
||||
[&](auto handle) {
|
||||
return handle->to_gfx_paint_style();
|
||||
});
|
||||
}
|
||||
|
||||
Gfx::Color CanvasState::FillOrStrokeStyle::to_color_but_fixme_should_accept_any_paint_style() const
|
||||
{
|
||||
return as_color().value_or(Gfx::Color::Black);
|
||||
}
|
||||
|
||||
Optional<Gfx::Color> CanvasState::FillOrStrokeStyle::as_color() const
|
||||
{
|
||||
if (auto* color = m_fill_or_stroke_style.get_pointer<Gfx::Color>())
|
||||
return *color;
|
||||
return {};
|
||||
}
|
||||
|
||||
}
|
119
Libraries/LibWeb/HTML/Canvas/CanvasState.h
Normal file
119
Libraries/LibWeb/HTML/Canvas/CanvasState.h
Normal file
|
@ -0,0 +1,119 @@
|
|||
/*
|
||||
* Copyright (c) 2022, Sam Atkins <atkinssj@serenityos.org>
|
||||
* Copyright (c) 2023, MacDue <macdue@dueutil.tech>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/Variant.h>
|
||||
#include <AK/Vector.h>
|
||||
#include <LibGfx/AffineTransform.h>
|
||||
#include <LibGfx/Color.h>
|
||||
#include <LibGfx/Font/Font.h>
|
||||
#include <LibGfx/PaintStyle.h>
|
||||
#include <LibGfx/Path.h>
|
||||
#include <LibGfx/WindingRule.h>
|
||||
#include <LibWeb/Bindings/CanvasRenderingContext2DPrototype.h>
|
||||
#include <LibWeb/HTML/CanvasGradient.h>
|
||||
#include <LibWeb/HTML/CanvasPattern.h>
|
||||
|
||||
namespace Web::HTML {
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/canvas.html#canvasstate
|
||||
class CanvasState {
|
||||
public:
|
||||
virtual ~CanvasState() = default;
|
||||
|
||||
virtual Gfx::Painter* painter_for_canvas_state() = 0;
|
||||
virtual Gfx::Path& path_for_canvas_state() = 0;
|
||||
|
||||
void save();
|
||||
void restore();
|
||||
void reset();
|
||||
bool is_context_lost();
|
||||
|
||||
using FillOrStrokeVariant = Variant<Gfx::Color, JS::Handle<CanvasGradient>, JS::Handle<CanvasPattern>>;
|
||||
|
||||
struct FillOrStrokeStyle {
|
||||
FillOrStrokeStyle(Gfx::Color color)
|
||||
: m_fill_or_stroke_style(color)
|
||||
{
|
||||
}
|
||||
|
||||
FillOrStrokeStyle(JS::Handle<CanvasGradient> gradient)
|
||||
: m_fill_or_stroke_style(gradient)
|
||||
{
|
||||
}
|
||||
|
||||
FillOrStrokeStyle(JS::Handle<CanvasPattern> pattern)
|
||||
: m_fill_or_stroke_style(pattern)
|
||||
{
|
||||
}
|
||||
|
||||
NonnullRefPtr<Gfx::PaintStyle> to_gfx_paint_style();
|
||||
|
||||
Optional<Gfx::Color> as_color() const;
|
||||
Gfx::Color to_color_but_fixme_should_accept_any_paint_style() const;
|
||||
|
||||
using JsFillOrStrokeStyle = Variant<String, JS::Handle<CanvasGradient>, JS::Handle<CanvasPattern>>;
|
||||
|
||||
JsFillOrStrokeStyle to_js_fill_or_stroke_style() const
|
||||
{
|
||||
return m_fill_or_stroke_style.visit(
|
||||
[&](Gfx::Color color) -> JsFillOrStrokeStyle {
|
||||
return color.to_string(Gfx::Color::HTMLCompatibleSerialization::Yes);
|
||||
},
|
||||
[&](auto handle) -> JsFillOrStrokeStyle {
|
||||
return handle;
|
||||
});
|
||||
}
|
||||
|
||||
private:
|
||||
FillOrStrokeVariant m_fill_or_stroke_style;
|
||||
RefPtr<Gfx::PaintStyle> m_color_paint_style { nullptr };
|
||||
};
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/canvas.html#drawing-state
|
||||
struct DrawingState {
|
||||
Gfx::AffineTransform transform;
|
||||
FillOrStrokeStyle fill_style { Gfx::Color::Black };
|
||||
FillOrStrokeStyle stroke_style { Gfx::Color::Black };
|
||||
float shadow_offset_x { 0.0f };
|
||||
float shadow_offset_y { 0.0f };
|
||||
Gfx::Color shadow_color { Gfx::Color::Transparent };
|
||||
float line_width { 1 };
|
||||
Bindings::CanvasLineCap line_cap { Bindings::CanvasLineCap::Butt };
|
||||
Bindings::CanvasLineJoin line_join { Bindings::CanvasLineJoin::Miter };
|
||||
float miter_limit { 10 };
|
||||
Vector<double> dash_list;
|
||||
float line_dash_offset { 0 };
|
||||
bool image_smoothing_enabled { true };
|
||||
Bindings::ImageSmoothingQuality image_smoothing_quality { Bindings::ImageSmoothingQuality::Low };
|
||||
float global_alpha = { 1 };
|
||||
RefPtr<CSS::CSSStyleValue> font_style_value { nullptr };
|
||||
RefPtr<Gfx::Font const> current_font { nullptr };
|
||||
Bindings::CanvasTextAlign text_align { Bindings::CanvasTextAlign::Start };
|
||||
Bindings::CanvasTextBaseline text_baseline { Bindings::CanvasTextBaseline::Alphabetic };
|
||||
};
|
||||
DrawingState& drawing_state() { return m_drawing_state; }
|
||||
DrawingState const& drawing_state() const { return m_drawing_state; }
|
||||
|
||||
void clear_drawing_state_stack() { m_drawing_state_stack.clear(); }
|
||||
void reset_drawing_state() { m_drawing_state = {}; }
|
||||
|
||||
virtual void reset_to_default_state() = 0;
|
||||
|
||||
protected:
|
||||
CanvasState() = default;
|
||||
|
||||
private:
|
||||
DrawingState m_drawing_state;
|
||||
Vector<DrawingState> m_drawing_state_stack;
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/canvas.html#concept-canvas-context-lost
|
||||
bool m_context_lost { false };
|
||||
};
|
||||
|
||||
}
|
7
Libraries/LibWeb/HTML/Canvas/CanvasState.idl
Normal file
7
Libraries/LibWeb/HTML/Canvas/CanvasState.idl
Normal file
|
@ -0,0 +1,7 @@
|
|||
// https://html.spec.whatwg.org/multipage/canvas.html#canvasstate
|
||||
interface mixin CanvasState {
|
||||
undefined save();
|
||||
undefined restore();
|
||||
undefined reset();
|
||||
boolean isContextLost();
|
||||
};
|
28
Libraries/LibWeb/HTML/Canvas/CanvasText.h
Normal file
28
Libraries/LibWeb/HTML/Canvas/CanvasText.h
Normal file
|
@ -0,0 +1,28 @@
|
|||
/*
|
||||
* Copyright (c) 2022, Sam Atkins <atkinssj@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/ByteString.h>
|
||||
#include <AK/Optional.h>
|
||||
#include <LibWeb/HTML/TextMetrics.h>
|
||||
|
||||
namespace Web::HTML {
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/canvas.html#canvastext
|
||||
class CanvasText {
|
||||
public:
|
||||
virtual ~CanvasText() = default;
|
||||
|
||||
virtual void fill_text(StringView, float x, float y, Optional<double> max_width) = 0;
|
||||
virtual void stroke_text(StringView, float x, float y, Optional<double> max_width) = 0;
|
||||
virtual JS::NonnullGCPtr<TextMetrics> measure_text(StringView text) = 0;
|
||||
|
||||
protected:
|
||||
CanvasText() = default;
|
||||
};
|
||||
|
||||
}
|
8
Libraries/LibWeb/HTML/Canvas/CanvasText.idl
Normal file
8
Libraries/LibWeb/HTML/Canvas/CanvasText.idl
Normal file
|
@ -0,0 +1,8 @@
|
|||
#import <HTML/TextMetrics.idl>
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/canvas.html#canvastext
|
||||
interface mixin CanvasText {
|
||||
undefined fillText(DOMString text, unrestricted double x, unrestricted double y, optional unrestricted double maxWidth);
|
||||
undefined strokeText(DOMString text, unrestricted double x, unrestricted double y, optional unrestricted double maxWidth);
|
||||
TextMetrics measureText(DOMString text);
|
||||
};
|
82
Libraries/LibWeb/HTML/Canvas/CanvasTextDrawingStyles.h
Normal file
82
Libraries/LibWeb/HTML/Canvas/CanvasTextDrawingStyles.h
Normal file
|
@ -0,0 +1,82 @@
|
|||
/*
|
||||
* Copyright (c) 2023, Bastiaan van der Plaat <bastiaan.v.d.plaat@gmail.com>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <LibWeb/CSS/Parser/Parser.h>
|
||||
#include <LibWeb/CSS/StyleComputer.h>
|
||||
#include <LibWeb/CSS/StyleValues/ShorthandStyleValue.h>
|
||||
#include <LibWeb/DOM/Document.h>
|
||||
#include <LibWeb/HTML/Canvas/CanvasState.h>
|
||||
|
||||
namespace Web::HTML {
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/canvas.html#canvastextdrawingstyles
|
||||
template<typename IncludingClass>
|
||||
class CanvasTextDrawingStyles {
|
||||
public:
|
||||
~CanvasTextDrawingStyles() = default;
|
||||
|
||||
ByteString font() const
|
||||
{
|
||||
// When font style value is empty return default string
|
||||
if (!my_drawing_state().font_style_value) {
|
||||
return "10px sans-serif";
|
||||
}
|
||||
|
||||
// On getting, the font attribute must return the serialized form of the current font of the context (with no 'line-height' component).
|
||||
auto const& font_style_value = my_drawing_state().font_style_value->as_shorthand();
|
||||
auto font_style = font_style_value.longhand(CSS::PropertyID::FontStyle);
|
||||
auto font_weight = font_style_value.longhand(CSS::PropertyID::FontWeight);
|
||||
auto font_size = font_style_value.longhand(CSS::PropertyID::FontSize);
|
||||
auto font_family = font_style_value.longhand(CSS::PropertyID::FontFamily);
|
||||
return ByteString::formatted("{} {} {} {}", font_style->to_string(), font_weight->to_string(), font_size->to_string(), font_family->to_string());
|
||||
}
|
||||
|
||||
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'),
|
||||
// and the resulting font must be assigned to the context, with the 'line-height' component forced to 'normal', with the 'font-size' component converted to CSS pixels,
|
||||
// and with system fonts being computed to explicit values.
|
||||
// FIXME: with the 'line-height' component forced to 'normal'
|
||||
// FIXME: with the 'font-size' component converted to CSS pixels
|
||||
auto parsing_context = CSS::Parser::ParsingContext { reinterpret_cast<IncludingClass&>(*this).realm() };
|
||||
auto font_style_value_result = parse_css_value(parsing_context, font, CSS::PropertyID::Font);
|
||||
|
||||
// If the new value is syntactically incorrect (including using property-independent style sheet syntax like 'inherit' or 'initial'), then it must be ignored, without assigning a new font value.
|
||||
// NOTE: ShorthandStyleValue should be the only valid option here. We implicitly VERIFY this below.
|
||||
if (!font_style_value_result || !font_style_value_result->is_shorthand()) {
|
||||
return;
|
||||
}
|
||||
my_drawing_state().font_style_value = font_style_value_result.release_nonnull();
|
||||
|
||||
// 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 = font_list->first();
|
||||
}
|
||||
|
||||
Bindings::CanvasTextAlign text_align() const { return my_drawing_state().text_align; }
|
||||
void set_text_align(Bindings::CanvasTextAlign text_align) { my_drawing_state().text_align = text_align; }
|
||||
|
||||
Bindings::CanvasTextBaseline text_baseline() const { return my_drawing_state().text_baseline; }
|
||||
void set_text_baseline(Bindings::CanvasTextBaseline text_baseline) { my_drawing_state().text_baseline = text_baseline; }
|
||||
|
||||
protected:
|
||||
CanvasTextDrawingStyles() = default;
|
||||
|
||||
private:
|
||||
CanvasState::DrawingState& my_drawing_state() { return reinterpret_cast<IncludingClass&>(*this).drawing_state(); }
|
||||
CanvasState::DrawingState const& my_drawing_state() const { return reinterpret_cast<IncludingClass const&>(*this).drawing_state(); }
|
||||
};
|
||||
|
||||
}
|
24
Libraries/LibWeb/HTML/Canvas/CanvasTextDrawingStyles.idl
Normal file
24
Libraries/LibWeb/HTML/Canvas/CanvasTextDrawingStyles.idl
Normal file
|
@ -0,0 +1,24 @@
|
|||
#import <HTML/CanvasRenderingContext2D.idl>
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/canvas.html#canvastextalign
|
||||
// enum CanvasTextAlign { "start", "end", "left", "right", "center" };
|
||||
// enum CanvasTextBaseline { "top", "hanging", "middle", "alphabetic", "ideographic", "bottom" };
|
||||
enum CanvasDirection { "ltr", "rtl", "inherit" };
|
||||
enum CanvasFontKerning { "auto", "normal", "none" };
|
||||
enum CanvasFontStretch { "ultra-condensed", "extra-condensed", "condensed", "semi-condensed", "normal", "semi-expanded", "expanded", "extra-expanded", "ultra-expanded" };
|
||||
enum CanvasFontVariantCaps { "normal", "small-caps", "all-small-caps", "petite-caps", "all-petite-caps", "unicase", "titling-caps" };
|
||||
enum CanvasTextRendering { "auto", "optimizeSpeed", "optimizeLegibility", "geometricPrecision" };
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/canvas.html#canvastextdrawingstyles
|
||||
interface mixin CanvasTextDrawingStyles {
|
||||
attribute DOMString font; // (default 10px sans-serif)
|
||||
attribute CanvasTextAlign textAlign; // (default: "start")
|
||||
attribute CanvasTextBaseline textBaseline; // (default: "alphabetic")
|
||||
[FIXME] attribute CanvasDirection direction; // (default: "inherit")
|
||||
[FIXME] attribute DOMString letterSpacing; // (default: "0px")
|
||||
[FIXME] attribute CanvasFontKerning fontKerning; // (default: "auto")
|
||||
[FIXME] attribute CanvasFontStretch fontStretch; // (default: "normal")
|
||||
[FIXME] attribute CanvasFontVariantCaps fontVariantCaps; // (default: "normal")
|
||||
[FIXME] attribute CanvasTextRendering textRendering; // (default: "auto")
|
||||
[FIXME] attribute DOMString wordSpacing; // (default: "0px")
|
||||
};
|
146
Libraries/LibWeb/HTML/Canvas/CanvasTransform.h
Normal file
146
Libraries/LibWeb/HTML/Canvas/CanvasTransform.h
Normal file
|
@ -0,0 +1,146 @@
|
|||
/*
|
||||
* Copyright (c) 2020-2022, Andreas Kling <andreas@ladybird.org>
|
||||
* Copyright (c) 2021-2022, Linus Groh <linusg@serenityos.org>
|
||||
* Copyright (c) 2022, Sam Atkins <atkinssj@serenityos.org>
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <AK/Debug.h>
|
||||
#include <LibGfx/Painter.h>
|
||||
#include <LibWeb/Geometry/DOMMatrix.h>
|
||||
#include <LibWeb/HTML/Canvas/CanvasPath.h>
|
||||
#include <LibWeb/HTML/Canvas/CanvasState.h>
|
||||
|
||||
namespace Web::HTML {
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/canvas.html#canvastransform
|
||||
template<typename IncludingClass>
|
||||
class CanvasTransform {
|
||||
public:
|
||||
~CanvasTransform() = default;
|
||||
|
||||
Gfx::Path& mutable_path() { return static_cast<IncludingClass&>(*this).path(); }
|
||||
|
||||
void scale(float sx, float sy)
|
||||
{
|
||||
dbgln_if(CANVAS_RENDERING_CONTEXT_2D_DEBUG, "CanvasTransform::scale({}, {})", sx, sy);
|
||||
if (!isfinite(sx) || !isfinite(sy))
|
||||
return;
|
||||
my_drawing_state().transform.scale(sx, sy);
|
||||
flush_transform();
|
||||
mutable_path().transform(Gfx::AffineTransform().scale(1.0 / sx, 1.0 / sy));
|
||||
}
|
||||
|
||||
void translate(float tx, float ty)
|
||||
{
|
||||
dbgln_if(CANVAS_RENDERING_CONTEXT_2D_DEBUG, "CanvasTransform::translate({}, {})", tx, ty);
|
||||
if (!isfinite(tx) || !isfinite(ty))
|
||||
return;
|
||||
my_drawing_state().transform.translate(tx, ty);
|
||||
flush_transform();
|
||||
mutable_path().transform(Gfx::AffineTransform().translate(-tx, -ty));
|
||||
}
|
||||
|
||||
void rotate(float radians)
|
||||
{
|
||||
dbgln_if(CANVAS_RENDERING_CONTEXT_2D_DEBUG, "CanvasTransform::rotate({})", radians);
|
||||
if (!isfinite(radians))
|
||||
return;
|
||||
my_drawing_state().transform.rotate_radians(radians);
|
||||
flush_transform();
|
||||
mutable_path().transform(Gfx::AffineTransform().rotate_radians(-radians));
|
||||
}
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/canvas.html#dom-context-2d-transform
|
||||
void transform(double a, double b, double c, double d, double e, double f)
|
||||
{
|
||||
// 1. If any of the arguments are infinite or NaN, then return.
|
||||
if (!isfinite(a) || !isfinite(b) || !isfinite(c) || !isfinite(d) || !isfinite(e) || !isfinite(f))
|
||||
return;
|
||||
|
||||
// 2. Replace the current transformation matrix with the result of multiplying the current transformation matrix with the matrix described by:
|
||||
// a c e
|
||||
// b d f
|
||||
// 0 0 1
|
||||
auto transform = Gfx::AffineTransform(a, b, c, d, e, f);
|
||||
my_drawing_state().transform.multiply(transform);
|
||||
|
||||
if (auto inverse = transform.inverse(); inverse.has_value()) {
|
||||
mutable_path().transform(inverse.value());
|
||||
}
|
||||
flush_transform();
|
||||
}
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/canvas.html#dom-context-2d-gettransform
|
||||
WebIDL::ExceptionOr<JS::NonnullGCPtr<Geometry::DOMMatrix>> get_transform()
|
||||
{
|
||||
auto& realm = static_cast<IncludingClass&>(*this).realm();
|
||||
auto transform = my_drawing_state().transform;
|
||||
Geometry::DOMMatrix2DInit init = { transform.a(), transform.b(), transform.c(), transform.d(), transform.e(), transform.f(), {}, {}, {}, {}, {}, {} };
|
||||
return Geometry::DOMMatrix::create_from_dom_matrix_2d_init(realm, init);
|
||||
}
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/canvas.html#dom-context-2d-settransform
|
||||
void set_transform(double a, double b, double c, double d, double e, double f)
|
||||
{
|
||||
// 1. If any of the arguments are infinite or NaN, then return.
|
||||
if (!isfinite(a) || !isfinite(b) || !isfinite(c) || !isfinite(d) || !isfinite(e) || !isfinite(f))
|
||||
return;
|
||||
|
||||
// 2. Reset the current transformation matrix to the identity matrix.
|
||||
my_drawing_state().transform = {};
|
||||
flush_transform();
|
||||
|
||||
// 3. Invoke the transform(a, b, c, d, e, f) method with the same arguments.
|
||||
transform(a, b, c, d, e, f);
|
||||
}
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/canvas.html#dom-context-2d-settransform-matrix
|
||||
WebIDL::ExceptionOr<void> set_transform(Geometry::DOMMatrix2DInit& init)
|
||||
{
|
||||
// 1. Let matrix be the result of creating a DOMMatrix from the 2D dictionary transform.
|
||||
auto& realm = static_cast<IncludingClass&>(*this).realm();
|
||||
auto matrix = TRY(Geometry::DOMMatrix::create_from_dom_matrix_2d_init(realm, init));
|
||||
|
||||
// 2. If one or more of matrix's m11 element, m12 element, m21 element, m22 element, m41 element, or m42 element are infinite or NaN, then return.
|
||||
if (!isfinite(matrix->m11()) || !isfinite(matrix->m12()) || !isfinite(matrix->m21()) || !isfinite(matrix->m22()) || !isfinite(matrix->m41()) || !isfinite(matrix->m42()))
|
||||
return {};
|
||||
|
||||
auto original_transform = my_drawing_state().transform;
|
||||
|
||||
// 3. Reset the current transformation matrix to matrix.
|
||||
auto transform = Gfx::AffineTransform { static_cast<float>(matrix->a()), static_cast<float>(matrix->b()), static_cast<float>(matrix->c()), static_cast<float>(matrix->d()), static_cast<float>(matrix->e()), static_cast<float>(matrix->f()) };
|
||||
my_drawing_state().transform = transform;
|
||||
|
||||
mutable_path().transform(original_transform);
|
||||
|
||||
flush_transform();
|
||||
return {};
|
||||
}
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/canvas.html#dom-context-2d-resettransform
|
||||
void reset_transform()
|
||||
{
|
||||
// The resetTransform() method, when invoked, must reset the current transformation matrix to the identity matrix.
|
||||
my_drawing_state().transform = {};
|
||||
flush_transform();
|
||||
}
|
||||
|
||||
void flush_transform()
|
||||
{
|
||||
if (auto* painter = static_cast<IncludingClass&>(*this).painter())
|
||||
painter->set_transform(my_drawing_state().transform);
|
||||
}
|
||||
|
||||
protected:
|
||||
CanvasTransform() = default;
|
||||
|
||||
private:
|
||||
CanvasState::DrawingState& my_drawing_state() { return reinterpret_cast<IncludingClass&>(*this).drawing_state(); }
|
||||
CanvasState::DrawingState const& my_drawing_state() const { return reinterpret_cast<IncludingClass const&>(*this).drawing_state(); }
|
||||
};
|
||||
|
||||
}
|
14
Libraries/LibWeb/HTML/Canvas/CanvasTransform.idl
Normal file
14
Libraries/LibWeb/HTML/Canvas/CanvasTransform.idl
Normal file
|
@ -0,0 +1,14 @@
|
|||
#import <Geometry/DOMMatrix.idl>
|
||||
|
||||
// https://html.spec.whatwg.org/multipage/canvas.html#canvastransform
|
||||
interface mixin CanvasTransform {
|
||||
undefined scale(unrestricted double x, unrestricted double y);
|
||||
undefined rotate(unrestricted double radians);
|
||||
undefined translate(unrestricted double x, unrestricted double y);
|
||||
undefined transform(unrestricted double a, unrestricted double b, unrestricted double c, unrestricted double d, unrestricted double e, unrestricted double f);
|
||||
|
||||
[NewObject] DOMMatrix getTransform();
|
||||
undefined setTransform(unrestricted double a, unrestricted double b, unrestricted double c, unrestricted double d, unrestricted double e, unrestricted double f);
|
||||
undefined setTransform(optional DOMMatrix2DInit transform = {});
|
||||
undefined resetTransform();
|
||||
};
|
5
Libraries/LibWeb/HTML/Canvas/CanvasUserInterface.idl
Normal file
5
Libraries/LibWeb/HTML/Canvas/CanvasUserInterface.idl
Normal file
|
@ -0,0 +1,5 @@
|
|||
// https://html.spec.whatwg.org/multipage/canvas.html#canvasuserinterface
|
||||
interface mixin CanvasUserInterface {
|
||||
[FIXME] undefined drawFocusIfNeeded(Element element);
|
||||
[FIXME] undefined drawFocusIfNeeded(Path2D path, Element element);
|
||||
};
|
Loading…
Add table
Add a link
Reference in a new issue