Flutter Engine Uber Docs
Docs for the entire Flutter Engine repo.
 
Loading...
Searching...
No Matches
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
12namespace {
13static constexpr int kPrecomputedDivisionCount = 1024;
14
15static 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
84static 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
140template <typename IndexT>
141impeller::IndexType IndexTypeFor();
142template <>
143impeller::IndexType IndexTypeFor<uint32_t>() {
145}
146template <>
147impeller::IndexType IndexTypeFor<uint16_t>() {
149}
150
151/// @brief A vertex writer that generates a triangle fan and requires primitive
152/// restart.
153template <typename IndexT>
154class 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.
186template <typename IndexT>
187class 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.
238template <typename IndexT>
239class 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
306template <typename IndexT>
307void 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
317}
318
319} // namespace
320
321namespace impeller {
322
323template <typename IndexT>
324class ConvexTessellatorImpl : public Tessellator::ConvexTessellator {
325 public:
326 ConvexTessellatorImpl() {
327 point_buffer_.reserve(2048);
328 index_buffer_.reserve(2048);
329 }
330
331 VertexBuffer TessellateConvex(const PathSource& path,
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
410Tessellator::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
419Tessellator::~Tessellator() = default;
420
421std::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
450void 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
467Tessellator::Trigs Tessellator::GetTrigsForDivisions(size_t divisions) {
468 return divisions < Tessellator::kCachedTrigCount
469 ? Trigs(precomputed_trigs_[divisions], divisions)
470 : Trigs(divisions);
471}
472
474using EllipticalVertexGenerator = Tessellator::EllipticalVertexGenerator;
475using ArcVertexGenerator = Tessellator::ArcVertexGenerator;
476
477EllipticalVertexGenerator::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
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),
497 {
498 .reference_centers = {center, center},
499 .radii = {radius, radius},
500 .half_width = -1.0f,
501 });
502}
503
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),
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
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
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
547ArcVertexGenerator::ArcVertexGenerator(const Arc::Iteration& iteration,
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 FML_DCHECK(!use_center_);
574 count *= 2;
575 if (cap_ == Cap::kSquare) {
576 count += 4;
577 } else if (cap_ == Cap::kRound) {
578 FML_DCHECK(round_cap_trigs_);
579 // 4 vertices for each Trig in round_cap_trigs_: 2 vertices for each in
580 // the start cap and 2 for each in the end cap. Subtract 4 because the cap
581 // vertices elide the beginning and end vertices of the arc.
582 count += round_cap_trigs_->size() * 4 - 4;
583 }
584 } else if (supports_triangle_fans_) {
585 if (use_center_) {
586 count++;
587 }
588 } else {
589 // corrugated triangle fan
590 count += (count + 1) / 2;
591 }
592 return count;
593}
594
596 const TessellatedVertexProc& proc) const {
597 if (half_width_ > 0) {
598 FML_DCHECK(!use_center_);
599 Tessellator::GenerateStrokedArc(trigs_, iteration_, oval_bounds_,
600 half_width_, cap_, round_cap_trigs_, proc);
601 } else if (supports_triangle_fans_) {
602 Tessellator::GenerateFilledArcFan(trigs_, iteration_, oval_bounds_,
603 use_center_, proc);
604 } else {
605 Tessellator::GenerateFilledArcStrip(trigs_, iteration_, oval_bounds_,
606 use_center_, proc);
607 }
608}
609
611 const Arc& arc,
612 bool supports_triangle_fans) {
613 size_t divisions = ComputeQuadrantDivisions(
614 view_transform.GetMaxBasisLengthXY() * arc.GetOvalSize().MaxDimension());
615
617 arc.ComputeIterations(divisions), GetTrigsForDivisions(divisions),
618 arc.GetOvalBounds(), arc.IncludeCenter(), supports_triangle_fans);
619};
620
622 const Arc& arc,
623 Cap cap,
624 Scalar half_width) {
625 FML_DCHECK(half_width > 0);
628 size_t divisions =
629 ComputeQuadrantDivisions(view_transform.GetMaxBasisLengthXY() *
630 (arc.GetOvalSize().MaxDimension() + half_width));
631
632 std::unique_ptr<Trigs> round_cap_trigs;
633 if (cap == Cap::kRound) {
634 round_cap_trigs = std::make_unique<Trigs>(GetTrigsForDeviceRadius(
635 view_transform.GetMaxBasisLengthXY() * half_width));
636 }
637
639 arc.ComputeIterations(divisions), GetTrigsForDivisions(divisions),
640 arc.GetOvalBounds(), half_width, cap, std::move(round_cap_trigs));
641}
642
644 const Matrix& view_transform,
645 const Point& p0,
646 const Point& p1,
647 Scalar radius) {
648 auto along = p1 - p0;
649 auto length = along.GetLength();
650 if (length > kEhCloseEnough) {
651 auto divisions =
652 ComputeQuadrantDivisions(view_transform.GetMaxBasisLengthXY() * radius);
653 return EllipticalVertexGenerator(Tessellator::GenerateRoundCapLine,
654 GetTrigsForDivisions(divisions),
656 {
657 .reference_centers = {p0, p1},
658 .radii = {radius, radius},
659 .half_width = -1.0f,
660 });
661 } else {
662 return FilledCircle(view_transform, p0, radius);
663 }
664}
665
667 const Matrix& view_transform,
668 const Rect& bounds) {
669 if (bounds.IsSquare()) {
670 return FilledCircle(view_transform, bounds.GetCenter(),
671 bounds.GetWidth() * 0.5f);
672 }
673 auto max_radius = bounds.GetSize().MaxDimension();
674 auto divisions = ComputeQuadrantDivisions(
675 view_transform.GetMaxBasisLengthXY() * max_radius);
676 auto center = bounds.GetCenter();
677 return EllipticalVertexGenerator(Tessellator::GenerateFilledEllipse,
678 GetTrigsForDivisions(divisions),
680 {
681 .reference_centers = {center, center},
682 .radii = bounds.GetSize() * 0.5f,
683 .half_width = -1.0f,
684 });
685}
686
688 const Matrix& view_transform,
689 const Rect& bounds,
690 const Size& radii) {
691 if (radii.width * 2 < bounds.GetWidth() ||
692 radii.height * 2 < bounds.GetHeight()) {
693 auto max_radius = radii.MaxDimension();
694 auto divisions = ComputeQuadrantDivisions(
695 view_transform.GetMaxBasisLengthXY() * max_radius);
696 auto upper_left = bounds.GetLeftTop() + radii;
697 auto lower_right = bounds.GetRightBottom() - radii;
698 return EllipticalVertexGenerator(Tessellator::GenerateFilledRoundRect,
699 GetTrigsForDivisions(divisions),
701 {
702 .reference_centers =
703 {
704 upper_left,
705 lower_right,
706 },
707 .radii = radii,
708 .half_width = -1.0f,
709 });
710 } else {
711 return FilledEllipse(view_transform, bounds);
712 }
713}
714
715void Tessellator::GenerateFilledCircle(
716 const Trigs& trigs,
717 const EllipticalVertexGenerator::Data& data,
718 const TessellatedVertexProc& proc) {
719 auto center = data.reference_centers[0];
720 auto radius = data.radii.width;
721
722 FML_DCHECK(center == data.reference_centers[1]);
723 FML_DCHECK(radius == data.radii.height);
724 FML_DCHECK(data.half_width < 0);
725
726 // Quadrant 1 connecting with Quadrant 4:
727 for (auto& trig : trigs) {
728 auto offset = trig * radius;
729 proc({center.x - offset.x, center.y + offset.y});
730 proc({center.x - offset.x, center.y - offset.y});
731 }
732
733 // The second half of the circle should be iterated in reverse, but
734 // we can instead iterate forward and swap the x/y values of the
735 // offset as the angles should be symmetric and thus should generate
736 // symmetrically reversed trig vectors.
737 // Quadrant 2 connecting with Quadrant 3:
738 for (auto& trig : trigs) {
739 auto offset = trig * radius;
740 proc({center.x + offset.y, center.y + offset.x});
741 proc({center.x + offset.y, center.y - offset.x});
742 }
743}
744
745void Tessellator::GenerateStrokedCircle(
746 const Trigs& trigs,
747 const EllipticalVertexGenerator::Data& data,
748 const TessellatedVertexProc& proc) {
749 auto center = data.reference_centers[0];
750
751 FML_DCHECK(center == data.reference_centers[1]);
752 FML_DCHECK(data.radii.IsSquare());
753 FML_DCHECK(data.half_width > 0 && data.half_width < data.radii.width);
754
755 auto outer_radius = data.radii.width + data.half_width;
756 auto inner_radius = data.radii.width - data.half_width;
757
758 // Zig-zag back and forth between points on the outer circle and the
759 // inner circle. Both circles are evaluated at the same number of
760 // quadrant divisions so the points for a given division should match
761 // 1 for 1 other than their applied radius.
762
763 // Quadrant 1:
764 for (auto& trig : trigs) {
765 auto outer = trig * outer_radius;
766 auto inner = trig * inner_radius;
767 proc({center.x - outer.x, center.y - outer.y});
768 proc({center.x - inner.x, center.y - inner.y});
769 }
770
771 // The even quadrants of the circle should be iterated in reverse, but
772 // we can instead iterate forward and swap the x/y values of the
773 // offset as the angles should be symmetric and thus should generate
774 // symmetrically reversed trig vectors.
775 // Quadrant 2:
776 for (auto& trig : trigs) {
777 auto outer = trig * outer_radius;
778 auto inner = trig * inner_radius;
779 proc({center.x + outer.y, center.y - outer.x});
780 proc({center.x + inner.y, center.y - inner.x});
781 }
782
783 // Quadrant 3:
784 for (auto& trig : trigs) {
785 auto outer = trig * outer_radius;
786 auto inner = trig * inner_radius;
787 proc({center.x + outer.x, center.y + outer.y});
788 proc({center.x + inner.x, center.y + inner.y});
789 }
790
791 // Quadrant 4:
792 for (auto& trig : trigs) {
793 auto outer = trig * outer_radius;
794 auto inner = trig * inner_radius;
795 proc({center.x - outer.y, center.y + outer.x});
796 proc({center.x - inner.y, center.y + inner.x});
797 }
798}
799
800void Tessellator::GenerateFilledArcFan(const Trigs& trigs,
801 const Arc::Iteration& iteration,
802 const Rect& oval_bounds,
803 bool use_center,
804 const TessellatedVertexProc& proc) {
805 Point center = oval_bounds.GetCenter();
806 Size radii = oval_bounds.GetSize() * 0.5f;
807
808 if (use_center) {
809 proc(center);
810 }
811 proc(center + iteration.start * radii);
812 for (size_t i = 0; i < iteration.quadrant_count; i++) {
813 auto quadrant = iteration.quadrants[i];
814 for (size_t j = quadrant.start_index; j < quadrant.end_index; j++) {
815 proc(center + trigs[j] * quadrant.axis * radii);
816 }
817 }
818 proc(center + iteration.end * radii);
819}
820
821void Tessellator::GenerateFilledArcStrip(const Trigs& trigs,
822 const Arc::Iteration& iteration,
823 const Rect& oval_bounds,
824 bool use_center,
825 const TessellatedVertexProc& proc) {
826 Point center = oval_bounds.GetCenter();
827 Size radii = oval_bounds.GetSize() * 0.5f;
828
829 Point origin;
830 if (use_center) {
831 origin = center;
832 } else {
833 Point midpoint = (iteration.start + iteration.end) * 0.5f;
834 origin = center + midpoint * radii;
835 }
836
837 proc(origin);
838 proc(center + iteration.start * radii);
839 bool insert_origin = false;
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 if (insert_origin) {
844 proc(origin);
845 }
846 insert_origin = !insert_origin;
847 proc(center + trigs[j] * quadrant.axis * radii);
848 }
849 }
850 if (insert_origin) {
851 proc(origin);
852 }
853 proc(center + iteration.end * radii);
854}
855
856void Tessellator::GenerateStrokedArc(
857 const Trigs& trigs,
858 const Arc::Iteration& iteration,
859 const Rect& oval_bounds,
860 Scalar half_width,
861 Cap cap,
862 const std::unique_ptr<Trigs>& round_cap_trigs,
863 const TessellatedVertexProc& proc) {
864 Point center = oval_bounds.GetCenter();
865 Size base_radii = oval_bounds.GetSize() * 0.5f;
866 Size inner_radii = base_radii - Size(half_width, half_width);
867 Size outer_radii = base_radii + Size(half_width, half_width);
868
869 // Starting cap
870 switch (cap) {
871 case Cap::kButt:
872 proc(center + iteration.start * inner_radii);
873 proc(center + iteration.start * outer_radii);
874 break;
875 case Cap::kRound:
876 FML_DCHECK(round_cap_trigs);
877 Tessellator::GenerateStartRoundCap(center + iteration.start * base_radii,
878 -iteration.start * half_width,
879 *round_cap_trigs, proc);
880 break;
881 case Cap::kSquare:
882 Vector2 offset =
883 Vector2{iteration.start.y, -iteration.start.x} * half_width;
884 proc(center + iteration.start * inner_radii + offset);
885 proc(center + iteration.start * outer_radii + offset);
886 proc(center + iteration.start * inner_radii);
887 proc(center + iteration.start * outer_radii);
888 break;
889 }
890
891 for (size_t i = 0; i < iteration.quadrant_count; i++) {
892 auto quadrant = iteration.quadrants[i];
893 for (size_t j = quadrant.start_index; j < quadrant.end_index; j++) {
894 proc(center + trigs[j] * quadrant.axis * inner_radii);
895 proc(center + trigs[j] * quadrant.axis * outer_radii);
896 }
897 }
898
899 // Ending cap
900 switch (cap) {
901 case Cap::kButt:
902 proc(center + iteration.end * inner_radii);
903 proc(center + iteration.end * outer_radii);
904 break;
905 case Cap::kRound:
906 FML_DCHECK(round_cap_trigs);
907 Tessellator::GenerateEndRoundCap(center + iteration.end * base_radii,
908 -iteration.end * half_width,
909 *round_cap_trigs, proc);
910 break;
911 case Cap::kSquare:
912 Vector2 offset = Vector2{-iteration.end.y, iteration.end.x} * half_width;
913 proc(center + iteration.end * inner_radii);
914 proc(center + iteration.end * outer_radii);
915 proc(center + iteration.end * inner_radii + offset);
916 proc(center + iteration.end * outer_radii + offset);
917 break;
918 }
919}
920
921void Tessellator::GenerateRoundCapLine(
922 const Trigs& trigs,
923 const EllipticalVertexGenerator::Data& data,
924 const TessellatedVertexProc& proc) {
925 auto p0 = data.reference_centers[0];
926 auto p1 = data.reference_centers[1];
927 auto radius = data.radii.width;
928
929 FML_DCHECK(radius == data.radii.height);
930 FML_DCHECK(data.half_width < 0);
931
932 auto along = p1 - p0;
933 along *= radius / along.GetLength();
934 auto across = Point(-along.y, along.x);
935
936 Tessellator::GenerateStartRoundCap(p0, across, trigs, proc);
937 Tessellator::GenerateEndRoundCap(p1, across, trigs, proc);
938}
939
940void Tessellator::GenerateFilledEllipse(
941 const Trigs& trigs,
942 const EllipticalVertexGenerator::Data& data,
943 const TessellatedVertexProc& proc) {
944 auto center = data.reference_centers[0];
945 auto radii = data.radii;
946
947 FML_DCHECK(center == data.reference_centers[1]);
948 FML_DCHECK(data.half_width < 0);
949
950 // Quadrant 1 connecting with Quadrant 4:
951 for (auto& trig : trigs) {
952 auto offset = trig * radii;
953 proc({center.x - offset.x, center.y + offset.y});
954 proc({center.x - offset.x, center.y - offset.y});
955 }
956
957 // The second half of the circle should be iterated in reverse, but
958 // we can instead iterate forward and swap the x/y values of the
959 // offset as the angles should be symmetric and thus should generate
960 // symmetrically reversed trig vectors.
961 // Quadrant 2 connecting with Quadrant 2:
962 for (auto& trig : trigs) {
963 auto offset = Point(trig.sin * radii.width, trig.cos * radii.height);
964 proc({center.x + offset.x, center.y + offset.y});
965 proc({center.x + offset.x, center.y - offset.y});
966 }
967}
968
969void Tessellator::GenerateFilledRoundRect(
970 const Trigs& trigs,
971 const EllipticalVertexGenerator::Data& data,
972 const TessellatedVertexProc& proc) {
973 Scalar left = data.reference_centers[0].x;
974 Scalar top = data.reference_centers[0].y;
975 Scalar right = data.reference_centers[1].x;
976 Scalar bottom = data.reference_centers[1].y;
977 auto radii = data.radii;
978
979 FML_DCHECK(data.half_width < 0);
980
981 // Quadrant 1 connecting with Quadrant 4:
982 for (auto& trig : trigs) {
983 auto offset = trig * radii;
984 proc({left - offset.x, bottom + offset.y});
985 proc({left - offset.x, top - offset.y});
986 }
987
988 // The second half of the round rect should be iterated in reverse, but
989 // we can instead iterate forward and swap the x/y values of the
990 // offset as the angles should be symmetric and thus should generate
991 // symmetrically reversed trig vectors.
992 // Quadrant 2 connecting with Quadrant 2:
993 for (auto& trig : trigs) {
994 auto offset = Point(trig.sin * radii.width, trig.cos * radii.height);
995 proc({right + offset.x, bottom + offset.y});
996 proc({right + offset.x, top - offset.y});
997 }
998}
999
1000void Tessellator::GenerateStartRoundCap(const Point& p,
1001 const Vector2& perpendicular,
1002 const Trigs& trigs,
1003 const TessellatedVertexProc& proc) {
1004 Point along(perpendicular.y, -perpendicular.x);
1005
1006 for (auto& trig : trigs) {
1007 auto relative_along = along * trig.cos;
1008 auto relative_across = perpendicular * trig.sin;
1009 proc(p - relative_along + relative_across);
1010 proc(p - relative_along - relative_across);
1011 }
1012}
1013
1014void Tessellator::GenerateEndRoundCap(const Point& p,
1015 const Vector2& perpendicular,
1016 const Trigs& trigs,
1017 const TessellatedVertexProc& proc) {
1018 Point along(perpendicular.y, -perpendicular.x);
1019
1020 // The ending round cap should be iterated in reverse, but
1021 // we can instead iterate forward and swap the sin/cos values as they
1022 // should be symmetric.
1023 for (auto& trig : trigs) {
1024 auto relative_along = along * trig.sin;
1025 auto relative_across = perpendicular * trig.cos;
1026 proc(p + relative_along + relative_across);
1027 proc(p + relative_along - relative_across);
1028 }
1029}
1030
1031} // namespace impeller
An interface for generating a multi contour polyline as a triangle strip.
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...
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)
size_t GetVertexCount() const override
|VertexGenerator|
PrimitiveType GetTriangleType() const override
|VertexGenerator|
static ArcVertexGenerator MakeFilled(const Arc::Iteration &iteration, Trigs &&trigs, const Rect &oval_bounds, bool use_center, bool supports_triangle_fans)
void GenerateVertices(const TessellatedVertexProc &proc) const override
|VertexGenerator|
The |VertexGenerator| implementation common to all shapes that are based on a polygonal representatio...
Trigs(Scalar pixel_radius)
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)
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...
std::vector< Point > & GetStrokePointCache()
Retrieve a pre-allocated arena of kPointArenaSize points.
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 ...
static constexpr Scalar kCircleTolerance
The pixel tolerance used by the algorighm to determine how many divisions to create for a circle.
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.
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...
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...
static void TessellateConvexInternal(const PathSource &path, std::vector< Point > &point_buffer, std::vector< uint16_t > &index_buffer, Scalar tolerance)
EllipticalVertexGenerator StrokedCircle(const Matrix &view_transform, const Point &center, Scalar radius, Scalar half_width)
Create a |VertexGenerator| that can produce vertices for a stroked circle of the given radius and hal...
EllipticalVertexGenerator FilledEllipse(const Matrix &view_transform, const Rect &bounds)
Create a |VertexGenerator| that can produce vertices for a filled ellipse inscribed within the given ...
Tessellator(bool supports_32bit_primitive_indices=true)
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...
#define FML_DCHECK(condition)
Definition logging.h:122
size_t length
PrimitiveType
Decides how backend draws pixels based on input vertices.
Definition formats.h:355
Point Vector2
Definition point.h:331
float Scalar
Definition scalar.h:19
constexpr float kEhCloseEnough
Definition constants.h:57
TRect< Scalar > Rect
Definition rect.h:788
TPoint< Scalar > Point
Definition point.h:327
Cap
An enum that describes ways to decorate the end of a path contour.
constexpr float kPiOver2
Definition constants.h:32
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 ref_ptr.h:261
Definition data.h:17
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
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
constexpr Type GetLength() const
Definition point.h:206
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 Type GetHeight() const
Returns the height of the rectangle, equivalent to |GetSize().height|.
Definition rect.h:347
constexpr TPoint< T > GetLeftTop() const
Definition rect.h:359
constexpr bool IsSquare() const
Returns true if width and height are equal and neither is NaN.
Definition rect.h:304
constexpr TPoint< T > GetRightBottom() const
Definition rect.h:371
constexpr Type GetWidth() const
Returns the width of the rectangle, equivalent to |GetSize().width|.
Definition rect.h:341
constexpr Point GetCenter() const
Get the center point as a |Point|.
Definition rect.h:382
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