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