Skip to content

Commit 62135be

Browse files
bderodnfield
authored andcommitted
Remove break corner cases, simplify strokes, and generate closed path joins (flutter#41)
1 parent 91ec4c2 commit 62135be

File tree

8 files changed

+281
-117
lines changed

8 files changed

+281
-117
lines changed

impeller/entity/contents.cc

+39-38
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
#include "impeller/entity/contents.h"
66

77
#include <memory>
8+
#include <tuple>
89

910
#include "flutter/fml/logging.h"
1011
#include "impeller/entity/content_context.h"
@@ -324,62 +325,60 @@ static VertexBuffer CreateSolidStrokeVertices(const Path& path,
324325
VertexBufferBuilder<VS::PerVertexData> vtx_builder;
325326
auto polyline = path.CreatePolyline();
326327

327-
size_t point_i = 0;
328328
if (polyline.points.size() < 2) {
329329
return {}; // Nothing to render.
330330
}
331331

332332
VS::PerVertexData vtx;
333333

334-
// Cursor state.
335-
Point direction;
334+
// Normal state.
336335
Point normal;
337336
Point previous_normal; // Used for computing joins.
338-
auto compute_normals = [&](size_t point_i) {
337+
338+
auto compute_normal = [&polyline, &normal, &previous_normal](size_t point_i) {
339339
previous_normal = normal;
340-
direction =
340+
Point direction =
341341
(polyline.points[point_i] - polyline.points[point_i - 1]).Normalize();
342342
normal = {-direction.y, direction.x};
343343
};
344-
compute_normals(1);
345344

346-
// Break state.
347-
auto breaks_it = polyline.breaks.begin();
348-
size_t break_end =
349-
breaks_it != polyline.breaks.end() ? *breaks_it : polyline.points.size();
345+
for (size_t contour_i = 0; contour_i < polyline.contours.size();
346+
contour_i++) {
347+
size_t contour_start_point_i, contour_end_point_i;
348+
std::tie(contour_start_point_i, contour_end_point_i) =
349+
polyline.GetContourPointBounds(contour_i);
350+
351+
if (contour_end_point_i - contour_start_point_i < 2) {
352+
continue; // This contour has no renderable content.
353+
}
350354

351-
while (point_i < polyline.points.size()) {
352-
if (point_i > 0) {
353-
compute_normals(point_i);
355+
// The first point's normal is always the same as
356+
compute_normal(contour_start_point_i + 1);
357+
const Point contour_first_normal = normal;
354358

359+
if (contour_i > 0) {
355360
// This branch only executes when we've just finished drawing a contour
356361
// and are switching to a new one.
357362
// We're drawing a triangle strip, so we need to "pick up the pen" by
358363
// appending transparent vertices between the end of the previous contour
359364
// and the beginning of the new contour.
360-
vtx.vertex_position = polyline.points[point_i - 1];
365+
vtx.vertex_position = polyline.points[contour_start_point_i - 1];
361366
vtx.vertex_normal = {};
362367
vtx.pen_down = 0.0;
363368
vtx_builder.AppendVertex(vtx);
364-
vtx.vertex_position = polyline.points[point_i];
369+
vtx.vertex_position = polyline.points[contour_start_point_i];
365370
vtx_builder.AppendVertex(vtx);
366371
}
367372

368373
// Generate start cap.
369-
CreateCap(vtx_builder, polyline.points[point_i], -direction);
374+
if (!polyline.contours[contour_i].is_closed) {
375+
CreateCap(vtx_builder, polyline.points[contour_start_point_i], -normal);
376+
}
370377

371378
// Generate contour geometry.
372-
size_t contour_point_i = 0;
373-
while (point_i < break_end) {
374-
if (contour_point_i > 0) {
375-
if (contour_point_i > 1) {
376-
// Generate join from the previous line to the current line.
377-
CreateJoin(vtx_builder, polyline.points[point_i - 1], previous_normal,
378-
normal);
379-
} else {
380-
compute_normals(point_i);
381-
}
382-
379+
for (size_t point_i = contour_start_point_i; point_i < contour_end_point_i;
380+
point_i++) {
381+
if (point_i > contour_start_point_i) {
383382
// Generate line rect.
384383
vtx.vertex_position = polyline.points[point_i - 1];
385384
vtx.pen_down = 1.0;
@@ -393,20 +392,22 @@ static VertexBuffer CreateSolidStrokeVertices(const Path& path,
393392
vtx.vertex_normal = -normal;
394393
vtx_builder.AppendVertex(vtx);
395394

396-
compute_normals(point_i + 1);
397-
}
395+
if (point_i < contour_end_point_i - 1) {
396+
compute_normal(point_i + 1);
398397

399-
++contour_point_i;
400-
++point_i;
398+
// Generate join from the current line to the next line.
399+
CreateJoin(vtx_builder, polyline.points[point_i], previous_normal,
400+
normal);
401+
}
402+
}
401403
}
402404

403-
// Generate end cap.
404-
CreateCap(vtx_builder, polyline.points[point_i - 1], -direction);
405-
406-
if (break_end < polyline.points.size()) {
407-
++breaks_it;
408-
break_end = breaks_it != polyline.breaks.end() ? *breaks_it
409-
: polyline.points.size();
405+
// Generate end cap or join.
406+
if (!polyline.contours[contour_i].is_closed) {
407+
CreateCap(vtx_builder, polyline.points[contour_end_point_i - 1], normal);
408+
} else {
409+
CreateJoin(vtx_builder, polyline.points[contour_start_point_i], normal,
410+
contour_first_normal);
410411
}
411412
}
412413

impeller/geometry/geometry_unittests.cc

+115-8
Original file line numberDiff line numberDiff line change
@@ -149,21 +149,21 @@ TEST(GeometryTest, SimplePath) {
149149
.AddQuadraticComponent({100, 100}, {200, 200}, {300, 300})
150150
.AddCubicComponent({300, 300}, {400, 400}, {500, 500}, {600, 600});
151151

152-
ASSERT_EQ(path.GetComponentCount(), 3u);
152+
ASSERT_EQ(path.GetComponentCount(), 4u);
153153

154154
path.EnumerateComponents(
155155
[](size_t index, const LinearPathComponent& linear) {
156156
Point p1(0, 0);
157157
Point p2(100, 100);
158-
ASSERT_EQ(index, 0u);
158+
ASSERT_EQ(index, 1u);
159159
ASSERT_EQ(linear.p1, p1);
160160
ASSERT_EQ(linear.p2, p2);
161161
},
162162
[](size_t index, const QuadraticPathComponent& quad) {
163163
Point p1(100, 100);
164164
Point cp(200, 200);
165165
Point p2(300, 300);
166-
ASSERT_EQ(index, 1u);
166+
ASSERT_EQ(index, 2u);
167167
ASSERT_EQ(quad.p1, p1);
168168
ASSERT_EQ(quad.cp, cp);
169169
ASSERT_EQ(quad.p2, p2);
@@ -173,13 +173,18 @@ TEST(GeometryTest, SimplePath) {
173173
Point cp1(400, 400);
174174
Point cp2(500, 500);
175175
Point p2(600, 600);
176-
ASSERT_EQ(index, 2u);
176+
ASSERT_EQ(index, 3u);
177177
ASSERT_EQ(cubic.p1, p1);
178178
ASSERT_EQ(cubic.cp1, cp1);
179179
ASSERT_EQ(cubic.cp2, cp2);
180180
ASSERT_EQ(cubic.p2, p2);
181181
},
182-
[](size_t index, const MovePathComponent& move) { ASSERT_TRUE(false); });
182+
[](size_t index, const ContourComponent& contour) {
183+
Point p1(0, 0);
184+
ASSERT_EQ(index, 0u);
185+
ASSERT_EQ(contour.destination, p1);
186+
ASSERT_FALSE(contour.is_closed);
187+
});
183188
}
184189

185190
TEST(GeometryTest, BoundingBoxCubic) {
@@ -638,15 +643,15 @@ TEST(GeometryTest, CubicPathComponentPolylineDoesNotIncludePointOne) {
638643

639644
TEST(GeometryTest, PathCreatePolyLineDoesNotDuplicatePoints) {
640645
Path path;
641-
path.AddMoveComponent({10, 10});
646+
path.AddContourComponent({10, 10});
642647
path.AddLinearComponent({10, 10}, {20, 20});
643648
path.AddLinearComponent({20, 20}, {30, 30});
644-
path.AddMoveComponent({40, 40});
649+
path.AddContourComponent({40, 40});
645650
path.AddLinearComponent({40, 40}, {50, 50});
646651

647652
auto polyline = path.CreatePolyline();
648653

649-
ASSERT_EQ(polyline.breaks.size(), 2u);
654+
ASSERT_EQ(polyline.contours.size(), 2u);
650655
ASSERT_EQ(polyline.points.size(), 5u);
651656
ASSERT_EQ(polyline.points[0].x, 10);
652657
ASSERT_EQ(polyline.points[1].x, 20);
@@ -655,5 +660,107 @@ TEST(GeometryTest, PathCreatePolyLineDoesNotDuplicatePoints) {
655660
ASSERT_EQ(polyline.points[4].x, 50);
656661
}
657662

663+
TEST(GeometryTest, PathBuilderSetsCorrectContourPropertiesForAddCommands) {
664+
// Closed shapes.
665+
{
666+
Path path = PathBuilder{}.AddCircle({100, 100}, 50).TakePath();
667+
ContourComponent contour;
668+
path.GetContourComponentAtIndex(0, contour);
669+
ASSERT_POINT_NEAR(contour.destination, Point(100, 50));
670+
ASSERT_TRUE(contour.is_closed);
671+
}
672+
673+
{
674+
Path path = PathBuilder{}.AddOval(Rect(100, 100, 100, 100)).TakePath();
675+
ContourComponent contour;
676+
path.GetContourComponentAtIndex(0, contour);
677+
ASSERT_POINT_NEAR(contour.destination, Point(150, 100));
678+
ASSERT_TRUE(contour.is_closed);
679+
}
680+
681+
{
682+
Path path = PathBuilder{}.AddRect(Rect(100, 100, 100, 100)).TakePath();
683+
ContourComponent contour;
684+
path.GetContourComponentAtIndex(0, contour);
685+
ASSERT_POINT_NEAR(contour.destination, Point(100, 100));
686+
ASSERT_TRUE(contour.is_closed);
687+
}
688+
689+
{
690+
Path path =
691+
PathBuilder{}.AddRoundedRect(Rect(100, 100, 100, 100), 10).TakePath();
692+
ContourComponent contour;
693+
path.GetContourComponentAtIndex(0, contour);
694+
ASSERT_POINT_NEAR(contour.destination, Point(110, 100));
695+
ASSERT_TRUE(contour.is_closed);
696+
}
697+
698+
// Open shapes.
699+
{
700+
Point p(100, 100);
701+
Path path = PathBuilder{}.AddLine(p, {200, 100}).TakePath();
702+
ContourComponent contour;
703+
path.GetContourComponentAtIndex(0, contour);
704+
ASSERT_POINT_NEAR(contour.destination, p);
705+
ASSERT_FALSE(contour.is_closed);
706+
}
707+
708+
{
709+
Path path =
710+
PathBuilder{}
711+
.AddCubicCurve({100, 100}, {100, 50}, {100, 150}, {200, 100})
712+
.TakePath();
713+
ContourComponent contour;
714+
path.GetContourComponentAtIndex(0, contour);
715+
ASSERT_POINT_NEAR(contour.destination, Point(100, 100));
716+
ASSERT_FALSE(contour.is_closed);
717+
}
718+
719+
{
720+
Path path = PathBuilder{}
721+
.AddQuadraticCurve({100, 100}, {100, 50}, {200, 100})
722+
.TakePath();
723+
ContourComponent contour;
724+
path.GetContourComponentAtIndex(0, contour);
725+
ASSERT_POINT_NEAR(contour.destination, Point(100, 100));
726+
ASSERT_FALSE(contour.is_closed);
727+
}
728+
}
729+
730+
TEST(GeometryTest, PathCreatePolylineGeneratesCorrectContourData) {
731+
Path::Polyline polyline = PathBuilder{}
732+
.AddLine({100, 100}, {200, 100})
733+
.MoveTo({100, 200})
734+
.LineTo({150, 250})
735+
.LineTo({200, 200})
736+
.Close()
737+
.TakePath()
738+
.CreatePolyline();
739+
ASSERT_EQ(polyline.points.size(), 6u);
740+
ASSERT_EQ(polyline.contours.size(), 2u);
741+
ASSERT_EQ(polyline.contours[0].is_closed, false);
742+
ASSERT_EQ(polyline.contours[0].start_index, 0u);
743+
ASSERT_EQ(polyline.contours[1].is_closed, true);
744+
ASSERT_EQ(polyline.contours[1].start_index, 2u);
745+
}
746+
747+
TEST(GeometryTest, PolylineGetContourPointBoundsReturnsCorrectRanges) {
748+
Path::Polyline polyline = PathBuilder{}
749+
.AddLine({100, 100}, {200, 100})
750+
.MoveTo({100, 200})
751+
.LineTo({150, 250})
752+
.LineTo({200, 200})
753+
.Close()
754+
.TakePath()
755+
.CreatePolyline();
756+
size_t a1, a2, b1, b2;
757+
std::tie(a1, a2) = polyline.GetContourPointBounds(0);
758+
std::tie(b1, b2) = polyline.GetContourPointBounds(1);
759+
ASSERT_EQ(a1, 0u);
760+
ASSERT_EQ(a2, 2u);
761+
ASSERT_EQ(b1, 2u);
762+
ASSERT_EQ(b2, 6u);
763+
}
764+
658765
} // namespace testing
659766
} // namespace impeller

0 commit comments

Comments
 (0)