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 #include "third_party/libtess2/Include/tesselator.h"
8 
9 namespace impeller {
10 
11 static void* HeapAlloc(void* userData, unsigned int size) {
12  return malloc(size);
13 }
14 
15 static void* HeapRealloc(void* userData, void* ptr, unsigned int size) {
16  return realloc(ptr, size);
17 }
18 
19 static void HeapFree(void* userData, void* ptr) {
20  free(ptr);
21 }
22 
23 // Note: these units are "number of entities" for bucket size and not in KB.
24 static const TESSalloc kAlloc = {
25  HeapAlloc, HeapRealloc, HeapFree, 0, /* =userData */
26  16, /* =meshEdgeBucketSize */
27  16, /* =meshVertexBucketSize */
28  16, /* =meshFaceBucketSize */
29  16, /* =dictNodeBucketSize */
30  16, /* =regionBucketSize */
31  0 /* =extraVertices */
32 };
33 
35  : point_buffer_(std::make_unique<std::vector<Point>>()),
36  c_tessellator_(nullptr, &DestroyTessellator) {
37  point_buffer_->reserve(2048);
38  TESSalloc alloc = kAlloc;
39  {
40  // libTess2 copies the TESSalloc despite the non-const argument.
41  CTessellator tessellator(::tessNewTess(&alloc), &DestroyTessellator);
42  c_tessellator_ = std::move(tessellator);
43  }
44 }
45 
46 Tessellator::~Tessellator() = default;
47 
48 static int ToTessWindingRule(FillType fill_type) {
49  switch (fill_type) {
50  case FillType::kOdd:
51  return TESS_WINDING_ODD;
52  case FillType::kNonZero:
53  return TESS_WINDING_NONZERO;
54  }
55  return TESS_WINDING_ODD;
56 }
57 
59  Scalar tolerance,
60  const BuilderCallback& callback) {
61  if (!callback) {
62  return Result::kInputError;
63  }
64 
65  point_buffer_->clear();
66  auto polyline =
67  path.CreatePolyline(tolerance, std::move(point_buffer_),
68  [this](Path::Polyline::PointBufferPtr point_buffer) {
69  point_buffer_ = std::move(point_buffer);
70  });
71 
72  auto fill_type = path.GetFillType();
73 
74  if (polyline.points->empty()) {
75  return Result::kInputError;
76  }
77 
78  auto tessellator = c_tessellator_.get();
79  if (!tessellator) {
81  }
82 
83  constexpr int kVertexSize = 2;
84  constexpr int kPolygonSize = 3;
85 
86  //----------------------------------------------------------------------------
87  /// Feed contour information to the tessellator.
88  ///
89  static_assert(sizeof(Point) == 2 * sizeof(float));
90  for (size_t contour_i = 0; contour_i < polyline.contours.size();
91  contour_i++) {
92  size_t start_point_index, end_point_index;
93  std::tie(start_point_index, end_point_index) =
94  polyline.GetContourPointBounds(contour_i);
95 
96  ::tessAddContour(tessellator, // the C tessellator
97  kVertexSize, //
98  polyline.points->data() + start_point_index, //
99  sizeof(Point), //
100  end_point_index - start_point_index //
101  );
102  }
103 
104  //----------------------------------------------------------------------------
105  /// Let's tessellate.
106  ///
107  auto result = ::tessTesselate(tessellator, // tessellator
108  ToTessWindingRule(fill_type), // winding
109  TESS_POLYGONS, // element type
110  kPolygonSize, // polygon size
111  kVertexSize, // vertex size
112  nullptr // normal (null is automatic)
113  );
114 
115  if (result != 1) {
117  }
118 
119  int element_item_count = tessGetElementCount(tessellator) * kPolygonSize;
120 
121  // We default to using a 16bit index buffer, but in cases where we generate
122  // more tessellated data than this can contain we need to fall back to
123  // dropping the index buffer entirely. Instead code could instead switch to
124  // a uint32 index buffer, but this is done for simplicity with the other
125  // fast path above.
126  if (element_item_count < USHRT_MAX) {
127  int vertex_item_count = tessGetVertexCount(tessellator);
128  auto vertices = tessGetVertices(tessellator);
129  auto elements = tessGetElements(tessellator);
130 
131  // libtess uses an int index internally due to usage of -1 as a sentinel
132  // value.
133  std::vector<uint16_t> indices(element_item_count);
134  for (int i = 0; i < element_item_count; i++) {
135  indices[i] = static_cast<uint16_t>(elements[i]);
136  }
137  if (!callback(vertices, vertex_item_count, indices.data(),
138  element_item_count)) {
139  return Result::kInputError;
140  }
141  } else {
142  std::vector<Point> points;
143  std::vector<float> data;
144 
145  int vertex_item_count = tessGetVertexCount(tessellator) * kVertexSize;
146  auto vertices = tessGetVertices(tessellator);
147  points.reserve(vertex_item_count);
148  for (int i = 0; i < vertex_item_count; i += 2) {
149  points.emplace_back(vertices[i], vertices[i + 1]);
150  }
151 
152  int element_item_count = tessGetElementCount(tessellator) * kPolygonSize;
153  auto elements = tessGetElements(tessellator);
154  data.reserve(element_item_count);
155  for (int i = 0; i < element_item_count; i++) {
156  data.emplace_back(points[elements[i]].x);
157  data.emplace_back(points[elements[i]].y);
158  }
159  if (!callback(data.data(), element_item_count, nullptr, 0u)) {
160  return Result::kInputError;
161  }
162  }
163 
164  return Result::kSuccess;
165 }
166 
168  Scalar tolerance) {
169  FML_DCHECK(point_buffer_);
170  point_buffer_->clear();
171  auto polyline =
172  path.CreatePolyline(tolerance, std::move(point_buffer_),
173  [this](Path::Polyline::PointBufferPtr point_buffer) {
174  point_buffer_ = std::move(point_buffer);
175  });
176  return polyline;
177 }
178 
179 std::vector<Point> Tessellator::TessellateConvex(const Path& path,
180  Scalar tolerance) {
181  FML_DCHECK(point_buffer_);
182 
183  std::vector<Point> output;
184  point_buffer_->clear();
185  auto polyline =
186  path.CreatePolyline(tolerance, std::move(point_buffer_),
187  [this](Path::Polyline::PointBufferPtr point_buffer) {
188  point_buffer_ = std::move(point_buffer);
189  });
190  if (polyline.points->size() == 0) {
191  return output;
192  }
193 
194  output.reserve(polyline.points->size() +
195  (4 * (polyline.contours.size() - 1)));
196  bool previous_contour_odd_points = false;
197  for (auto j = 0u; j < polyline.contours.size(); j++) {
198  auto [start, end] = polyline.GetContourPointBounds(j);
199  auto first_point = polyline.GetPoint(start);
200  // Some polygons will not self close and an additional triangle
201  // must be inserted, others will self close and we need to avoid
202  // inserting an extra triangle.
203  if (polyline.GetPoint(end - 1) == first_point) {
204  end--;
205  }
206 
207  if (j > 0) {
208  // Triangle strip break.
209  output.emplace_back(output.back());
210  output.emplace_back(first_point);
211  output.emplace_back(first_point);
212 
213  // If the contour has an odd number of points, insert an extra point when
214  // bridging to the next contour to preserve the correct triangle winding
215  // order.
216  if (previous_contour_odd_points) {
217  output.emplace_back(first_point);
218  }
219  } else {
220  output.emplace_back(first_point);
221  }
222 
223  if (start != end) {
224  size_t a = start + 1;
225  size_t b = end - 1;
226  while (a < b) {
227  output.emplace_back(polyline.GetPoint(a));
228  output.emplace_back(polyline.GetPoint(b));
229  a++;
230  b--;
231  }
232  if (a == b) {
233  previous_contour_odd_points = false;
234  output.emplace_back(polyline.GetPoint(a));
235  } else {
236  previous_contour_odd_points = true;
237  }
238  }
239  }
240  return output;
241 }
242 
243 void DestroyTessellator(TESStesselator* tessellator) {
244  if (tessellator != nullptr) {
245  ::tessDeleteTess(tessellator);
246  }
247 }
248 
249 static constexpr int kPrecomputedDivisionCount = 1024;
251  // clang-format off
252  1, 2, 3, 4, 4, 4, 5, 5, 5, 6, 6, 6, 7, 7, 7, 7,
253  8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10,
254  10, 11, 11, 11, 11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 13,
255  13, 13, 13, 13, 13, 13, 13, 14, 14, 14, 14, 14, 14, 14, 14, 14,
256  15, 15, 15, 15, 15, 15, 15, 15, 15, 16, 16, 16, 16, 16, 16, 16,
257  16, 16, 16, 16, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 18, 18,
258  18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 19, 19, 19, 19, 19, 19,
259  19, 19, 19, 19, 19, 19, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20,
260  20, 20, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21,
261  22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 23, 23, 23,
262  23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24,
263  24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 25, 25, 25, 25, 25,
264  25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 26, 26, 26, 26, 26,
265  26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 27, 27, 27, 27,
266  27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 28, 28, 28,
267  28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 29,
268  29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29,
269  29, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30,
270  30, 30, 30, 30, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31,
271  31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32,
272  32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33,
273  33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33,
274  33, 33, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34,
275  34, 34, 34, 34, 34, 34, 34, 35, 35, 35, 35, 35, 35, 35, 35, 35,
276  35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 36, 36,
277  36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36,
278  36, 36, 36, 36, 36, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37,
279  37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 38, 38, 38, 38,
280  38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38,
281  38, 38, 38, 38, 38, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39,
282  39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 40, 40,
283  40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40,
284  40, 40, 40, 40, 40, 40, 40, 41, 41, 41, 41, 41, 41, 41, 41, 41,
285  41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41,
286  41, 41, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42,
287  42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 43, 43, 43, 43,
288  43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43,
289  43, 43, 43, 43, 43, 43, 43, 43, 44, 44, 44, 44, 44, 44, 44, 44,
290  44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44,
291  44, 44, 44, 44, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45,
292  45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45,
293  45, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46,
294  46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 47,
295  47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47,
296  47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 48, 48, 48,
297  48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48,
298  48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 49, 49, 49, 49,
299  49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49,
300  49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 50, 50, 50, 50, 50,
301  50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50,
302  50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 51, 51, 51, 51, 51,
303  51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51,
304  51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 52, 52, 52, 52,
305  52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52,
306  52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 53, 53, 53,
307  53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53,
308  53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 54,
309  54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
310  54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
311  54, 54, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55,
312  55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55,
313  55, 55, 55, 55, 55, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56,
314  56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56,
315  56, 56, 56, 56, 56, 56, 56, 56, 56, 57, 57, 57, 57, 57, 57, 57,
316  // clang-format on
317 };
318 
319 static size_t ComputeQuadrantDivisions(Scalar pixel_radius) {
320  if (pixel_radius <= 0.0) {
321  return 1;
322  }
323  int radius_index = ceil(pixel_radius);
324  if (radius_index < kPrecomputedDivisionCount) {
325  return kPrecomputedDivisions[radius_index];
326  }
327 
328  // For a circle with N divisions per quadrant, the maximum deviation of
329  // the polgyon approximation from the true circle will be at the center
330  // of the base of each triangular pie slice. We can compute that distance
331  // by finding the midpoint of the line of the first slice and compare
332  // its distance from the center of the circle to the radius. We will aim
333  // to have the length of that bisector to be within |kCircleTolerance|
334  // from the radius in pixels.
335  //
336  // Each vertex will appear at an angle of:
337  // theta(i) = (kPi / 2) * (i / N) // for i in [0..N]
338  // with each point falling at:
339  // point(i) = r * (cos(theta), sin(theta))
340  // If we consider the unit circle to simplify the calculations below then
341  // we need to scale the tolerance from its absolute quantity into a unit
342  // circle fraction:
343  // k = tolerance / radius
344  // Using this scaled tolerance below to avoid multiplying by the radius
345  // throughout all of the math, we have:
346  // first point = (1, 0) // theta(0) == 0
347  // theta = kPi / 2 / N // theta(1)
348  // second point = (cos(theta), sin(theta)) = (c, s)
349  // midpoint = (first + second) * 0.5 = ((1 + c)/2, s/2)
350  // |midpoint| = sqrt((1 + c)*(1 + c)/4 + s*s/4)
351  // = sqrt((1 + c + c + c*c + s*s) / 4)
352  // = sqrt((1 + 2c + 1) / 4)
353  // = sqrt((2 + 2c) / 4)
354  // = sqrt((1 + c) / 2)
355  // = cos(theta / 2) // using half-angle cosine formula
356  // error = 1 - |midpoint| = 1 - cos(theta / 2)
357  // cos(theta/2) = 1 - error
358  // theta/2 = acos(1 - error)
359  // kPi / 2 / N / 2 = acos(1 - error)
360  // kPi / 4 / acos(1 - error) = N
361  // Since we need error <= k, we want divisions >= N, so we use:
362  // N = ceil(kPi / 4 / acos(1 - k))
363  //
364  // Math is confirmed in https://math.stackexchange.com/a/4132095
365  // (keeping in mind that we are computing quarter circle divisions here)
366  // which also points out a performance optimization that is accurate
367  // to within an over-estimation of 1 division would be:
368  // N = ceil(kPi / 4 / sqrt(2 * k))
369  // Since we have precomputed the divisions for radii up to 1024, we can
370  // afford to be more accurate using the acos formula here for larger radii.
371  double k = Tessellator::kCircleTolerance / pixel_radius;
372  return ceil(kPiOver4 / std::acos(1 - k));
373 }
374 
375 void Tessellator::Trigs::init(size_t divisions) {
376  if (!trigs_.empty()) {
377  return;
378  }
379 
380  // Either not cached yet, or we are using the temp storage...
381  trigs_.reserve(divisions + 1);
382 
383  double angle_scale = kPiOver2 / divisions;
384 
385  trigs_.emplace_back(1.0, 0.0);
386  for (size_t i = 1; i < divisions; i++) {
387  trigs_.emplace_back(Radians(i * angle_scale));
388  }
389  trigs_.emplace_back(0.0, 1.0);
390 }
391 
392 Tessellator::Trigs Tessellator::GetTrigsForDivisions(size_t divisions) {
393  return divisions < Tessellator::kCachedTrigCount
394  ? Trigs(precomputed_trigs_[divisions], divisions)
395  : Trigs(divisions);
396 }
397 
400 
401 EllipticalVertexGenerator::EllipticalVertexGenerator(
402  EllipticalVertexGenerator::GeneratorProc& generator,
403  Trigs&& trigs,
404  PrimitiveType triangle_type,
405  size_t vertices_per_trig,
406  Data&& data)
407  : impl_(generator),
408  trigs_(std::move(trigs)),
409  data_(data),
410  vertices_per_trig_(vertices_per_trig) {}
411 
412 EllipticalVertexGenerator Tessellator::FilledCircle(
413  const Matrix& view_transform,
414  const Point& center,
415  Scalar radius) {
416  auto divisions =
417  ComputeQuadrantDivisions(view_transform.GetMaxBasisLength() * radius);
418  return EllipticalVertexGenerator(Tessellator::GenerateFilledCircle,
419  GetTrigsForDivisions(divisions),
420  PrimitiveType::kTriangleStrip, 4,
421  {
422  .reference_centers = {center, center},
423  .radii = {radius, radius},
424  .half_width = -1.0f,
425  });
426 }
427 
428 EllipticalVertexGenerator Tessellator::StrokedCircle(
429  const Matrix& view_transform,
430  const Point& center,
431  Scalar radius,
432  Scalar half_width) {
433  if (half_width > 0) {
434  auto divisions = ComputeQuadrantDivisions(
435  view_transform.GetMaxBasisLength() * radius + half_width);
436  return EllipticalVertexGenerator(Tessellator::GenerateStrokedCircle,
437  GetTrigsForDivisions(divisions),
438  PrimitiveType::kTriangleStrip, 8,
439  {
440  .reference_centers = {center, center},
441  .radii = {radius, radius},
442  .half_width = half_width,
443  });
444  } else {
445  return FilledCircle(view_transform, center, radius);
446  }
447 }
448 
449 EllipticalVertexGenerator Tessellator::RoundCapLine(
450  const Matrix& view_transform,
451  const Point& p0,
452  const Point& p1,
453  Scalar radius) {
454  auto along = p1 - p0;
455  auto length = along.GetLength();
456  if (length > kEhCloseEnough) {
457  auto divisions =
458  ComputeQuadrantDivisions(view_transform.GetMaxBasisLength() * radius);
459  return EllipticalVertexGenerator(Tessellator::GenerateRoundCapLine,
460  GetTrigsForDivisions(divisions),
461  PrimitiveType::kTriangleStrip, 4,
462  {
463  .reference_centers = {p0, p1},
464  .radii = {radius, radius},
465  .half_width = -1.0f,
466  });
467  } else {
468  return FilledCircle(view_transform, p0, radius);
469  }
470 }
471 
472 EllipticalVertexGenerator Tessellator::FilledEllipse(
473  const Matrix& view_transform,
474  const Rect& bounds) {
475  if (bounds.IsSquare()) {
476  return FilledCircle(view_transform, bounds.GetCenter(),
477  bounds.GetWidth() * 0.5f);
478  }
479  auto max_radius = bounds.GetSize().MaxDimension();
480  auto divisions =
481  ComputeQuadrantDivisions(view_transform.GetMaxBasisLength() * max_radius);
482  auto center = bounds.GetCenter();
483  return EllipticalVertexGenerator(Tessellator::GenerateFilledEllipse,
484  GetTrigsForDivisions(divisions),
485  PrimitiveType::kTriangleStrip, 4,
486  {
487  .reference_centers = {center, center},
488  .radii = bounds.GetSize() * 0.5f,
489  .half_width = -1.0f,
490  });
491 }
492 
493 EllipticalVertexGenerator Tessellator::FilledRoundRect(
494  const Matrix& view_transform,
495  const Rect& bounds,
496  const Size& radii) {
497  if (radii.width * 2 < bounds.GetWidth() ||
498  radii.height * 2 < bounds.GetHeight()) {
499  auto max_radius = radii.MaxDimension();
500  auto divisions = ComputeQuadrantDivisions(
501  view_transform.GetMaxBasisLength() * max_radius);
502  auto upper_left = bounds.GetLeftTop() + radii;
503  auto lower_right = bounds.GetRightBottom() - radii;
504  return EllipticalVertexGenerator(Tessellator::GenerateFilledRoundRect,
505  GetTrigsForDivisions(divisions),
506  PrimitiveType::kTriangleStrip, 4,
507  {
508  .reference_centers =
509  {
510  upper_left,
511  lower_right,
512  },
513  .radii = radii,
514  .half_width = -1.0f,
515  });
516  } else {
517  return FilledEllipse(view_transform, bounds);
518  }
519 }
520 
521 void Tessellator::GenerateFilledCircle(
522  const Trigs& trigs,
523  const EllipticalVertexGenerator::Data& data,
524  const TessellatedVertexProc& proc) {
525  auto center = data.reference_centers[0];
526  auto radius = data.radii.width;
527 
528  FML_DCHECK(center == data.reference_centers[1]);
529  FML_DCHECK(radius == data.radii.height);
530  FML_DCHECK(data.half_width < 0);
531 
532  // Quadrant 1 connecting with Quadrant 4:
533  for (auto& trig : trigs) {
534  auto offset = trig * radius;
535  proc({center.x - offset.x, center.y + offset.y});
536  proc({center.x - offset.x, center.y - offset.y});
537  }
538 
539  // The second half of the circle should be iterated in reverse, but
540  // we can instead iterate forward and swap the x/y values of the
541  // offset as the angles should be symmetric and thus should generate
542  // symmetrically reversed trig vectors.
543  // Quadrant 2 connecting with Quadrant 2:
544  for (auto& trig : trigs) {
545  auto offset = trig * radius;
546  proc({center.x + offset.y, center.y + offset.x});
547  proc({center.x + offset.y, center.y - offset.x});
548  }
549 }
550 
551 void Tessellator::GenerateStrokedCircle(
552  const Trigs& trigs,
553  const EllipticalVertexGenerator::Data& data,
554  const TessellatedVertexProc& proc) {
555  auto center = data.reference_centers[0];
556 
557  FML_DCHECK(center == data.reference_centers[1]);
558  FML_DCHECK(data.radii.IsSquare());
559  FML_DCHECK(data.half_width > 0 && data.half_width < data.radii.width);
560 
561  auto outer_radius = data.radii.width + data.half_width;
562  auto inner_radius = data.radii.width - data.half_width;
563 
564  // Zig-zag back and forth between points on the outer circle and the
565  // inner circle. Both circles are evaluated at the same number of
566  // quadrant divisions so the points for a given division should match
567  // 1 for 1 other than their applied radius.
568 
569  // Quadrant 1:
570  for (auto& trig : trigs) {
571  auto outer = trig * outer_radius;
572  auto inner = trig * inner_radius;
573  proc({center.x - outer.x, center.y - outer.y});
574  proc({center.x - inner.x, center.y - inner.y});
575  }
576 
577  // The even quadrants of the circle should be iterated in reverse, but
578  // we can instead iterate forward and swap the x/y values of the
579  // offset as the angles should be symmetric and thus should generate
580  // symmetrically reversed trig vectors.
581  // Quadrant 2:
582  for (auto& trig : trigs) {
583  auto outer = trig * outer_radius;
584  auto inner = trig * inner_radius;
585  proc({center.x + outer.y, center.y - outer.x});
586  proc({center.x + inner.y, center.y - inner.x});
587  }
588 
589  // Quadrant 3:
590  for (auto& trig : trigs) {
591  auto outer = trig * outer_radius;
592  auto inner = trig * inner_radius;
593  proc({center.x + outer.x, center.y + outer.y});
594  proc({center.x + inner.x, center.y + inner.y});
595  }
596 
597  // Quadrant 4:
598  for (auto& trig : trigs) {
599  auto outer = trig * outer_radius;
600  auto inner = trig * inner_radius;
601  proc({center.x - outer.y, center.y + outer.x});
602  proc({center.x - inner.y, center.y + inner.x});
603  }
604 }
605 
606 void Tessellator::GenerateRoundCapLine(
607  const Trigs& trigs,
608  const EllipticalVertexGenerator::Data& data,
609  const TessellatedVertexProc& proc) {
610  auto p0 = data.reference_centers[0];
611  auto p1 = data.reference_centers[1];
612  auto radius = data.radii.width;
613 
614  FML_DCHECK(radius == data.radii.height);
615  FML_DCHECK(data.half_width < 0);
616 
617  auto along = p1 - p0;
618  along *= radius / along.GetLength();
619  auto across = Point(-along.y, along.x);
620 
621  for (auto& trig : trigs) {
622  auto relative_along = along * trig.cos;
623  auto relative_across = across * trig.sin;
624  proc(p0 - relative_along + relative_across);
625  proc(p0 - relative_along - relative_across);
626  }
627 
628  // The second half of the round caps should be iterated in reverse, but
629  // we can instead iterate forward and swap the sin/cos values as they
630  // should be symmetric.
631  for (auto& trig : trigs) {
632  auto relative_along = along * trig.sin;
633  auto relative_across = across * trig.cos;
634  proc(p1 + relative_along + relative_across);
635  proc(p1 + relative_along - relative_across);
636  }
637 }
638 
639 void Tessellator::GenerateFilledEllipse(
640  const Trigs& trigs,
641  const EllipticalVertexGenerator::Data& data,
642  const TessellatedVertexProc& proc) {
643  auto center = data.reference_centers[0];
644  auto radii = data.radii;
645 
646  FML_DCHECK(center == data.reference_centers[1]);
647  FML_DCHECK(data.half_width < 0);
648 
649  // Quadrant 1 connecting with Quadrant 4:
650  for (auto& trig : trigs) {
651  auto offset = trig * radii;
652  proc({center.x - offset.x, center.y + offset.y});
653  proc({center.x - offset.x, center.y - offset.y});
654  }
655 
656  // The second half of the circle should be iterated in reverse, but
657  // we can instead iterate forward and swap the x/y values of the
658  // offset as the angles should be symmetric and thus should generate
659  // symmetrically reversed trig vectors.
660  // Quadrant 2 connecting with Quadrant 2:
661  for (auto& trig : trigs) {
662  auto offset = Point(trig.sin * radii.width, trig.cos * radii.height);
663  proc({center.x + offset.x, center.y + offset.y});
664  proc({center.x + offset.x, center.y - offset.y});
665  }
666 }
667 
668 void Tessellator::GenerateFilledRoundRect(
669  const Trigs& trigs,
670  const EllipticalVertexGenerator::Data& data,
671  const TessellatedVertexProc& proc) {
672  Scalar left = data.reference_centers[0].x;
673  Scalar top = data.reference_centers[0].y;
674  Scalar right = data.reference_centers[1].x;
675  Scalar bottom = data.reference_centers[1].y;
676  auto radii = data.radii;
677 
678  FML_DCHECK(data.half_width < 0);
679 
680  // Quadrant 1 connecting with Quadrant 4:
681  for (auto& trig : trigs) {
682  auto offset = trig * radii;
683  proc({left - offset.x, bottom + offset.y});
684  proc({left - offset.x, top - offset.y});
685  }
686 
687  // The second half of the round rect should be iterated in reverse, but
688  // we can instead iterate forward and swap the x/y values of the
689  // offset as the angles should be symmetric and thus should generate
690  // symmetrically reversed trig vectors.
691  // Quadrant 2 connecting with Quadrant 2:
692  for (auto& trig : trigs) {
693  auto offset = Point(trig.sin * radii.width, trig.cos * radii.height);
694  proc({right + offset.x, bottom + offset.y});
695  proc({right + offset.x, top - offset.y});
696  }
697 }
698 
699 } // namespace impeller
impeller::kAlloc
static const TESSalloc kAlloc
Definition: tessellator.cc:24
impeller::EllipticalVertexGenerator
Tessellator::EllipticalVertexGenerator EllipticalVertexGenerator
Definition: tessellator.cc:399
impeller::Tessellator::~Tessellator
~Tessellator()
polyline
const Path::Polyline & polyline
Definition: stroke_path_geometry.cc:300
impeller::Scalar
float Scalar
Definition: scalar.h:18
impeller::FillType::kOdd
@ kOdd
impeller::HeapRealloc
static void * HeapRealloc(void *userData, void *ptr, unsigned int size)
Definition: tessellator.cc:15
impeller::kEhCloseEnough
constexpr float kEhCloseEnough
Definition: constants.h:56
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:80
impeller::HeapFree
static void HeapFree(void *userData, void *ptr)
Definition: tessellator.cc:19
impeller::Tessellator::Result::kInputError
@ kInputError
impeller::TRect::GetLeftTop
constexpr TPoint< T > GetLeftTop() const
Definition: rect.h:326
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:350
impeller::ComputeQuadrantDivisions
static size_t ComputeQuadrantDivisions(Scalar pixel_radius)
Definition: tessellator.cc:319
impeller::TRect::GetHeight
constexpr Type GetHeight() const
Returns the height of the rectangle, equivalent to |GetSize().height|.
Definition: rect.h:314
impeller::kPiOver2
constexpr float kPiOver2
Definition: constants.h:32
tessellator.h
impeller::Path::Polyline
Definition: path.h:94
impeller::TSize< Scalar >
impeller::kPrecomputedDivisions
static int kPrecomputedDivisions[kPrecomputedDivisionCount]
Definition: tessellator.cc:250
impeller::PrimitiveType
PrimitiveType
Decides how backend draws pixels based on input vertices.
Definition: formats.h:353
impeller::Point
TPoint< Scalar > Point
Definition: point.h:316
impeller::Tessellator::Result::kTessellationError
@ kTessellationError
impeller::Tessellator::TessellateConvex
std::vector< Point > TessellateConvex(const Path &path, Scalar tolerance)
Given a convex path, create a triangle fan structure.
Definition: tessellator.cc:179
impeller::Path
Paths are lightweight objects that describe a collection of linear, quadratic, or cubic segments....
Definition: path.h:51
impeller::DestroyTessellator
void DestroyTessellator(TESStesselator *tessellator)
Definition: tessellator.cc:243
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:308
impeller::TRect::IsSquare
constexpr bool IsSquare() const
Returns true if width and height are equal and neither is NaN.
Definition: rect.h:271
impeller::Tessellator::Result::kSuccess
@ kSuccess
impeller::FillType
FillType
Definition: path.h:29
impeller::TessellatedVertexProc
Tessellator::TessellatedVertexProc TessellatedVertexProc
Definition: tessellator.cc:398
impeller::Tessellator::EllipticalVertexGenerator
The |VertexGenerator| implementation common to all shapes that are based on a polygonal representatio...
Definition: tessellator.h:123
impeller::TSize::width
Type width
Definition: size.h:22
impeller::FillType::kNonZero
@ kNonZero
impeller::HeapAlloc
static void * HeapAlloc(void *userData, unsigned int size)
Definition: tessellator.cc:11
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:294
impeller::TPoint::GetLength
constexpr Type GetLength() const
Definition: point.h:206
std
Definition: comparable.h:95
impeller::TPoint< Scalar >
impeller::saturated::b
SI b
Definition: saturated_math.h:87
impeller::Tessellator::Tessellate
Tessellator::Result Tessellate(const Path &path, Scalar tolerance, const BuilderCallback &callback)
Generates filled triangles from the path. A callback is invoked once for the entire tessellation.
Definition: tessellator.cc:58
impeller::Tessellator::Tessellator
Tessellator()
Definition: tessellator.cc:34
impeller::ToTessWindingRule
static int ToTessWindingRule(FillType fill_type)
Definition: tessellator.cc:48
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:228
impeller::Path::Polyline::PointBufferPtr
std::unique_ptr< std::vector< Point > > PointBufferPtr
Definition: path.h:97
impeller::TSize::height
Type height
Definition: size.h:23
impeller::kPrecomputedDivisionCount
static constexpr int kPrecomputedDivisionCount
Definition: tessellator.cc:249
offset
Point offset
Definition: stroke_path_geometry.cc:308
impeller::TRect::GetRightBottom
constexpr TPoint< T > GetRightBottom() const
Definition: rect.h:338
impeller::Tessellator::Result
Result
Definition: tessellator.h:72
impeller
Definition: aiks_blur_unittests.cc:20
impeller::Tessellator::BuilderCallback
std::function< bool(const float *vertices, size_t vertices_count, const uint16_t *indices, size_t indices_count)> BuilderCallback
A callback that returns the results of the tessellation.
Definition: tessellator.h:183
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:167
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:198
impeller::kPiOver4
constexpr float kPiOver4
Definition: constants.h:35
impeller::TRect< Scalar >
impeller::CTessellator
std::unique_ptr< TESStesselator, decltype(&DestroyTessellator)> CTessellator
Definition: tessellator.h:24
impeller::Matrix
A 4x4 matrix using column-major storage.
Definition: matrix.h:37
impeller::Path::GetFillType
FillType GetFillType() const
Definition: path.cc:51