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 // 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);
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
717void 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
747void 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
802void 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
823void 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
851void 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
916void 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
935void 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
964void 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
995void 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
1009void 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
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: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
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:209
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