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