mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-05-02 09:18:52 +00:00
LibGfx: Implement filling paths
There are some imperfections with intersecting edges (because the main algorithm used is scanline, and that is not geared towards drawing complex shapes), however, it behaves mostly fine for normal use :^)
This commit is contained in:
parent
4d20cf57db
commit
f54b41f748
Notes:
sideshowbarker
2024-07-19 06:56:14 +09:00
Author: https://github.com/alimpfard
Commit: f54b41f748
Pull-request: https://github.com/SerenityOS/serenity/pull/2129
Reviewed-by: https://github.com/awesomekling
5 changed files with 274 additions and 8 deletions
|
@ -24,7 +24,10 @@
|
|||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <AK/Function.h>
|
||||
#include <AK/QuickSort.h>
|
||||
#include <AK/StringBuilder.h>
|
||||
#include <LibGfx/Painter.h>
|
||||
#include <LibGfx/Path.h>
|
||||
|
||||
namespace Gfx {
|
||||
|
@ -34,6 +37,8 @@ void Path::close()
|
|||
if (m_segments.size() <= 1)
|
||||
return;
|
||||
|
||||
invalidate_split_lines();
|
||||
|
||||
auto& last_point = m_segments.last().point;
|
||||
|
||||
for (ssize_t i = m_segments.size() - 1; i >= 0; --i) {
|
||||
|
@ -80,4 +85,60 @@ String Path::to_string() const
|
|||
return builder.to_string();
|
||||
}
|
||||
|
||||
void Path::segmentize_path()
|
||||
{
|
||||
Vector<LineSegment> segments;
|
||||
|
||||
auto add_line = [&](const auto& p0, const auto& p1) {
|
||||
if (p0.y() == p1.y())
|
||||
return; // horizontal lines are not needed (there's nothing to fill inside)
|
||||
float ymax = p0.y(), ymin = p1.y(), x_of_ymin = p1.x(), x_of_ymax = p0.x();
|
||||
auto slope = p0.x() == p1.x() ? 0 : ((float)(p0.y() - p1.y())) / ((float)(p0.x() - p1.x()));
|
||||
if (p0.y() < p1.y()) {
|
||||
ymin = ymax;
|
||||
ymax = p1.y();
|
||||
x_of_ymax = x_of_ymin;
|
||||
x_of_ymin = p0.x();
|
||||
}
|
||||
|
||||
segments.append({ Point(p0.x(), p0.y()),
|
||||
Point(p1.x(), p1.y()),
|
||||
slope == 0 ? 0 : 1 / slope,
|
||||
x_of_ymin,
|
||||
ymax, ymin, x_of_ymax });
|
||||
};
|
||||
|
||||
FloatPoint cursor { 0, 0 };
|
||||
for (auto& segment : m_segments) {
|
||||
switch (segment.type) {
|
||||
case Segment::Type::MoveTo:
|
||||
cursor = segment.point;
|
||||
break;
|
||||
case Segment::Type::LineTo: {
|
||||
add_line(cursor, segment.point);
|
||||
cursor = segment.point;
|
||||
break;
|
||||
}
|
||||
case Segment::Type::QuadraticBezierCurveTo: {
|
||||
auto& control = segment.through.value();
|
||||
Painter::for_each_line_segment_on_bezier_curve(control, cursor, segment.point, [&](const FloatPoint& p0, const FloatPoint& p1) {
|
||||
add_line(Point(p0.x(), p0.y()), Point(p1.x(), p1.y()));
|
||||
});
|
||||
cursor = segment.point;
|
||||
break;
|
||||
}
|
||||
case Segment::Type::Invalid:
|
||||
ASSERT_NOT_REACHED();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// sort segments by ymax
|
||||
quick_sort(segments, [](const auto& line0, const auto& line1) {
|
||||
return line1.maximum_y < line0.maximum_y;
|
||||
});
|
||||
|
||||
m_split_lines = move(segments);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue