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  vtx.position = polyline.GetPoint(point_i + 1) + offset;
280  vtx_builder.AppendVertex(vtx.position);
281  vtx.position = polyline.GetPoint(point_i + 1) - offset;
282  vtx_builder.AppendVertex(vtx.position);
283  // Generate join from the current line to the next line.
284  if (!is_last_component) {
285  join_proc(vtx_builder, polyline.GetPoint(point_i + 1),
287  }
288  }
289  }
290  }
291 
292  const Path::Polyline& polyline;
295  const JoinProc<VertexWriter>& join_proc;
296  const CapProc<VertexWriter>& cap_proc;
297  const Scalar scale;
298 
301  SolidFillVertexShader::PerVertexData vtx;
302 };
303 
304 template <typename VertexWriter>
305 void CreateButtCap(VertexWriter& vtx_builder,
306  const Point& position,
307  const Point& offset,
308  Scalar scale,
309  bool reverse) {
310  Point orientation = offset * (reverse ? -1 : 1);
311  VS::PerVertexData vtx;
312  vtx.position = position + orientation;
313  vtx_builder.AppendVertex(vtx.position);
314  vtx.position = position - orientation;
315  vtx_builder.AppendVertex(vtx.position);
316 }
317 
318 template <typename VertexWriter>
319 void CreateRoundCap(VertexWriter& vtx_builder,
320  const Point& position,
321  const Point& offset,
322  Scalar scale,
323  bool reverse) {
324  Point orientation = offset * (reverse ? -1 : 1);
325  Point forward(offset.y, -offset.x);
326  Point forward_normal = forward.Normalize();
327 
328  CubicPathComponent arc;
329  if (reverse) {
330  arc = CubicPathComponent(
331  forward, forward + orientation * PathBuilder::kArcApproximationMagic,
332  orientation + forward * PathBuilder::kArcApproximationMagic,
333  orientation);
334  } else {
335  arc = CubicPathComponent(
336  orientation,
337  orientation + forward * PathBuilder::kArcApproximationMagic,
338  forward + orientation * PathBuilder::kArcApproximationMagic, forward);
339  }
340 
341  Point vtx = position + orientation;
342  vtx_builder.AppendVertex(vtx);
343  vtx = position - orientation;
344  vtx_builder.AppendVertex(vtx);
345 
346  arc.ToLinearPathComponents(scale, [&vtx_builder, &vtx, forward_normal,
347  position](const Point& point) {
348  vtx = position + point;
349  vtx_builder.AppendVertex(vtx);
350  vtx = position + (-point).Reflect(forward_normal);
351  vtx_builder.AppendVertex(vtx);
352  });
353 }
354 
355 template <typename VertexWriter>
356 void CreateSquareCap(VertexWriter& vtx_builder,
357  const Point& position,
358  const Point& offset,
359  Scalar scale,
360  bool reverse) {
361  Point orientation = offset * (reverse ? -1 : 1);
362  Point forward(offset.y, -offset.x);
363 
364  Point vtx = position + orientation;
365  vtx_builder.AppendVertex(vtx);
366  vtx = position - orientation;
367  vtx_builder.AppendVertex(vtx);
368  vtx = position + orientation + forward;
369  vtx_builder.AppendVertex(vtx);
370  vtx = position - orientation + forward;
371  vtx_builder.AppendVertex(vtx);
372 }
373 
374 template <typename VertexWriter>
375 Scalar CreateBevelAndGetDirection(VertexWriter& vtx_builder,
376  const Point& position,
377  const Point& start_offset,
378  const Point& end_offset) {
379  Point vtx = position;
380  vtx_builder.AppendVertex(vtx);
381 
382  Scalar dir = start_offset.Cross(end_offset) > 0 ? -1 : 1;
383  vtx = position + start_offset * dir;
384  vtx_builder.AppendVertex(vtx);
385  vtx = position + end_offset * dir;
386  vtx_builder.AppendVertex(vtx);
387 
388  return dir;
389 }
390 
391 template <typename VertexWriter>
392 void CreateMiterJoin(VertexWriter& vtx_builder,
393  const Point& position,
394  const Point& start_offset,
395  const Point& end_offset,
396  Scalar miter_limit,
397  Scalar scale) {
398  Point start_normal = start_offset.Normalize();
399  Point end_normal = end_offset.Normalize();
400 
401  // 1 for no joint (straight line), 0 for max joint (180 degrees).
402  Scalar alignment = (start_normal.Dot(end_normal) + 1) / 2;
403  if (ScalarNearlyEqual(alignment, 1)) {
404  return;
405  }
406 
407  Scalar direction = CreateBevelAndGetDirection(vtx_builder, position,
408  start_offset, end_offset);
409 
410  Point miter_point = (((start_offset + end_offset) / 2) / alignment);
411  if (miter_point.GetDistanceSquared({0, 0}) > miter_limit * miter_limit) {
412  return; // Convert to bevel when we exceed the miter limit.
413  }
414 
415  // Outer miter point.
416  VS::PerVertexData vtx;
417  vtx.position = position + miter_point * direction;
418  vtx_builder.AppendVertex(vtx.position);
419 }
420 
421 template <typename VertexWriter>
422 void CreateRoundJoin(VertexWriter& vtx_builder,
423  const Point& position,
424  const Point& start_offset,
425  const Point& end_offset,
426  Scalar miter_limit,
427  Scalar scale) {
428  Point start_normal = start_offset.Normalize();
429  Point end_normal = end_offset.Normalize();
430 
431  // 0 for no joint (straight line), 1 for max joint (180 degrees).
432  Scalar alignment = 1 - (start_normal.Dot(end_normal) + 1) / 2;
433  if (ScalarNearlyEqual(alignment, 0)) {
434  return;
435  }
436 
437  Scalar direction = CreateBevelAndGetDirection(vtx_builder, position,
438  start_offset, end_offset);
439 
440  Point middle =
441  (start_offset + end_offset).Normalize() * start_offset.GetLength();
442  Point middle_normal = middle.Normalize();
443 
444  Point middle_handle = middle + Point(-middle.y, middle.x) *
446  alignment * direction;
447  Point start_handle = start_offset + Point(start_offset.y, -start_offset.x) *
449  alignment * direction;
450 
451  VS::PerVertexData vtx;
452  CubicPathComponent(start_offset, start_handle, middle_handle, middle)
453  .ToLinearPathComponents(scale, [&vtx_builder, direction, &vtx, position,
454  middle_normal](const Point& point) {
455  vtx.position = position + point * direction;
456  vtx_builder.AppendVertex(vtx.position);
457  vtx.position = position + (-point * direction).Reflect(middle_normal);
458  vtx_builder.AppendVertex(vtx.position);
459  });
460 }
461 
462 template <typename VertexWriter>
463 void CreateBevelJoin(VertexWriter& vtx_builder,
464  const Point& position,
465  const Point& start_offset,
466  const Point& end_offset,
467  Scalar miter_limit,
468  Scalar scale) {
469  CreateBevelAndGetDirection(vtx_builder, position, start_offset, end_offset);
470 }
471 
472 template <typename VertexWriter>
473 void CreateSolidStrokeVertices(VertexWriter& vtx_builder,
474  const Path::Polyline& polyline,
477  const JoinProc<VertexWriter>& join_proc,
478  const CapProc<VertexWriter>& cap_proc,
479  Scalar scale) {
480  StrokeGenerator stroke_generator(polyline, stroke_width, scaled_miter_limit,
482  stroke_generator.Generate(vtx_builder);
483 }
484 
485 // static
486 template <typename VertexWriter>
487 JoinProc<VertexWriter> GetJoinProc(Join stroke_join) {
488  switch (stroke_join) {
489  case Join::kBevel:
490  return &CreateBevelJoin<VertexWriter>;
491  case Join::kMiter:
492  return &CreateMiterJoin<VertexWriter>;
493  case Join::kRound:
494  return &CreateRoundJoin<VertexWriter>;
495  }
496 }
497 
498 template <typename VertexWriter>
499 CapProc<VertexWriter> GetCapProc(Cap stroke_cap) {
500  switch (stroke_cap) {
501  case Cap::kButt:
502  return &CreateButtCap<VertexWriter>;
503  case Cap::kRound:
504  return &CreateRoundCap<VertexWriter>;
505  case Cap::kSquare:
506  return &CreateSquareCap<VertexWriter>;
507  }
508 }
509 } // namespace
510 
511 std::vector<SolidFillVertexShader::PerVertexData>
512 StrokePathGeometry::GenerateSolidStrokeVertices(const Path::Polyline& polyline,
514  Scalar miter_limit,
515  Join stroke_join,
516  Cap stroke_cap,
517  Scalar scale) {
518  auto scaled_miter_limit = stroke_width * miter_limit * 0.5f;
519  auto join_proc = GetJoinProc<PositionWriter>(stroke_join);
520  auto cap_proc = GetCapProc<PositionWriter>(stroke_cap);
521  StrokeGenerator stroke_generator(polyline, stroke_width, scaled_miter_limit,
523  PositionWriter vtx_builder;
524  stroke_generator.Generate(vtx_builder);
525  return vtx_builder.GetData();
526 }
527 
528 std::vector<TextureFillVertexShader::PerVertexData>
529 StrokePathGeometry::GenerateSolidStrokeVerticesUV(
530  const Path::Polyline& polyline,
532  Scalar miter_limit,
533  Join stroke_join,
534  Cap stroke_cap,
535  Scalar scale,
536  Point texture_origin,
537  Size texture_size,
538  const Matrix& effect_transform) {
539  auto scaled_miter_limit = stroke_width * miter_limit * 0.5f;
540  auto join_proc = GetJoinProc<PositionUVWriter>(stroke_join);
541  auto cap_proc = GetCapProc<PositionUVWriter>(stroke_cap);
542  StrokeGenerator stroke_generator(polyline, stroke_width, scaled_miter_limit,
544  PositionUVWriter vtx_builder(texture_origin, texture_size, effect_transform);
545  stroke_generator.Generate(vtx_builder);
546  return vtx_builder.GetData();
547 }
548 
549 StrokePathGeometry::StrokePathGeometry(const Path& path,
551  Scalar miter_limit,
552  Cap stroke_cap,
553  Join stroke_join)
554  : path_(path),
555  stroke_width_(stroke_width),
556  miter_limit_(miter_limit),
557  stroke_cap_(stroke_cap),
558  stroke_join_(stroke_join) {}
559 
561 
563  return stroke_width_;
564 }
565 
567  return miter_limit_;
568 }
569 
571  return stroke_cap_;
572 }
573 
575  return stroke_join_;
576 }
577 
578 GeometryResult StrokePathGeometry::GetPositionBuffer(
579  const ContentContext& renderer,
580  const Entity& entity,
581  RenderPass& pass) const {
582  if (stroke_width_ < 0.0) {
583  return {};
584  }
585  auto determinant = entity.GetTransform().GetDeterminant();
586  if (determinant == 0) {
587  return {};
588  }
589 
590  Scalar min_size = 1.0f / sqrt(std::abs(determinant));
591  Scalar stroke_width = std::max(stroke_width_, min_size);
592 
593  auto& host_buffer = renderer.GetTransientsBuffer();
594  auto scale = entity.GetTransform().GetMaxBasisLength();
595 
596  PositionWriter position_writer;
597  auto polyline = renderer.GetTessellator()->CreateTempPolyline(path_, scale);
598  CreateSolidStrokeVertices(position_writer, polyline, stroke_width,
599  miter_limit_ * stroke_width_ * 0.5f,
600  GetJoinProc<PositionWriter>(stroke_join_),
601  GetCapProc<PositionWriter>(stroke_cap_), scale);
602 
603  BufferView buffer_view =
604  host_buffer.Emplace(position_writer.GetData().data(),
605  position_writer.GetData().size() *
606  sizeof(SolidFillVertexShader::PerVertexData),
607  alignof(SolidFillVertexShader::PerVertexData));
608 
609  return GeometryResult{
611  .vertex_buffer =
612  {
613  .vertex_buffer = buffer_view,
614  .vertex_count = position_writer.GetData().size(),
615  .index_type = IndexType::kNone,
616  },
617  .transform = entity.GetShaderTransform(pass),
619  };
620 }
621 
622 GeometryResult StrokePathGeometry::GetPositionUVBuffer(
623  Rect texture_coverage,
624  Matrix effect_transform,
625  const ContentContext& renderer,
626  const Entity& entity,
627  RenderPass& pass) const {
628  if (stroke_width_ < 0.0) {
629  return {};
630  }
631  auto determinant = entity.GetTransform().GetDeterminant();
632  if (determinant == 0) {
633  return {};
634  }
635 
636  Scalar min_size = 1.0f / sqrt(std::abs(determinant));
637  Scalar stroke_width = std::max(stroke_width_, min_size);
638 
639  auto& host_buffer = renderer.GetTransientsBuffer();
640  auto scale = entity.GetTransform().GetMaxBasisLength();
641  auto polyline = renderer.GetTessellator()->CreateTempPolyline(path_, scale);
642 
643  PositionUVWriter writer(Point{0, 0}, texture_coverage.GetSize(),
644  effect_transform);
645  CreateSolidStrokeVertices(writer, polyline, stroke_width,
646  miter_limit_ * stroke_width_ * 0.5f,
647  GetJoinProc<PositionUVWriter>(stroke_join_),
648  GetCapProc<PositionUVWriter>(stroke_cap_), scale);
649 
650  BufferView buffer_view = host_buffer.Emplace(
651  writer.GetData().data(),
652  writer.GetData().size() * sizeof(TextureFillVertexShader::PerVertexData),
653  alignof(TextureFillVertexShader::PerVertexData));
654 
655  return GeometryResult{
657  .vertex_buffer =
658  {
659  .vertex_buffer = buffer_view,
660  .vertex_count = writer.GetData().size(),
661  .index_type = IndexType::kNone,
662  },
663  .transform = entity.GetShaderTransform(pass),
665  };
666 }
667 
668 GeometryResult::Mode StrokePathGeometry::GetResultMode() const {
670 }
671 
672 GeometryVertexType StrokePathGeometry::GetVertexType() const {
674 }
675 
676 std::optional<Rect> StrokePathGeometry::GetCoverage(
677  const Matrix& transform) const {
678  auto path_bounds = path_.GetBoundingBox();
679  if (!path_bounds.has_value()) {
680  return std::nullopt;
681  }
682 
683  Scalar max_radius = 0.5;
684  if (stroke_cap_ == Cap::kSquare) {
685  max_radius = max_radius * kSqrt2;
686  }
687  if (stroke_join_ == Join::kMiter) {
688  max_radius = std::max(max_radius, miter_limit_ * 0.5f);
689  }
690  Scalar determinant = transform.GetDeterminant();
691  if (determinant == 0) {
692  return std::nullopt;
693  }
694  Scalar min_size = 1.0f / sqrt(std::abs(determinant));
695  max_radius *= std::max(stroke_width_, min_size);
696  return path_bounds->Expand(max_radius).TransformBounds(transform);
697 }
698 
699 } // 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:292
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:299
impeller::Path::PolylineContour
Definition: path.h:60
stroke_path_geometry.h
vtx
SolidFillVertexShader::PerVertexData vtx
Definition: stroke_path_geometry.cc:301
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:293
scaled_miter_limit
const Scalar scaled_miter_limit
Definition: stroke_path_geometry.cc:294
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:566
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:570
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:574
join_proc
const JoinProc< VertexWriter > & join_proc
Definition: stroke_path_geometry.cc:295
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:296
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:297
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:300
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:562
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