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