1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-25 16:27:35 +00:00

LibWeb: Extract CanvasState class from CRC2D

As with CanvasPath, this is to better match the spec IDL.
This commit is contained in:
Sam Atkins 2022-08-12 14:00:00 +01:00 committed by Andreas Kling
parent 7b61d16262
commit 08e6071ebb
7 changed files with 154 additions and 93 deletions

View file

@ -136,6 +136,7 @@ set(SOURCES
HTML/BrowsingContext.cpp HTML/BrowsingContext.cpp
HTML/BrowsingContextContainer.cpp HTML/BrowsingContextContainer.cpp
HTML/Canvas/CanvasPath.cpp HTML/Canvas/CanvasPath.cpp
HTML/Canvas/CanvasState.cpp
HTML/CanvasGradient.cpp HTML/CanvasGradient.cpp
HTML/CanvasRenderingContext2D.cpp HTML/CanvasRenderingContext2D.cpp
HTML/CrossOrigin/Reporting.cpp HTML/CrossOrigin/Reporting.cpp

View file

@ -0,0 +1,42 @@
/*
* Copyright (c) 2021-2022, Linus Groh <linusg@serenityos.org>
* Copyright (c) 2022, Sam Atkins <atkinssj@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#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);
}
// 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();
}
// 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;
}
}

View file

@ -0,0 +1,51 @@
/*
* Copyright (c) 2022, Sam Atkins <atkinssj@serenityos.org>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/Vector.h>
#include <LibGfx/AffineTransform.h>
#include <LibGfx/Color.h>
namespace Web::HTML {
// https://html.spec.whatwg.org/multipage/canvas.html#canvasstate
class CanvasState {
public:
virtual ~CanvasState() = default;
void save();
void restore();
void reset();
bool is_context_lost();
// https://html.spec.whatwg.org/multipage/canvas.html#drawing-state
struct DrawingState {
Gfx::AffineTransform transform;
Gfx::Color fill_style { Gfx::Color::Black };
Gfx::Color stroke_style { Gfx::Color::Black };
float line_width { 1 };
};
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 };
};
}

View 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();
};

View file

@ -49,12 +49,12 @@ NonnullRefPtr<HTMLCanvasElement> CanvasRenderingContext2D::canvas_for_binding()
void CanvasRenderingContext2D::set_fill_style(String style) void CanvasRenderingContext2D::set_fill_style(String 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. // 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.
m_drawing_state.fill_style = Gfx::Color::from_string(style).value_or(Color::Black); drawing_state().fill_style = Gfx::Color::from_string(style).value_or(Color::Black);
} }
String CanvasRenderingContext2D::fill_style() const String CanvasRenderingContext2D::fill_style() const
{ {
return m_drawing_state.fill_style.to_string(); return drawing_state().fill_style.to_string();
} }
void CanvasRenderingContext2D::fill_rect(float x, float y, float width, float height) void CanvasRenderingContext2D::fill_rect(float x, float y, float width, float height)
@ -63,8 +63,10 @@ void CanvasRenderingContext2D::fill_rect(float x, float y, float width, float he
if (!painter) if (!painter)
return; return;
auto rect = m_drawing_state.transform.map(Gfx::FloatRect(x, y, width, height)); auto& drawing_state = this->drawing_state();
painter->fill_rect(enclosing_int_rect(rect), m_drawing_state.fill_style);
auto rect = drawing_state.transform.map(Gfx::FloatRect(x, y, width, height));
painter->fill_rect(enclosing_int_rect(rect), drawing_state.fill_style);
did_draw(rect); did_draw(rect);
} }
@ -74,7 +76,7 @@ void CanvasRenderingContext2D::clear_rect(float x, float y, float width, float h
if (!painter) if (!painter)
return; return;
auto rect = m_drawing_state.transform.map(Gfx::FloatRect(x, y, width, height)); auto rect = drawing_state().transform.map(Gfx::FloatRect(x, y, width, height));
painter->clear_rect(enclosing_int_rect(rect), Color()); painter->clear_rect(enclosing_int_rect(rect), Color());
did_draw(rect); did_draw(rect);
} }
@ -82,12 +84,12 @@ void CanvasRenderingContext2D::clear_rect(float x, float y, float width, float h
void CanvasRenderingContext2D::set_stroke_style(String style) void CanvasRenderingContext2D::set_stroke_style(String 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. // 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.
m_drawing_state.stroke_style = Gfx::Color::from_string(style).value_or(Color::Black); drawing_state().stroke_style = Gfx::Color::from_string(style).value_or(Color::Black);
} }
String CanvasRenderingContext2D::stroke_style() const String CanvasRenderingContext2D::stroke_style() const
{ {
return m_drawing_state.stroke_style.to_string(); return drawing_state().stroke_style.to_string();
} }
void CanvasRenderingContext2D::stroke_rect(float x, float y, float width, float height) void CanvasRenderingContext2D::stroke_rect(float x, float y, float width, float height)
@ -96,17 +98,19 @@ void CanvasRenderingContext2D::stroke_rect(float x, float y, float width, float
if (!painter) if (!painter)
return; return;
auto rect = m_drawing_state.transform.map(Gfx::FloatRect(x, y, width, height)); auto& drawing_state = this->drawing_state();
auto top_left = m_drawing_state.transform.map(Gfx::FloatPoint(x, y)).to_type<int>(); auto rect = drawing_state.transform.map(Gfx::FloatRect(x, y, width, height));
auto top_right = m_drawing_state.transform.map(Gfx::FloatPoint(x + width - 1, y)).to_type<int>();
auto bottom_left = m_drawing_state.transform.map(Gfx::FloatPoint(x, y + height - 1)).to_type<int>();
auto bottom_right = m_drawing_state.transform.map(Gfx::FloatPoint(x + width - 1, y + height - 1)).to_type<int>();
painter->draw_line(top_left, top_right, m_drawing_state.stroke_style, m_drawing_state.line_width); auto top_left = drawing_state.transform.map(Gfx::FloatPoint(x, y)).to_type<int>();
painter->draw_line(top_right, bottom_right, m_drawing_state.stroke_style, m_drawing_state.line_width); auto top_right = drawing_state.transform.map(Gfx::FloatPoint(x + width - 1, y)).to_type<int>();
painter->draw_line(bottom_right, bottom_left, m_drawing_state.stroke_style, m_drawing_state.line_width); auto bottom_left = drawing_state.transform.map(Gfx::FloatPoint(x, y + height - 1)).to_type<int>();
painter->draw_line(bottom_left, top_left, m_drawing_state.stroke_style, m_drawing_state.line_width); auto bottom_right = drawing_state.transform.map(Gfx::FloatPoint(x + width - 1, y + height - 1)).to_type<int>();
painter->draw_line(top_left, top_right, drawing_state.stroke_style, drawing_state.line_width);
painter->draw_line(top_right, bottom_right, drawing_state.stroke_style, drawing_state.line_width);
painter->draw_line(bottom_right, bottom_left, drawing_state.stroke_style, drawing_state.line_width);
painter->draw_line(bottom_left, top_left, drawing_state.stroke_style, drawing_state.line_width);
did_draw(rect); did_draw(rect);
} }
@ -197,10 +201,12 @@ DOM::ExceptionOr<void> CanvasRenderingContext2D::draw_image(CanvasImageSource co
if (!painter) if (!painter)
return {}; return {};
if (m_drawing_state.transform.is_identity_or_translation()) { auto& drawing_state = this->drawing_state();
painter->translate(m_drawing_state.transform.e(), m_drawing_state.transform.f());
if (drawing_state.transform.is_identity_or_translation()) {
painter->translate(drawing_state.transform.e(), drawing_state.transform.f());
painter->draw_scaled_bitmap(destination_rect.to_rounded<int>(), *bitmap, source_rect, 1.0f, Gfx::Painter::ScalingMode::BilinearBlend); painter->draw_scaled_bitmap(destination_rect.to_rounded<int>(), *bitmap, source_rect, 1.0f, Gfx::Painter::ScalingMode::BilinearBlend);
painter->translate(-m_drawing_state.transform.e(), -m_drawing_state.transform.f()); painter->translate(-drawing_state.transform.e(), -drawing_state.transform.f());
} else { } else {
// The context has an affine transform, we have to draw through it! // The context has an affine transform, we have to draw through it!
@ -215,11 +221,11 @@ DOM::ExceptionOr<void> CanvasRenderingContext2D::draw_image(CanvasImageSource co
// FIXME: Gfx::Painter should have an affine transform as part of its state and handle all of this instead. // FIXME: Gfx::Painter should have an affine transform as part of its state and handle all of this instead.
auto inverse_transform = m_drawing_state.transform.inverse(); auto inverse_transform = drawing_state.transform.inverse();
if (!inverse_transform.has_value()) if (!inverse_transform.has_value())
return {}; return {};
auto destination_quad = m_drawing_state.transform.map_to_quad(destination_rect); auto destination_quad = drawing_state.transform.map_to_quad(destination_rect);
auto destination_bounding_rect = destination_quad.bounding_rect().to_rounded<int>(); auto destination_bounding_rect = destination_quad.bounding_rect().to_rounded<int>();
Gfx::AffineTransform source_transform; Gfx::AffineTransform source_transform;
@ -260,19 +266,19 @@ DOM::ExceptionOr<void> CanvasRenderingContext2D::draw_image(CanvasImageSource co
void CanvasRenderingContext2D::scale(float sx, float sy) void CanvasRenderingContext2D::scale(float sx, float sy)
{ {
dbgln_if(CANVAS_RENDERING_CONTEXT_2D_DEBUG, "CanvasRenderingContext2D::scale({}, {})", sx, sy); dbgln_if(CANVAS_RENDERING_CONTEXT_2D_DEBUG, "CanvasRenderingContext2D::scale({}, {})", sx, sy);
m_drawing_state.transform.scale(sx, sy); drawing_state().transform.scale(sx, sy);
} }
void CanvasRenderingContext2D::translate(float tx, float ty) void CanvasRenderingContext2D::translate(float tx, float ty)
{ {
dbgln_if(CANVAS_RENDERING_CONTEXT_2D_DEBUG, "CanvasRenderingContext2D::translate({}, {})", tx, ty); dbgln_if(CANVAS_RENDERING_CONTEXT_2D_DEBUG, "CanvasRenderingContext2D::translate({}, {})", tx, ty);
m_drawing_state.transform.translate(tx, ty); drawing_state().transform.translate(tx, ty);
} }
void CanvasRenderingContext2D::rotate(float radians) void CanvasRenderingContext2D::rotate(float radians)
{ {
dbgln_if(CANVAS_RENDERING_CONTEXT_2D_DEBUG, "CanvasRenderingContext2D::rotate({})", radians); dbgln_if(CANVAS_RENDERING_CONTEXT_2D_DEBUG, "CanvasRenderingContext2D::rotate({})", radians);
m_drawing_state.transform.rotate_radians(radians); drawing_state().transform.rotate_radians(radians);
} }
void CanvasRenderingContext2D::did_draw(Gfx::FloatRect const&) void CanvasRenderingContext2D::did_draw(Gfx::FloatRect const&)
@ -302,10 +308,12 @@ void CanvasRenderingContext2D::fill_text(String const& text, float x, float y, O
if (!painter) if (!painter)
return; return;
auto& drawing_state = this->drawing_state();
// FIXME: painter only supports integer rects for text right now, so this effectively chops off any fractional position // FIXME: painter only supports integer rects for text right now, so this effectively chops off any fractional position
auto text_rect = Gfx::IntRect(x, y, max_width.has_value() ? max_width.value() : painter->font().width(text), painter->font().pixel_size()); auto text_rect = Gfx::IntRect(x, y, max_width.has_value() ? max_width.value() : painter->font().width(text), painter->font().pixel_size());
auto transformed_rect = m_drawing_state.transform.map(text_rect); auto transformed_rect = drawing_state.transform.map(text_rect);
painter->draw_text(transformed_rect, text, Gfx::TextAlignment::TopLeft, m_drawing_state.fill_style); painter->draw_text(transformed_rect, text, Gfx::TextAlignment::TopLeft, drawing_state.fill_style);
did_draw(transformed_rect.to_type<float>()); did_draw(transformed_rect.to_type<float>());
} }
@ -326,7 +334,9 @@ void CanvasRenderingContext2D::stroke_internal(Gfx::Path const& path)
if (!painter) if (!painter)
return; return;
painter->stroke_path(path, m_drawing_state.stroke_style, m_drawing_state.line_width); auto& drawing_state = this->drawing_state();
painter->stroke_path(path, drawing_state.stroke_style, drawing_state.line_width);
did_draw(path.bounding_box()); did_draw(path.bounding_box());
} }
@ -337,7 +347,7 @@ void CanvasRenderingContext2D::stroke()
void CanvasRenderingContext2D::stroke(Path2D const& path) void CanvasRenderingContext2D::stroke(Path2D const& path)
{ {
auto transformed_path = path.path().copy_transformed(m_drawing_state.transform); auto transformed_path = path.path().copy_transformed(drawing_state().transform);
stroke_internal(transformed_path); stroke_internal(transformed_path);
} }
@ -357,7 +367,7 @@ void CanvasRenderingContext2D::fill_internal(Gfx::Path& path, String const& fill
else else
dbgln("Unrecognized fillRule for CRC2D.fill() - this problem goes away once we pass an enum instead of a string"); dbgln("Unrecognized fillRule for CRC2D.fill() - this problem goes away once we pass an enum instead of a string");
painter->fill_path(path, m_drawing_state.fill_style, winding); painter->fill_path(path, drawing_state().fill_style, winding);
did_draw(path.bounding_box()); did_draw(path.bounding_box());
} }
@ -368,7 +378,7 @@ void CanvasRenderingContext2D::fill(String const& fill_rule)
void CanvasRenderingContext2D::fill(Path2D& path, String const& fill_rule) void CanvasRenderingContext2D::fill(Path2D& path, String const& fill_rule)
{ {
auto transformed_path = path.path().copy_transformed(m_drawing_state.transform); auto transformed_path = path.path().copy_transformed(drawing_state().transform);
return fill_internal(transformed_path, fill_rule); return fill_internal(transformed_path, fill_rule);
} }
@ -432,36 +442,6 @@ void CanvasRenderingContext2D::put_image_data(ImageData const& image_data, float
did_draw(Gfx::FloatRect(x, y, image_data.width(), image_data.height())); did_draw(Gfx::FloatRect(x, y, image_data.width(), image_data.height()));
} }
// https://html.spec.whatwg.org/multipage/canvas.html#dom-context-2d-save
void CanvasRenderingContext2D::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);
}
// https://html.spec.whatwg.org/multipage/canvas.html#dom-context-2d-restore
void CanvasRenderingContext2D::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();
}
// https://html.spec.whatwg.org/multipage/canvas.html#dom-context-2d-reset
void CanvasRenderingContext2D::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 CanvasRenderingContext2D::is_context_lost()
{
// The isContextLost() method steps are to return this's context lost.
return m_context_lost;
}
// https://html.spec.whatwg.org/multipage/canvas.html#reset-the-rendering-context-to-its-default-state // https://html.spec.whatwg.org/multipage/canvas.html#reset-the-rendering-context-to-its-default-state
void CanvasRenderingContext2D::reset_to_default_state() void CanvasRenderingContext2D::reset_to_default_state()
{ {
@ -475,10 +455,10 @@ void CanvasRenderingContext2D::reset_to_default_state()
path().clear(); path().clear();
// 3. Clear the context's drawing state stack. // 3. Clear the context's drawing state stack.
m_drawing_state_stack.clear(); clear_drawing_state_stack();
// 4. Reset everything that drawing state consists of to their initial values. // 4. Reset everything that drawing state consists of to their initial values.
m_drawing_state = {}; reset_drawing_state();
if (painter) if (painter)
did_draw(painter->target()->rect().to_type<float>()); did_draw(painter->target()->rect().to_type<float>());
@ -632,7 +612,7 @@ void CanvasRenderingContext2D::transform(double a, double b, double c, double d,
// a c e // a c e
// b d f // b d f
// 0 0 1 // 0 0 1
m_drawing_state.transform.multiply({ static_cast<float>(a), static_cast<float>(b), static_cast<float>(c), static_cast<float>(d), static_cast<float>(e), static_cast<float>(f) }); drawing_state().transform.multiply({ static_cast<float>(a), static_cast<float>(b), static_cast<float>(c), static_cast<float>(d), static_cast<float>(e), static_cast<float>(f) });
} }
// https://html.spec.whatwg.org/multipage/canvas.html#dom-context-2d-settransform // https://html.spec.whatwg.org/multipage/canvas.html#dom-context-2d-settransform
@ -643,7 +623,7 @@ void CanvasRenderingContext2D::set_transform(double a, double b, double c, doubl
return; return;
// 2. Reset the current transformation matrix to the identity matrix. // 2. Reset the current transformation matrix to the identity matrix.
m_drawing_state.transform = {}; drawing_state().transform = {};
// 3. Invoke the transform(a, b, c, d, e, f) method with the same arguments. // 3. Invoke the transform(a, b, c, d, e, f) method with the same arguments.
transform(a, b, c, d, e, f); transform(a, b, c, d, e, f);
@ -653,7 +633,7 @@ void CanvasRenderingContext2D::set_transform(double a, double b, double c, doubl
void CanvasRenderingContext2D::reset_transform() void CanvasRenderingContext2D::reset_transform()
{ {
// The resetTransform() method, when invoked, must reset the current transformation matrix to the identity matrix. // The resetTransform() method, when invoked, must reset the current transformation matrix to the identity matrix.
m_drawing_state.transform = {}; drawing_state().transform = {};
} }
void CanvasRenderingContext2D::clip() void CanvasRenderingContext2D::clip()

View file

@ -17,6 +17,7 @@
#include <LibWeb/Bindings/Wrappable.h> #include <LibWeb/Bindings/Wrappable.h>
#include <LibWeb/DOM/ExceptionOr.h> #include <LibWeb/DOM/ExceptionOr.h>
#include <LibWeb/HTML/Canvas/CanvasPath.h> #include <LibWeb/HTML/Canvas/CanvasPath.h>
#include <LibWeb/HTML/Canvas/CanvasState.h>
#include <LibWeb/HTML/CanvasGradient.h> #include <LibWeb/HTML/CanvasGradient.h>
#include <LibWeb/Layout/InlineNode.h> #include <LibWeb/Layout/InlineNode.h>
#include <LibWeb/Layout/LineBox.h> #include <LibWeb/Layout/LineBox.h>
@ -30,7 +31,8 @@ using CanvasImageSource = Variant<NonnullRefPtr<HTMLImageElement>, NonnullRefPtr
class CanvasRenderingContext2D class CanvasRenderingContext2D
: public RefCountForwarder<HTMLCanvasElement> : public RefCountForwarder<HTMLCanvasElement>
, public Bindings::Wrappable , public Bindings::Wrappable
, public CanvasPath { , public CanvasPath
, public CanvasState {
AK_MAKE_NONCOPYABLE(CanvasRenderingContext2D); AK_MAKE_NONCOPYABLE(CanvasRenderingContext2D);
AK_MAKE_NONMOVABLE(CanvasRenderingContext2D); AK_MAKE_NONMOVABLE(CanvasRenderingContext2D);
@ -59,8 +61,8 @@ public:
void translate(float x, float y); void translate(float x, float y);
void rotate(float degrees); void rotate(float degrees);
void set_line_width(float line_width) { m_drawing_state.line_width = line_width; } void set_line_width(float line_width) { drawing_state().line_width = line_width; }
float line_width() const { return m_drawing_state.line_width; } float line_width() const { return drawing_state().line_width; }
void begin_path(); void begin_path();
void stroke(); void stroke();
@ -76,12 +78,7 @@ public:
DOM::ExceptionOr<RefPtr<ImageData>> get_image_data(int x, int y, int width, int height) const; DOM::ExceptionOr<RefPtr<ImageData>> get_image_data(int x, int y, int width, int height) const;
void put_image_data(ImageData const&, float x, float y); void put_image_data(ImageData const&, float x, float y);
void save(); virtual void reset_to_default_state() override;
void restore();
void reset();
bool is_context_lost();
void reset_to_default_state();
NonnullRefPtr<HTMLCanvasElement> canvas_for_binding() const; NonnullRefPtr<HTMLCanvasElement> canvas_for_binding() const;
@ -121,22 +118,8 @@ private:
void stroke_internal(Gfx::Path const&); void stroke_internal(Gfx::Path const&);
void fill_internal(Gfx::Path&, String const& fill_rule); void fill_internal(Gfx::Path&, String const& fill_rule);
// https://html.spec.whatwg.org/multipage/canvas.html#drawing-state
struct DrawingState {
Gfx::AffineTransform transform;
Gfx::Color fill_style { Gfx::Color::Black };
Gfx::Color stroke_style { Gfx::Color::Black };
float line_width { 1 };
};
DrawingState m_drawing_state;
Vector<DrawingState> m_drawing_state_stack;
// https://html.spec.whatwg.org/multipage/canvas.html#concept-canvas-origin-clean // https://html.spec.whatwg.org/multipage/canvas.html#concept-canvas-origin-clean
bool m_origin_clean { true }; bool m_origin_clean { true };
// https://html.spec.whatwg.org/multipage/canvas.html#concept-canvas-context-lost
bool m_context_lost { false };
}; };
enum class CanvasImageSourceUsability { enum class CanvasImageSourceUsability {

View file

@ -4,6 +4,7 @@
#import <HTML/TextMetrics.idl> #import <HTML/TextMetrics.idl>
#import <HTML/CanvasGradient.idl> #import <HTML/CanvasGradient.idl>
#import <HTML/Canvas/CanvasPath.idl> #import <HTML/Canvas/CanvasPath.idl>
#import <HTML/Canvas/CanvasState.idl>
#import <HTML/Path2D.idl> #import <HTML/Path2D.idl>
// https://html.spec.whatwg.org/multipage/canvas.html#canvasrenderingcontext2d // https://html.spec.whatwg.org/multipage/canvas.html#canvasrenderingcontext2d
@ -41,11 +42,6 @@ interface CanvasRenderingContext2D {
ImageData getImageData(long sx, long sy, long sw, long sh); ImageData getImageData(long sx, long sy, long sw, long sh);
undefined putImageData(ImageData imagedata, double dx, double dy); undefined putImageData(ImageData imagedata, double dx, double dy);
undefined save();
undefined restore();
undefined reset();
boolean isContextLost();
[ImplementedAs=canvas_for_binding] readonly attribute HTMLCanvasElement canvas; [ImplementedAs=canvas_for_binding] readonly attribute HTMLCanvasElement canvas;
TextMetrics measureText(DOMString text); TextMetrics measureText(DOMString text);
@ -65,4 +61,5 @@ interface CanvasRenderingContext2D {
}; };
CanvasRenderingContext2D includes CanvasState;
CanvasRenderingContext2D includes CanvasPath; CanvasRenderingContext2D includes CanvasPath;