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 template <typename IndexT>
141 impeller::IndexType IndexTypeFor();
142 template <>
143 impeller::IndexType IndexTypeFor<uint32_t>() {
145 }
146 template <>
147 impeller::IndexType IndexTypeFor<uint16_t>() {
149 }
150 
151 /// @brief A vertex writer that generates a triangle fan and requires primitive
152 /// restart.
153 template <typename IndexT>
154 class FanPathVertexWriter : public impeller::PathTessellator::VertexWriter {
155  public:
156  explicit FanPathVertexWriter(impeller::Point* point_buffer,
157  IndexT* index_buffer)
158  : point_buffer_(point_buffer), index_buffer_(index_buffer) {}
159 
160  ~FanPathVertexWriter() = default;
161 
162  size_t GetIndexCount() const { return index_count_; }
163  size_t GetPointCount() const { return count_; }
164 
165  void EndContour() override {
166  if (count_ == 0) {
167  return;
168  }
169  index_buffer_[index_count_++] = static_cast<IndexT>(-1);
170  }
171 
172  void Write(impeller::Point point) override {
173  index_buffer_[index_count_++] = count_;
174  point_buffer_[count_++] = point;
175  }
176 
177  private:
178  size_t count_ = 0;
179  size_t index_count_ = 0;
180  impeller::Point* point_buffer_ = nullptr;
181  IndexT* index_buffer_ = nullptr;
182 };
183 
184 /// @brief A vertex writer that generates a triangle strip and requires
185 /// primitive restart.
186 template <typename IndexT>
187 class StripPathVertexWriter : public impeller::PathTessellator::VertexWriter {
188  public:
189  explicit StripPathVertexWriter(impeller::Point* point_buffer,
190  IndexT* index_buffer)
191  : point_buffer_(point_buffer), index_buffer_(index_buffer) {}
192 
193  ~StripPathVertexWriter() = default;
194 
195  size_t GetIndexCount() const { return index_count_; }
196  size_t GetPointCount() const { return count_; }
197 
198  void EndContour() override {
199  if (count_ == 0u || contour_start_ == count_ - 1) {
200  // Empty or first contour.
201  return;
202  }
203 
204  size_t start = contour_start_;
205  size_t end = count_ - 1;
206 
207  index_buffer_[index_count_++] = start;
208 
209  size_t a = start + 1;
210  size_t b = end;
211  while (a < b) {
212  index_buffer_[index_count_++] = a;
213  index_buffer_[index_count_++] = b;
214  a++;
215  b--;
216  }
217  if (a == b) {
218  index_buffer_[index_count_++] = a;
219  }
220 
221  contour_start_ = count_;
222  index_buffer_[index_count_++] = static_cast<IndexT>(-1);
223  }
224 
225  void Write(impeller::Point point) override {
226  point_buffer_[count_++] = point;
227  }
228 
229  private:
230  size_t count_ = 0;
231  size_t index_count_ = 0;
232  size_t contour_start_ = 0;
233  impeller::Point* point_buffer_ = nullptr;
234  IndexT* index_buffer_ = nullptr;
235 };
236 
237 /// @brief A vertex writer that has no hardware requirements.
238 template <typename IndexT>
239 class GLESPathVertexWriter : public impeller::PathTessellator::VertexWriter {
240  public:
241  explicit GLESPathVertexWriter(std::vector<impeller::Point>& points,
242  std::vector<IndexT>& indices)
243  : points_(points), indices_(indices) {}
244 
245  ~GLESPathVertexWriter() = default;
246 
247  void EndContour() override {
248  if (points_.size() == 0u || contour_start_ == points_.size() - 1) {
249  // Empty or first contour.
250  return;
251  }
252 
253  auto start = contour_start_;
254  auto end = points_.size() - 1;
255  // All filled paths are drawn as if they are closed, but if
256  // there is an explicit close then a lineTo to the origin
257  // is inserted. This point isn't strictly necesary to
258  // correctly render the shape and can be dropped.
259  if (points_[end] == points_[start]) {
260  end--;
261  }
262 
263  // Triangle strip break for subsequent contours
264  if (contour_start_ != 0) {
265  auto back = indices_.back();
266  indices_.push_back(back);
267  indices_.push_back(start);
268  indices_.push_back(start);
269 
270  // If the contour has an odd number of points, insert an extra point when
271  // bridging to the next contour to preserve the correct triangle winding
272  // order.
273  if (previous_contour_odd_points_) {
274  indices_.push_back(start);
275  }
276  } else {
277  indices_.push_back(start);
278  }
279 
280  size_t a = start + 1;
281  size_t b = end;
282  while (a < b) {
283  indices_.push_back(a);
284  indices_.push_back(b);
285  a++;
286  b--;
287  }
288  if (a == b) {
289  indices_.push_back(a);
290  previous_contour_odd_points_ = false;
291  } else {
292  previous_contour_odd_points_ = true;
293  }
294  contour_start_ = points_.size();
295  }
296 
297  void Write(impeller::Point point) override { points_.push_back(point); }
298 
299  private:
300  bool previous_contour_odd_points_ = false;
301  size_t contour_start_ = 0u;
302  std::vector<impeller::Point>& points_;
303  std::vector<IndexT>& indices_;
304 };
305 
306 template <typename IndexT>
307 void DoTessellateConvexInternal(const impeller::PathSource& path,
308  std::vector<impeller::Point>& point_buffer,
309  std::vector<IndexT>& index_buffer,
310  impeller::Scalar tolerance) {
311  point_buffer.clear();
312  index_buffer.clear();
313 
314  GLESPathVertexWriter writer(point_buffer, index_buffer);
315 
316  impeller::PathTessellator::PathToFilledVertices(path, writer, tolerance);
317 }
318 
319 } // namespace
320 
321 namespace impeller {
322 
323 template <typename IndexT>
324 class ConvexTessellatorImpl : public Tessellator::ConvexTessellator {
325  public:
327  point_buffer_.reserve(2048);
328  index_buffer_.reserve(2048);
329  }
330 
332  HostBuffer& data_host_buffer,
333  HostBuffer& indexes_host_buffer,
334  Scalar tolerance,
335  bool supports_primitive_restart,
336  bool supports_triangle_fan) override {
337  if (supports_primitive_restart) {
338  // Primitive Restart.
339  const auto [point_count, contour_count] =
340  PathTessellator::CountFillStorage(path, tolerance);
341  BufferView point_buffer = data_host_buffer.Emplace(
342  nullptr, sizeof(Point) * point_count, alignof(Point));
343  BufferView index_buffer = indexes_host_buffer.Emplace(
344  nullptr, sizeof(IndexT) * (point_count + contour_count),
345  alignof(IndexT));
346 
347  auto* points_ptr =
348  reinterpret_cast<Point*>(point_buffer.GetBuffer()->OnGetContents() +
349  point_buffer.GetRange().offset);
350  auto* indices_ptr =
351  reinterpret_cast<IndexT*>(index_buffer.GetBuffer()->OnGetContents() +
352  index_buffer.GetRange().offset);
353 
354  auto tessellate_path = [&](auto& writer) {
355  PathTessellator::PathToFilledVertices(path, writer, tolerance);
356  FML_DCHECK(writer.GetPointCount() <= point_count);
357  FML_DCHECK(writer.GetIndexCount() <= (point_count + contour_count));
358  point_buffer.GetBuffer()->Flush(point_buffer.GetRange());
359  index_buffer.GetBuffer()->Flush(index_buffer.GetRange());
360 
361  return VertexBuffer{
362  .vertex_buffer = std::move(point_buffer),
363  .index_buffer = std::move(index_buffer),
364  .vertex_count = writer.GetIndexCount(),
365  .index_type = IndexTypeFor<IndexT>(),
366  };
367  };
368 
369  if (supports_triangle_fan) {
370  FanPathVertexWriter writer(points_ptr, indices_ptr);
371  return tessellate_path(writer);
372  } else {
373  StripPathVertexWriter writer(points_ptr, indices_ptr);
374  return tessellate_path(writer);
375  }
376  }
377 
378  DoTessellateConvexInternal(path, point_buffer_, index_buffer_, tolerance);
379 
380  if (point_buffer_.empty()) {
381  return VertexBuffer{
382  .vertex_buffer = {},
383  .index_buffer = {},
384  .vertex_count = 0u,
385  .index_type = IndexTypeFor<IndexT>(),
386  };
387  }
388 
389  BufferView vertex_buffer = data_host_buffer.Emplace(
390  point_buffer_.data(), sizeof(Point) * point_buffer_.size(),
391  alignof(Point));
392 
393  BufferView index_buffer = indexes_host_buffer.Emplace(
394  index_buffer_.data(), sizeof(IndexT) * index_buffer_.size(),
395  alignof(IndexT));
396 
397  return VertexBuffer{
398  .vertex_buffer = std::move(vertex_buffer),
399  .index_buffer = std::move(index_buffer),
400  .vertex_count = index_buffer_.size(),
401  .index_type = IndexTypeFor<IndexT>(),
402  };
403  }
404 
405  private:
406  std::vector<Point> point_buffer_;
407  std::vector<IndexT> index_buffer_;
408 };
409 
410 Tessellator::Tessellator(bool supports_32bit_primitive_indices)
411  : stroke_points_(kPointArenaSize) {
412  if (supports_32bit_primitive_indices) {
413  convex_tessellator_ = std::make_unique<ConvexTessellatorImpl<uint32_t>>();
414  } else {
415  convex_tessellator_ = std::make_unique<ConvexTessellatorImpl<uint16_t>>();
416  }
417 }
418 
419 Tessellator::~Tessellator() = default;
420 
421 std::vector<Point>& Tessellator::GetStrokePointCache() {
422  return stroke_points_;
423 }
424 
426  return GetTrigsForDivisions(ComputeQuadrantDivisions(pixel_radius));
427 }
428 
430  HostBuffer& data_host_buffer,
431  HostBuffer& indexes_host_buffer,
432  Scalar tolerance,
433  bool supports_primitive_restart,
434  bool supports_triangle_fan) {
435  return convex_tessellator_->TessellateConvex(
436  path, data_host_buffer, indexes_host_buffer, tolerance,
437  supports_primitive_restart, supports_triangle_fan);
438 }
439 
441  std::vector<Point>& point_buffer,
442  std::vector<uint16_t>& index_buffer,
443  Scalar tolerance) {
444  DoTessellateConvexInternal(path, point_buffer, index_buffer, tolerance);
445 }
446 
448  : Tessellator::Trigs(ComputeQuadrantDivisions(pixel_radius)) {}
449 
450 void Tessellator::Trigs::init(size_t divisions) {
451  if (!trigs_.empty()) {
452  return;
453  }
454 
455  // Either not cached yet, or we are using the temp storage...
456  trigs_.reserve(divisions + 1);
457 
458  double angle_scale = kPiOver2 / divisions;
459 
460  trigs_.emplace_back(1.0, 0.0);
461  for (size_t i = 1; i < divisions; i++) {
462  trigs_.emplace_back(Radians(i * angle_scale));
463  }
464  trigs_.emplace_back(0.0, 1.0);
465 }
466 
467 Tessellator::Trigs Tessellator::GetTrigsForDivisions(size_t divisions) {
468  return divisions < Tessellator::kCachedTrigCount
469  ? Trigs(precomputed_trigs_[divisions], divisions)
470  : Trigs(divisions);
471 }
472 
476 
477 EllipticalVertexGenerator::EllipticalVertexGenerator(
478  EllipticalVertexGenerator::GeneratorProc& generator,
479  Trigs&& trigs,
480  PrimitiveType triangle_type,
481  size_t vertices_per_trig,
482  Data&& data)
483  : impl_(generator),
484  trigs_(std::move(trigs)),
485  data_(data),
486  vertices_per_trig_(vertices_per_trig) {}
487 
488 EllipticalVertexGenerator Tessellator::FilledCircle(
489  const Matrix& view_transform,
490  const Point& center,
491  Scalar radius) {
492  size_t divisions =
493  ComputeQuadrantDivisions(view_transform.GetMaxBasisLengthXY() * radius);
494  return EllipticalVertexGenerator(Tessellator::GenerateFilledCircle,
495  GetTrigsForDivisions(divisions),
496  PrimitiveType::kTriangleStrip, 4,
497  {
498  .reference_centers = {center, center},
499  .radii = {radius, radius},
500  .half_width = -1.0f,
501  });
502 }
503 
504 EllipticalVertexGenerator Tessellator::StrokedCircle(
505  const Matrix& view_transform,
506  const Point& center,
507  Scalar radius,
508  Scalar half_width) {
509  if (half_width > 0) {
510  auto divisions = ComputeQuadrantDivisions(
511  view_transform.GetMaxBasisLengthXY() * radius + half_width);
512  return EllipticalVertexGenerator(Tessellator::GenerateStrokedCircle,
513  GetTrigsForDivisions(divisions),
514  PrimitiveType::kTriangleStrip, 8,
515  {
516  .reference_centers = {center, center},
517  .radii = {radius, radius},
518  .half_width = half_width,
519  });
520  } else {
521  return FilledCircle(view_transform, center, radius);
522  }
523 }
524 
525 ArcVertexGenerator ArcVertexGenerator::MakeFilled(
526  const Arc::Iteration& iteration,
527  Trigs&& trigs,
528  const Rect& oval_bounds,
529  bool use_center,
530  bool supports_triangle_fans) {
531  return ArcVertexGenerator(iteration, std::move(trigs), oval_bounds,
532  use_center, supports_triangle_fans, -1.0f,
533  Cap::kSquare, nullptr);
534 }
535 
536 ArcVertexGenerator ArcVertexGenerator::MakeStroked(
537  const Arc::Iteration& iteration,
538  Trigs&& trigs,
539  const Rect& oval_bounds,
540  Scalar half_width,
541  Cap cap,
542  std::unique_ptr<Trigs> round_cap_trigs) {
543  return ArcVertexGenerator(iteration, std::move(trigs), oval_bounds, false,
544  false, half_width, cap, std::move(round_cap_trigs));
545 }
546 
548  Trigs&& trigs,
549  const Rect& oval_bounds,
550  bool use_center,
551  bool supports_triangle_fans,
552  Scalar half_width,
553  Cap cap,
554  std::unique_ptr<Trigs> round_cap_trigs)
555  : iteration_(iteration),
556  trigs_(std::move(trigs)),
557  oval_bounds_(oval_bounds),
558  use_center_(use_center),
559  supports_triangle_fans_(supports_triangle_fans),
560  half_width_(half_width),
561  cap_(cap),
562  round_cap_trigs_(std::move(round_cap_trigs)) {}
563 
565  return (half_width_ < 0 && supports_triangle_fans_)
568 }
569 
571  size_t count = iteration_.GetPointCount();
572  if (half_width_ > 0) {
573  // Stroked arc
574  FML_DCHECK(!use_center_);
575  count *= 2;
576  if (cap_ == Cap::kSquare) {
577  count += 4;
578  } else if (cap_ == Cap::kRound) {
579  FML_DCHECK(round_cap_trigs_);
580  // 4 vertices for each Trig in round_cap_trigs_: 2 vertices for each in
581  // the start cap and 2 for each in the end cap. Subtract 4 because the cap
582  // vertices elide the beginning and end vertices of the arc.
583  count += round_cap_trigs_->size() * 4 - 4;
584  }
585  } else if (supports_triangle_fans_) {
586  // Filled arc using a triangle fan
587  if (use_center_) {
588  count++;
589  }
590  } else {
591  // Filled arc using a triangle strip
592  count = (2 * count) - 1;
593  }
594  return count;
595 }
596 
598  const TessellatedVertexProc& proc) const {
599  if (half_width_ > 0) {
600  FML_DCHECK(!use_center_);
601  Tessellator::GenerateStrokedArc(trigs_, iteration_, oval_bounds_,
602  half_width_, cap_, round_cap_trigs_, proc);
603  } else if (supports_triangle_fans_) {
604  Tessellator::GenerateFilledArcFan(trigs_, iteration_, oval_bounds_,
605  use_center_, proc);
606  } else {
607  Tessellator::GenerateFilledArcStrip(trigs_, iteration_, oval_bounds_,
608  use_center_, proc);
609  }
610 }
611 
613  const Arc& arc,
614  bool supports_triangle_fans) {
615  size_t divisions = ComputeQuadrantDivisions(
616  view_transform.GetMaxBasisLengthXY() * arc.GetOvalSize().MaxDimension());
617 
619  arc.ComputeIterations(divisions), GetTrigsForDivisions(divisions),
620  arc.GetOvalBounds(), arc.IncludeCenter(), supports_triangle_fans);
621 };
622 
624  const Arc& arc,
625  Cap cap,
626  Scalar half_width) {
627  FML_DCHECK(half_width > 0);
628  FML_DCHECK(arc.IsPerfectCircle());
629  FML_DCHECK(!arc.IncludeCenter());
630  size_t divisions =
631  ComputeQuadrantDivisions(view_transform.GetMaxBasisLengthXY() *
632  (arc.GetOvalSize().MaxDimension() + half_width));
633 
634  std::unique_ptr<Trigs> round_cap_trigs;
635  if (cap == Cap::kRound) {
636  round_cap_trigs = std::make_unique<Trigs>(GetTrigsForDeviceRadius(
637  view_transform.GetMaxBasisLengthXY() * half_width));
638  }
639 
641  arc.ComputeIterations(divisions), GetTrigsForDivisions(divisions),
642  arc.GetOvalBounds(), half_width, cap, std::move(round_cap_trigs));
643 }
644 
646  const Matrix& view_transform,
647  const Point& p0,
648  const Point& p1,
649  Scalar radius) {
650  auto along = p1 - p0;
651  auto length = along.GetLength();
652  if (length > kEhCloseEnough) {
653  auto divisions =
654  ComputeQuadrantDivisions(view_transform.GetMaxBasisLengthXY() * radius);
655  return EllipticalVertexGenerator(Tessellator::GenerateRoundCapLine,
656  GetTrigsForDivisions(divisions),
658  {
659  .reference_centers = {p0, p1},
660  .radii = {radius, radius},
661  .half_width = -1.0f,
662  });
663  } else {
664  return FilledCircle(view_transform, p0, radius);
665  }
666 }
667 
669  const Matrix& view_transform,
670  const Rect& bounds) {
671  if (bounds.IsSquare()) {
672  return FilledCircle(view_transform, bounds.GetCenter(),
673  bounds.GetWidth() * 0.5f);
674  }
675  auto max_radius = bounds.GetSize().MaxDimension();
676  auto divisions = ComputeQuadrantDivisions(
677  view_transform.GetMaxBasisLengthXY() * max_radius);
678  auto center = bounds.GetCenter();
679  return EllipticalVertexGenerator(Tessellator::GenerateFilledEllipse,
680  GetTrigsForDivisions(divisions),
682  {
683  .reference_centers = {center, center},
684  .radii = bounds.GetSize() * 0.5f,
685  .half_width = -1.0f,
686  });
687 }
688 
690  const Matrix& view_transform,
691  const Rect& bounds,
692  const Size& radii) {
693  if (radii.width * 2 < bounds.GetWidth() ||
694  radii.height * 2 < bounds.GetHeight()) {
695  auto max_radius = radii.MaxDimension();
696  auto divisions = ComputeQuadrantDivisions(
697  view_transform.GetMaxBasisLengthXY() * max_radius);
698  auto upper_left = bounds.GetLeftTop() + radii;
699  auto lower_right = bounds.GetRightBottom() - radii;
700  return EllipticalVertexGenerator(Tessellator::GenerateFilledRoundRect,
701  GetTrigsForDivisions(divisions),
703  {
704  .reference_centers =
705  {
706  upper_left,
707  lower_right,
708  },
709  .radii = radii,
710  .half_width = -1.0f,
711  });
712  } else {
713  return FilledEllipse(view_transform, bounds);
714  }
715 }
716 
717 void Tessellator::GenerateFilledCircle(
718  const Trigs& trigs,
719  const EllipticalVertexGenerator::Data& data,
720  const TessellatedVertexProc& proc) {
721  auto center = data.reference_centers[0];
722  auto radius = data.radii.width;
723 
724  FML_DCHECK(center == data.reference_centers[1]);
725  FML_DCHECK(radius == data.radii.height);
726  FML_DCHECK(data.half_width < 0);
727 
728  // Quadrant 1 connecting with Quadrant 4:
729  for (auto& trig : trigs) {
730  auto offset = trig * radius;
731  proc({center.x - offset.x, center.y + offset.y});
732  proc({center.x - offset.x, center.y - offset.y});
733  }
734 
735  // The second half of the circle should be iterated in reverse, but
736  // we can instead iterate forward and swap the x/y values of the
737  // offset as the angles should be symmetric and thus should generate
738  // symmetrically reversed trig vectors.
739  // Quadrant 2 connecting with Quadrant 3:
740  for (auto& trig : trigs) {
741  auto offset = trig * radius;
742  proc({center.x + offset.y, center.y + offset.x});
743  proc({center.x + offset.y, center.y - offset.x});
744  }
745 }
746 
747 void Tessellator::GenerateStrokedCircle(
748  const Trigs& trigs,
749  const EllipticalVertexGenerator::Data& data,
750  const TessellatedVertexProc& proc) {
751  auto center = data.reference_centers[0];
752 
753  FML_DCHECK(center == data.reference_centers[1]);
754  FML_DCHECK(data.radii.IsSquare());
755  FML_DCHECK(data.half_width > 0 && data.half_width < data.radii.width);
756 
757  auto outer_radius = data.radii.width + data.half_width;
758  auto inner_radius = data.radii.width - data.half_width;
759 
760  // Zig-zag back and forth between points on the outer circle and the
761  // inner circle. Both circles are evaluated at the same number of
762  // quadrant divisions so the points for a given division should match
763  // 1 for 1 other than their applied radius.
764 
765  // Quadrant 1:
766  for (auto& trig : trigs) {
767  auto outer = trig * outer_radius;
768  auto inner = trig * inner_radius;
769  proc({center.x - outer.x, center.y - outer.y});
770  proc({center.x - inner.x, center.y - inner.y});
771  }
772 
773  // The even quadrants of the circle should be iterated in reverse, but
774  // we can instead iterate forward and swap the x/y values of the
775  // offset as the angles should be symmetric and thus should generate
776  // symmetrically reversed trig vectors.
777  // Quadrant 2:
778  for (auto& trig : trigs) {
779  auto outer = trig * outer_radius;
780  auto inner = trig * inner_radius;
781  proc({center.x + outer.y, center.y - outer.x});
782  proc({center.x + inner.y, center.y - inner.x});
783  }
784 
785  // Quadrant 3:
786  for (auto& trig : trigs) {
787  auto outer = trig * outer_radius;
788  auto inner = trig * inner_radius;
789  proc({center.x + outer.x, center.y + outer.y});
790  proc({center.x + inner.x, center.y + inner.y});
791  }
792 
793  // Quadrant 4:
794  for (auto& trig : trigs) {
795  auto outer = trig * outer_radius;
796  auto inner = trig * inner_radius;
797  proc({center.x - outer.y, center.y + outer.x});
798  proc({center.x - inner.y, center.y + inner.x});
799  }
800 }
801 
802 void Tessellator::GenerateFilledArcFan(const Trigs& trigs,
803  const Arc::Iteration& iteration,
804  const Rect& oval_bounds,
805  bool use_center,
806  const TessellatedVertexProc& proc) {
807  Point center = oval_bounds.GetCenter();
808  Size radii = oval_bounds.GetSize() * 0.5f;
809 
810  if (use_center) {
811  proc(center);
812  }
813  proc(center + iteration.start * radii);
814  for (size_t i = 0; i < iteration.quadrant_count; i++) {
815  auto quadrant = iteration.quadrants[i];
816  for (size_t j = quadrant.start_index; j < quadrant.end_index; j++) {
817  proc(center + trigs[j] * quadrant.axis * radii);
818  }
819  }
820  proc(center + iteration.end * radii);
821 }
822 
823 void Tessellator::GenerateFilledArcStrip(const Trigs& trigs,
824  const Arc::Iteration& iteration,
825  const Rect& oval_bounds,
826  bool use_center,
827  const TessellatedVertexProc& proc) {
828  Point center = oval_bounds.GetCenter();
829  Size radii = oval_bounds.GetSize() * 0.5f;
830 
831  Point origin;
832  if (use_center) {
833  origin = center;
834  } else {
835  Point midpoint = (iteration.start + iteration.end) * 0.5f;
836  origin = center + midpoint * radii;
837  }
838 
839  proc(center + iteration.start * radii);
840  for (size_t i = 0; i < iteration.quadrant_count; i++) {
841  auto quadrant = iteration.quadrants[i];
842  for (size_t j = quadrant.start_index; j < quadrant.end_index; j++) {
843  proc(origin);
844  proc(center + trigs[j] * quadrant.axis * radii);
845  }
846  }
847  proc(origin);
848  proc(center + iteration.end * radii);
849 }
850 
851 void Tessellator::GenerateStrokedArc(
852  const Trigs& trigs,
853  const Arc::Iteration& iteration,
854  const Rect& oval_bounds,
855  Scalar half_width,
856  Cap cap,
857  const std::unique_ptr<Trigs>& round_cap_trigs,
858  const TessellatedVertexProc& proc) {
859  Point center = oval_bounds.GetCenter();
860  Size base_radii = oval_bounds.GetSize() * 0.5f;
861  Size inner_radii = base_radii - Size(half_width, half_width);
862  Size outer_radii = base_radii + Size(half_width, half_width);
863 
864  // Starting cap
865  switch (cap) {
866  case Cap::kButt:
867  proc(center + iteration.start * inner_radii);
868  proc(center + iteration.start * outer_radii);
869  break;
870  case Cap::kRound:
871  FML_DCHECK(round_cap_trigs);
872  Tessellator::GenerateStartRoundCap(center + iteration.start * base_radii,
873  -iteration.start * half_width,
874  *round_cap_trigs, proc);
875  break;
876  case Cap::kSquare:
877  Vector2 offset =
878  Vector2{iteration.start.y, -iteration.start.x} * half_width;
879  proc(center + iteration.start * inner_radii + offset);
880  proc(center + iteration.start * outer_radii + offset);
881  proc(center + iteration.start * inner_radii);
882  proc(center + iteration.start * outer_radii);
883  break;
884  }
885 
886  for (size_t i = 0; i < iteration.quadrant_count; i++) {
887  auto quadrant = iteration.quadrants[i];
888  for (size_t j = quadrant.start_index; j < quadrant.end_index; j++) {
889  proc(center + trigs[j] * quadrant.axis * inner_radii);
890  proc(center + trigs[j] * quadrant.axis * outer_radii);
891  }
892  }
893 
894  // Ending cap
895  switch (cap) {
896  case Cap::kButt:
897  proc(center + iteration.end * inner_radii);
898  proc(center + iteration.end * outer_radii);
899  break;
900  case Cap::kRound:
901  FML_DCHECK(round_cap_trigs);
902  Tessellator::GenerateEndRoundCap(center + iteration.end * base_radii,
903  -iteration.end * half_width,
904  *round_cap_trigs, proc);
905  break;
906  case Cap::kSquare:
907  Vector2 offset = Vector2{-iteration.end.y, iteration.end.x} * half_width;
908  proc(center + iteration.end * inner_radii);
909  proc(center + iteration.end * outer_radii);
910  proc(center + iteration.end * inner_radii + offset);
911  proc(center + iteration.end * outer_radii + offset);
912  break;
913  }
914 }
915 
916 void Tessellator::GenerateRoundCapLine(
917  const Trigs& trigs,
918  const EllipticalVertexGenerator::Data& data,
919  const TessellatedVertexProc& proc) {
920  auto p0 = data.reference_centers[0];
921  auto p1 = data.reference_centers[1];
922  auto radius = data.radii.width;
923 
924  FML_DCHECK(radius == data.radii.height);
925  FML_DCHECK(data.half_width < 0);
926 
927  auto along = p1 - p0;
928  along *= radius / along.GetLength();
929  auto across = Point(-along.y, along.x);
930 
931  Tessellator::GenerateStartRoundCap(p0, across, trigs, proc);
932  Tessellator::GenerateEndRoundCap(p1, across, trigs, proc);
933 }
934 
935 void Tessellator::GenerateFilledEllipse(
936  const Trigs& trigs,
937  const EllipticalVertexGenerator::Data& data,
938  const TessellatedVertexProc& proc) {
939  auto center = data.reference_centers[0];
940  auto radii = data.radii;
941 
942  FML_DCHECK(center == data.reference_centers[1]);
943  FML_DCHECK(data.half_width < 0);
944 
945  // Quadrant 1 connecting with Quadrant 4:
946  for (auto& trig : trigs) {
947  auto offset = trig * radii;
948  proc({center.x - offset.x, center.y + offset.y});
949  proc({center.x - offset.x, center.y - offset.y});
950  }
951 
952  // The second half of the circle should be iterated in reverse, but
953  // we can instead iterate forward and swap the x/y values of the
954  // offset as the angles should be symmetric and thus should generate
955  // symmetrically reversed trig vectors.
956  // Quadrant 2 connecting with Quadrant 2:
957  for (auto& trig : trigs) {
958  auto offset = Point(trig.sin * radii.width, trig.cos * radii.height);
959  proc({center.x + offset.x, center.y + offset.y});
960  proc({center.x + offset.x, center.y - offset.y});
961  }
962 }
963 
964 void Tessellator::GenerateFilledRoundRect(
965  const Trigs& trigs,
966  const EllipticalVertexGenerator::Data& data,
967  const TessellatedVertexProc& proc) {
968  Scalar left = data.reference_centers[0].x;
969  Scalar top = data.reference_centers[0].y;
970  Scalar right = data.reference_centers[1].x;
971  Scalar bottom = data.reference_centers[1].y;
972  auto radii = data.radii;
973 
974  FML_DCHECK(data.half_width < 0);
975 
976  // Quadrant 1 connecting with Quadrant 4:
977  for (auto& trig : trigs) {
978  auto offset = trig * radii;
979  proc({left - offset.x, bottom + offset.y});
980  proc({left - offset.x, top - offset.y});
981  }
982 
983  // The second half of the round rect should be iterated in reverse, but
984  // we can instead iterate forward and swap the x/y values of the
985  // offset as the angles should be symmetric and thus should generate
986  // symmetrically reversed trig vectors.
987  // Quadrant 2 connecting with Quadrant 2:
988  for (auto& trig : trigs) {
989  auto offset = Point(trig.sin * radii.width, trig.cos * radii.height);
990  proc({right + offset.x, bottom + offset.y});
991  proc({right + offset.x, top - offset.y});
992  }
993 }
994 
995 void Tessellator::GenerateStartRoundCap(const Point& p,
996  const Vector2& perpendicular,
997  const Trigs& trigs,
998  const TessellatedVertexProc& proc) {
999  Point along(perpendicular.y, -perpendicular.x);
1000 
1001  for (auto& trig : trigs) {
1002  auto relative_along = along * trig.cos;
1003  auto relative_across = perpendicular * trig.sin;
1004  proc(p - relative_along + relative_across);
1005  proc(p - relative_along - relative_across);
1006  }
1007 }
1008 
1009 void Tessellator::GenerateEndRoundCap(const Point& p,
1010  const Vector2& perpendicular,
1011  const Trigs& trigs,
1012  const TessellatedVertexProc& proc) {
1013  Point along(perpendicular.y, -perpendicular.x);
1014 
1015  // The ending round cap should be iterated in reverse, but
1016  // we can instead iterate forward and swap the sin/cos values as they
1017  // should be symmetric.
1018  for (auto& trig : trigs) {
1019  auto relative_along = along * trig.sin;
1020  auto relative_across = perpendicular * trig.cos;
1021  proc(p + relative_along + relative_across);
1022  proc(p + relative_along - relative_across);
1023  }
1024 }
1025 
1026 } // namespace impeller
bool use_center
VertexBuffer TessellateConvex(const PathSource &path, HostBuffer &data_host_buffer, HostBuffer &indexes_host_buffer, Scalar tolerance, bool supports_primitive_restart, bool supports_triangle_fan) override
Definition: tessellator.cc:331
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
static ArcVertexGenerator MakeStroked(const Arc::Iteration &iteration, Trigs &&trigs, const Rect &oval_bounds, Scalar half_width, Cap cap, std::unique_ptr< Trigs > round_cap_trigs)
Definition: tessellator.cc:536
size_t GetVertexCount() const override
|VertexGenerator|
Definition: tessellator.cc:570
PrimitiveType GetTriangleType() const override
|VertexGenerator|
Definition: tessellator.cc:564
static ArcVertexGenerator MakeFilled(const Arc::Iteration &iteration, Trigs &&trigs, const Rect &oval_bounds, bool use_center, bool supports_triangle_fans)
Definition: tessellator.cc:525
void GenerateVertices(const TessellatedVertexProc &proc) const override
|VertexGenerator|
Definition: tessellator.cc:597
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:447
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:425
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:645
std::vector< Point > & GetStrokePointCache()
Retrieve a pre-allocated arena of kPointArenaSize points.
Definition: tessellator.cc:421
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:689
static constexpr Scalar kCircleTolerance
The pixel tolerance used by the algorighm to determine how many divisions to create for a circle.
Definition: tessellator.h:280
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:429
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:488
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:623
static void TessellateConvexInternal(const PathSource &path, std::vector< Point > &point_buffer, std::vector< uint16_t > &index_buffer, Scalar tolerance)
Definition: tessellator.cc:440
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:668
Tessellator(bool supports_32bit_primitive_indices=true)
Definition: tessellator.cc:410
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:612
Tessellator::TessellatedVertexProc TessellatedVertexProc
Definition: tessellator.cc:473
PrimitiveType
Decides how backend draws pixels based on input vertices.
Definition: formats.h:355
Point Vector2
Definition: point.h:429
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:425
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:474
Tessellator::ArcVertexGenerator ArcVertexGenerator
Definition: tessellator.cc:475
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:209
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:69