Skip to content

Commit f41c0a9

Browse files
committed
Support subcontours in strokes, lay groundwork for fills (flutter#31)
1 parent 2dceced commit f41c0a9

12 files changed

+183
-36
lines changed

impeller/compiler/reflector.cc

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -611,6 +611,14 @@ static VertexType VertexTypeFromInputResource(
611611
type.columns == 1u && type.vecsize == 3u &&
612612
type.width == sizeof(float) * 8u) {
613613
result.type_name = "Vector3";
614+
} else if (type.basetype == spirv_cross::SPIRType::BaseType::Float &&
615+
type.columns == 1u && type.vecsize == 1u &&
616+
type.width == sizeof(float) * 8u) {
617+
result.type_name = "Scalar";
618+
} else if (type.basetype == spirv_cross::SPIRType::BaseType::Int &&
619+
type.columns == 1u && type.vecsize == 1u &&
620+
type.width == sizeof(int32_t) * 8u) {
621+
result.type_name = "int32_t";
614622
} else {
615623
// Catch all unknown padding.
616624
result.type_name = TypeNameWithPaddingOfSize(total_size);

impeller/entity/contents.cc

Lines changed: 22 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -235,8 +235,7 @@ bool TextureContents::Render(const ContentContext& renderer,
235235
auto coverage_coords =
236236
(vtx - coverage_rect->origin) / coverage_rect->size;
237237
data.texture_coords =
238-
(source_rect_.origin +
239-
source_rect_.size * coverage_coords) /
238+
(source_rect_.origin + source_rect_.size * coverage_coords) /
240239
texture_size;
241240
vertex_builder.AppendVertex(data);
242241
});
@@ -301,11 +300,13 @@ static VertexBuffer CreateSolidStrokeVertices(const Path& path,
301300
VertexBufferBuilder<VS::PerVertexData> vtx_builder;
302301
auto polyline = path.CreatePolyline();
303302

304-
for (size_t i = 0, polyline_size = polyline.size(); i < polyline_size; i++) {
303+
for (size_t i = 0, polyline_size = polyline.points.size(); i < polyline_size;
304+
i++) {
305305
const auto is_last_point = i == polyline_size - 1;
306306

307-
const auto& p1 = polyline[i];
308-
const auto& p2 = is_last_point ? polyline[i - 1] : polyline[i + 1];
307+
const auto& p1 = polyline.points[i];
308+
const auto& p2 =
309+
is_last_point ? polyline.points[i - 1] : polyline.points[i + 1];
309310

310311
const auto diff = p2 - p1;
311312

@@ -316,18 +317,27 @@ static VertexBuffer CreateSolidStrokeVertices(const Path& path,
316317

317318
VS::PerVertexData vtx;
318319
vtx.vertex_position = p1;
319-
320-
if (i == 0) {
321-
vtx.vertex_normal = -normal;
322-
vtx_builder.AppendVertex(vtx);
323-
vtx.vertex_normal = normal;
324-
vtx_builder.AppendVertex(vtx);
325-
}
320+
auto pen_down =
321+
polyline.breaks.find(i) == polyline.breaks.end() ? 1.0 : 0.0;
326322

327323
vtx.vertex_normal = normal;
324+
vtx.pen_down = pen_down;
328325
vtx_builder.AppendVertex(vtx);
326+
329327
vtx.vertex_normal = -normal;
328+
vtx.pen_down = pen_down;
330329
vtx_builder.AppendVertex(vtx);
330+
331+
// Put the pen down again for the next contour.
332+
if (!pen_down) {
333+
vtx.vertex_normal = normal;
334+
vtx.pen_down = 1.0;
335+
vtx_builder.AppendVertex(vtx);
336+
337+
vtx.vertex_normal = -normal;
338+
vtx.pen_down = 1.0;
339+
vtx_builder.AppendVertex(vtx);
340+
}
331341
}
332342

333343
return vtx_builder.CreateVertexBuffer(buffer);

impeller/entity/entity_unittests.cc

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,47 @@ TEST_F(EntityTest, CanDrawRect) {
2525
ASSERT_TRUE(OpenPlaygroundHere(entity));
2626
}
2727

28+
TEST_F(EntityTest, ThreeStrokesInOnePath) {
29+
Path path = PathBuilder{}
30+
.MoveTo({100, 100})
31+
.LineTo({100, 200})
32+
.MoveTo({100, 300})
33+
.LineTo({100, 400})
34+
.MoveTo({100, 500})
35+
.LineTo({100, 600})
36+
.TakePath();
37+
38+
Entity entity;
39+
entity.SetPath(path);
40+
auto contents = std::make_unique<SolidStrokeContents>();
41+
contents->SetColor(Color::Red());
42+
contents->SetStrokeSize(5.0);
43+
entity.SetContents(std::move(contents));
44+
ASSERT_TRUE(OpenPlaygroundHere(entity));
45+
}
46+
47+
TEST_F(EntityTest, TriangleInsideASquare) {
48+
Path path = PathBuilder{}
49+
.MoveTo({10, 10})
50+
.LineTo({210, 10})
51+
.LineTo({210, 210})
52+
.LineTo({10, 210})
53+
.Close()
54+
.MoveTo({50, 50})
55+
.LineTo({100, 50})
56+
.LineTo({50, 150})
57+
.Close()
58+
.TakePath();
59+
60+
Entity entity;
61+
entity.SetPath(path);
62+
auto contents = std::make_unique<SolidStrokeContents>();
63+
contents->SetColor(Color::Red());
64+
contents->SetStrokeSize(5.0);
65+
entity.SetContents(std::move(contents));
66+
ASSERT_TRUE(OpenPlaygroundHere(entity));
67+
}
68+
2869
TEST_F(EntityTest, DISABLED_BadCubicCurveTest) {
2970
// Compare with https://fiddle.skia.org/c/b3625f26122c9de7afe7794fcf25ead3
3071
Path path =
@@ -45,7 +86,6 @@ TEST_F(EntityTest, DISABLED_BadCubicCurveTest) {
4586
entity.SetPath(path);
4687
entity.SetContents(SolidColorContents::Make(Color::Red()));
4788
ASSERT_TRUE(OpenPlaygroundHere(entity));
48-
4989
}
5090

5191
TEST_F(EntityTest, DISABLED_BadCubicCurveAndOverlapTest) {

impeller/entity/shaders/solid_stroke.frag

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,11 @@
33
// found in the LICENSE file.
44

55
in vec4 stroke_color;
6+
in float v_pen_down;
67

78
out vec4 frag_color;
89

910
void main() {
1011
frag_color = stroke_color;
12+
frag_color.a *= floor(v_pen_down);
1113
}

impeller/entity/shaders/solid_stroke.vert

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,15 @@ uniform StrokeInfo {
1313

1414
in vec2 vertex_position;
1515
in vec2 vertex_normal;
16+
in float pen_down;
1617

1718
out vec4 stroke_color;
19+
out float v_pen_down;
1820

1921
void main() {
2022
// Push one vertex by the half stroke size along the normal vector.
2123
vec2 offset = vertex_normal * vec2(stroke_info.size * 0.5);
2224
gl_Position = frame_info.mvp * vec4(vertex_position + offset, 0.0, 1.0);
2325
stroke_color = stroke_info.color;
26+
v_pen_down = pen_down;
2427
}

impeller/geometry/geometry_unittests.cc

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,8 @@ TEST(GeometryTest, SimplePath) {
178178
ASSERT_EQ(cubic.cp1, cp1);
179179
ASSERT_EQ(cubic.cp2, cp2);
180180
ASSERT_EQ(cubic.p2, p2);
181-
});
181+
},
182+
[](size_t index, const MovePathComponent& move) { ASSERT_TRUE(false); });
182183
}
183184

184185
TEST(GeometryTest, BoundingBoxCubic) {
@@ -586,7 +587,7 @@ TEST(GeometryTest, RectContainsRect) {
586587
}
587588

588589
TEST(GeometryTest, CubicPathComponentPolylineDoesNotIncludePointOne) {
589-
CubicPathComponent component({10, 10}, {20,35}, {35, 20}, {40, 40});
590+
CubicPathComponent component({10, 10}, {20, 35}, {35, 20}, {40, 40});
590591
SmoothingApproximation approximation;
591592
auto polyline = component.CreatePolyline(approximation);
592593
ASSERT_NE(polyline.front().x, 10);

impeller/geometry/path.cc

Lines changed: 53 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -42,10 +42,16 @@ Path& Path::AddCubicComponent(Point p1, Point cp1, Point cp2, Point p2) {
4242
return *this;
4343
}
4444

45-
void Path::EnumerateComponents(
46-
Applier<LinearPathComponent> linear_applier,
47-
Applier<QuadraticPathComponent> quad_applier,
48-
Applier<CubicPathComponent> cubic_applier) const {
45+
Path& Path::AddMoveComponent(Point destination) {
46+
moves_.emplace_back(destination);
47+
components_.emplace_back(ComponentType::kMove, moves_.size() - 1);
48+
return *this;
49+
}
50+
51+
void Path::EnumerateComponents(Applier<LinearPathComponent> linear_applier,
52+
Applier<QuadraticPathComponent> quad_applier,
53+
Applier<CubicPathComponent> cubic_applier,
54+
Applier<MovePathComponent> move_applier) const {
4955
size_t currentIndex = 0;
5056
for (const auto& component : components_) {
5157
switch (component.type) {
@@ -64,6 +70,11 @@ void Path::EnumerateComponents(
6470
cubic_applier(currentIndex, cubics_[component.index]);
6571
}
6672
break;
73+
case ComponentType::kMove:
74+
if (move_applier) {
75+
move_applier(currentIndex, moves_[component.index]);
76+
}
77+
break;
6778
}
6879
currentIndex++;
6980
}
@@ -112,6 +123,20 @@ bool Path::GetCubicComponentAtIndex(size_t index,
112123
return true;
113124
}
114125

126+
bool Path::GetMoveComponentAtIndex(size_t index,
127+
MovePathComponent& move) const {
128+
if (index >= components_.size()) {
129+
return false;
130+
}
131+
132+
if (components_[index].type != ComponentType::kMove) {
133+
return false;
134+
}
135+
136+
move = moves_[components_[index].index];
137+
return true;
138+
}
139+
115140
bool Path::UpdateLinearComponentAtIndex(size_t index,
116141
const LinearPathComponent& linear) {
117142
if (index >= components_.size()) {
@@ -155,12 +180,27 @@ bool Path::UpdateCubicComponentAtIndex(size_t index,
155180
return true;
156181
}
157182

158-
std::vector<Point> Path::CreatePolyline(
183+
bool Path::UpdateMoveComponentAtIndex(size_t index,
184+
const MovePathComponent& move) {
185+
if (index >= components_.size()) {
186+
return false;
187+
}
188+
189+
if (components_[index].type != ComponentType::kMove) {
190+
return false;
191+
}
192+
193+
moves_[components_[index].index] = move;
194+
return true;
195+
}
196+
197+
Path::Polyline Path::CreatePolyline(
159198
const SmoothingApproximation& approximation) const {
160-
std::vector<Point> points;
161-
auto collect_points = [&points](const std::vector<Point>& collection) {
162-
points.reserve(points.size() + collection.size());
163-
points.insert(points.end(), collection.begin(), collection.end());
199+
Polyline polyline;
200+
auto collect_points = [&polyline](const std::vector<Point>& collection) {
201+
polyline.points.reserve(polyline.points.size() + collection.size());
202+
polyline.points.insert(polyline.points.end(), collection.begin(),
203+
collection.end());
164204
};
165205
for (const auto& component : components_) {
166206
switch (component.type) {
@@ -173,9 +213,12 @@ std::vector<Point> Path::CreatePolyline(
173213
case ComponentType::kCubic:
174214
collect_points(cubics_[component.index].CreatePolyline(approximation));
175215
break;
216+
case ComponentType::kMove:
217+
polyline.breaks.insert(polyline.points.size());
218+
break;
176219
}
177220
}
178-
return points;
221+
return polyline;
179222
}
180223

181224
std::optional<Rect> Path::GetBoundingBox() const {

impeller/geometry/path.h

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
#include <functional>
88
#include <optional>
9+
#include <set>
910
#include <vector>
1011

1112
#include "impeller/geometry/path_component.h"
@@ -22,7 +23,9 @@ enum class FillType {
2223

2324
//------------------------------------------------------------------------------
2425
/// @brief Paths are lightweight objects that describe a collection of
25-
/// linear, quadratic, or cubic segments.
26+
/// linear, quadratic, or cubic segments. These segments may be
27+
/// be broken up by move commands, which are effectively linear
28+
/// commands that pick up the pen rather than continuing to draw.
2629
///
2730
/// All shapes supported by Impeller are paths either directly or
2831
/// via approximation (in the case of circles).
@@ -36,6 +39,17 @@ class Path {
3639
kLinear,
3740
kQuadratic,
3841
kCubic,
42+
kMove,
43+
};
44+
45+
/// One or more contours represented as a series of points and indices in
46+
/// the point vector representing the start of a new contour.
47+
struct Polyline {
48+
/// Points in the polyline, which may represent multiple contours specified
49+
/// by indices in |breaks|.
50+
std::vector<Point> points;
51+
/// Indices of points that end a subcontour.
52+
std::set<size_t> breaks;
3953
};
4054

4155
Path();
@@ -54,11 +68,14 @@ class Path {
5468

5569
Path& AddCubicComponent(Point p1, Point cp1, Point cp2, Point p2);
5670

71+
Path& AddMoveComponent(Point destination);
72+
5773
template <class T>
5874
using Applier = std::function<void(size_t index, const T& component)>;
5975
void EnumerateComponents(Applier<LinearPathComponent> linearApplier,
6076
Applier<QuadraticPathComponent> quadApplier,
61-
Applier<CubicPathComponent> cubicApplier) const;
77+
Applier<CubicPathComponent> cubicApplier,
78+
Applier<MovePathComponent> moveApplier) const;
6279

6380
bool GetLinearComponentAtIndex(size_t index,
6481
LinearPathComponent& linear) const;
@@ -68,6 +85,8 @@ class Path {
6885

6986
bool GetCubicComponentAtIndex(size_t index, CubicPathComponent& cubic) const;
7087

88+
bool GetMoveComponentAtIndex(size_t index, MovePathComponent& move) const;
89+
7190
bool UpdateLinearComponentAtIndex(size_t index,
7291
const LinearPathComponent& linear);
7392

@@ -76,7 +95,9 @@ class Path {
7695

7796
bool UpdateCubicComponentAtIndex(size_t index, CubicPathComponent& cubic);
7897

79-
std::vector<Point> CreatePolyline(
98+
bool UpdateMoveComponentAtIndex(size_t index, const MovePathComponent& move);
99+
100+
Polyline CreatePolyline(
80101
const SmoothingApproximation& approximation = {}) const;
81102

82103
std::optional<Rect> GetBoundingBox() const;
@@ -99,6 +120,7 @@ class Path {
99120
std::vector<LinearPathComponent> linears_;
100121
std::vector<QuadraticPathComponent> quads_;
101122
std::vector<CubicPathComponent> cubics_;
123+
std::vector<MovePathComponent> moves_;
102124
};
103125

104126
} // namespace impeller

impeller/geometry/path_builder.cc

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ Path PathBuilder::TakePath(FillType fill) {
2727
PathBuilder& PathBuilder::MoveTo(Point point, bool relative) {
2828
current_ = relative ? current_ + point : point;
2929
subpath_start_ = current_;
30+
prototype_.AddMoveComponent(current_);
3031
return *this;
3132
}
3233

@@ -343,7 +344,10 @@ PathBuilder& PathBuilder::AddPath(const Path& path) {
343344
auto cubic = [&](size_t index, const CubicPathComponent& c) {
344345
prototype_.AddCubicComponent(c.p1, c.cp1, c.cp2, c.p2);
345346
};
346-
path.EnumerateComponents(linear, quadratic, cubic);
347+
auto move = [&](size_t index, const MovePathComponent& m) {
348+
prototype_.AddMoveComponent(m.destination);
349+
};
350+
path.EnumerateComponents(linear, quadratic, cubic, move);
347351
return *this;
348352
}
349353

0 commit comments

Comments
 (0)