LibGfx+LibWeb: Move generally useful path methods to LibGfx

This adds:

  - Path::rect()
  - Path::quad()
  - Path::rounded_rect()

Moving the corresponding implementations from LibWeb.
This commit is contained in:
MacDue 2024-06-02 12:38:51 +01:00 committed by Nico Weber
parent f9c944cc85
commit 318f2925d3
13 changed files with 114 additions and 89 deletions

View file

@ -6,6 +6,7 @@
#pragma once
#include <LibGfx/CornerRadius.h>
#include <LibGfx/Painter.h>
#include <LibGfx/Path.h>
#include <LibGfx/Quad.h>
@ -56,22 +57,6 @@ public:
void fill_rect_with_rounded_corners(IntRect const&, Color, int radius);
void fill_rect_with_rounded_corners(IntRect const&, Color, int top_left_radius, int top_right_radius, int bottom_right_radius, int bottom_left_radius);
struct CornerRadius {
int horizontal_radius;
int vertical_radius;
inline operator bool() const
{
return horizontal_radius > 0 && vertical_radius > 0;
}
Gfx::IntRect as_rect() const
{
return { 0, 0, horizontal_radius, vertical_radius };
}
};
void fill_rect_with_rounded_corners(IntRect const&, Color, CornerRadius top_left, CornerRadius top_right, CornerRadius bottom_right, CornerRadius bottom_left, BlendMode blend_mode = BlendMode::Normal);
Gfx::Painter& underlying_painter() { return m_underlying_painter; }

View file

