1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-25 04:57:44 +00:00

LibWeb: Add initial implementation of CRC2D.globalAlpha

Works for fills and strokes (using colors, gradients, or patterns),
along with images.

fill_rect() has been updated to use fill_path(), which allows it to
easily transform the rect, and already supports opacity.

Co-authored-by: MacDue <macdue@dueutil.tech>
This commit is contained in:
Simon Danner 2023-05-19 22:42:47 +02:00 committed by Andreas Kling
parent ff5d530aa3
commit 45f86466bb
8 changed files with 150 additions and 39 deletions

View file

@ -69,19 +69,28 @@ JS::NonnullGCPtr<HTMLCanvasElement> CanvasRenderingContext2D::canvas_for_binding
return *m_element;
}
Gfx::Path CanvasRenderingContext2D::rect_path(float x, float y, float width, float height)
{
auto& drawing_state = this->drawing_state();
auto top_left = drawing_state.transform.map(Gfx::FloatPoint(x, y));
auto top_right = drawing_state.transform.map(Gfx::FloatPoint(x + width, y));
auto bottom_left = drawing_state.transform.map(Gfx::FloatPoint(x, y + height));
auto bottom_right = drawing_state.transform.map(Gfx::FloatPoint(x + width, y + height));
Gfx::Path path;
path.move_to(top_left);
path.line_to(top_right);
path.line_to(bottom_right);
path.line_to(bottom_left);
path.line_to(top_left);
return path;
}
void CanvasRenderingContext2D::fill_rect(float x, float y, float width, float height)
{
draw_clipped([&](auto& painter) {
auto& drawing_state = this->drawing_state();
auto rect = drawing_state.transform.map(Gfx::FloatRect(x, y, width, height));
if (auto color = drawing_state.fill_style.as_color(); color.has_value()) {
painter.fill_rect(rect, *color);
} else {
// FIXME: This should use AntiAliasingPainter::fill_rect() too but that does not support PaintStyle yet.
painter.underlying_painter().fill_rect(rect.to_rounded<int>(), *drawing_state.fill_style.to_gfx_paint_style());
}
return rect;
});
return fill_internal(rect_path(x, y, width, height), Gfx::Painter::WindingRule::EvenOdd);
}
void CanvasRenderingContext2D::clear_rect(float x, float y, float width, float height)
@ -95,21 +104,7 @@ void CanvasRenderingContext2D::clear_rect(float x, float y, float width, float h
void CanvasRenderingContext2D::stroke_rect(float x, float y, float width, float height)
{
auto& drawing_state = this->drawing_state();
auto top_left = drawing_state.transform.map(Gfx::FloatPoint(x, y));
auto top_right = drawing_state.transform.map(Gfx::FloatPoint(x + width - 1, y));
auto bottom_left = drawing_state.transform.map(Gfx::FloatPoint(x, y + height - 1));
auto bottom_right = drawing_state.transform.map(Gfx::FloatPoint(x + width - 1, y + height - 1));
Gfx::Path path;
path.move_to(top_left);
path.line_to(top_right);
path.line_to(bottom_right);
path.line_to(bottom_left);
path.line_to(top_left);
stroke_internal(path);
stroke_internal(rect_path(x, y, width, height));
}
// 4.12.5.1.14 Drawing images, https://html.spec.whatwg.org/multipage/canvas.html#drawing-images
@ -164,7 +159,7 @@ WebIDL::ExceptionOr<void> CanvasRenderingContext2D::draw_image_internal(CanvasIm
scaling_mode = Gfx::Painter::ScalingMode::BilinearBlend;
}
painter.underlying_painter().draw_scaled_bitmap_with_transform(destination_rect.to_rounded<int>(), *bitmap, source_rect, drawing_state().transform, 1.0f, scaling_mode);
painter.underlying_painter().draw_scaled_bitmap_with_transform(destination_rect.to_rounded<int>(), *bitmap, source_rect, drawing_state().transform, drawing_state().global_alpha, scaling_mode);
// 7. If image is not origin-clean, then set the CanvasRenderingContext2D's origin-clean flag to false.
if (image_is_not_origin_clean(image))
@ -212,7 +207,8 @@ void CanvasRenderingContext2D::fill_text(DeprecatedString const& text, float x,
auto& base_painter = painter.underlying_painter();
auto text_rect = Gfx::FloatRect(x, y, max_width.has_value() ? static_cast<float>(max_width.value()) : base_painter.font().width(text), base_painter.font().pixel_size());
auto transformed_rect = drawing_state.transform.map(text_rect);
base_painter.draw_text(transformed_rect, text, Gfx::TextAlignment::TopLeft, drawing_state.fill_style.to_color_but_fixme_should_accept_any_paint_style());
auto color = drawing_state.fill_style.to_color_but_fixme_should_accept_any_paint_style();
base_painter.draw_text(transformed_rect, text, Gfx::TextAlignment::TopLeft, color.with_opacity(drawing_state.global_alpha));
return transformed_rect;
});
}
@ -233,9 +229,9 @@ void CanvasRenderingContext2D::stroke_internal(Gfx::Path const& path)
draw_clipped([&](auto& painter) {
auto& drawing_state = this->drawing_state();
if (auto color = drawing_state.stroke_style.as_color(); color.has_value()) {
painter.stroke_path(path, *color, drawing_state.line_width);
painter.stroke_path(path, color->with_opacity(drawing_state.global_alpha), drawing_state.line_width);
} else {
painter.stroke_path(path, drawing_state.stroke_style.to_gfx_paint_style(), drawing_state.line_width);
painter.stroke_path(path, drawing_state.stroke_style.to_gfx_paint_style(), drawing_state.line_width, drawing_state.global_alpha);
}
return path.bounding_box();
});
@ -262,17 +258,18 @@ static Gfx::Painter::WindingRule parse_fill_rule(StringView fill_rule)
return Gfx::Painter::WindingRule::Nonzero;
}
void CanvasRenderingContext2D::fill_internal(Gfx::Path& path, Gfx::Painter::WindingRule winding_rule)
void CanvasRenderingContext2D::fill_internal(Gfx::Path const& path, Gfx::Painter::WindingRule winding_rule)
{
draw_clipped([=, this](auto& painter) mutable {
path.close_all_subpaths();
draw_clipped([&, this](auto& painter) mutable {
auto path_to_fill = path;
path_to_fill.close_all_subpaths();
auto& drawing_state = this->drawing_state();
if (auto color = drawing_state.fill_style.as_color(); color.has_value()) {
painter.fill_path(path, *color, winding_rule);
painter.fill_path(path_to_fill, color->with_opacity(drawing_state.global_alpha), winding_rule);
} else {
painter.fill_path(path, drawing_state.fill_style.to_gfx_paint_style(), 1.0f, winding_rule);
painter.fill_path(path_to_fill, drawing_state.fill_style.to_gfx_paint_style(), drawing_state.global_alpha, winding_rule);
}
return path.bounding_box();
return path_to_fill.bounding_box();
});
}
@ -591,4 +588,20 @@ void CanvasRenderingContext2D::set_image_smoothing_quality(Bindings::ImageSmooth
drawing_state().image_smoothing_quality = quality;
}
float CanvasRenderingContext2D::global_alpha() const
{
return drawing_state().global_alpha;
}
// https://html.spec.whatwg.org/multipage/canvas.html#dom-context-2d-globalalpha
void CanvasRenderingContext2D::set_global_alpha(float alpha)
{
// 1. If the given value is either infinite, NaN, or not in the range 0.0 to 1.0, then return.
if (!isfinite(alpha) || alpha < 0.0f || alpha > 1.0f) {
return;
}
// 2. Otherwise, set this's global alpha to the given value.
drawing_state().global_alpha = alpha;
}
}