From 9f3f98d4c0c111496e35010ae3488dc172da56db Mon Sep 17 00:00:00 2001 From: AnotherTest Date: Tue, 5 May 2020 05:45:17 +0430 Subject: [PATCH] LibGfx: Add Painter::draw_quadratic_bezier_curve() Also adds a QuadraticBezierCurveTo mode to Gfx::Path --- Libraries/LibGfx/Painter.cpp | 46 ++++++++++++++++++++++++++++++++++++ Libraries/LibGfx/Painter.h | 1 + Libraries/LibGfx/Path.cpp | 3 +++ Libraries/LibGfx/Path.h | 9 ++++++- Libraries/LibGfx/Point.h | 18 +++++++++++++- 5 files changed, 75 insertions(+), 2 deletions(-) diff --git a/Libraries/LibGfx/Painter.cpp b/Libraries/LibGfx/Painter.cpp index 6435512404..85c21d0121 100644 --- a/Libraries/LibGfx/Painter.cpp +++ b/Libraries/LibGfx/Painter.cpp @@ -1049,6 +1049,47 @@ void Painter::draw_line(const Point& p1, const Point& p2, Color color, int thick } } +static void draw_split_quadratic_bezier_curve(Painter& painter, const Point& original_control, const Point& p1, const Point& p2, Color color, int thickness, bool dotted) +{ + auto po1_midpoint = original_control + p1; + po1_midpoint /= 2; + + auto po2_midpoint = original_control + p2; + po2_midpoint /= 2; + + auto new_segment = po1_midpoint + po2_midpoint; + new_segment /= 2; + + painter.draw_quadratic_bezier_curve(po1_midpoint, p1, new_segment, color, thickness, dotted); + painter.draw_quadratic_bezier_curve(po2_midpoint, new_segment, p2, color, thickness, dotted); +} + +static bool can_approximate_bezier_curve(const Point& p1, const Point& p2, const Point& control) +{ + constexpr static int tolerance = 15; + + auto p1x = 3 * control.x() - 2 * p1.x() - p2.x(); + auto p1y = 3 * control.y() - 2 * p1.y() - p2.y(); + auto p2x = 3 * control.x() - 2 * p2.x() - p1.x(); + auto p2y = 3 * control.y() - 2 * p2.y() - p1.y(); + + p1x = p1x * p1x; + p1y = p1y * p1y; + p2x = p2x * p2x; + p2y = p2y * p2y; + + return max(p1x, p2x) + max(p1y, p2y) <= tolerance; +} + +void Painter::draw_quadratic_bezier_curve(const Point& control_point, const Point& p1, const Point& p2, Color color, int thickness, bool dotted) +{ + if (can_approximate_bezier_curve(p1, p2, control_point)) { + draw_line(p1, p2, color, thickness, dotted); + } else { + draw_split_quadratic_bezier_curve(*this, control_point, p1, p2, color, thickness, dotted); + } +} + void Painter::add_clip_rect(const Rect& rect) { state().clip_rect.intersect(rect.translated(m_clip_origin.location())); @@ -1087,6 +1128,11 @@ void Painter::stroke_path(const Path& path, Color color, int thickness) draw_line(Point(cursor.x(), cursor.y()), Point(segment.point.x(), segment.point.y()), color, thickness); cursor = segment.point; break; + case Path::Segment::Type::QuadraticBezierCurveTo: + ASSERT(segment.through.has_value()); + draw_quadratic_bezier_curve(Point(segment.through.value().x(), segment.through.value().y()), Point(cursor.x(), cursor.y()), Point(segment.point.x(), segment.point.y()), color, thickness); + cursor = segment.point; + break; } } } diff --git a/Libraries/LibGfx/Painter.h b/Libraries/LibGfx/Painter.h index b7b452303a..97fe226eeb 100644 --- a/Libraries/LibGfx/Painter.h +++ b/Libraries/LibGfx/Painter.h @@ -55,6 +55,7 @@ public: void draw_ellipse_intersecting(const Rect&, Color, int thickness = 1); void set_pixel(const Point&, Color); void draw_line(const Point&, const Point&, Color, int thickness = 1, bool dotted = false); + void draw_quadratic_bezier_curve(const Point& control_point, const Point&, const Point&, Color, int thickness = 1, bool dotted = false); void draw_scaled_bitmap(const Rect& dst_rect, const Gfx::Bitmap&, const Rect& src_rect); void blit(const Point&, const Gfx::Bitmap&, const Rect& src_rect, float opacity = 1.0f); void blit_dimmed(const Point&, const Gfx::Bitmap&, const Rect& src_rect); diff --git a/Libraries/LibGfx/Path.cpp b/Libraries/LibGfx/Path.cpp index cd7e3dfcb8..da6c9358db 100644 --- a/Libraries/LibGfx/Path.cpp +++ b/Libraries/LibGfx/Path.cpp @@ -59,6 +59,9 @@ String Path::to_string() const case Segment::Type::LineTo: builder.append("LineTo"); break; + case Segment::Type::QuadraticBezierCurveTo: + builder.append("QuadraticBezierCurveTo"); + break; case Segment::Type::Invalid: builder.append("Invalid"); break; diff --git a/Libraries/LibGfx/Path.h b/Libraries/LibGfx/Path.h index 2d9c74495c..43f773a819 100644 --- a/Libraries/LibGfx/Path.h +++ b/Libraries/LibGfx/Path.h @@ -39,13 +39,15 @@ public: Invalid, MoveTo, LineTo, + QuadraticBezierCurveTo, }; Type type { Type::Invalid }; FloatPoint point; + Optional through {}; }; - Path() {} + Path() { } void move_to(const FloatPoint& point) { @@ -57,6 +59,11 @@ public: m_segments.append({ Segment::Type::LineTo, point }); } + void quadratic_bezier_curve_to(const FloatPoint& through, const FloatPoint& point) + { + m_segments.append({ Segment::Type::QuadraticBezierCurveTo, point, through }); + } + void close(); const Vector& segments() const { return m_segments; } diff --git a/Libraries/LibGfx/Point.h b/Libraries/LibGfx/Point.h index 67a123cb4c..daf466d6a3 100644 --- a/Libraries/LibGfx/Point.h +++ b/Libraries/LibGfx/Point.h @@ -38,7 +38,7 @@ class Rect; class Point { public: - Point() {} + Point() { } Point(int x, int y) : m_x(x) , m_y(y) @@ -107,6 +107,22 @@ public: } Point operator+(const Point& other) const { return { m_x + other.m_x, m_y + other.m_y }; } + Point& operator*=(int factor) + { + m_x *= factor; + m_y *= factor; + return *this; + } + Point operator*(int factor) const { return { m_x * factor, m_y * factor }; } + + Point& operator/=(int factor) + { + m_x /= factor; + m_y /= factor; + return *this; + } + Point operator/(int factor) const { return { m_x / factor, m_y / factor }; } + String to_string() const; bool is_null() const { return !m_x && !m_y; }