mirror of
https://github.com/SerenityOS/serenity
synced 2024-10-15 04:13:11 +00:00
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:
parent
f9c944cc85
commit
318f2925d3
|
@ -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; }
|
||||
|
|
26
Userland/Libraries/LibGfx/CornerRadius.h
Normal file
26
Userland/Libraries/LibGfx/CornerRadius.h
Normal 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 };
|
||||
}
|
||||
};
|
||||
}
|
|
@ -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)) {
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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; }
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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()
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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; }
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 = {});
|
||||
|
||||
|
|
Loading…
Reference in a new issue