| Flutter Impeller |

tessellator.cc
Go to the documentation of this file.
1 // Copyright 2013 The Flutter Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "impeller/tessellator/tessellator.h"
6
7 namespace impeller {
8
9 Tessellator::Tessellator()
10 : point_buffer_(std::make_unique<std::vector<Point>>()),
11 index_buffer_(std::make_unique<std::vector<uint16_t>>()) {
12 point_buffer_->reserve(2048);
13 index_buffer_->reserve(2048);
14 }
15
16 Tessellator::~Tessellator() = default;
17
18 Path::Polyline Tessellator::CreateTempPolyline(const Path& path,
19 Scalar tolerance) {
20 FML_DCHECK(point_buffer_);
21 point_buffer_->clear();
22 auto polyline =
23 path.CreatePolyline(tolerance, std::move(point_buffer_),
24 [this](Path::Polyline::PointBufferPtr point_buffer) {
25 point_buffer_ = std::move(point_buffer);
26 });
27 return polyline;
28 }
29
30 VertexBuffer Tessellator::TessellateConvex(const Path& path,
31 HostBuffer& host_buffer,
32 Scalar tolerance) {
33 FML_DCHECK(point_buffer_);
34 FML_DCHECK(index_buffer_);
35 TessellateConvexInternal(path, *point_buffer_, *index_buffer_, tolerance);
36
37 if (point_buffer_->empty()) {
38 return VertexBuffer{
39 .vertex_buffer = {},
40 .index_buffer = {},
41 .vertex_count = 0u,
42 .index_type = IndexType::k16bit,
43 };
44 }
45
46 BufferView vertex_buffer = host_buffer.Emplace(
47 point_buffer_->data(), sizeof(Point) * point_buffer_->size(),
48 alignof(Point));
49
50 BufferView index_buffer = host_buffer.Emplace(
51 index_buffer_->data(), sizeof(uint16_t) * index_buffer_->size(),
52 alignof(uint16_t));
53
54 return VertexBuffer{
55 .vertex_buffer = std::move(vertex_buffer),
56 .index_buffer = std::move(index_buffer),
57 .vertex_count = index_buffer_->size(),
58 .index_type = IndexType::k16bit,
59 };
60 }
61
62 void Tessellator::TessellateConvexInternal(const Path& path,
63 std::vector<Point>& point_buffer,
64 std::vector<uint16_t>& index_buffer,
65 Scalar tolerance) {
66 point_buffer.clear();
67 index_buffer.clear();
68
69 VertexWriter writer(point_buffer, index_buffer);
70
71 path.WritePolyline(tolerance, writer);
72 }
73
74 static constexpr int kPrecomputedDivisionCount = 1024;
75 static int kPrecomputedDivisions[kPrecomputedDivisionCount] = {
76 // clang-format off
77 1, 2, 3, 4, 4, 4, 5, 5, 5, 6, 6, 6, 7, 7, 7, 7,
78 8, 8, 8, 8, 8, 9, 9, 9, 9, 9, 9, 10, 10, 10, 10, 10,
79 10, 11, 11, 11, 11, 11, 11, 11, 12, 12, 12, 12, 12, 12, 12, 13,
80 13, 13, 13, 13, 13, 13, 13, 14, 14, 14, 14, 14, 14, 14, 14, 14,
81 15, 15, 15, 15, 15, 15, 15, 15, 15, 16, 16, 16, 16, 16, 16, 16,
82 16, 16, 16, 16, 17, 17, 17, 17, 17, 17, 17, 17, 17, 17, 18, 18,
83 18, 18, 18, 18, 18, 18, 18, 18, 18, 18, 19, 19, 19, 19, 19, 19,
84 19, 19, 19, 19, 19, 19, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20,
85 20, 20, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21, 21,
86 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 22, 23, 23, 23,
87 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 23, 24, 24, 24, 24,
88 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 24, 25, 25, 25, 25, 25,
89 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 25, 26, 26, 26, 26, 26,
90 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 26, 27, 27, 27, 27,
91 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 27, 28, 28, 28,
92 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 28, 29,
93 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29,
94 29, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30, 30,
95 30, 30, 30, 30, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31, 31,
96 31, 31, 31, 31, 31, 31, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32,
97 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 33, 33, 33,
98 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33, 33,
99 33, 33, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34,
100 34, 34, 34, 34, 34, 34, 34, 35, 35, 35, 35, 35, 35, 35, 35, 35,
101 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 35, 36, 36,
102 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36, 36,
103 36, 36, 36, 36, 36, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37,
104 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 37, 38, 38, 38, 38,
105 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38, 38,
106 38, 38, 38, 38, 38, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39,
107 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 39, 40, 40,
108 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40, 40,
109 40, 40, 40, 40, 40, 40, 40, 41, 41, 41, 41, 41, 41, 41, 41, 41,
110 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41, 41,
111 41, 41, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42,
112 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 42, 43, 43, 43, 43,
113 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43, 43,
114 43, 43, 43, 43, 43, 43, 43, 43, 44, 44, 44, 44, 44, 44, 44, 44,
115 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44, 44,
116 44, 44, 44, 44, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45,
117 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45, 45,
118 45, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46,
119 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 46, 47,
120 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47,
121 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 47, 48, 48, 48,
122 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48,
123 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 49, 49, 49, 49,
124 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49,
125 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 49, 50, 50, 50, 50, 50,
126 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50,
127 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 50, 51, 51, 51, 51, 51,
128 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51,
129 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 51, 52, 52, 52, 52,
130 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52,
131 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 52, 53, 53, 53,
132 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53,
133 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 53, 54,
134 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
135 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54, 54,
136 54, 54, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55,
137 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55, 55,
138 55, 55, 55, 55, 55, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56,
139 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56, 56,
140 56, 56, 56, 56, 56, 56, 56, 56, 56, 57, 57, 57, 57, 57, 57, 57,
141 // clang-format on
142 };
143
144 static size_t ComputeQuadrantDivisions(Scalar pixel_radius) {
145 if (pixel_radius <= 0.0) {
146 return 1;
147 }
148 int radius_index = ceil(pixel_radius);
149 if (radius_index < kPrecomputedDivisionCount) {
150 return kPrecomputedDivisions[radius_index];
151 }
152
153 // For a circle with N divisions per quadrant, the maximum deviation of
154 // the polgyon approximation from the true circle will be at the center
155 // of the base of each triangular pie slice. We can compute that distance
156 // by finding the midpoint of the line of the first slice and compare
157 // its distance from the center of the circle to the radius. We will aim
158 // to have the length of that bisector to be within |kCircleTolerance|
159 // from the radius in pixels.
160 //
161 // Each vertex will appear at an angle of:
162 // theta(i) = (kPi / 2) * (i / N) // for i in [0..N]
163 // with each point falling at:
164 // point(i) = r * (cos(theta), sin(theta))
165 // If we consider the unit circle to simplify the calculations below then
166 // we need to scale the tolerance from its absolute quantity into a unit
167 // circle fraction:
168 // k = tolerance / radius
169 // Using this scaled tolerance below to avoid multiplying by the radius
170 // throughout all of the math, we have:
171 // first point = (1, 0) // theta(0) == 0
172 // theta = kPi / 2 / N // theta(1)
173 // second point = (cos(theta), sin(theta)) = (c, s)
174 // midpoint = (first + second) * 0.5 = ((1 + c)/2, s/2)
175 // |midpoint| = sqrt((1 + c)*(1 + c)/4 + s*s/4)
176 // = sqrt((1 + c + c + c*c + s*s) / 4)
177 // = sqrt((1 + 2c + 1) / 4)
178 // = sqrt((2 + 2c) / 4)
179 // = sqrt((1 + c) / 2)
180 // = cos(theta / 2) // using half-angle cosine formula
181 // error = 1 - |midpoint| = 1 - cos(theta / 2)
182 // cos(theta/2) = 1 - error
183 // theta/2 = acos(1 - error)
184 // kPi / 2 / N / 2 = acos(1 - error)
185 // kPi / 4 / acos(1 - error) = N
186 // Since we need error <= k, we want divisions >= N, so we use:
187 // N = ceil(kPi / 4 / acos(1 - k))
188 //
189 // Math is confirmed in https://math.stackexchange.com/a/4132095
190 // (keeping in mind that we are computing quarter circle divisions here)
191 // which also points out a performance optimization that is accurate
192 // to within an over-estimation of 1 division would be:
193 // N = ceil(kPi / 4 / sqrt(2 * k))
194 // Since we have precomputed the divisions for radii up to 1024, we can
195 // afford to be more accurate using the acos formula here for larger radii.
196 double k = Tessellator::kCircleTolerance / pixel_radius;
197 return ceil(kPiOver4 / std::acos(1 - k));
198 }
199
200 void Tessellator::Trigs::init(size_t divisions) {
201 if (!trigs_.empty()) {
202 return;
203 }
204
205 // Either not cached yet, or we are using the temp storage...
206 trigs_.reserve(divisions + 1);
207
208 double angle_scale = kPiOver2 / divisions;
209
210 trigs_.emplace_back(1.0, 0.0);
211 for (size_t i = 1; i < divisions; i++) {
212 trigs_.emplace_back(Radians(i * angle_scale));
213 }
214 trigs_.emplace_back(0.0, 1.0);
215 }
216
217 Tessellator::Trigs Tessellator::GetTrigsForDivisions(size_t divisions) {
218 return divisions < Tessellator::kCachedTrigCount
219 ? Trigs(precomputed_trigs_[divisions], divisions)
220 : Trigs(divisions);
221 }
222
223 using TessellatedVertexProc = Tessellator::TessellatedVertexProc;
224 using EllipticalVertexGenerator = Tessellator::EllipticalVertexGenerator;
225
226 EllipticalVertexGenerator::EllipticalVertexGenerator(
227 EllipticalVertexGenerator::GeneratorProc& generator,
228 Trigs&& trigs,
229 PrimitiveType triangle_type,
230 size_t vertices_per_trig,
231 Data&& data)
232 : impl_(generator),
233 trigs_(std::move(trigs)),
234 data_(data),
235 vertices_per_trig_(vertices_per_trig) {}
236
237 EllipticalVertexGenerator Tessellator::FilledCircle(
238 const Matrix& view_transform,
239 const Point& center,
240 Scalar radius) {
241 auto divisions =
242 ComputeQuadrantDivisions(view_transform.GetMaxBasisLength() * radius);
243 return EllipticalVertexGenerator(Tessellator::GenerateFilledCircle,
244 GetTrigsForDivisions(divisions),
245 PrimitiveType::kTriangleStrip, 4,
246 {
247 .reference_centers = {center, center},
248 .radii = {radius, radius},
249 .half_width = -1.0f,
250 });
251 }
252
253 EllipticalVertexGenerator Tessellator::StrokedCircle(
254 const Matrix& view_transform,
255 const Point& center,
256 Scalar radius,
257 Scalar half_width) {
258 if (half_width > 0) {
259 auto divisions = ComputeQuadrantDivisions(
260 view_transform.GetMaxBasisLength() * radius + half_width);
261 return EllipticalVertexGenerator(Tessellator::GenerateStrokedCircle,
262 GetTrigsForDivisions(divisions),
263 PrimitiveType::kTriangleStrip, 8,
264 {
265 .reference_centers = {center, center},
266 .radii = {radius, radius},
267 .half_width = half_width,
268 });
269 } else {
270 return FilledCircle(view_transform, center, radius);
271 }
272 }
273
274 EllipticalVertexGenerator Tessellator::RoundCapLine(
275 const Matrix& view_transform,
276 const Point& p0,
277 const Point& p1,
278 Scalar radius) {
279 auto along = p1 - p0;
280 auto length = along.GetLength();
281 if (length > kEhCloseEnough) {
282 auto divisions =
283 ComputeQuadrantDivisions(view_transform.GetMaxBasisLength() * radius);
284 return EllipticalVertexGenerator(Tessellator::GenerateRoundCapLine,
285 GetTrigsForDivisions(divisions),
286 PrimitiveType::kTriangleStrip, 4,
287 {
288 .reference_centers = {p0, p1},
289 .radii = {radius, radius},
290 .half_width = -1.0f,
291 });
292 } else {
293 return FilledCircle(view_transform, p0, radius);
294 }
295 }
296
297 EllipticalVertexGenerator Tessellator::FilledEllipse(
298 const Matrix& view_transform,
299 const Rect& bounds) {
300 if (bounds.IsSquare()) {
301 return FilledCircle(view_transform, bounds.GetCenter(),
302 bounds.GetWidth() * 0.5f);
303 }
304 auto max_radius = bounds.GetSize().MaxDimension();
305 auto divisions =
306 ComputeQuadrantDivisions(view_transform.GetMaxBasisLength() * max_radius);
307 auto center = bounds.GetCenter();
308 return EllipticalVertexGenerator(Tessellator::GenerateFilledEllipse,
309 GetTrigsForDivisions(divisions),
310 PrimitiveType::kTriangleStrip, 4,
311 {
312 .reference_centers = {center, center},
313 .radii = bounds.GetSize() * 0.5f,
314 .half_width = -1.0f,
315 });
316 }
317
318 EllipticalVertexGenerator Tessellator::FilledRoundRect(
319 const Matrix& view_transform,
320 const Rect& bounds,
321 const Size& radii) {
322 if (radii.width * 2 < bounds.GetWidth() ||
323 radii.height * 2 < bounds.GetHeight()) {
324 auto max_radius = radii.MaxDimension();
325 auto divisions = ComputeQuadrantDivisions(
326 view_transform.GetMaxBasisLength() * max_radius);
327 auto upper_left = bounds.GetLeftTop() + radii;
328 auto lower_right = bounds.GetRightBottom() - radii;
329 return EllipticalVertexGenerator(Tessellator::GenerateFilledRoundRect,
330 GetTrigsForDivisions(divisions),
331 PrimitiveType::kTriangleStrip, 4,
332 {
333 .reference_centers =
334 {
335 upper_left,
336 lower_right,
337 },
338 .radii = radii,
339 .half_width = -1.0f,
340 });
341 } else {
342 return FilledEllipse(view_transform, bounds);
343 }
344 }
345
346 void Tessellator::GenerateFilledCircle(
347 const Trigs& trigs,
348 const EllipticalVertexGenerator::Data& data,
349 const TessellatedVertexProc& proc) {
350 auto center = data.reference_centers[0];
351 auto radius = data.radii.width;
352
353 FML_DCHECK(center == data.reference_centers[1]);
354 FML_DCHECK(radius == data.radii.height);
355 FML_DCHECK(data.half_width < 0);
356
357 // Quadrant 1 connecting with Quadrant 4:
358 for (auto& trig : trigs) {
359 auto offset = trig * radius;
360 proc({center.x - offset.x, center.y + offset.y});
361 proc({center.x - offset.x, center.y - offset.y});
362 }
363
364 // The second half of the circle should be iterated in reverse, but
365 // we can instead iterate forward and swap the x/y values of the
366 // offset as the angles should be symmetric and thus should generate
367 // symmetrically reversed trig vectors.
368 // Quadrant 2 connecting with Quadrant 2:
369 for (auto& trig : trigs) {
370 auto offset = trig * radius;
371 proc({center.x + offset.y, center.y + offset.x});
372 proc({center.x + offset.y, center.y - offset.x});
373 }
374 }
375
376 void Tessellator::GenerateStrokedCircle(
377 const Trigs& trigs,
378 const EllipticalVertexGenerator::Data& data,
379 const TessellatedVertexProc& proc) {
380 auto center = data.reference_centers[0];
381
382 FML_DCHECK(center == data.reference_centers[1]);
383 FML_DCHECK(data.radii.IsSquare());
384 FML_DCHECK(data.half_width > 0 && data.half_width < data.radii.width);
385
386 auto outer_radius = data.radii.width + data.half_width;
387 auto inner_radius = data.radii.width - data.half_width;
388
389 // Zig-zag back and forth between points on the outer circle and the
390 // inner circle. Both circles are evaluated at the same number of
391 // quadrant divisions so the points for a given division should match
392 // 1 for 1 other than their applied radius.
393
394 // Quadrant 1:
395 for (auto& trig : trigs) {
396 auto outer = trig * outer_radius;
397 auto inner = trig * inner_radius;
398 proc({center.x - outer.x, center.y - outer.y});
399 proc({center.x - inner.x, center.y - inner.y});
400 }
401
402 // The even quadrants of the circle should be iterated in reverse, but
403 // we can instead iterate forward and swap the x/y values of the
404 // offset as the angles should be symmetric and thus should generate
405 // symmetrically reversed trig vectors.
406 // Quadrant 2:
407 for (auto& trig : trigs) {
408 auto outer = trig * outer_radius;
409 auto inner = trig * inner_radius;
410 proc({center.x + outer.y, center.y - outer.x});
411 proc({center.x + inner.y, center.y - inner.x});
412 }
413
414 // Quadrant 3:
415 for (auto& trig : trigs) {
416 auto outer = trig * outer_radius;
417 auto inner = trig * inner_radius;
418 proc({center.x + outer.x, center.y + outer.y});
419 proc({center.x + inner.x, center.y + inner.y});
420 }
421
422 // Quadrant 4:
423 for (auto& trig : trigs) {
424 auto outer = trig * outer_radius;
425 auto inner = trig * inner_radius;
426 proc({center.x - outer.y, center.y + outer.x});
427 proc({center.x - inner.y, center.y + inner.x});
428 }
429 }
430
431 void Tessellator::GenerateRoundCapLine(
432 const Trigs& trigs,
433 const EllipticalVertexGenerator::Data& data,
434 const TessellatedVertexProc& proc) {
435 auto p0 = data.reference_centers[0];
436 auto p1 = data.reference_centers[1];
437 auto radius = data.radii.width;
438
439 FML_DCHECK(radius == data.radii.height);
440 FML_DCHECK(data.half_width < 0);
441
442 auto along = p1 - p0;
443 along *= radius / along.GetLength();
444 auto across = Point(-along.y, along.x);
445
446 for (auto& trig : trigs) {
447 auto relative_along = along * trig.cos;
448 auto relative_across = across * trig.sin;
449 proc(p0 - relative_along + relative_across);
450 proc(p0 - relative_along - relative_across);
451 }
452
453 // The second half of the round caps should be iterated in reverse, but
454 // we can instead iterate forward and swap the sin/cos values as they
455 // should be symmetric.
456 for (auto& trig : trigs) {
457 auto relative_along = along * trig.sin;
458 auto relative_across = across * trig.cos;
459 proc(p1 + relative_along + relative_across);
460 proc(p1 + relative_along - relative_across);
461 }
462 }
463
464 void Tessellator::GenerateFilledEllipse(
465 const Trigs& trigs,
466 const EllipticalVertexGenerator::Data& data,
467 const TessellatedVertexProc& proc) {
468 auto center = data.reference_centers[0];
469 auto radii = data.radii;
470
471 FML_DCHECK(center == data.reference_centers[1]);
472 FML_DCHECK(data.half_width < 0);
473
474 // Quadrant 1 connecting with Quadrant 4:
475 for (auto& trig : trigs) {
476 auto offset = trig * radii;
477 proc({center.x - offset.x, center.y + offset.y});
478 proc({center.x - offset.x, center.y - offset.y});
479 }
480
481 // The second half of the circle should be iterated in reverse, but
482 // we can instead iterate forward and swap the x/y values of the
483 // offset as the angles should be symmetric and thus should generate
484 // symmetrically reversed trig vectors.
485 // Quadrant 2 connecting with Quadrant 2:
486 for (auto& trig : trigs) {
487 auto offset = Point(trig.sin * radii.width, trig.cos * radii.height);
488 proc({center.x + offset.x, center.y + offset.y});
489 proc({center.x + offset.x, center.y - offset.y});
490 }
491 }
492
493 void Tessellator::GenerateFilledRoundRect(
494 const Trigs& trigs,
495 const EllipticalVertexGenerator::Data& data,
496 const TessellatedVertexProc& proc) {
497 Scalar left = data.reference_centers[0].x;
498 Scalar top = data.reference_centers[0].y;
499 Scalar right = data.reference_centers[1].x;
500 Scalar bottom = data.reference_centers[1].y;
501 auto radii = data.radii;
502
503 FML_DCHECK(data.half_width < 0);
504
505 // Quadrant 1 connecting with Quadrant 4:
506 for (auto& trig : trigs) {
507 auto offset = trig * radii;
508 proc({left - offset.x, bottom + offset.y});
509 proc({left - offset.x, top - offset.y});
510 }
511
512 // The second half of the round rect should be iterated in reverse, but
513 // we can instead iterate forward and swap the x/y values of the
514 // offset as the angles should be symmetric and thus should generate
515 // symmetrically reversed trig vectors.
516 // Quadrant 2 connecting with Quadrant 2:
517 for (auto& trig : trigs) {
518 auto offset = Point(trig.sin * radii.width, trig.cos * radii.height);
519 proc({right + offset.x, bottom + offset.y});
520 proc({right + offset.x, top - offset.y});
521 }
522 }
523
524 } // namespace impeller
impeller::EllipticalVertexGenerator
Tessellator::EllipticalVertexGenerator EllipticalVertexGenerator
Definition: tessellator.cc:224
impeller::Tessellator::~Tessellator
virtual ~Tessellator()
impeller::HostBuffer::Emplace
BufferView Emplace(const BufferType &buffer, size_t alignment=0)
Emplace non-uniform data (like contiguous vertices) onto the host buffer.
Definition: host_buffer.h:95
polyline
const Path::Polyline & polyline
Definition: stroke_path_geometry.cc:303
impeller::IndexType::k16bit
@ k16bit
impeller::Scalar
float Scalar
Definition: scalar.h:18
impeller::kEhCloseEnough
constexpr float kEhCloseEnough
Definition: constants.h:56
impeller::HostBuffer
Definition: host_buffer.h:28
data
std::shared_ptr< const fml::Mapping > data
Definition: texture_gles.cc:63
impeller::Tessellator::TessellatedVertexProc
std::function< void(const Point &p)> TessellatedVertexProc
A callback function for a |VertexGenerator| to deliver the vertices it computes as |Point| objects.
Definition: tessellator.h:78
impeller::VertexBuffer
Definition: vertex_buffer.h:13
impeller::TRect::GetLeftTop
constexpr TPoint< T > GetLeftTop() const
Definition: rect.h:349
impeller::TSize::MaxDimension
constexpr Type MaxDimension() const
Definition: size.h:88
impeller::TRect::GetCenter
constexpr Point GetCenter() const
Get the center point as a |Point|.
Definition: rect.h:373
impeller::VertexBuffer::vertex_buffer
BufferView vertex_buffer
Definition: vertex_buffer.h:14
impeller::ComputeQuadrantDivisions
static size_t ComputeQuadrantDivisions(Scalar pixel_radius)
Definition: tessellator.cc:144
impeller::TRect::GetHeight
constexpr Type GetHeight() const
Returns the height of the rectangle, equivalent to |GetSize().height|.
Definition: rect.h:337
offset
SeparatedVector2 offset
Definition: stroke_path_geometry.cc:311
impeller::kPiOver2
constexpr float kPiOver2
Definition: constants.h:32
tessellator.h
impeller::Path::Polyline
Definition: path.h:95
impeller::Path::WritePolyline
void WritePolyline(Scalar scale, VertexWriter &writer) const
Definition: path.cc:106
impeller::TSize< Scalar >
impeller::kPrecomputedDivisions
static int kPrecomputedDivisions[kPrecomputedDivisionCount]
Definition: tessellator.cc:75
impeller::PrimitiveType
PrimitiveType
Decides how backend draws pixels based on input vertices.
Definition: formats.h:352
impeller::Point
TPoint< Scalar > Point
Definition: point.h:322
impeller::Path
Paths are lightweight objects that describe a collection of linear, quadratic, or cubic segments....
Definition: path.h:52
impeller::Matrix::GetMaxBasisLength
Scalar GetMaxBasisLength() const
Definition: matrix.cc:196
impeller::TRect::GetWidth
constexpr Type GetWidth() const
Returns the width of the rectangle, equivalent to |GetSize().width|.
Definition: rect.h:331
impeller::TRect::IsSquare
constexpr bool IsSquare() const
Returns true if width and height are equal and neither is NaN.
Definition: rect.h:294
impeller::Tessellator::point_buffer_
std::unique_ptr< std::vector< Point > > point_buffer_
Used for polyline generation.
Definition: tessellator.h:287
impeller::TessellatedVertexProc
Tessellator::TessellatedVertexProc TessellatedVertexProc
Definition: tessellator.cc:223
impeller::Tessellator::EllipticalVertexGenerator
The |VertexGenerator| implementation common to all shapes that are based on a polygonal representatio...
Definition: tessellator.h:121
impeller::TSize::width
Type width
Definition: size.h:22
impeller::Tessellator::TessellateConvexInternal
static void TessellateConvexInternal(const Path &path, std::vector< Point > &point_buffer, std::vector< uint16_t > &index_buffer, Scalar tolerance)
Definition: tessellator.cc:62
impeller::VertexWriter
An interface for generating a multi contour polyline as a triangle strip.
Definition: path_component.h:21
impeller::Tessellator::index_buffer_
std::unique_ptr< std::vector< uint16_t > > index_buffer_
Definition: tessellator.h:288
impeller::BufferView
Definition: buffer_view.h:15
impeller::TRect::GetSize
constexpr TSize< Type > GetSize() const
Returns the size of the rectangle which may be negative in either width or height and may have been c...
Definition: rect.h:317
impeller::TPoint::GetLength
constexpr Type GetLength() const
Definition: point.h:206
std
Definition: comparable.h:95
impeller::TPoint< Scalar >
impeller::Tessellator::Tessellator
Tessellator()
Definition: tessellator.cc:9
impeller::Tessellator::kCircleTolerance
static constexpr Scalar kCircleTolerance
The pixel tolerance used by the algorighm to determine how many divisions to create for a circle.
Definition: tessellator.h:214
impeller::Tessellator::TessellateConvex
VertexBuffer TessellateConvex(const Path &path, HostBuffer &host_buffer, Scalar tolerance)
Given a convex path, create a triangle fan structure.
Definition: tessellator.cc:30
impeller::Path::Polyline::PointBufferPtr
std::unique_ptr< std::vector< Point > > PointBufferPtr
Definition: path.h:98
impeller::TSize::height
Type height
Definition: size.h:23
impeller::kPrecomputedDivisionCount
static constexpr int kPrecomputedDivisionCount
Definition: tessellator.cc:74
impeller::TRect::GetRightBottom
constexpr TPoint< T > GetRightBottom() const
Definition: rect.h:361
impeller
Definition: aiks_blend_unittests.cc:18
impeller::Tessellator::CreateTempPolyline
Path::Polyline CreateTempPolyline(const Path &path, Scalar tolerance)
Create a temporary polyline. Only one per-process can exist at a time.
Definition: tessellator.cc:18
impeller::Path::CreatePolyline
Polyline CreatePolyline(Scalar scale, Polyline::PointBufferPtr point_buffer=std::make_unique< std::vector< Point >>(), Polyline::ReclaimPointBufferCallback reclaim=nullptr) const
Definition: path.cc:264
impeller::kPiOver4
constexpr float kPiOver4
Definition: constants.h:35
impeller::TRect< Scalar >
impeller::Matrix
A 4x4 matrix using column-major storage.
Definition: matrix.h:37