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 
7 namespace impeller {
8 
10  : point_buffer_(std::make_unique<std::vector<Point>>()),
11  index_buffer_(std::make_unique<std::vector<uint16_t>>()) {
12  point_buffer_->reserve(2048);
13  index_buffer_->reserve(2048);
14 }
15 
16 Tessellator::~Tessellator() = default;
17 
19  Scalar tolerance) {
20  FML_DCHECK(point_buffer_);
21  point_buffer_->clear();
22  auto polyline =
23  path.CreatePolyline(tolerance, std::move(point_buffer_),
24  [this](Path::Polyline::PointBufferPtr point_buffer) {
25  point_buffer_ = std::move(point_buffer);
26  });
27  return polyline;
28 }
29 
31  HostBuffer& host_buffer,
32  Scalar tolerance) {
33  FML_DCHECK(point_buffer_);
34  FML_DCHECK(index_buffer_);
36 
37  if (point_buffer_->empty()) {
38  return VertexBuffer{
39  .vertex_buffer = {},
40  .index_buffer = {},
41  .vertex_count = 0u,
42  .index_type = IndexType::k16bit,
43  };
44  }
45 
46  BufferView vertex_buffer = host_buffer.Emplace(
47  point_buffer_->data(), sizeof(Point) * point_buffer_->size(),
48  alignof(Point));
49 
50  BufferView index_buffer = host_buffer.Emplace(
51  index_buffer_->data(), sizeof(uint16_t) * index_buffer_->size(),
52  alignof(uint16_t));
53 
54  return VertexBuffer{
55  .vertex_buffer = std::move(vertex_buffer),
56  .index_buffer = std::move(index_buffer),
57  .vertex_count = index_buffer_->size(),
58  .index_type = IndexType::k16bit,
59  };
60 }
61 
63  std::vector<Point>& point_buffer,
64  std::vector<uint16_t>& index_buffer,
65  Scalar tolerance) {
66  point_buffer.clear();
67  index_buffer.clear();
68 
69  VertexWriter writer(point_buffer, index_buffer);
70 
71  path.WritePolyline(tolerance, writer);
72 }
73 
74 static constexpr int kPrecomputedDivisionCount = 1024;
76  // clang-format off
77  1, 2, 3, 4, 4, 4, 5, 5, 5, 6, 6, 6, 7, 7, 7, 7,
78  8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10,
79  10, 11, 11, 11, 11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 13,
80  13, 13, 13, 13, 13, 13, 13, 14, 14, 14, 14, 14, 14, 14, 14, 14,
81  15, 15, 15, 15, 15, 15, 15, 15, 15, 16, 16, 16, 16, 16, 16, 16,
82  16, 16, 16, 16, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 18, 18,
83  18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 19, 19, 19, 19, 19, 19,
84  19, 19, 19, 19, 19, 19, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20,
85  20, 20, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21,
86  22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 23, 23, 23,
87  23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24,
88  24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 25, 25, 25, 25, 25,
89  25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 26, 26, 26, 26, 26,
90  26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 27, 27, 27, 27,
91  27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 28, 28, 28,
92  28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 29,
93  29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29,
94  29, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30,
95  30, 30, 30, 30, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31,
96  31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32,
97  32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33,
98  33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33,
99  33, 33, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34,
100  34, 34, 34, 34, 34, 34, 34, 35, 35, 35, 35, 35, 35, 35, 35, 35,
101  35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 36, 36,
102  36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36,
103  36, 36, 36, 36, 36, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37,
104  37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 38, 38, 38, 38,
105  38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38,
106  38, 38, 38, 38, 38, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39,
107  39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 40, 40,
108  40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40,
109  40, 40, 40, 40, 40, 40, 40, 41, 41, 41, 41, 41, 41, 41, 41, 41,
110  41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41,
111  41, 41, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42,
112  42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 43, 43, 43, 43,
113  43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43,
114  43, 43, 43, 43, 43, 43, 43, 43, 44, 44, 44, 44, 44, 44, 44, 44,
115  44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44,
116  44, 44, 44, 44, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45,
117  45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45,
118  45, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46,
119  46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 47,
120  47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47,
121  47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 48, 48, 48,
122  48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48,
123  48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 49, 49, 49, 49,
124  49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49,
125  49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 50, 50, 50, 50, 50,
126  50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50,
127  50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 51, 51, 51, 51, 51,
128  51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51,
129  51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 52, 52, 52, 52,
130  52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52,
131  52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 53, 53, 53,
132  53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53,
133  53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 54,
134  54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
135  54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
136  54, 54, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55,
137  55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55,
138  55, 55, 55, 55, 55, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56,
139  56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56,
140  56, 56, 56, 56, 56, 56, 56, 56, 56, 57, 57, 57, 57, 57, 57, 57,
141  // clang-format on
142 };
143 
144 static size_t ComputeQuadrantDivisions(Scalar pixel_radius) {
145  if (pixel_radius <= 0.0) {
146  return 1;
147  }
148  int radius_index = ceil(pixel_radius);
149  if (radius_index < kPrecomputedDivisionCount) {
150  return kPrecomputedDivisions[radius_index];
151  }
152 
153  // For a circle with N divisions per quadrant, the maximum deviation of
154  // the polgyon approximation from the true circle will be at the center
155  // of the base of each triangular pie slice. We can compute that distance
156  // by finding the midpoint of the line of the first slice and compare
157  // its distance from the center of the circle to the radius. We will aim
158  // to have the length of that bisector to be within |kCircleTolerance|
159  // from the radius in pixels.
160  //
161  // Each vertex will appear at an angle of:
162  // theta(i) = (kPi / 2) * (i / N) // for i in [0..N]
163  // with each point falling at:
164  // point(i) = r * (cos(theta), sin(theta))
165  // If we consider the unit circle to simplify the calculations below then
166  // we need to scale the tolerance from its absolute quantity into a unit
167  // circle fraction:
168  // k = tolerance / radius
169  // Using this scaled tolerance below to avoid multiplying by the radius
170  // throughout all of the math, we have:
171  // first point = (1, 0) // theta(0) == 0
172  // theta = kPi / 2 / N // theta(1)
173  // second point = (cos(theta), sin(theta)) = (c, s)
174  // midpoint = (first + second) * 0.5 = ((1 + c)/2, s/2)
175  // |midpoint| = sqrt((1 + c)*(1 + c)/4 + s*s/4)
176  // = sqrt((1 + c + c + c*c + s*s) / 4)
177  // = sqrt((1 + 2c + 1) / 4)
178  // = sqrt((2 + 2c) / 4)
179  // = sqrt((1 + c) / 2)
180  // = cos(theta / 2) // using half-angle cosine formula
181  // error = 1 - |midpoint| = 1 - cos(theta / 2)
182  // cos(theta/2) = 1 - error
183  // theta/2 = acos(1 - error)
184  // kPi / 2 / N / 2 = acos(1 - error)
185  // kPi / 4 / acos(1 - error) = N
186  // Since we need error <= k, we want divisions >= N, so we use:
187  // N = ceil(kPi / 4 / acos(1 - k))
188  //
189  // Math is confirmed in https://math.stackexchange.com/a/4132095
190  // (keeping in mind that we are computing quarter circle divisions here)
191  // which also points out a performance optimization that is accurate
192  // to within an over-estimation of 1 division would be:
193  // N = ceil(kPi / 4 / sqrt(2 * k))
194  // Since we have precomputed the divisions for radii up to 1024, we can
195  // afford to be more accurate using the acos formula here for larger radii.
196  double k = Tessellator::kCircleTolerance / pixel_radius;
197  return ceil(kPiOver4 / std::acos(1 - k));
198 }
199 
200 void Tessellator::Trigs::init(size_t divisions) {
201  if (!trigs_.empty()) {
202  return;
203  }
204 
205  // Either not cached yet, or we are using the temp storage...
206  trigs_.reserve(divisions + 1);
207 
208  double angle_scale = kPiOver2 / divisions;
209 
210  trigs_.emplace_back(1.0, 0.0);
211  for (size_t i = 1; i < divisions; i++) {
212  trigs_.emplace_back(Radians(i * angle_scale));
213  }
214  trigs_.emplace_back(0.0, 1.0);
215 }
216 
217 Tessellator::Trigs Tessellator::GetTrigsForDivisions(size_t divisions) {
218  return divisions < Tessellator::kCachedTrigCount
219  ? Trigs(precomputed_trigs_[divisions], divisions)
220  : Trigs(divisions);
221 }
222 
225 
226 EllipticalVertexGenerator::EllipticalVertexGenerator(
227  EllipticalVertexGenerator::GeneratorProc& generator,
228  Trigs&& trigs,
229  PrimitiveType triangle_type,
230  size_t vertices_per_trig,
231  Data&& data)
232  : impl_(generator),
233  trigs_(std::move(trigs)),
234  data_(data),
235  vertices_per_trig_(vertices_per_trig) {}
236 
237 EllipticalVertexGenerator Tessellator::FilledCircle(
238  const Matrix& view_transform,
239  const Point& center,
240  Scalar radius) {
241  auto divisions =
242  ComputeQuadrantDivisions(view_transform.GetMaxBasisLength() * radius);
243  return EllipticalVertexGenerator(Tessellator::GenerateFilledCircle,
244  GetTrigsForDivisions(divisions),
245  PrimitiveType::kTriangleStrip, 4,
246  {
247  .reference_centers = {center, center},
248  .radii = {radius, radius},
249  .half_width = -1.0f,
250  });
251 }
252 
253 EllipticalVertexGenerator Tessellator::StrokedCircle(
254  const Matrix& view_transform,
255  const Point& center,
256  Scalar radius,
257  Scalar half_width) {
258  if (half_width > 0) {
259  auto divisions = ComputeQuadrantDivisions(
260  view_transform.GetMaxBasisLength() * radius + half_width);
261  return EllipticalVertexGenerator(Tessellator::GenerateStrokedCircle,
262  GetTrigsForDivisions(divisions),
263  PrimitiveType::kTriangleStrip, 8,
264  {
265  .reference_centers = {center, center},
266  .radii = {radius, radius},
267  .half_width = half_width,
268  });
269  } else {
270  return FilledCircle(view_transform, center, radius);
271  }
272 }
273 
274 EllipticalVertexGenerator Tessellator::RoundCapLine(
275  const Matrix& view_transform,
276  const Point& p0,
277  const Point& p1,
278  Scalar radius) {
279  auto along = p1 - p0;
280  auto length = along.GetLength();
281  if (length > kEhCloseEnough) {
282  auto divisions =
283  ComputeQuadrantDivisions(view_transform.GetMaxBasisLength() * radius);
284  return EllipticalVertexGenerator(Tessellator::GenerateRoundCapLine,
285  GetTrigsForDivisions(divisions),
286  PrimitiveType::kTriangleStrip, 4,
287  {
288  .reference_centers = {p0, p1},
289  .radii = {radius, radius},
290  .half_width = -1.0f,
291  });
292  } else {
293  return FilledCircle(view_transform, p0, radius);
294  }
295 }
296 
297 EllipticalVertexGenerator Tessellator::FilledEllipse(
298  const Matrix& view_transform,
299  const Rect& bounds) {
300  if (bounds.IsSquare()) {
301  return FilledCircle(view_transform, bounds.GetCenter(),
302  bounds.GetWidth() * 0.5f);
303  }
304  auto max_radius = bounds.GetSize().MaxDimension();
305  auto divisions =
306  ComputeQuadrantDivisions(view_transform.GetMaxBasisLength() * max_radius);
307  auto center = bounds.GetCenter();
308  return EllipticalVertexGenerator(Tessellator::GenerateFilledEllipse,
309  GetTrigsForDivisions(divisions),
310  PrimitiveType::kTriangleStrip, 4,
311  {
312  .reference_centers = {center, center},
313  .radii = bounds.GetSize() * 0.5f,
314  .half_width = -1.0f,
315  });
316 }
317 
318 EllipticalVertexGenerator Tessellator::FilledRoundRect(
319  const Matrix& view_transform,
320  const Rect& bounds,
321  const Size& radii) {
322  if (radii.width * 2 < bounds.GetWidth() ||
323  radii.height * 2 < bounds.GetHeight()) {
324  auto max_radius = radii.MaxDimension();
325  auto divisions = ComputeQuadrantDivisions(
326  view_transform.GetMaxBasisLength() * max_radius);
327  auto upper_left = bounds.GetLeftTop() + radii;
328  auto lower_right = bounds.GetRightBottom() - radii;
329  return EllipticalVertexGenerator(Tessellator::GenerateFilledRoundRect,
330  GetTrigsForDivisions(divisions),
331  PrimitiveType::kTriangleStrip, 4,
332  {
333  .reference_centers =
334  {
335  upper_left,
336  lower_right,
337  },
338  .radii = radii,
339  .half_width = -1.0f,
340  });
341  } else {
342  return FilledEllipse(view_transform, bounds);
343  }
344 }
345 
346 void Tessellator::GenerateFilledCircle(
347  const Trigs& trigs,
348  const EllipticalVertexGenerator::Data& data,
349  const TessellatedVertexProc& proc) {
350  auto center = data.reference_centers[0];
351  auto radius = data.radii.width;
352 
353  FML_DCHECK(center == data.reference_centers[1]);
354  FML_DCHECK(radius == data.radii.height);
355  FML_DCHECK(data.half_width < 0);
356 
357  // Quadrant 1 connecting with Quadrant 4:
358  for (auto& trig : trigs) {
359  auto offset = trig * radius;
360  proc({center.x - offset.x, center.y + offset.y});
361  proc({center.x - offset.x, center.y - offset.y});
362  }
363 
364  // The second half of the circle should be iterated in reverse, but
365  // we can instead iterate forward and swap the x/y values of the
366  // offset as the angles should be symmetric and thus should generate
367  // symmetrically reversed trig vectors.
368  // Quadrant 2 connecting with Quadrant 2:
369  for (auto& trig : trigs) {
370  auto offset = trig * radius;
371  proc({center.x + offset.y, center.y + offset.x});
372  proc({center.x + offset.y, center.y - offset.x});
373  }
374 }
375 
376 void Tessellator::GenerateStrokedCircle(
377  const Trigs& trigs,
378  const EllipticalVertexGenerator::Data& data,
379  const TessellatedVertexProc& proc) {
380  auto center = data.reference_centers[0];
381 
382  FML_DCHECK(center == data.reference_centers[1]);
383  FML_DCHECK(data.radii.IsSquare());
384  FML_DCHECK(data.half_width > 0 && data.half_width < data.radii.width);
385 
386  auto outer_radius = data.radii.width + data.half_width;
387  auto inner_radius = data.radii.width - data.half_width;
388 
389  // Zig-zag back and forth between points on the outer circle and the
390  // inner circle. Both circles are evaluated at the same number of
391  // quadrant divisions so the points for a given division should match
392  // 1 for 1 other than their applied radius.
393 
394  // Quadrant 1:
395  for (auto& trig : trigs) {
396  auto outer = trig * outer_radius;
397  auto inner = trig * inner_radius;
398  proc({center.x - outer.x, center.y - outer.y});
399  proc({center.x - inner.x, center.y - inner.y});
400  }
401 
402  // The even quadrants of the circle should be iterated in reverse, but
403  // we can instead iterate forward and swap the x/y values of the
404  // offset as the angles should be symmetric and thus should generate
405  // symmetrically reversed trig vectors.
406  // Quadrant 2:
407  for (auto& trig : trigs) {
408  auto outer = trig * outer_radius;
409  auto inner = trig * inner_radius;
410  proc({center.x + outer.y, center.y - outer.x});
411  proc({center.x + inner.y, center.y - inner.x});
412  }
413 
414  // Quadrant 3:
415  for (auto& trig : trigs) {
416  auto outer = trig * outer_radius;
417  auto inner = trig * inner_radius;
418  proc({center.x + outer.x, center.y + outer.y});
419  proc({center.x + inner.x, center.y + inner.y});
420  }
421 
422  // Quadrant 4:
423  for (auto& trig : trigs) {
424  auto outer = trig * outer_radius;
425  auto inner = trig * inner_radius;
426  proc({center.x - outer.y, center.y + outer.x});
427  proc({center.x - inner.y, center.y + inner.x});
428  }
429 }
430 
431 void Tessellator::GenerateRoundCapLine(
432  const Trigs& trigs,
433  const EllipticalVertexGenerator::Data& data,
434  const TessellatedVertexProc& proc) {
435  auto p0 = data.reference_centers[0];
436  auto p1 = data.reference_centers[1];
437  auto radius = data.radii.width;
438 
439  FML_DCHECK(radius == data.radii.height);
440  FML_DCHECK(data.half_width < 0);
441 
442  auto along = p1 - p0;
443  along *= radius / along.GetLength();
444  auto across = Point(-along.y, along.x);
445 
446  for (auto& trig : trigs) {
447  auto relative_along = along * trig.cos;
448  auto relative_across = across * trig.sin;
449  proc(p0 - relative_along + relative_across);
450  proc(p0 - relative_along - relative_across);
451  }
452 
453  // The second half of the round caps should be iterated in reverse, but
454  // we can instead iterate forward and swap the sin/cos values as they
455  // should be symmetric.
456  for (auto& trig : trigs) {
457  auto relative_along = along * trig.sin;
458  auto relative_across = across * trig.cos;
459  proc(p1 + relative_along + relative_across);
460  proc(p1 + relative_along - relative_across);
461  }
462 }
463 
464 void Tessellator::GenerateFilledEllipse(
465  const Trigs& trigs,
466  const EllipticalVertexGenerator::Data& data,
467  const TessellatedVertexProc& proc) {
468  auto center = data.reference_centers[0];
469  auto radii = data.radii;
470 
471  FML_DCHECK(center == data.reference_centers[1]);
472  FML_DCHECK(data.half_width < 0);
473 
474  // Quadrant 1 connecting with Quadrant 4:
475  for (auto& trig : trigs) {
476  auto offset = trig * radii;
477  proc({center.x - offset.x, center.y + offset.y});
478  proc({center.x - offset.x, center.y - offset.y});
479  }
480 
481  // The second half of the circle should be iterated in reverse, but
482  // we can instead iterate forward and swap the x/y values of the
483  // offset as the angles should be symmetric and thus should generate
484  // symmetrically reversed trig vectors.
485  // Quadrant 2 connecting with Quadrant 2:
486  for (auto& trig : trigs) {
487  auto offset = Point(trig.sin * radii.width, trig.cos * radii.height);
488  proc({center.x + offset.x, center.y + offset.y});
489  proc({center.x + offset.x, center.y - offset.y});
490  }
491 }
492 
493 void Tessellator::GenerateFilledRoundRect(
494  const Trigs& trigs,
495  const EllipticalVertexGenerator::Data& data,
496  const TessellatedVertexProc& proc) {
497  Scalar left = data.reference_centers[0].x;
498  Scalar top = data.reference_centers[0].y;
499  Scalar right = data.reference_centers[1].x;
500  Scalar bottom = data.reference_centers[1].y;
501  auto radii = data.radii;
502 
503  FML_DCHECK(data.half_width < 0);
504 
505  // Quadrant 1 connecting with Quadrant 4:
506  for (auto& trig : trigs) {
507  auto offset = trig * radii;
508  proc({left - offset.x, bottom + offset.y});
509  proc({left - offset.x, top - offset.y});
510  }
511 
512  // The second half of the round rect should be iterated in reverse, but
513  // we can instead iterate forward and swap the x/y values of the
514  // offset as the angles should be symmetric and thus should generate
515  // symmetrically reversed trig vectors.
516  // Quadrant 2 connecting with Quadrant 2:
517  for (auto& trig : trigs) {
518  auto offset = Point(trig.sin * radii.width, trig.cos * radii.height);
519  proc({right + offset.x, bottom + offset.y});
520  proc({right + offset.x, top - offset.y});
521  }
522 }
523 
524 } // namespace impeller
impeller::EllipticalVertexGenerator
Tessellator::EllipticalVertexGenerator EllipticalVertexGenerator
Definition: tessellator.cc:224
impeller::Tessellator::~Tessellator
virtual ~Tessellator()
impeller::HostBuffer::Emplace
BufferView Emplace(const BufferType &buffer, size_t alignment=0)
Emplace non-uniform data (like contiguous vertices) onto the host buffer.
Definition: host_buffer.h:95
polyline
const Path::Polyline & polyline
Definition: stroke_path_geometry.cc:303
impeller::IndexType::k16bit
@ k16bit
impeller::Scalar
float Scalar
Definition: scalar.h:18
impeller::kEhCloseEnough
constexpr float kEhCloseEnough
Definition: constants.h:56
impeller::HostBuffer
Definition: host_buffer.h:28
data
std::shared_ptr< const fml::Mapping > data
Definition: texture_gles.cc:63
impeller::Tessellator::TessellatedVertexProc
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:78
impeller::VertexBuffer
Definition: vertex_buffer.h:13
impeller::TRect::GetLeftTop
constexpr TPoint< T > GetLeftTop() const
Definition: rect.h:349
impeller::TSize::MaxDimension
constexpr Type MaxDimension() const
Definition: size.h:88
impeller::TRect::GetCenter
constexpr Point GetCenter() const
Get the center point as a |Point|.
Definition: rect.h:373
impeller::VertexBuffer::vertex_buffer
BufferView vertex_buffer
Definition: vertex_buffer.h:14
impeller::ComputeQuadrantDivisions
static size_t ComputeQuadrantDivisions(Scalar pixel_radius)
Definition: tessellator.cc:144
impeller::TRect::GetHeight
constexpr Type GetHeight() const
Returns the height of the rectangle, equivalent to |GetSize().height|.
Definition: rect.h:337
offset
SeparatedVector2 offset
Definition: stroke_path_geometry.cc:311
impeller::kPiOver2
constexpr float kPiOver2
Definition: constants.h:32
tessellator.h
impeller::Path::Polyline
Definition: path.h:95
impeller::Path::WritePolyline
void WritePolyline(Scalar scale, VertexWriter &writer) const
Definition: path.cc:106
impeller::TSize< Scalar >
impeller::kPrecomputedDivisions
static int kPrecomputedDivisions[kPrecomputedDivisionCount]
Definition: tessellator.cc:75
impeller::PrimitiveType
PrimitiveType
Decides how backend draws pixels based on input vertices.
Definition: formats.h:352
impeller::Point
TPoint< Scalar > Point
Definition: point.h:322
impeller::Path
Paths are lightweight objects that describe a collection of linear, quadratic, or cubic segments....
Definition: path.h:52
impeller::Matrix::GetMaxBasisLength
Scalar GetMaxBasisLength() const
Definition: matrix.cc:196
impeller::TRect::GetWidth
constexpr Type GetWidth() const
Returns the width of the rectangle, equivalent to |GetSize().width|.
Definition: rect.h:331
impeller::TRect::IsSquare
constexpr bool IsSquare() const
Returns true if width and height are equal and neither is NaN.
Definition: rect.h:294
impeller::Tessellator::point_buffer_
std::unique_ptr< std::vector< Point > > point_buffer_
Used for polyline generation.
Definition: tessellator.h:287
impeller::TessellatedVertexProc
Tessellator::TessellatedVertexProc TessellatedVertexProc
Definition: tessellator.cc:223
impeller::Tessellator::EllipticalVertexGenerator
The |VertexGenerator| implementation common to all shapes that are based on a polygonal representatio...
Definition: tessellator.h:121
impeller::TSize::width
Type width
Definition: size.h:22
impeller::Tessellator::TessellateConvexInternal
static void TessellateConvexInternal(const Path &path, std::vector< Point > &point_buffer, std::vector< uint16_t > &index_buffer, Scalar tolerance)
Definition: tessellator.cc:62
impeller::VertexWriter
An interface for generating a multi contour polyline as a triangle strip.
Definition: path_component.h:21
impeller::Tessellator::index_buffer_
std::unique_ptr< std::vector< uint16_t > > index_buffer_
Definition: tessellator.h:288
impeller::BufferView
Definition: buffer_view.h:15
impeller::TRect::GetSize
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:317
impeller::TPoint::GetLength
constexpr Type GetLength() const
Definition: point.h:206
std
Definition: comparable.h:95
impeller::TPoint< Scalar >
impeller::Tessellator::Tessellator
Tessellator()
Definition: tessellator.cc:9
impeller::Tessellator::kCircleTolerance
static constexpr Scalar kCircleTolerance
The pixel tolerance used by the algorighm to determine how many divisions to create for a circle.
Definition: tessellator.h:214
impeller::Tessellator::TessellateConvex
VertexBuffer TessellateConvex(const Path &path, HostBuffer &host_buffer, Scalar tolerance)
Given a convex path, create a triangle fan structure.
Definition: tessellator.cc:30
impeller::Path::Polyline::PointBufferPtr
std::unique_ptr< std::vector< Point > > PointBufferPtr
Definition: path.h:98
impeller::TSize::height
Type height
Definition: size.h:23
impeller::kPrecomputedDivisionCount
static constexpr int kPrecomputedDivisionCount
Definition: tessellator.cc:74
impeller::TRect::GetRightBottom
constexpr TPoint< T > GetRightBottom() const
Definition: rect.h:361
impeller
Definition: aiks_blend_unittests.cc:18
impeller::Tessellator::CreateTempPolyline
Path::Polyline CreateTempPolyline(const Path &path, Scalar tolerance)
Create a temporary polyline. Only one per-process can exist at a time.
Definition: tessellator.cc:18
impeller::Path::CreatePolyline
Polyline CreatePolyline(Scalar scale, Polyline::PointBufferPtr point_buffer=std::make_unique< std::vector< Point >>(), Polyline::ReclaimPointBufferCallback reclaim=nullptr) const
Definition: path.cc:264
impeller::kPiOver4
constexpr float kPiOver4
Definition: constants.h:35
impeller::TRect< Scalar >
impeller::Matrix
A 4x4 matrix using column-major storage.
Definition: matrix.h:37