@ -0,0 +1,26 @@
/*
* Copyright (c) 2024, MacDue <macdue@dueutil.tech>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <LibGfx/Rect.h>
namespace Gfx {
struct CornerRadius {
int horizontal_radius;
int vertical_radius;
inline operator bool() const
{
return horizontal_radius > 0 && vertical_radius > 0;
}
Gfx::IntRect as_rect() const
{
return { 0, 0, horizontal_radius, vertical_radius };
}
};
}

View file

@ -158,6 +158,56 @@ void Path::elliptical_arc_to(FloatPoint point, FloatSize radii, float x_axis_rot
theta_delta);
}
void Path::quad(FloatQuad const& quad)
{
move_to(quad.p1());
line_to(quad.p2());
line_to(quad.p3());
line_to(quad.p4());
close();
}
void Path::rounded_rect(FloatRect const& rect, CornerRadius top_left, CornerRadius top_right, CornerRadius bottom_right, CornerRadius bottom_left)
{
auto x = rect.x();
auto y = rect.y();
auto width = rect.width();
auto height = rect.height();
if (top_left)
move_to({ x + top_left.horizontal_radius, y });
else
move_to({ x, y });
if (top_right) {
horizontal_line_to(x + width - top_right.horizontal_radius);
elliptical_arc_to({ x + width, y + top_right.horizontal_radius }, { top_right.horizontal_radius, top_right.vertical_radius }, 0, false, true);
} else {
horizontal_line_to(x + width);
}
if (bottom_right) {
vertical_line_to(y + height - bottom_right.vertical_radius);
elliptical_arc_to({ x + width - bottom_right.horizontal_radius, y + height }, { bottom_right.horizontal_radius, bottom_right.vertical_radius }, 0, false, true);
} else {
vertical_line_to(y + height);
}
if (bottom_left) {
horizontal_line_to(x + bottom_left.horizontal_radius);
elliptical_arc_to({ x, y + height - bottom_left.vertical_radius }, { bottom_left.horizontal_radius, bottom_left.vertical_radius }, 0, false, true);
} else {
horizontal_line_to(x);
}
if (top_left) {
vertical_line_to(y + top_left.vertical_radius);
elliptical_arc_to({ x + top_left.horizontal_radius, y }, { top_left.horizontal_radius, top_left.vertical_radius }, 0, false, true);
} else {
vertical_line_to(y);
}
}
void Path::text(Utf8View text, Font const& font)
{
if (!is<ScaledFont>(font)) {

View file

@ -9,9 +9,11 @@
#include <AK/ByteString.h>
#include <AK/Optional.h>
#include <AK/Vector.h>
#include <LibGfx/CornerRadius.h>
#include <LibGfx/Forward.h>
#include <LibGfx/Line.h>
#include <LibGfx/Point.h>
#include <LibGfx/Quad.h>
#include <LibGfx/Rect.h>
namespace Gfx {
@ -183,6 +185,15 @@ public:
elliptical_arc_to(point, { radius, radius }, 0, large_arc, sweep);
}
void rect(FloatRect const& rect)
{
return quad(rect);
}
void rounded_rect(FloatRect const& rect, CornerRadius top_left, CornerRadius top_right, CornerRadius bottom_right, CornerRadius bottom_left);
void quad(FloatQuad const& quad);
void text(Utf8View, Font const&);
FloatPoint last_point()

View file

@ -7,6 +7,7 @@
#pragma once
#include <LibGfx/Point.h>
#include <LibGfx/Rect.h>
#include <LibGfx/Triangle.h>
namespace Gfx {
@ -22,6 +23,11 @@ public:
{
}
Quad(Rect<T> const& rect)
: Quad(rect.top_left(), rect.top_right(), rect.bottom_right(), rect.bottom_left())
{
}
Point<T> const& p1() const { return m_p1; }
Point<T> const& p2() const { return m_p2; }
Point<T> const& p3() const { return m_p3; }

View file

@ -40,17 +40,11 @@ void AffineCommandExecutorCPU::prepare_clipping(Gfx::IntRect bounding_rect)
m_expensive_clipping_target = Gfx::Bitmap::create(Gfx::BitmapFormat::BGRA8888, clip_bounds.size()).release_value_but_fixme_should_propagate_errors();
m_expensive_clipping_mask = Gfx::Bitmap::create(Gfx::BitmapFormat::BGRA8888, clip_bounds.size()).release_value_but_fixme_should_propagate_errors();
auto const& clip_quad = current_stacking_context.clip.quad;
// Prepare clip mask:
m_painter = Gfx::Painter(*m_expensive_clipping_mask);
m_painter.translate(-clip_bounds.top_left());
Gfx::Path clip_path;
clip_path.move_to(clip_quad.p1());
clip_path.line_to(clip_quad.p2());
clip_path.line_to(clip_quad.p3());
clip_path.line_to(clip_quad.p4());
clip_path.close();
clip_path.quad(current_stacking_context.clip.quad);
aa_painter().fill_path(clip_path, Gfx::Color::Black, Gfx::Painter::WindingRule::EvenOdd);
// Prepare painter:
@ -72,17 +66,6 @@ void AffineCommandExecutorCPU::flush_clipping()
m_painter.add_clip_rect(current_stacking_context.clip.bounds);
}
static Gfx::Path rect_path(Gfx::FloatRect const& rect)
{
Gfx::Path path;
path.move_to({ rect.x(), rect.y() });
path.line_to({ rect.x() + rect.width(), rect.y() });
path.line_to({ rect.x() + rect.width(), rect.y() + rect.height() });
path.line_to({ rect.x(), rect.y() + rect.height() });
path.close();
return path;
}
AffineCommandExecutorCPU::AffineCommandExecutorCPU(Gfx::Bitmap& bitmap, Gfx::AffineTransform transform, Gfx::IntRect clip)
: m_painter(bitmap)
{
@ -112,8 +95,9 @@ CommandResult AffineCommandExecutorCPU::fill_rect(FillRect const& command)
{
prepare_clipping(command.bounding_rect());
// FIXME: Support clip_paths.
auto path = rect_path(command.rect.to_type<float>()).copy_transformed(stacking_context().transform);
aa_painter().fill_path(path, command.color, Gfx::Painter::WindingRule::EvenOdd);
Gfx::Path path;
path.rect(command.rect.to_type<float>());
aa_painter().fill_path(path.copy_transformed(stacking_context().transform), command.color, Gfx::Painter::WindingRule::EvenOdd);
return CommandResult::Continue;
}
@ -253,45 +237,7 @@ CommandResult AffineCommandExecutorCPU::fill_rect_with_rounded_corners(FillRectW
{
prepare_clipping(command.bounding_rect());
Gfx::Path path;
auto x = command.rect.x();
auto y = command.rect.y();
auto width = command.rect.width();
auto height = command.rect.height();
if (command.top_left_radius)
path.move_to({ x + command.top_left_radius.horizontal_radius, y });
else
path.move_to({ x, y });
if (command.top_right_radius) {
path.horizontal_line_to(x + width - command.top_right_radius.horizontal_radius);
path.elliptical_arc_to({ x + width, y + command.top_right_radius.horizontal_radius }, { command.top_right_radius.horizontal_radius, command.top_right_radius.vertical_radius }, 0, false, true);
} else {
path.horizontal_line_to(x + width);
}
if (command.bottom_right_radius) {
path.vertical_line_to(y + height - command.bottom_right_radius.vertical_radius);
path.elliptical_arc_to({ x + width - command.bottom_right_radius.horizontal_radius, y + height }, { command.bottom_right_radius.horizontal_radius, command.bottom_right_radius.vertical_radius }, 0, false, true);
} else {
path.vertical_line_to(y + height);
}
if (command.bottom_left_radius) {
path.horizontal_line_to(x + command.bottom_left_radius.horizontal_radius);
path.elliptical_arc_to({ x, y + height - command.bottom_left_radius.vertical_radius }, { command.bottom_left_radius.horizontal_radius, command.bottom_left_radius.vertical_radius }, 0, false, true);
} else {
path.horizontal_line_to(x);
}
if (command.top_left_radius) {
path.vertical_line_to(y + command.top_left_radius.vertical_radius);
path.elliptical_arc_to({ x + command.top_left_radius.horizontal_radius, y }, { command.top_left_radius.horizontal_radius, command.top_left_radius.vertical_radius }, 0, false, true);
} else {
path.vertical_line_to(y);
}
path.rounded_rect(command.rect.to_type<float>(), command.top_left_radius, command.top_right_radius, command.bottom_right_radius, command.bottom_left_radius);
path = path.copy_transformed(stacking_context().transform);
aa_painter().fill_path(path, command.color, Gfx::Painter::WindingRule::EvenOdd);
return CommandResult::Continue;
@ -363,8 +309,9 @@ CommandResult AffineCommandExecutorCPU::apply_backdrop_filter(ApplyBackdropFilte
CommandResult AffineCommandExecutorCPU::draw_rect(DrawRect const& command)
{
prepare_clipping(command.bounding_rect());
auto path = rect_path(command.rect.to_type<float>()).copy_transformed(stacking_context().transform);
aa_painter().stroke_path(path, command.color, 1);
Gfx::Path path;
path.rect(command.rect.to_type<float>());
aa_painter().stroke_path(path.copy_transformed(stacking_context().transform), command.color, 1);
return CommandResult::Continue;
}

View file

@ -61,7 +61,7 @@ Gfx::Color border_color(BorderEdge edge, BordersDataDevicePixels const& borders_
return border_data.color;
}
void paint_border(RecordingPainter& painter, BorderEdge edge, DevicePixelRect const& rect, Gfx::AntiAliasingPainter::CornerRadius const& radius, Gfx::AntiAliasingPainter::CornerRadius const& opposite_radius, BordersDataDevicePixels const& borders_data, Gfx::Path& path, bool last)
void paint_border(RecordingPainter& painter, BorderEdge edge, DevicePixelRect const& rect, Gfx::CornerRadius const& radius, Gfx::CornerRadius const& opposite_radius, BordersDataDevicePixels const& borders_data, Gfx::Path& path, bool last)
{
auto const& border_data = [&] {
switch (edge) {

View file

@ -26,7 +26,7 @@ enum class BorderEdge {
// Returns OptionalNone if there is no outline to paint.
Optional<BordersData> borders_data_for_outline(Layout::Node const&, Color outline_color, CSS::OutlineStyle outline_style, CSSPixels outline_width);
void paint_border(RecordingPainter& painter, BorderEdge edge, DevicePixelRect const& rect, Gfx::AntiAliasingPainter::CornerRadius const& radius, Gfx::AntiAliasingPainter::CornerRadius const& opposite_radius, BordersDataDevicePixels const& borders_data, Gfx::Path& path, bool last);
void paint_border(RecordingPainter& painter, BorderEdge edge, DevicePixelRect const& rect, Gfx::CornerRadius const& radius, Gfx::CornerRadius const& opposite_radius, BordersDataDevicePixels const& borders_data, Gfx::Path& path, bool last);
void paint_all_borders(RecordingPainter& painter, DevicePixelRect const& border_rect, CornerRadii const& corner_radii, BordersDataDevicePixels const&);
Gfx::Color border_color(BorderEdge edge, BordersDataDevicePixels const& borders_data);

View file

@ -10,9 +10,9 @@
namespace Web::Painting {
Gfx::AntiAliasingPainter::CornerRadius BorderRadiusData::as_corner(PaintContext const& context) const
Gfx::CornerRadius BorderRadiusData::as_corner(PaintContext const& context) const
{
return Gfx::AntiAliasingPainter::CornerRadius {
return Gfx::CornerRadius {
context.floored_device_pixels(horizontal_radius).value(),
context.floored_device_pixels(vertical_radius).value()
};

View file

@ -17,7 +17,7 @@ struct BorderRadiusData {
CSSPixels horizontal_radius { 0 };
CSSPixels vertical_radius { 0 };
Gfx::AntiAliasingPainter::CornerRadius as_corner(PaintContext const& context) const;
Gfx::CornerRadius as_corner(PaintContext const& context) const;
inline operator bool() const
{
@ -39,7 +39,7 @@ struct BorderRadiusData {
}
};
using CornerRadius = Gfx::AntiAliasingPainter::CornerRadius;
using CornerRadius = Gfx::CornerRadius;
struct CornerRadii {
CornerRadius top_left;

View file

@ -169,10 +169,10 @@ struct PaintTextShadow {
struct FillRectWithRoundedCorners {
Gfx::IntRect rect;
Color color;
Gfx::AntiAliasingPainter::CornerRadius top_left_radius;
Gfx::AntiAliasingPainter::CornerRadius top_right_radius;
Gfx::AntiAliasingPainter::CornerRadius bottom_left_radius;
Gfx::AntiAliasingPainter::CornerRadius bottom_right_radius;
Gfx::CornerRadius top_left_radius;
Gfx::CornerRadius top_right_radius;
Gfx::CornerRadius bottom_left_radius;
Gfx::CornerRadius bottom_right_radius;
Vector<Gfx::Path> clip_paths;
[[nodiscard]] Gfx::IntRect bounding_rect() const { return rect; }

View file

@ -369,7 +369,7 @@ void RecordingPainter::paint_text_shadow(int blur_radius, Gfx::IntRect bounding_
.draw_location = state().translation.map(draw_location) });
}
void RecordingPainter::fill_rect_with_rounded_corners(Gfx::IntRect const& rect, Color color, Gfx::AntiAliasingPainter::CornerRadius top_left_radius, Gfx::AntiAliasingPainter::CornerRadius top_right_radius, Gfx::AntiAliasingPainter::CornerRadius bottom_right_radius, Gfx::AntiAliasingPainter::CornerRadius bottom_left_radius, Vector<Gfx::Path> const& clip_paths)
void RecordingPainter::fill_rect_with_rounded_corners(Gfx::IntRect const& rect, Color color, Gfx::CornerRadius top_left_radius, Gfx::CornerRadius top_right_radius, Gfx::CornerRadius bottom_right_radius, Gfx::CornerRadius bottom_left_radius, Vector<Gfx::Path> const& clip_paths)
{
if (rect.is_empty())
return;

View file

@ -135,7 +135,7 @@ public:
void paint_inner_box_shadow_params(PaintOuterBoxShadowParams params);
void paint_text_shadow(int blur_radius, Gfx::IntRect bounding_rect, Gfx::IntRect text_rect, Span<Gfx::DrawGlyphOrEmoji const> glyph_run, Color color, int fragment_baseline, Gfx::IntPoint draw_location);
void fill_rect_with_rounded_corners(Gfx::IntRect const& rect, Color color, Gfx::AntiAliasingPainter::CornerRadius top_left_radius, Gfx::AntiAliasingPainter::CornerRadius top_right_radius, Gfx::AntiAliasingPainter::CornerRadius bottom_right_radius, Gfx::AntiAliasingPainter::CornerRadius bottom_left_radius, Vector<Gfx::Path> const& clip_paths = {});
void fill_rect_with_rounded_corners(Gfx::IntRect const& rect, Color color, Gfx::CornerRadius top_left_radius, Gfx::CornerRadius top_right_radius, Gfx::CornerRadius bottom_right_radius, Gfx::CornerRadius bottom_left_radius, Vector<Gfx::Path> const& clip_paths = {});
void fill_rect_with_rounded_corners(Gfx::IntRect const& a_rect, Color color, int radius, Vector<Gfx::Path> const& clip_paths = {});
void fill_rect_with_rounded_corners(Gfx::IntRect const& a_rect, Color color, int top_left_radius, int top_right_radius, int bottom_right_radius, int bottom_left_radius, Vector<Gfx::Path> const& clip_paths = {});