LibGfx: Add Painter::draw_quadratic_bezier_curve()

Also adds a QuadraticBezierCurveTo mode to Gfx::Path
This commit is contained in:
AnotherTest 2020-05-05 05:45:17 +04:30 committed by Andreas Kling
parent 73a7a589c2
commit 9f3f98d4c0
5 changed files with 75 additions and 2 deletions

View file

@ -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;
}
}
}

View file

@ -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);

View file

@ -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;

View file

@ -39,13 +39,15 @@ public:
Invalid,
MoveTo,
LineTo,
QuadraticBezierCurveTo,
};
Type type { Type::Invalid };
FloatPoint point;
Optional<FloatPoint> 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<Segment>& segments() const { return m_segments; }

View file

@ -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; }