| Flutter Impeller |

stroke_path_geometry.cc
Go to the documentation of this file.
1 // Copyright 2013 The Flutter Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "impeller/entity/geometry/stroke_path_geometry.h"
6
7 #include "impeller/core/buffer_view.h"
8 #include "impeller/core/formats.h"
9 #include "impeller/entity/geometry/geometry.h"
10 #include "impeller/entity/texture_fill.vert.h"
11 #include "impeller/geometry/path_builder.h"
12 #include "impeller/geometry/path_component.h"
13
14 namespace impeller {
15 using VS = SolidFillVertexShader;
16
17 namespace {
18
19 template <typename VertexWriter>
20 using CapProc = std::function<void(VertexWriter& vtx_builder,
21 const Point& position,
22 const Point& offset,
23 Scalar scale,
24 bool reverse)>;
25
26 template <typename VertexWriter>
27 using JoinProc = std::function<void(VertexWriter& vtx_builder,
28 const Point& position,
29 const Point& start_offset,
30 const Point& end_offset,
31 Scalar miter_limit,
32 Scalar scale)>;
33
34 class PositionWriter {
35 public:
36 void AppendVertex(const Point& point) {
37 data_.emplace_back(SolidFillVertexShader::PerVertexData{.position = point});
38 }
39
40 const std::vector<SolidFillVertexShader::PerVertexData>& GetData() const {
41 return data_;
42 }
43
44 private:
45 std::vector<SolidFillVertexShader::PerVertexData> data_ = {};
46 };
47
48 class PositionUVWriter {
49 public:
50 PositionUVWriter(const Point& texture_origin,
51 const Size& texture_size,
52 const Matrix& effect_transform)
53 : texture_origin_(texture_origin),
54 texture_size_(texture_size),
55 effect_transform_(effect_transform) {}
56
57 const std::vector<TextureFillVertexShader::PerVertexData>& GetData() {
58 if (effect_transform_.IsIdentity()) {
59 auto origin = texture_origin_;
60 auto scale = 1.0 / texture_size_;
61
62 for (auto& pvd : data_) {
63 pvd.texture_coords = (pvd.position - origin) * scale;
64 }
65 } else {
66 auto texture_rect = Rect::MakeOriginSize(texture_origin_, texture_size_);
67 Matrix uv_transform =
68 texture_rect.GetNormalizingTransform() * effect_transform_;
69
70 for (auto& pvd : data_) {
71 pvd.texture_coords = uv_transform * pvd.position;
72 }
73 }
74 return data_;
75 }
76
77 void AppendVertex(const Point& point) {
78 data_.emplace_back(TextureFillVertexShader::PerVertexData{
79 .position = point,
80 // .texture_coords = default, will be filled in during |GetData()|
81 });
82 }
83
84 private:
85 std::vector<TextureFillVertexShader::PerVertexData> data_ = {};
86 const Point texture_origin_;
87 const Size texture_size_;
88 const Matrix effect_transform_;
89 };
90
91 template <typename VertexWriter>
92 class StrokeGenerator {
93 public:
94 StrokeGenerator(const Path::Polyline& p_polyline,
95 const Scalar p_stroke_width,
96 const Scalar p_scaled_miter_limit,
97 const JoinProc<VertexWriter>& p_join_proc,
98 const CapProc<VertexWriter>& p_cap_proc,
99 const Scalar p_scale)
100 : polyline(p_polyline),
101 stroke_width(p_stroke_width),
102 scaled_miter_limit(p_scaled_miter_limit),
103 join_proc(p_join_proc),
104 cap_proc(p_cap_proc),
105 scale(p_scale) {}
106
107 void Generate(VertexWriter& vtx_builder) {
108 for (size_t contour_i = 0; contour_i < polyline.contours.size();
109 contour_i++) {
110 const Path::PolylineContour& contour = polyline.contours[contour_i];
111 size_t contour_start_point_i, contour_end_point_i;
112 std::tie(contour_start_point_i, contour_end_point_i) =
113 polyline.GetContourPointBounds(contour_i);
114
115 auto contour_delta = contour_end_point_i - contour_start_point_i;
116 if (contour_delta == 1) {
117 Point p = polyline.GetPoint(contour_start_point_i);
118 cap_proc(vtx_builder, p, {-stroke_width * 0.5f, 0}, scale,
119 /*reverse=*/false);
120 cap_proc(vtx_builder, p, {stroke_width * 0.5f, 0}, scale,
121 /*reverse=*/false);
122 continue;
123 } else if (contour_delta == 0) {
124 continue; // This contour has no renderable content.
125 }
126
127 previous_offset = offset;
128 offset = ComputeOffset(contour_start_point_i, contour_start_point_i,
129 contour_end_point_i, contour);
130 const Point contour_first_offset = offset;
131
132 if (contour_i > 0) {
133 // This branch only executes when we've just finished drawing a contour
134 // and are switching to a new one.
135 // We're drawing a triangle strip, so we need to "pick up the pen" by
136 // appending two vertices at the end of the previous contour and two
137 // vertices at the start of the new contour (thus connecting the two
138 // contours with two zero volume triangles, which will be discarded by
139 // the rasterizer).
140 vtx.position = polyline.GetPoint(contour_start_point_i - 1);
141 // Append two vertices when "picking up" the pen so that the triangle
142 // drawn when moving to the beginning of the new contour will have zero
143 // volume.
144 vtx_builder.AppendVertex(vtx.position);
145 vtx_builder.AppendVertex(vtx.position);
146
147 vtx.position = polyline.GetPoint(contour_start_point_i);
148 // Append two vertices at the beginning of the new contour, which
149 // appends two triangles of zero area.
150 vtx_builder.AppendVertex(vtx.position);
151 vtx_builder.AppendVertex(vtx.position);
152 }
153
154 // Generate start cap.
155 if (!polyline.contours[contour_i].is_closed) {
156 Point cap_offset =
157 Vector2(-contour.start_direction.y, contour.start_direction.x) *
158 stroke_width * 0.5f; // Counterclockwise normal
159 cap_proc(vtx_builder, polyline.GetPoint(contour_start_point_i),
160 cap_offset, scale, /*reverse=*/true);
161 }
162
163 for (size_t contour_component_i = 0;
164 contour_component_i < contour.components.size();
165 contour_component_i++) {
166 const Path::PolylineContour::Component& component =
167 contour.components[contour_component_i];
168 bool is_last_component =
169 contour_component_i == contour.components.size() - 1;
170
171 size_t component_start_index = component.component_start_index;
172 size_t component_end_index =
173 is_last_component ? contour_end_point_i - 1
174 : contour.components[contour_component_i + 1]
175 .component_start_index;
176 if (component.is_curve) {
177 AddVerticesForCurveComponent(
178 vtx_builder, component_start_index, component_end_index,
179 contour_start_point_i, contour_end_point_i, contour);
180 } else {
181 AddVerticesForLinearComponent(
182 vtx_builder, component_start_index, component_end_index,
183 contour_start_point_i, contour_end_point_i, contour);
184 }
185 }
186
187 // Generate end cap or join.
188 if (!contour.is_closed) {
189 auto cap_offset =
190 Vector2(-contour.end_direction.y, contour.end_direction.x) *
191 stroke_width * 0.5f; // Clockwise normal
192 cap_proc(vtx_builder, polyline.GetPoint(contour_end_point_i - 1),
193 cap_offset, scale, /*reverse=*/false);
194 } else {
195 join_proc(vtx_builder, polyline.GetPoint(contour_start_point_i), offset,
196 contour_first_offset, scaled_miter_limit, scale);
197 }
198 }
199 }
200
201 /// Computes offset by calculating the direction from point_i - 1 to point_i
202 /// if point_i is within `contour_start_point_i` and `contour_end_point_i`;
203 /// Otherwise, it uses direction from contour.
204 Point ComputeOffset(const size_t point_i,
205 const size_t contour_start_point_i,
206 const size_t contour_end_point_i,
207 const Path::PolylineContour& contour) const {
208 Point direction;
209 if (point_i >= contour_end_point_i) {
210 direction = contour.end_direction;
211 } else if (point_i <= contour_start_point_i) {
212 direction = -contour.start_direction;
213 } else {
214 direction = (polyline.GetPoint(point_i) - polyline.GetPoint(point_i - 1))
215 .Normalize();
216 }
217 return Vector2{-direction.y, direction.x} * stroke_width * 0.5f;
218 }
219
220 void AddVerticesForLinearComponent(VertexWriter& vtx_builder,
221 const size_t component_start_index,
222 const size_t component_end_index,
223 const size_t contour_start_point_i,
224 const size_t contour_end_point_i,
225 const Path::PolylineContour& contour) {
226 bool is_last_component = component_start_index ==
227 contour.components.back().component_start_index;
228
229 for (size_t point_i = component_start_index; point_i < component_end_index;
230 point_i++) {
231 bool is_end_of_component = point_i == component_end_index - 1;
232 vtx.position = polyline.GetPoint(point_i) + offset;
233 vtx_builder.AppendVertex(vtx.position);
234 vtx.position = polyline.GetPoint(point_i) - offset;
235 vtx_builder.AppendVertex(vtx.position);
236
237 // For line components, two additional points need to be appended
238 // prior to appending a join connecting the next component.
239 vtx.position = polyline.GetPoint(point_i + 1) + offset;
240 vtx_builder.AppendVertex(vtx.position);
241 vtx.position = polyline.GetPoint(point_i + 1) - offset;
242 vtx_builder.AppendVertex(vtx.position);
243
244 previous_offset = offset;
245 offset = ComputeOffset(point_i + 2, contour_start_point_i,
246 contour_end_point_i, contour);
247 if (!is_last_component && is_end_of_component) {
248 // Generate join from the current line to the next line.
249 join_proc(vtx_builder, polyline.GetPoint(point_i + 1), previous_offset,
250 offset, scaled_miter_limit, scale);
251 }
252 }
253 }
254
255 void AddVerticesForCurveComponent(VertexWriter& vtx_builder,
256 const size_t component_start_index,
257 const size_t component_end_index,
258 const size_t contour_start_point_i,
259 const size_t contour_end_point_i,
260 const Path::PolylineContour& contour) {
261 bool is_last_component = component_start_index ==
262 contour.components.back().component_start_index;
263
264 for (size_t point_i = component_start_index; point_i < component_end_index;
265 point_i++) {
266 bool is_end_of_component = point_i == component_end_index - 1;
267
268 vtx.position = polyline.GetPoint(point_i) + offset;
269 vtx_builder.AppendVertex(vtx.position);
270 vtx.position = polyline.GetPoint(point_i) - offset;
271 vtx_builder.AppendVertex(vtx.position);
272
273 previous_offset = offset;
274 offset = ComputeOffset(point_i + 2, contour_start_point_i,
275 contour_end_point_i, contour);
276 // For curve components, the polyline is detailed enough such that
277 // it can avoid worrying about joins altogether.
278 if (is_end_of_component) {
279 vtx.position = polyline.GetPoint(point_i + 1) + offset;
280 vtx_builder.AppendVertex(vtx.position);
281 vtx.position = polyline.GetPoint(point_i + 1) - offset;
282 vtx_builder.AppendVertex(vtx.position);
283 // Generate join from the current line to the next line.
284 if (!is_last_component) {
285 join_proc(vtx_builder, polyline.GetPoint(point_i + 1),
286 previous_offset, offset, scaled_miter_limit, scale);
287 }
288 }
289 }
290 }
291
292 const Path::Polyline& polyline;
293 const Scalar stroke_width;
294 const Scalar scaled_miter_limit;
295 const JoinProc<VertexWriter>& join_proc;
296 const CapProc<VertexWriter>& cap_proc;
297 const Scalar scale;
298
299 Point previous_offset;
300 Point offset;
301 SolidFillVertexShader::PerVertexData vtx;
302 };
303
304 template <typename VertexWriter>
305 void CreateButtCap(VertexWriter& vtx_builder,
306 const Point& position,
307 const Point& offset,
308 Scalar scale,
309 bool reverse) {
310 Point orientation = offset * (reverse ? -1 : 1);
311 VS::PerVertexData vtx;
312 vtx.position = position + orientation;
313 vtx_builder.AppendVertex(vtx.position);
314 vtx.position = position - orientation;
315 vtx_builder.AppendVertex(vtx.position);
316 }
317
318 template <typename VertexWriter>
319 void CreateRoundCap(VertexWriter& vtx_builder,
320 const Point& position,
321 const Point& offset,
322 Scalar scale,
323 bool reverse) {
324 Point orientation = offset * (reverse ? -1 : 1);
325 Point forward(offset.y, -offset.x);
326 Point forward_normal = forward.Normalize();
327
328 CubicPathComponent arc;
329 if (reverse) {
330 arc = CubicPathComponent(
331 forward, forward + orientation * PathBuilder::kArcApproximationMagic,
332 orientation + forward * PathBuilder::kArcApproximationMagic,
333 orientation);
334 } else {
335 arc = CubicPathComponent(
336 orientation,
337 orientation + forward * PathBuilder::kArcApproximationMagic,
338 forward + orientation * PathBuilder::kArcApproximationMagic, forward);
339 }
340
341 Point vtx = position + orientation;
342 vtx_builder.AppendVertex(vtx);
343 vtx = position - orientation;
344 vtx_builder.AppendVertex(vtx);
345
346 arc.ToLinearPathComponents(scale, [&vtx_builder, &vtx, forward_normal,
347 position](const Point& point) {
348 vtx = position + point;
349 vtx_builder.AppendVertex(vtx);
350 vtx = position + (-point).Reflect(forward_normal);
351 vtx_builder.AppendVertex(vtx);
352 });
353 }
354
355 template <typename VertexWriter>
356 void CreateSquareCap(VertexWriter& vtx_builder,
357 const Point& position,
358 const Point& offset,
359 Scalar scale,
360 bool reverse) {
361 Point orientation = offset * (reverse ? -1 : 1);
362 Point forward(offset.y, -offset.x);
363
364 Point vtx = position + orientation;
365 vtx_builder.AppendVertex(vtx);
366 vtx = position - orientation;
367 vtx_builder.AppendVertex(vtx);
368 vtx = position + orientation + forward;
369 vtx_builder.AppendVertex(vtx);
370 vtx = position - orientation + forward;
371 vtx_builder.AppendVertex(vtx);
372 }
373
374 template <typename VertexWriter>
375 Scalar CreateBevelAndGetDirection(VertexWriter& vtx_builder,
376 const Point& position,
377 const Point& start_offset,
378 const Point& end_offset) {
379 Point vtx = position;
380 vtx_builder.AppendVertex(vtx);
381
382 Scalar dir = start_offset.Cross(end_offset) > 0 ? -1 : 1;
383 vtx = position + start_offset * dir;
384 vtx_builder.AppendVertex(vtx);
385 vtx = position + end_offset * dir;
386 vtx_builder.AppendVertex(vtx);
387
388 return dir;
389 }
390
391 template <typename VertexWriter>
392 void CreateMiterJoin(VertexWriter& vtx_builder,
393 const Point& position,
394 const Point& start_offset,
395 const Point& end_offset,
396 Scalar miter_limit,
397 Scalar scale) {
398 Point start_normal = start_offset.Normalize();
399 Point end_normal = end_offset.Normalize();
400
401 // 1 for no joint (straight line), 0 for max joint (180 degrees).
402 Scalar alignment = (start_normal.Dot(end_normal) + 1) / 2;
403 if (ScalarNearlyEqual(alignment, 1)) {
404 return;
405 }
406
407 Scalar direction = CreateBevelAndGetDirection(vtx_builder, position,
408 start_offset, end_offset);
409
410 Point miter_point = (((start_offset + end_offset) / 2) / alignment);
411 if (miter_point.GetDistanceSquared({0, 0}) > miter_limit * miter_limit) {
412 return; // Convert to bevel when we exceed the miter limit.
413 }
414
415 // Outer miter point.
416 VS::PerVertexData vtx;
417 vtx.position = position + miter_point * direction;
418 vtx_builder.AppendVertex(vtx.position);
419 }
420
421 template <typename VertexWriter>
422 void CreateRoundJoin(VertexWriter& vtx_builder,
423 const Point& position,
424 const Point& start_offset,
425 const Point& end_offset,
426 Scalar miter_limit,
427 Scalar scale) {
428 Point start_normal = start_offset.Normalize();
429 Point end_normal = end_offset.Normalize();
430
431 // 0 for no joint (straight line), 1 for max joint (180 degrees).
432 Scalar alignment = 1 - (start_normal.Dot(end_normal) + 1) / 2;
433 if (ScalarNearlyEqual(alignment, 0)) {
434 return;
435 }
436
437 Scalar direction = CreateBevelAndGetDirection(vtx_builder, position,
438 start_offset, end_offset);
439
440 Point middle =
441 (start_offset + end_offset).Normalize() * start_offset.GetLength();
442 Point middle_normal = middle.Normalize();
443
444 Point middle_handle = middle + Point(-middle.y, middle.x) *
445 PathBuilder::kArcApproximationMagic *
446 alignment * direction;
447 Point start_handle = start_offset + Point(start_offset.y, -start_offset.x) *
448 PathBuilder::kArcApproximationMagic *
449 alignment * direction;
450
451 VS::PerVertexData vtx;
452 CubicPathComponent(start_offset, start_handle, middle_handle, middle)
453 .ToLinearPathComponents(scale, [&vtx_builder, direction, &vtx, position,
454 middle_normal](const Point& point) {
455 vtx.position = position + point * direction;
456 vtx_builder.AppendVertex(vtx.position);
457 vtx.position = position + (-point * direction).Reflect(middle_normal);
458 vtx_builder.AppendVertex(vtx.position);
459 });
460 }
461
462 template <typename VertexWriter>
463 void CreateBevelJoin(VertexWriter& vtx_builder,
464 const Point& position,
465 const Point& start_offset,
466 const Point& end_offset,
467 Scalar miter_limit,
468 Scalar scale) {
469 CreateBevelAndGetDirection(vtx_builder, position, start_offset, end_offset);
470 }
471
472 template <typename VertexWriter>
473 void CreateSolidStrokeVertices(VertexWriter& vtx_builder,
474 const Path::Polyline& polyline,
475 Scalar stroke_width,
476 Scalar scaled_miter_limit,
477 const JoinProc<VertexWriter>& join_proc,
478 const CapProc<VertexWriter>& cap_proc,
479 Scalar scale) {
480 StrokeGenerator stroke_generator(polyline, stroke_width, scaled_miter_limit,
481 join_proc, cap_proc, scale);
482 stroke_generator.Generate(vtx_builder);
483 }
484
485 // static
486 template <typename VertexWriter>
487 JoinProc<VertexWriter> GetJoinProc(Join stroke_join) {
488 switch (stroke_join) {
489 case Join::kBevel:
490 return &CreateBevelJoin<VertexWriter>;
491 case Join::kMiter:
492 return &CreateMiterJoin<VertexWriter>;
493 case Join::kRound:
494 return &CreateRoundJoin<VertexWriter>;
495 }
496 }
497
498 template <typename VertexWriter>
499 CapProc<VertexWriter> GetCapProc(Cap stroke_cap) {
500 switch (stroke_cap) {
501 case Cap::kButt:
502 return &CreateButtCap<VertexWriter>;
503 case Cap::kRound:
504 return &CreateRoundCap<VertexWriter>;
505 case Cap::kSquare:
506 return &CreateSquareCap<VertexWriter>;
507 }
508 }
509 } // namespace
510
511 std::vector<SolidFillVertexShader::PerVertexData>
512 StrokePathGeometry::GenerateSolidStrokeVertices(const Path::Polyline& polyline,
513 Scalar stroke_width,
514 Scalar miter_limit,
515 Join stroke_join,
516 Cap stroke_cap,
517 Scalar scale) {
518 auto scaled_miter_limit = stroke_width * miter_limit * 0.5f;
519 auto join_proc = GetJoinProc<PositionWriter>(stroke_join);
520 auto cap_proc = GetCapProc<PositionWriter>(stroke_cap);
521 StrokeGenerator stroke_generator(polyline, stroke_width, scaled_miter_limit,
522 join_proc, cap_proc, scale);
523 PositionWriter vtx_builder;
524 stroke_generator.Generate(vtx_builder);
525 return vtx_builder.GetData();
526 }
527
528 std::vector<TextureFillVertexShader::PerVertexData>
529 StrokePathGeometry::GenerateSolidStrokeVerticesUV(
530 const Path::Polyline& polyline,
531 Scalar stroke_width,
532 Scalar miter_limit,
533 Join stroke_join,
534 Cap stroke_cap,
535 Scalar scale,
536 Point texture_origin,
537 Size texture_size,
538 const Matrix& effect_transform) {
539 auto scaled_miter_limit = stroke_width * miter_limit * 0.5f;
540 auto join_proc = GetJoinProc<PositionUVWriter>(stroke_join);
541 auto cap_proc = GetCapProc<PositionUVWriter>(stroke_cap);
542 StrokeGenerator stroke_generator(polyline, stroke_width, scaled_miter_limit,
543 join_proc, cap_proc, scale);
544 PositionUVWriter vtx_builder(texture_origin, texture_size, effect_transform);
545 stroke_generator.Generate(vtx_builder);
546 return vtx_builder.GetData();
547 }
548
549 StrokePathGeometry::StrokePathGeometry(const Path& path,
550 Scalar stroke_width,
551 Scalar miter_limit,
552 Cap stroke_cap,
553 Join stroke_join)
554 : path_(path),
555 stroke_width_(stroke_width),
556 miter_limit_(miter_limit),
557 stroke_cap_(stroke_cap),
558 stroke_join_(stroke_join) {}
559
560 StrokePathGeometry::~StrokePathGeometry() = default;
561
562 Scalar StrokePathGeometry::GetStrokeWidth() const {
563 return stroke_width_;
564 }
565
566 Scalar StrokePathGeometry::GetMiterLimit() const {
567 return miter_limit_;
568 }
569
570 Cap StrokePathGeometry::GetStrokeCap() const {
571 return stroke_cap_;
572 }
573
574 Join StrokePathGeometry::GetStrokeJoin() const {
575 return stroke_join_;
576 }
577
578 GeometryResult StrokePathGeometry::GetPositionBuffer(
579 const ContentContext& renderer,
580 const Entity& entity,
581 RenderPass& pass) const {
582 if (stroke_width_ < 0.0) {
583 return {};
584 }
585 auto determinant = entity.GetTransform().GetDeterminant();
586 if (determinant == 0) {
587 return {};
588 }
589
590 Scalar min_size = 1.0f / sqrt(std::abs(determinant));
591 Scalar stroke_width = std::max(stroke_width_, min_size);
592
593 auto& host_buffer = renderer.GetTransientsBuffer();
594 auto scale = entity.GetTransform().GetMaxBasisLength();
595
596 PositionWriter position_writer;
597 auto polyline = renderer.GetTessellator()->CreateTempPolyline(path_, scale);
598 CreateSolidStrokeVertices(position_writer, polyline, stroke_width,
599 miter_limit_ * stroke_width_ * 0.5f,
600 GetJoinProc<PositionWriter>(stroke_join_),
601 GetCapProc<PositionWriter>(stroke_cap_), scale);
602
603 BufferView buffer_view =
604 host_buffer.Emplace(position_writer.GetData().data(),
605 position_writer.GetData().size() *
606 sizeof(SolidFillVertexShader::PerVertexData),
607 alignof(SolidFillVertexShader::PerVertexData));
608
609 return GeometryResult{
610 .type = PrimitiveType::kTriangleStrip,
611 .vertex_buffer =
612 {
613 .vertex_buffer = buffer_view,
614 .vertex_count = position_writer.GetData().size(),
615 .index_type = IndexType::kNone,
616 },
617 .transform = entity.GetShaderTransform(pass),
618 .mode = GeometryResult::Mode::kPreventOverdraw,
619 };
620 }
621
622 GeometryResult StrokePathGeometry::GetPositionUVBuffer(
623 Rect texture_coverage,
624 Matrix effect_transform,
625 const ContentContext& renderer,
626 const Entity& entity,
627 RenderPass& pass) const {
628 if (stroke_width_ < 0.0) {
629 return {};
630 }
631 auto determinant = entity.GetTransform().GetDeterminant();
632 if (determinant == 0) {
633 return {};
634 }
635
636 Scalar min_size = 1.0f / sqrt(std::abs(determinant));
637 Scalar stroke_width = std::max(stroke_width_, min_size);
638
639 auto& host_buffer = renderer.GetTransientsBuffer();
640 auto scale = entity.GetTransform().GetMaxBasisLength();
641 auto polyline = renderer.GetTessellator()->CreateTempPolyline(path_, scale);
642
643 PositionUVWriter writer(Point{0, 0}, texture_coverage.GetSize(),
644 effect_transform);
645 CreateSolidStrokeVertices(writer, polyline, stroke_width,
646 miter_limit_ * stroke_width_ * 0.5f,
647 GetJoinProc<PositionUVWriter>(stroke_join_),
648 GetCapProc<PositionUVWriter>(stroke_cap_), scale);
649
650 BufferView buffer_view = host_buffer.Emplace(
651 writer.GetData().data(),
652 writer.GetData().size() * sizeof(TextureFillVertexShader::PerVertexData),
653 alignof(TextureFillVertexShader::PerVertexData));
654
655 return GeometryResult{
656 .type = PrimitiveType::kTriangleStrip,
657 .vertex_buffer =
658 {
659 .vertex_buffer = buffer_view,
660 .vertex_count = writer.GetData().size(),
661 .index_type = IndexType::kNone,
662 },
663 .transform = entity.GetShaderTransform(pass),
664 .mode = GeometryResult::Mode::kPreventOverdraw,
665 };
666 }
667
668 GeometryResult::Mode StrokePathGeometry::GetResultMode() const {
669 return GeometryResult::Mode::kPreventOverdraw;
670 }
671
672 GeometryVertexType StrokePathGeometry::GetVertexType() const {
673 return GeometryVertexType::kPosition;
674 }
675
676 std::optional<Rect> StrokePathGeometry::GetCoverage(
677 const Matrix& transform) const {
678 auto path_bounds = path_.GetBoundingBox();
679 if (!path_bounds.has_value()) {
680 return std::nullopt;
681 }
682
683 Scalar max_radius = 0.5;
684 if (stroke_cap_ == Cap::kSquare) {
685 max_radius = max_radius * kSqrt2;
686 }
687 if (stroke_join_ == Join::kMiter) {
688 max_radius = std::max(max_radius, miter_limit_ * 0.5f);
689 }
690 Scalar determinant = transform.GetDeterminant();
691 if (determinant == 0) {
692 return std::nullopt;
693 }
694 Scalar min_size = 1.0f / sqrt(std::abs(determinant));
695 max_radius *= std::max(stroke_width_, min_size);
696 return path_bounds->Expand(max_radius).TransformBounds(transform);
697 }
698
699 } // namespace impeller
impeller::Entity::GetShaderTransform
Matrix GetShaderTransform(const RenderPass &pass) const
Get the vertex shader transform used for drawing this Entity.
Definition: entity.cc:53
impeller::Cap::kRound
@ kRound
impeller::Cap::kSquare
@ kSquare
polyline
const Path::Polyline & polyline
Definition: stroke_path_geometry.cc:292
impeller::TPoint::y
Type y
Definition: point.h:31
impeller::Scalar
float Scalar
Definition: scalar.h:18
impeller::StrokePathGeometry::~StrokePathGeometry
~StrokePathGeometry()
impeller::kSqrt2
constexpr float kSqrt2
Definition: constants.h:47
impeller::Entity::GetTransform
const Matrix & GetTransform() const
Get the global transform matrix for this Entity.
Definition: entity.cc:49
previous_offset
Point previous_offset
Definition: stroke_path_geometry.cc:299
impeller::Path::PolylineContour
Definition: path.h:60
stroke_path_geometry.h
vtx
SolidFillVertexShader::PerVertexData vtx
Definition: stroke_path_geometry.cc:301
impeller::Path::PolylineContour::components
std::vector< Component > components
Definition: path.h:85
formats.h
impeller::Vector2
Point Vector2
Definition: point.h:320
impeller::Path::GetBoundingBox
std::optional< Rect > GetBoundingBox() const
Definition: path.cc:339
impeller::Matrix::GetDeterminant
Scalar GetDeterminant() const
Definition: matrix.cc:162
impeller::Size
TSize< Scalar > Size
Definition: size.h:137
impeller::PathBuilder::kArcApproximationMagic
constexpr static const Scalar kArcApproximationMagic
Definition: path_builder.h:23
impeller::Cap::kButt
@ kButt
stroke_width
const Scalar stroke_width
Definition: stroke_path_geometry.cc:293
scaled_miter_limit
const Scalar scaled_miter_limit
Definition: stroke_path_geometry.cc:294
impeller::GeometryVertexType
GeometryVertexType
Definition: geometry.h:49
impeller::GeometryResult::Mode
Mode
Definition: geometry.h:21
impeller::Join::kMiter
@ kMiter
path_builder.h
impeller::VS
SolidFillVertexShader VS
Definition: stroke_path_geometry.cc:15
impeller::Path::Polyline
Definition: path.h:94
impeller::Entity
Definition: entity.h:21
impeller::TSize
Definition: size.h:19
impeller::PrimitiveType::kTriangleStrip
@ kTriangleStrip
impeller::Point
TPoint< Scalar > Point
Definition: point.h:316
impeller::Path::PolylineContour::start_direction
Vector2 start_direction
The direction of the contour's start cap.
Definition: path.h:77
impeller::Path
Paths are lightweight objects that describe a collection of linear, quadratic, or cubic segments....
Definition: path.h:51
impeller::Matrix::GetMaxBasisLength
Scalar GetMaxBasisLength() const
Definition: matrix.cc:196
impeller::StrokePathGeometry::GetMiterLimit
Scalar GetMiterLimit() const
Definition: stroke_path_geometry.cc:566
impeller::TRect< Scalar >::MakeOriginSize
constexpr static TRect MakeOriginSize(const TPoint< Type > &origin, const TSize< Type > &size)
Definition: rect.h:140
geometry.h
impeller::GeometryResult
Definition: geometry.h:20
impeller::IndexType::kNone
@ kNone
Does not use the index buffer.
impeller::Path::PolylineContour::Component
Definition: path.h:61
impeller::Rect
TRect< Scalar > Rect
Definition: rect.h:661
impeller::Join::kRound
@ kRound
impeller::StrokePathGeometry::GetStrokeCap
Cap GetStrokeCap() const
Definition: stroke_path_geometry.cc:570
impeller::ContentContext::GetTessellator
std::shared_ptr< Tessellator > GetTessellator() const
Definition: content_context.cc:560
impeller::Path::PolylineContour::end_direction
Vector2 end_direction
The direction of the contour's end cap.
Definition: path.h:79
impeller::StrokePathGeometry::GetStrokeJoin
Join GetStrokeJoin() const
Definition: stroke_path_geometry.cc:574
join_proc
const JoinProc< VertexWriter > & join_proc
Definition: stroke_path_geometry.cc:295
impeller::TPoint::x
Type x
Definition: point.h:30
impeller::RenderPass
Render passes encode render commands directed as one specific render target into an underlying comman...
Definition: render_pass.h:33
cap_proc
const CapProc< VertexWriter > & cap_proc
Definition: stroke_path_geometry.cc:296
impeller::Join::kBevel
@ kBevel
impeller::Path::PolylineContour::Component::is_curve
bool is_curve
Definition: path.h:67
impeller::Join
Join
Definition: path.h:23
impeller::TPoint::GetLength
constexpr Type GetLength() const
Definition: point.h:206
buffer_view.h
impeller::TPoint< Scalar >
scale
const Scalar scale
Definition: stroke_path_geometry.cc:297
impeller::ScalarNearlyEqual
constexpr bool ScalarNearlyEqual(Scalar x, Scalar y, Scalar tolerance=kEhCloseEnough)
Definition: scalar.h:30
impeller::GeometryResult::Mode::kPreventOverdraw
@ kPreventOverdraw
offset
Point offset
Definition: stroke_path_geometry.cc:300
path_component.h
impeller::TPoint::Normalize
constexpr TPoint Normalize() const
Definition: point.h:208
impeller::Path::PolylineContour::Component::component_start_index
size_t component_start_index
Definition: path.h:62
impeller
Definition: aiks_blur_unittests.cc:20
impeller::kPosition
@ kPosition
Definition: geometry.h:50
impeller::ContentContext
Definition: content_context.h:392
impeller::Matrix
A 4x4 matrix using column-major storage.
Definition: matrix.h:37
impeller::StrokePathGeometry::GetStrokeWidth
Scalar GetStrokeWidth() const
Definition: stroke_path_geometry.cc:562
impeller::Path::PolylineContour::is_closed
bool is_closed
Definition: path.h:74
impeller::ContentContext::GetTransientsBuffer
HostBuffer & GetTransientsBuffer() const
Retrieve the currnent host buffer for transient storage.
Definition: content_context.h:833
impeller::Cap
Cap
Definition: path.h:17