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