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