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