Flutter Impeller
tessellator.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 #include <cstdint>
7 #include <cstring>
8 
11 
12 namespace {
13 static constexpr int kPrecomputedDivisionCount = 1024;
14 
15 static int kPrecomputedDivisions[kPrecomputedDivisionCount] = {
16  // clang-format off
17  1, 2, 3, 4, 4, 4, 5, 5, 5, 6, 6, 6, 7, 7, 7, 7,
18  8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10,
19  10, 11, 11, 11, 11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 13,
20  13, 13, 13, 13, 13, 13, 13, 14, 14, 14, 14, 14, 14, 14, 14, 14,
21  15, 15, 15, 15, 15, 15, 15, 15, 15, 16, 16, 16, 16, 16, 16, 16,
22  16, 16, 16, 16, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 18, 18,
23  18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 19, 19, 19, 19, 19, 19,
24  19, 19, 19, 19, 19, 19, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20,
25  20, 20, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21,
26  22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 23, 23, 23,
27  23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24,
28  24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 25, 25, 25, 25, 25,
29  25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 26, 26, 26, 26, 26,
30  26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 27, 27, 27, 27,
31  27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 28, 28, 28,
32  28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 29,
33  29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29,
34  29, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30,
35  30, 30, 30, 30, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31,
36  31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32,
37  32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33,
38  33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33,
39  33, 33, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34,
40  34, 34, 34, 34, 34, 34, 34, 35, 35, 35, 35, 35, 35, 35, 35, 35,
41  35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 36, 36,
42  36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36,
43  36, 36, 36, 36, 36, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37,
44  37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 38, 38, 38, 38,
45  38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38,
46  38, 38, 38, 38, 38, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39,
47  39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 40, 40,
48  40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40,
49  40, 40, 40, 40, 40, 40, 40, 41, 41, 41, 41, 41, 41, 41, 41, 41,
50  41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41,
51  41, 41, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42,
52  42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 43, 43, 43, 43,
53  43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43,
54  43, 43, 43, 43, 43, 43, 43, 43, 44, 44, 44, 44, 44, 44, 44, 44,
55  44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44,
56  44, 44, 44, 44, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45,
57  45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45,
58  45, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46,
59  46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 47,
60  47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47,
61  47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 48, 48, 48,
62  48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48,
63  48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 49, 49, 49, 49,
64  49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49,
65  49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 50, 50, 50, 50, 50,
66  50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50,
67  50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 51, 51, 51, 51, 51,
68  51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51,
69  51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 52, 52, 52, 52,
70  52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52,
71  52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 53, 53, 53,
72  53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53,
73  53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 54,
74  54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
75  54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
76  54, 54, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55,
77  55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55,
78  55, 55, 55, 55, 55, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56,
79  56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56,
80  56, 56, 56, 56, 56, 56, 56, 56, 56, 57, 57, 57, 57, 57, 57, 57,
81  // clang-format on
82 };
83 
84 static size_t ComputeQuadrantDivisions(impeller::Scalar pixel_radius) {
85  if (pixel_radius <= 0.0) {
86  return 1;
87  }
88  int radius_index = ceil(pixel_radius);
89  if (radius_index < kPrecomputedDivisionCount) {
90  return kPrecomputedDivisions[radius_index];
91  }
92 
93  // For a circle with N divisions per quadrant, the maximum deviation of
94  // the polgyon approximation from the true circle will be at the center
95  // of the base of each triangular pie slice. We can compute that distance
96  // by finding the midpoint of the line of the first slice and compare
97  // its distance from the center of the circle to the radius. We will aim
98  // to have the length of that bisector to be within |kCircleTolerance|
99  // from the radius in pixels.
100  //
101  // Each vertex will appear at an angle of:
102  // theta(i) = (kPi / 2) * (i / N) // for i in [0..N]
103  // with each point falling at:
104  // point(i) = r * (cos(theta), sin(theta))
105  // If we consider the unit circle to simplify the calculations below then
106  // we need to scale the tolerance from its absolute quantity into a unit
107  // circle fraction:
108  // k = tolerance / radius
109  // Using this scaled tolerance below to avoid multiplying by the radius
110  // throughout all of the math, we have:
111  // first point = (1, 0) // theta(0) == 0
112  // theta = kPi / 2 / N // theta(1)
113  // second point = (cos(theta), sin(theta)) = (c, s)
114  // midpoint = (first + second) * 0.5 = ((1 + c)/2, s/2)
115  // |midpoint| = sqrt((1 + c)*(1 + c)/4 + s*s/4)
116  // = sqrt((1 + c + c + c*c + s*s) / 4)
117  // = sqrt((1 + 2c + 1) / 4)
118  // = sqrt((2 + 2c) / 4)
119  // = sqrt((1 + c) / 2)
120  // = cos(theta / 2) // using half-angle cosine formula
121  // error = 1 - |midpoint| = 1 - cos(theta / 2)
122  // cos(theta/2) = 1 - error
123  // theta/2 = acos(1 - error)
124  // kPi / 2 / N / 2 = acos(1 - error)
125  // kPi / 4 / acos(1 - error) = N
126  // Since we need error <= k, we want divisions >= N, so we use:
127  // N = ceil(kPi / 4 / acos(1 - k))
128  //
129  // Math is confirmed in https://math.stackexchange.com/a/4132095
130  // (keeping in mind that we are computing quarter circle divisions here)
131  // which also points out a performance optimization that is accurate
132  // to within an over-estimation of 1 division would be:
133  // N = ceil(kPi / 4 / sqrt(2 * k))
134  // Since we have precomputed the divisions for radii up to 1024, we can
135  // afford to be more accurate using the acos formula here for larger radii.
136  double k = impeller::Tessellator::kCircleTolerance / pixel_radius;
137  return ceil(impeller::kPiOver4 / std::acos(1 - k));
138 }
139 
140 /// @brief A vertex writer that generates a triangle fan and requires primitive
141 /// restart.
142 class FanPathVertexWriter : public impeller::PathTessellator::VertexWriter {
143  public:
144  explicit FanPathVertexWriter(impeller::Point* point_buffer,
145  uint16_t* index_buffer)
146  : point_buffer_(point_buffer), index_buffer_(index_buffer) {}
147 
148  ~FanPathVertexWriter() = default;
149 
150  size_t GetIndexCount() const { return index_count_; }
151  size_t GetPointCount() const { return count_; }
152 
153  void EndContour() override {
154  if (count_ == 0) {
155  return;
156  }
157  index_buffer_[index_count_++] = 0xFFFF;
158  }
159 
160  void Write(impeller::Point point) override {
161  index_buffer_[index_count_++] = count_;
162  point_buffer_[count_++] = point;
163  }
164 
165  private:
166  size_t count_ = 0;
167  size_t index_count_ = 0;
168  impeller::Point* point_buffer_ = nullptr;
169  uint16_t* index_buffer_ = nullptr;
170 };
171 
172 /// @brief A vertex writer that generates a triangle strip and requires
173 /// primitive restart.
174 class StripPathVertexWriter : public impeller::PathTessellator::VertexWriter {
175  public:
176  explicit StripPathVertexWriter(impeller::Point* point_buffer,
177  uint16_t* index_buffer)
178  : point_buffer_(point_buffer), index_buffer_(index_buffer) {}
179 
180  ~StripPathVertexWriter() = default;
181 
182  size_t GetIndexCount() const { return index_count_; }
183  size_t GetPointCount() const { return count_; }
184 
185  void EndContour() override {
186  if (count_ == 0u || contour_start_ == count_ - 1) {
187  // Empty or first contour.
188  return;
189  }
190 
191  size_t start = contour_start_;
192  size_t end = count_ - 1;
193 
194  index_buffer_[index_count_++] = start;
195 
196  size_t a = start + 1;
197  size_t b = end;
198  while (a < b) {
199  index_buffer_[index_count_++] = a;
200  index_buffer_[index_count_++] = b;
201  a++;
202  b--;
203  }
204  if (a == b) {
205  index_buffer_[index_count_++] = a;
206  }
207 
208  contour_start_ = count_;
209  index_buffer_[index_count_++] = 0xFFFF;
210  }
211 
212  void Write(impeller::Point point) override {
213  point_buffer_[count_++] = point;
214  }
215 
216  private:
217  size_t count_ = 0;
218  size_t index_count_ = 0;
219  size_t contour_start_ = 0;
220  impeller::Point* point_buffer_ = nullptr;
221  uint16_t* index_buffer_ = nullptr;
222 };
223 
224 /// @brief A vertex writer that has no hardware requirements.
225 class GLESPathVertexWriter : public impeller::PathTessellator::VertexWriter {
226  public:
227  explicit GLESPathVertexWriter(std::vector<impeller::Point>& points,
228  std::vector<uint16_t>& indices)
229  : points_(points), indices_(indices) {}
230 
231  ~GLESPathVertexWriter() = default;
232 
233  void EndContour() override {
234  if (points_.size() == 0u || contour_start_ == points_.size() - 1) {
235  // Empty or first contour.
236  return;
237  }
238 
239  auto start = contour_start_;
240  auto end = points_.size() - 1;
241  // All filled paths are drawn as if they are closed, but if
242  // there is an explicit close then a lineTo to the origin
243  // is inserted. This point isn't strictly necesary to
244  // correctly render the shape and can be dropped.
245  if (points_[end] == points_[start]) {
246  end--;
247  }
248 
249  // Triangle strip break for subsequent contours
250  if (contour_start_ != 0) {
251  auto back = indices_.back();
252  indices_.push_back(back);
253  indices_.push_back(start);
254  indices_.push_back(start);
255 
256  // If the contour has an odd number of points, insert an extra point when
257  // bridging to the next contour to preserve the correct triangle winding
258  // order.
259  if (previous_contour_odd_points_) {
260  indices_.push_back(start);
261  }
262  } else {
263  indices_.push_back(start);
264  }
265 
266  size_t a = start + 1;
267  size_t b = end;
268  while (a < b) {
269  indices_.push_back(a);
270  indices_.push_back(b);
271  a++;
272  b--;
273  }
274  if (a == b) {
275  indices_.push_back(a);
276  previous_contour_odd_points_ = false;
277  } else {
278  previous_contour_odd_points_ = true;
279  }
280  contour_start_ = points_.size();
281  }
282 
283  void Write(impeller::Point point) override { points_.push_back(point); }
284 
285  private:
286  bool previous_contour_odd_points_ = false;
287  size_t contour_start_ = 0u;
288  std::vector<impeller::Point>& points_;
289  std::vector<uint16_t>& indices_;
290 };
291 
292 } // namespace
293 
294 namespace impeller {
295 
297  : point_buffer_(std::make_unique<std::vector<Point>>()),
298  index_buffer_(std::make_unique<std::vector<uint16_t>>()),
299  stroke_points_(kPointArenaSize) {
300  point_buffer_->reserve(2048);
301  index_buffer_->reserve(2048);
302 }
303 
304 Tessellator::~Tessellator() = default;
305 
306 std::vector<Point>& Tessellator::GetStrokePointCache() {
307  return stroke_points_;
308 }
309 
311  return GetTrigsForDivisions(ComputeQuadrantDivisions(pixel_radius));
312 }
313 
315  HostBuffer& data_host_buffer,
316  HostBuffer& indexes_host_buffer,
317  Scalar tolerance,
318  bool supports_primitive_restart,
319  bool supports_triangle_fan) {
320  if (supports_primitive_restart) {
321  // Primitive Restart.
322  const auto [point_count, contour_count] =
323  PathTessellator::CountFillStorage(path, tolerance);
324  BufferView point_buffer = data_host_buffer.Emplace(
325  nullptr, sizeof(Point) * point_count, alignof(Point));
326  BufferView index_buffer = indexes_host_buffer.Emplace(
327  nullptr, sizeof(uint16_t) * (point_count + contour_count),
328  alignof(uint16_t));
329 
330  if (supports_triangle_fan) {
331  FanPathVertexWriter writer(
332  reinterpret_cast<Point*>(point_buffer.GetBuffer()->OnGetContents() +
333  point_buffer.GetRange().offset),
334  reinterpret_cast<uint16_t*>(
335  index_buffer.GetBuffer()->OnGetContents() +
336  index_buffer.GetRange().offset));
337  PathTessellator::PathToFilledVertices(path, writer, tolerance);
338  FML_DCHECK(writer.GetPointCount() <= point_count);
339  FML_DCHECK(writer.GetIndexCount() <= (point_count + contour_count));
340  point_buffer.GetBuffer()->Flush(point_buffer.GetRange());
341  index_buffer.GetBuffer()->Flush(index_buffer.GetRange());
342 
343  return VertexBuffer{
344  .vertex_buffer = std::move(point_buffer),
345  .index_buffer = std::move(index_buffer),
346  .vertex_count = writer.GetIndexCount(),
347  .index_type = IndexType::k16bit,
348  };
349  } else {
350  StripPathVertexWriter writer(
351  reinterpret_cast<Point*>(point_buffer.GetBuffer()->OnGetContents() +
352  point_buffer.GetRange().offset),
353  reinterpret_cast<uint16_t*>(
354  index_buffer.GetBuffer()->OnGetContents() +
355  index_buffer.GetRange().offset));
356  PathTessellator::PathToFilledVertices(path, writer, tolerance);
357  FML_DCHECK(writer.GetPointCount() <= point_count);
358  FML_DCHECK(writer.GetIndexCount() <= (point_count + contour_count));
359  point_buffer.GetBuffer()->Flush(point_buffer.GetRange());
360  index_buffer.GetBuffer()->Flush(index_buffer.GetRange());
361 
362  return VertexBuffer{
363  .vertex_buffer = std::move(point_buffer),
364  .index_buffer = std::move(index_buffer),
365  .vertex_count = writer.GetIndexCount(),
366  .index_type = IndexType::k16bit,
367  };
368  }
369  }
370 
371  FML_DCHECK(point_buffer_);
372  FML_DCHECK(index_buffer_);
374 
375  if (point_buffer_->empty()) {
376  return VertexBuffer{
377  .vertex_buffer = {},
378  .index_buffer = {},
379  .vertex_count = 0u,
380  .index_type = IndexType::k16bit,
381  };
382  }
383 
384  BufferView vertex_buffer = data_host_buffer.Emplace(
385  point_buffer_->data(), sizeof(Point) * point_buffer_->size(),
386  alignof(Point));
387 
388  BufferView index_buffer = indexes_host_buffer.Emplace(
389  index_buffer_->data(), sizeof(uint16_t) * index_buffer_->size(),
390  alignof(uint16_t));
391 
392  return VertexBuffer{
393  .vertex_buffer = std::move(vertex_buffer),
394  .index_buffer = std::move(index_buffer),
395  .vertex_count = index_buffer_->size(),
396  .index_type = IndexType::k16bit,
397  };
398 }
399 
401  std::vector<Point>& point_buffer,
402  std::vector<uint16_t>& index_buffer,
403  Scalar tolerance) {
404  point_buffer.clear();
405  index_buffer.clear();
406 
407  GLESPathVertexWriter writer(point_buffer, index_buffer);
408 
409  PathTessellator::PathToFilledVertices(path, writer, tolerance);
410 }
411 
413  : Tessellator::Trigs(ComputeQuadrantDivisions(pixel_radius)) {}
414 
415 void Tessellator::Trigs::init(size_t divisions) {
416  if (!trigs_.empty()) {
417  return;
418  }
419 
420  // Either not cached yet, or we are using the temp storage...
421  trigs_.reserve(divisions + 1);
422 
423  double angle_scale = kPiOver2 / divisions;
424 
425  trigs_.emplace_back(1.0, 0.0);
426  for (size_t i = 1; i < divisions; i++) {
427  trigs_.emplace_back(Radians(i * angle_scale));
428  }
429  trigs_.emplace_back(0.0, 1.0);
430 }
431 
432 Tessellator::Trigs Tessellator::GetTrigsForDivisions(size_t divisions) {
433  return divisions < Tessellator::kCachedTrigCount
434  ? Trigs(precomputed_trigs_[divisions], divisions)
435  : Trigs(divisions);
436 }
437 
441 
442 EllipticalVertexGenerator::EllipticalVertexGenerator(
443  EllipticalVertexGenerator::GeneratorProc& generator,
444  Trigs&& trigs,
445  PrimitiveType triangle_type,
446  size_t vertices_per_trig,
447  Data&& data)
448  : impl_(generator),
449  trigs_(std::move(trigs)),
450  data_(data),
451  vertices_per_trig_(vertices_per_trig) {}
452 
453 EllipticalVertexGenerator Tessellator::FilledCircle(
454  const Matrix& view_transform,
455  const Point& center,
456  Scalar radius) {
457  size_t divisions =
458  ComputeQuadrantDivisions(view_transform.GetMaxBasisLengthXY() * radius);
459  return EllipticalVertexGenerator(Tessellator::GenerateFilledCircle,
460  GetTrigsForDivisions(divisions),
461  PrimitiveType::kTriangleStrip, 4,
462  {
463  .reference_centers = {center, center},
464  .radii = {radius, radius},
465  .half_width = -1.0f,
466  });
467 }
468 
469 EllipticalVertexGenerator Tessellator::StrokedCircle(
470  const Matrix& view_transform,
471  const Point& center,
472  Scalar radius,
473  Scalar half_width) {
474  if (half_width > 0) {
475  auto divisions = ComputeQuadrantDivisions(
476  view_transform.GetMaxBasisLengthXY() * radius + half_width);
477  return EllipticalVertexGenerator(Tessellator::GenerateStrokedCircle,
478  GetTrigsForDivisions(divisions),
479  PrimitiveType::kTriangleStrip, 8,
480  {
481  .reference_centers = {center, center},
482  .radii = {radius, radius},
483  .half_width = half_width,
484  });
485  } else {
486  return FilledCircle(view_transform, center, radius);
487  }
488 }
489 
491  Trigs&& trigs,
492  const Rect& oval_bounds,
493  bool use_center,
494  bool supports_triangle_fans)
495  : iteration_(iteration),
496  trigs_(std::move(trigs)),
497  oval_bounds_(oval_bounds),
498  use_center_(use_center),
499  half_width_(-1.0f),
500  cap_(Cap::kButt),
501  supports_triangle_fans_(supports_triangle_fans) {}
502 
503 ArcVertexGenerator::ArcVertexGenerator(const Arc::Iteration& iteration,
504  Trigs&& trigs,
505  const Rect& oval_bounds,
506  Scalar half_width,
507  Cap cap)
508  : iteration_(iteration),
509  trigs_(std::move(trigs)),
510  oval_bounds_(oval_bounds),
511  use_center_(false),
512  half_width_(half_width),
513  cap_(cap),
514  supports_triangle_fans_(false) {}
515 
517  return (half_width_ < 0 && supports_triangle_fans_)
520 }
521 
523  size_t count = iteration_.GetPointCount();
524  if (half_width_ > 0) {
525  FML_DCHECK(!use_center_);
526  FML_DCHECK(cap_ != Cap::kRound);
527  count *= 2;
528  if (cap_ == Cap::kSquare) {
529  count += 4;
530  }
531  } else if (supports_triangle_fans_) {
532  if (use_center_) {
533  count++;
534  }
535  } else {
536  // corrugated triangle fan
537  count += (count + 1) / 2;
538  }
539  return count;
540 }
541 
543  const TessellatedVertexProc& proc) const {
544  if (half_width_ > 0) {
545  FML_DCHECK(!use_center_);
546  Tessellator::GenerateStrokedArc(trigs_, iteration_, oval_bounds_,
547  half_width_, cap_, proc);
548  } else if (supports_triangle_fans_) {
549  Tessellator::GenerateFilledArcFan(trigs_, iteration_, oval_bounds_,
550  use_center_, proc);
551  } else {
552  Tessellator::GenerateFilledArcStrip(trigs_, iteration_, oval_bounds_,
553  use_center_, proc);
554  }
555 }
556 
558  const Arc& arc,
559  bool supports_triangle_fans) {
560  size_t divisions = ComputeQuadrantDivisions(
561  view_transform.GetMaxBasisLengthXY() * arc.GetOvalSize().MaxDimension());
562 
563  return ArcVertexGenerator(
564  arc.ComputeIterations(divisions), GetTrigsForDivisions(divisions),
565  arc.GetOvalBounds(), arc.IncludeCenter(), supports_triangle_fans);
566 };
567 
569  const Arc& arc,
570  Cap cap,
571  Scalar half_width) {
572  FML_DCHECK(half_width > 0);
573  FML_DCHECK(arc.IsPerfectCircle());
574  FML_DCHECK(!arc.IncludeCenter());
575  size_t divisions =
576  ComputeQuadrantDivisions(view_transform.GetMaxBasisLengthXY() *
577  (arc.GetOvalSize().MaxDimension() + half_width));
578 
579  return ArcVertexGenerator(arc.ComputeIterations(divisions),
580  GetTrigsForDivisions(divisions),
581  arc.GetOvalBounds(), half_width, cap);
582 }
583 
585  const Matrix& view_transform,
586  const Point& p0,
587  const Point& p1,
588  Scalar radius) {
589  auto along = p1 - p0;
590  auto length = along.GetLength();
591  if (length > kEhCloseEnough) {
592  auto divisions =
593  ComputeQuadrantDivisions(view_transform.GetMaxBasisLengthXY() * radius);
594  return EllipticalVertexGenerator(Tessellator::GenerateRoundCapLine,
595  GetTrigsForDivisions(divisions),
597  {
598  .reference_centers = {p0, p1},
599  .radii = {radius, radius},
600  .half_width = -1.0f,
601  });
602  } else {
603  return FilledCircle(view_transform, p0, radius);
604  }
605 }
606 
608  const Matrix& view_transform,
609  const Rect& bounds) {
610  if (bounds.IsSquare()) {
611  return FilledCircle(view_transform, bounds.GetCenter(),
612  bounds.GetWidth() * 0.5f);
613  }
614  auto max_radius = bounds.GetSize().MaxDimension();
615  auto divisions = ComputeQuadrantDivisions(
616  view_transform.GetMaxBasisLengthXY() * max_radius);
617  auto center = bounds.GetCenter();
618  return EllipticalVertexGenerator(Tessellator::GenerateFilledEllipse,
619  GetTrigsForDivisions(divisions),
621  {
622  .reference_centers = {center, center},
623  .radii = bounds.GetSize() * 0.5f,
624  .half_width = -1.0f,
625  });
626 }
627 
629  const Matrix& view_transform,
630  const Rect& bounds,
631  const Size& radii) {
632  if (radii.width * 2 < bounds.GetWidth() ||
633  radii.height * 2 < bounds.GetHeight()) {
634  auto max_radius = radii.MaxDimension();
635  auto divisions = ComputeQuadrantDivisions(
636  view_transform.GetMaxBasisLengthXY() * max_radius);
637  auto upper_left = bounds.GetLeftTop() + radii;
638  auto lower_right = bounds.GetRightBottom() - radii;
639  return EllipticalVertexGenerator(Tessellator::GenerateFilledRoundRect,
640  GetTrigsForDivisions(divisions),
642  {
643  .reference_centers =
644  {
645  upper_left,
646  lower_right,
647  },
648  .radii = radii,
649  .half_width = -1.0f,
650  });
651  } else {
652  return FilledEllipse(view_transform, bounds);
653  }
654 }
655 
656 void Tessellator::GenerateFilledCircle(
657  const Trigs& trigs,
658  const EllipticalVertexGenerator::Data& data,
659  const TessellatedVertexProc& proc) {
660  auto center = data.reference_centers[0];
661  auto radius = data.radii.width;
662 
663  FML_DCHECK(center == data.reference_centers[1]);
664  FML_DCHECK(radius == data.radii.height);
665  FML_DCHECK(data.half_width < 0);
666 
667  // Quadrant 1 connecting with Quadrant 4:
668  for (auto& trig : trigs) {
669  auto offset = trig * radius;
670  proc({center.x - offset.x, center.y + offset.y});
671  proc({center.x - offset.x, center.y - offset.y});
672  }
673 
674  // The second half of the circle should be iterated in reverse, but
675  // we can instead iterate forward and swap the x/y values of the
676  // offset as the angles should be symmetric and thus should generate
677  // symmetrically reversed trig vectors.
678  // Quadrant 2 connecting with Quadrant 3:
679  for (auto& trig : trigs) {
680  auto offset = trig * radius;
681  proc({center.x + offset.y, center.y + offset.x});
682  proc({center.x + offset.y, center.y - offset.x});
683  }
684 }
685 
686 void Tessellator::GenerateStrokedCircle(
687  const Trigs& trigs,
688  const EllipticalVertexGenerator::Data& data,
689  const TessellatedVertexProc& proc) {
690  auto center = data.reference_centers[0];
691 
692  FML_DCHECK(center == data.reference_centers[1]);
693  FML_DCHECK(data.radii.IsSquare());
694  FML_DCHECK(data.half_width > 0 && data.half_width < data.radii.width);
695 
696  auto outer_radius = data.radii.width + data.half_width;
697  auto inner_radius = data.radii.width - data.half_width;
698 
699  // Zig-zag back and forth between points on the outer circle and the
700  // inner circle. Both circles are evaluated at the same number of
701  // quadrant divisions so the points for a given division should match
702  // 1 for 1 other than their applied radius.
703 
704  // Quadrant 1:
705  for (auto& trig : trigs) {
706  auto outer = trig * outer_radius;
707  auto inner = trig * inner_radius;
708  proc({center.x - outer.x, center.y - outer.y});
709  proc({center.x - inner.x, center.y - inner.y});
710  }
711 
712  // The even quadrants of the circle should be iterated in reverse, but
713  // we can instead iterate forward and swap the x/y values of the
714  // offset as the angles should be symmetric and thus should generate
715  // symmetrically reversed trig vectors.
716  // Quadrant 2:
717  for (auto& trig : trigs) {
718  auto outer = trig * outer_radius;
719  auto inner = trig * inner_radius;
720  proc({center.x + outer.y, center.y - outer.x});
721  proc({center.x + inner.y, center.y - inner.x});
722  }
723 
724  // Quadrant 3:
725  for (auto& trig : trigs) {
726  auto outer = trig * outer_radius;
727  auto inner = trig * inner_radius;
728  proc({center.x + outer.x, center.y + outer.y});
729  proc({center.x + inner.x, center.y + inner.y});
730  }
731 
732  // Quadrant 4:
733  for (auto& trig : trigs) {
734  auto outer = trig * outer_radius;
735  auto inner = trig * inner_radius;
736  proc({center.x - outer.y, center.y + outer.x});
737  proc({center.x - inner.y, center.y + inner.x});
738  }
739 }
740 
741 void Tessellator::GenerateFilledArcFan(const Trigs& trigs,
742  const Arc::Iteration& iteration,
743  const Rect& oval_bounds,
744  bool use_center,
745  const TessellatedVertexProc& proc) {
746  Point center = oval_bounds.GetCenter();
747  Size radii = oval_bounds.GetSize() * 0.5f;
748 
749  if (use_center) {
750  proc(center);
751  }
752  proc(center + iteration.start * radii);
753  for (size_t i = 0; i < iteration.quadrant_count; i++) {
754  auto quadrant = iteration.quadrants[i];
755  for (size_t j = quadrant.start_index; j < quadrant.end_index; j++) {
756  proc(center + trigs[j] * quadrant.axis * radii);
757  }
758  }
759  proc(center + iteration.end * radii);
760 }
761 
762 void Tessellator::GenerateFilledArcStrip(const Trigs& trigs,
763  const Arc::Iteration& iteration,
764  const Rect& oval_bounds,
765  bool use_center,
766  const TessellatedVertexProc& proc) {
767  Point center = oval_bounds.GetCenter();
768  Size radii = oval_bounds.GetSize() * 0.5f;
769 
770  Point origin;
771  if (use_center) {
772  origin = center;
773  } else {
774  Point midpoint = (iteration.start + iteration.end) * 0.5f;
775  origin = center + midpoint * radii;
776  }
777 
778  proc(origin);
779  proc(center + iteration.start * radii);
780  bool insert_origin = false;
781  for (size_t i = 0; i < iteration.quadrant_count; i++) {
782  auto quadrant = iteration.quadrants[i];
783  for (size_t j = quadrant.start_index; j < quadrant.end_index; j++) {
784  if (insert_origin) {
785  proc(origin);
786  }
787  insert_origin = !insert_origin;
788  proc(center + trigs[j] * quadrant.axis * radii);
789  }
790  }
791  if (insert_origin) {
792  proc(origin);
793  }
794  proc(center + iteration.end * radii);
795 }
796 
797 void Tessellator::GenerateStrokedArc(const Trigs& trigs,
798  const Arc::Iteration& iteration,
799  const Rect& oval_bounds,
800  Scalar half_width,
801  Cap cap,
802  const TessellatedVertexProc& proc) {
803  Point center = oval_bounds.GetCenter();
804  Size base_radii = oval_bounds.GetSize() * 0.5f;
805  Size inner_radii = base_radii - Size(half_width, half_width);
806  Size outer_radii = base_radii + Size(half_width, half_width);
807 
808  FML_DCHECK(cap != Cap::kRound);
809  if (cap == Cap::kSquare) {
810  Vector2 offset =
811  Vector2{iteration.start.y, -iteration.start.x} * half_width;
812  proc(center + iteration.start * inner_radii + offset);
813  proc(center + iteration.start * outer_radii + offset);
814  }
815  proc(center + iteration.start * inner_radii);
816  proc(center + iteration.start * outer_radii);
817  for (size_t i = 0; i < iteration.quadrant_count; i++) {
818  auto quadrant = iteration.quadrants[i];
819  for (size_t j = quadrant.start_index; j < quadrant.end_index; j++) {
820  proc(center + trigs[j] * quadrant.axis * inner_radii);
821  proc(center + trigs[j] * quadrant.axis * outer_radii);
822  }
823  }
824  proc(center + iteration.end * inner_radii);
825  proc(center + iteration.end * outer_radii);
826  if (cap == Cap::kSquare) {
827  Vector2 offset = Vector2{-iteration.end.y, iteration.end.x} * half_width;
828  proc(center + iteration.end * inner_radii + offset);
829  proc(center + iteration.end * outer_radii + offset);
830  }
831 }
832 
833 void Tessellator::GenerateRoundCapLine(
834  const Trigs& trigs,
835  const EllipticalVertexGenerator::Data& data,
836  const TessellatedVertexProc& proc) {
837  auto p0 = data.reference_centers[0];
838  auto p1 = data.reference_centers[1];
839  auto radius = data.radii.width;
840 
841  FML_DCHECK(radius == data.radii.height);
842  FML_DCHECK(data.half_width < 0);
843 
844  auto along = p1 - p0;
845  along *= radius / along.GetLength();
846  auto across = Point(-along.y, along.x);
847 
848  for (auto& trig : trigs) {
849  auto relative_along = along * trig.cos;
850  auto relative_across = across * trig.sin;
851  proc(p0 - relative_along + relative_across);
852  proc(p0 - relative_along - relative_across);
853  }
854 
855  // The second half of the round caps should be iterated in reverse, but
856  // we can instead iterate forward and swap the sin/cos values as they
857  // should be symmetric.
858  for (auto& trig : trigs) {
859  auto relative_along = along * trig.sin;
860  auto relative_across = across * trig.cos;
861  proc(p1 + relative_along + relative_across);
862  proc(p1 + relative_along - relative_across);
863  }
864 }
865 
866 void Tessellator::GenerateFilledEllipse(
867  const Trigs& trigs,
868  const EllipticalVertexGenerator::Data& data,
869  const TessellatedVertexProc& proc) {
870  auto center = data.reference_centers[0];
871  auto radii = data.radii;
872 
873  FML_DCHECK(center == data.reference_centers[1]);
874  FML_DCHECK(data.half_width < 0);
875 
876  // Quadrant 1 connecting with Quadrant 4:
877  for (auto& trig : trigs) {
878  auto offset = trig * radii;
879  proc({center.x - offset.x, center.y + offset.y});
880  proc({center.x - offset.x, center.y - offset.y});
881  }
882 
883  // The second half of the circle should be iterated in reverse, but
884  // we can instead iterate forward and swap the x/y values of the
885  // offset as the angles should be symmetric and thus should generate
886  // symmetrically reversed trig vectors.
887  // Quadrant 2 connecting with Quadrant 2:
888  for (auto& trig : trigs) {
889  auto offset = Point(trig.sin * radii.width, trig.cos * radii.height);
890  proc({center.x + offset.x, center.y + offset.y});
891  proc({center.x + offset.x, center.y - offset.y});
892  }
893 }
894 
895 void Tessellator::GenerateFilledRoundRect(
896  const Trigs& trigs,
897  const EllipticalVertexGenerator::Data& data,
898  const TessellatedVertexProc& proc) {
899  Scalar left = data.reference_centers[0].x;
900  Scalar top = data.reference_centers[0].y;
901  Scalar right = data.reference_centers[1].x;
902  Scalar bottom = data.reference_centers[1].y;
903  auto radii = data.radii;
904 
905  FML_DCHECK(data.half_width < 0);
906 
907  // Quadrant 1 connecting with Quadrant 4:
908  for (auto& trig : trigs) {
909  auto offset = trig * radii;
910  proc({left - offset.x, bottom + offset.y});
911  proc({left - offset.x, top - offset.y});
912  }
913 
914  // The second half of the round rect should be iterated in reverse, but
915  // we can instead iterate forward and swap the x/y values of the
916  // offset as the angles should be symmetric and thus should generate
917  // symmetrically reversed trig vectors.
918  // Quadrant 2 connecting with Quadrant 2:
919  for (auto& trig : trigs) {
920  auto offset = Point(trig.sin * radii.width, trig.cos * radii.height);
921  proc({right + offset.x, bottom + offset.y});
922  proc({right + offset.x, top - offset.y});
923  }
924 }
925 
926 } // namespace impeller
bool use_center
virtual void Flush(std::optional< Range > range=std::nullopt) const
virtual uint8_t * OnGetContents() const =0
BufferView Emplace(const BufferType &buffer, size_t alignment=0)
Emplace non-uniform data (like contiguous vertices) onto the host buffer.
Definition: host_buffer.h:92
An interface for generating a multi contour polyline as a triangle strip.
virtual void Write(Point point)=0
static void PathToFilledVertices(const PathSource &source, VertexWriter &writer, Scalar scale)
static std::pair< size_t, size_t > CountFillStorage(const PathSource &source, Scalar scale)
The |VertexGenerator| implementation common to all shapes that are based on a polygonal representatio...
Definition: tessellator.h:191
size_t GetVertexCount() const override
|VertexGenerator|
Definition: tessellator.cc:522
PrimitiveType GetTriangleType() const override
|VertexGenerator|
Definition: tessellator.cc:516
void GenerateVertices(const TessellatedVertexProc &proc) const override
|VertexGenerator|
Definition: tessellator.cc:542
The |VertexGenerator| implementation common to all shapes that are based on a polygonal representatio...
Definition: tessellator.h:140
Trigs(Scalar pixel_radius)
Definition: tessellator.cc:412
A utility that generates triangles of the specified fill type given a polyline. This happens on the C...
Definition: tessellator.h:37
Trigs GetTrigsForDeviceRadius(Scalar pixel_radius)
Definition: tessellator.cc:310
std::vector< Point > stroke_points_
Used for stroke path generation.
Definition: tessellator.h:383
EllipticalVertexGenerator RoundCapLine(const Matrix &view_transform, const Point &p0, const Point &p1, Scalar radius)
Create a |VertexGenerator| that can produce vertices for a line with round end caps of the given radi...
Definition: tessellator.cc:584
std::vector< Point > & GetStrokePointCache()
Retrieve a pre-allocated arena of kPointArenaSize points.
Definition: tessellator.cc:306
EllipticalVertexGenerator FilledRoundRect(const Matrix &view_transform, const Rect &bounds, const Size &radii)
Create a |VertexGenerator| that can produce vertices for a filled round rect within the given bounds ...
Definition: tessellator.cc:628
static constexpr Scalar kCircleTolerance
The pixel tolerance used by the algorighm to determine how many divisions to create for a circle.
Definition: tessellator.h:264
VertexBuffer TessellateConvex(const PathSource &path, HostBuffer &data_host_buffer, HostBuffer &indexes_host_buffer, Scalar tolerance, bool supports_primitive_restart=false, bool supports_triangle_fan=false)
Given a convex path, create a triangle fan structure.
Definition: tessellator.cc:314
EllipticalVertexGenerator FilledCircle(const Matrix &view_transform, const Point &center, Scalar radius)
Create a |VertexGenerator| that can produce vertices for a filled circle of the given radius around t...
Definition: tessellator.cc:453
ArcVertexGenerator StrokedArc(const Matrix &view_transform, const Arc &arc, Cap cap, Scalar half_width)
Create a |VertexGenerator| that can produce vertices for a stroked arc inscribed within the given ova...
Definition: tessellator.cc:568
static void TessellateConvexInternal(const PathSource &path, std::vector< Point > &point_buffer, std::vector< uint16_t > &index_buffer, Scalar tolerance)
Definition: tessellator.cc:400
std::unique_ptr< std::vector< Point > > point_buffer_
Used for polyline generation.
Definition: tessellator.h:380
std::unique_ptr< std::vector< uint16_t > > index_buffer_
Definition: tessellator.h:381
EllipticalVertexGenerator FilledEllipse(const Matrix &view_transform, const Rect &bounds)
Create a |VertexGenerator| that can produce vertices for a filled ellipse inscribed within the given ...
Definition: tessellator.cc:607
std::function< void(const Point &p)> TessellatedVertexProc
A callback function for a |VertexGenerator| to deliver the vertices it computes as |Point| objects.
Definition: tessellator.h:97
ArcVertexGenerator FilledArc(const Matrix &view_transform, const Arc &arc, bool supports_triangle_fans)
Create a |VertexGenerator| that can produce vertices for a stroked arc inscribed within the given ova...
Definition: tessellator.cc:557
Tessellator::TessellatedVertexProc TessellatedVertexProc
Definition: tessellator.cc:438
PrimitiveType
Decides how backend draws pixels based on input vertices.
Definition: formats.h:352
Point Vector2
Definition: point.h:331
float Scalar
Definition: scalar.h:19
constexpr float kEhCloseEnough
Definition: constants.h:57
TRect< Scalar > Rect
Definition: rect.h:788
TPoint< Scalar > Point
Definition: point.h:327
Cap
An enum that describes ways to decorate the end of a path contour.
constexpr float kPiOver2
Definition: constants.h:32
Tessellator::EllipticalVertexGenerator EllipticalVertexGenerator
Definition: tessellator.cc:439
Tessellator::ArcVertexGenerator ArcVertexGenerator
Definition: tessellator.cc:440
constexpr float kPiOver4
Definition: constants.h:35
TSize< Scalar > Size
Definition: size.h:159
static constexpr size_t kPointArenaSize
The size of the point arena buffer stored on the tessellator.
Definition: tessellator.h:25
Definition: comparable.h:95
size_t GetPointCount() const
Definition: arc.cc:36
Iteration ComputeIterations(size_t step_count, bool simplify_360=true) const
Definition: arc.cc:102
constexpr bool IncludeCenter() const
Definition: arc.h:110
const Size GetOvalSize() const
Returns the size of the oval bounds.
Definition: arc.h:100
constexpr bool IsPerfectCircle() const
Definition: arc.h:112
const Rect & GetOvalBounds() const
Return the bounds of the oval in which this arc is inscribed.
Definition: arc.h:94
Range GetRange() const
Definition: buffer_view.h:27
const DeviceBuffer * GetBuffer() const
Definition: buffer_view.cc:17
A 4x4 matrix using column-major storage.
Definition: matrix.h:37
Scalar GetMaxBasisLengthXY() const
Return the maximum scale applied specifically to either the X axis or Y axis unit vectors (the bases)...
Definition: matrix.h:328
size_t offset
Definition: range.h:14
constexpr Type GetLength() const
Definition: point.h:206
constexpr Type GetHeight() const
Returns the height of the rectangle, equivalent to |GetSize().height|.
Definition: rect.h:347
constexpr TSize< Type > GetSize() const
Returns the size of the rectangle which may be negative in either width or height and may have been c...
Definition: rect.h:327
constexpr bool IsSquare() const
Returns true if width and height are equal and neither is NaN.
Definition: rect.h:304
constexpr Type GetWidth() const
Returns the width of the rectangle, equivalent to |GetSize().width|.
Definition: rect.h:341
constexpr TPoint< T > GetRightBottom() const
Definition: rect.h:371
constexpr Point GetCenter() const
Get the center point as a |Point|.
Definition: rect.h:382
constexpr TPoint< T > GetLeftTop() const
Definition: rect.h:359
constexpr Type MaxDimension() const
Definition: size.h:106
Type height
Definition: size.h:29
Type width
Definition: size.h:28
const size_t start
const size_t end
std::vector< Point > points
std::shared_ptr< const fml::Mapping > data
Definition: texture_gles.cc:68