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