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