mirror of
https://github.com/RGBCube/serenity
synced 2025-07-25 16:47:36 +00:00
LibGfx: Add Painter::draw_quadratic_bezier_curve()
Also adds a QuadraticBezierCurveTo mode to Gfx::Path
This commit is contained in:
parent
73a7a589c2
commit
9f3f98d4c0
5 changed files with 75 additions and 2 deletions
|
@ -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)
|
void Painter::add_clip_rect(const Rect& rect)
|
||||||
{
|
{
|
||||||
state().clip_rect.intersect(rect.translated(m_clip_origin.location()));
|
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);
|
draw_line(Point(cursor.x(), cursor.y()), Point(segment.point.x(), segment.point.y()), color, thickness);
|
||||||
cursor = segment.point;
|
cursor = segment.point;
|
||||||
break;
|
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -55,6 +55,7 @@ public:
|
||||||
void draw_ellipse_intersecting(const Rect&, Color, int thickness = 1);
|
void draw_ellipse_intersecting(const Rect&, Color, int thickness = 1);
|
||||||
void set_pixel(const Point&, Color);
|
void set_pixel(const Point&, Color);
|
||||||
void draw_line(const Point&, const Point&, Color, int thickness = 1, bool dotted = false);
|
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 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(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);
|
void blit_dimmed(const Point&, const Gfx::Bitmap&, const Rect& src_rect);
|
||||||
|
|
|
@ -59,6 +59,9 @@ String Path::to_string() const
|
||||||
case Segment::Type::LineTo:
|
case Segment::Type::LineTo:
|
||||||
builder.append("LineTo");
|
builder.append("LineTo");
|
||||||
break;
|
break;
|
||||||
|
case Segment::Type::QuadraticBezierCurveTo:
|
||||||
|
builder.append("QuadraticBezierCurveTo");
|
||||||
|
break;
|
||||||
case Segment::Type::Invalid:
|
case Segment::Type::Invalid:
|
||||||
builder.append("Invalid");
|
builder.append("Invalid");
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -39,13 +39,15 @@ public:
|
||||||
Invalid,
|
Invalid,
|
||||||
MoveTo,
|
MoveTo,
|
||||||
LineTo,
|
LineTo,
|
||||||
|
QuadraticBezierCurveTo,
|
||||||
};
|
};
|
||||||
|
|
||||||
Type type { Type::Invalid };
|
Type type { Type::Invalid };
|
||||||
FloatPoint point;
|
FloatPoint point;
|
||||||
|
Optional<FloatPoint> through {};
|
||||||
};
|
};
|
||||||
|
|
||||||
Path() {}
|
Path() { }
|
||||||
|
|
||||||
void move_to(const FloatPoint& point)
|
void move_to(const FloatPoint& point)
|
||||||
{
|
{
|
||||||
|
@ -57,6 +59,11 @@ public:
|
||||||
m_segments.append({ Segment::Type::LineTo, point });
|
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();
|
void close();
|
||||||
|
|
||||||
const Vector<Segment>& segments() const { return m_segments; }
|
const Vector<Segment>& segments() const { return m_segments; }
|
||||||
|
|
|
@ -38,7 +38,7 @@ class Rect;
|
||||||
|
|
||||||
class Point {
|
class Point {
|
||||||
public:
|
public:
|
||||||
Point() {}
|
Point() { }
|
||||||
Point(int x, int y)
|
Point(int x, int y)
|
||||||
: m_x(x)
|
: m_x(x)
|
||||||
, m_y(y)
|
, 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+(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;
|
String to_string() const;
|
||||||
|
|
||||||
bool is_null() const { return !m_x && !m_y; }
|
bool is_null() const { return !m_x && !m_y; }
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue