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