Flutter Impeller
round_superellipse_geometry.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 <cmath>
6 #include <variant>
7 
9 
11 
12 namespace impeller {
13 
14 namespace {
15 
16 // An interface for classes that arranges a point list that forms a convex
17 // contour into a triangle strip.
18 class ConvexRearranger {
19  public:
20  ConvexRearranger() {}
21 
22  virtual ~ConvexRearranger() {}
23 
24  virtual size_t ContourLength() const = 0;
25 
26  virtual Point GetPoint(size_t i) const = 0;
27 
28  void RearrangeIntoTriangleStrip(Point* output) {
29  size_t index_count = 0;
30 
31  output[index_count++] = GetPoint(0);
32 
33  size_t a = 1;
34  size_t contour_length = ContourLength();
35  size_t b = contour_length - 1;
36  while (a < b) {
37  output[index_count++] = GetPoint(a);
38  output[index_count++] = GetPoint(b);
39  a++;
40  b--;
41  }
42  if (a == b) {
43  output[index_count++] = GetPoint(b);
44  }
45  }
46 
47  private:
48  ConvexRearranger(const ConvexRearranger&) = delete;
49  ConvexRearranger& operator=(const ConvexRearranger&) = delete;
50 };
51 
52 // A convex rearranger whose contour is concatenated from 4 quadrant segments.
53 //
54 // The input quadrant curves must travel from the Y axis to the X axis, and
55 // include both ends. This means that the points on the axes are duplicate
56 // between segments, and will be omitted by this class.
57 class UnevenQuadrantsRearranger : public ConvexRearranger {
58  public:
59  UnevenQuadrantsRearranger(Point* cache, size_t segment_capacity)
60  : cache_(cache), segment_capacity_(segment_capacity) {}
61 
62  Point* QuadCache(size_t i) { return cache_ + segment_capacity_ * i; }
63 
64  const Point* QuadCache(size_t i) const {
65  return cache_ + segment_capacity_ * i;
66  }
67 
68  size_t& QuadSize(size_t i) { return lengths_[i]; }
69 
70  size_t ContourLength() const override {
71  return lengths_[0] + lengths_[1] + lengths_[2] + lengths_[3] - 4;
72  }
73 
74  Point GetPoint(size_t i) const override {
75  // output from index
76  // 0 ... l0-2 quads[0] 0 ... l0-2
77  // next 0 ... l1-2 quads[1] l1-1 ... 1
78  // next 0 ... l2-2 quads[2] 0 ... l2-2
79  // next 0 ... l3-2 quads[3] l3-1 ... 1
80  size_t high = lengths_[0] - 1;
81  if (i < high) {
82  return QuadCache(0)[i];
83  }
84  high += lengths_[1] - 1;
85  if (i < high) {
86  return QuadCache(1)[high - i];
87  }
88  size_t low = high;
89  high += lengths_[2] - 1;
90  if (i < high) {
91  return QuadCache(2)[i - low];
92  }
93  high += lengths_[3] - 1;
94  if (i < high) {
95  return QuadCache(3)[high - i];
96  } else {
97  // Unreachable
98  return Point();
99  }
100  }
101 
102  private:
103  Point* cache_;
104  size_t segment_capacity_;
105  size_t lengths_[4];
106 };
107 
108 // A convex rearranger whose contour is concatenated from 4 identical quadrant
109 // segments.
110 //
111 // The input curve must travel from the Y axis to the X axis and include both
112 // ends. This means that the points on the axes are duplicate between segments,
113 // and will be omitted by this class.
114 class MirroredQuadrantRearranger : public ConvexRearranger {
115  public:
116  MirroredQuadrantRearranger(Point center, Point* cache)
117  : center_(center), cache_(cache) {}
118 
119  size_t& QuadSize() { return l_; }
120 
121  size_t ContourLength() const override { return l_ * 4 - 4; }
122 
123  Point GetPoint(size_t i) const override {
124  // output from index
125  // 0 ... l-2 quad 0 ... l-2
126  // next 0 ... l-2 quad l-1 ... 1
127  // next 0 ... l-2 quad 0 ... l-2
128  // next 0 ... l-2 quad l-1 ... 1
129  size_t high = l_ - 1;
130  if (i < high) {
131  return cache_[i] + center_;
132  }
133  high += l_ - 1;
134  if (i < high) {
135  return cache_[high - i] * Point{1, -1} + center_;
136  }
137  size_t low = high;
138  high += l_ - 1;
139  if (i < high) {
140  return cache_[i - low] * Point{-1, -1} + center_;
141  }
142  high += l_ - 1;
143  if (i < high) {
144  return cache_[high - i] * Point{-1, 1} + center_;
145  } else {
146  // Unreachable
147  return Point();
148  }
149  }
150 
151  private:
152  Point center_;
153  Point* cache_;
154  size_t l_ = 0;
155 };
156 
157 // A matrix that swaps the coordinates of a point.
158 // clang-format off
159 constexpr Matrix kFlip = Matrix(
160  0.0f, 1.0f, 0.0f, 0.0f,
161  1.0f, 0.0f, 0.0f, 0.0f,
162  0.0f, 0.0f, 1.0f, 0.0f,
163  0.0f, 0.0f, 0.0f, 1.0f);
164 // clang-format on
165 
166 // A look up table with precomputed variables.
167 //
168 // The columns represent the following variabls respectively:
169 //
170 // * ratio = size / a
171 // * n
172 // * d / a
173 // * thetaJ
174 //
175 // For definition of the variables, see DrawOctantSquareLikeSquircle.
176 constexpr Scalar kPrecomputedVariables[][4] = {
177  {2.000, 2.00000, 0.00000, 0.24040}, //
178  {2.020, 2.03340, 0.01447, 0.24040}, //
179  {2.040, 2.06540, 0.02575, 0.21167}, //
180  {2.060, 2.09800, 0.03668, 0.20118}, //
181  {2.080, 2.13160, 0.04719, 0.19367}, //
182  {2.100, 2.17840, 0.05603, 0.16233}, //
183  {2.120, 2.19310, 0.06816, 0.20020}, //
184  {2.140, 2.22990, 0.07746, 0.19131}, //
185  {2.160, 2.26360, 0.08693, 0.19008}, //
186  {2.180, 2.30540, 0.09536, 0.17935}, //
187  {2.200, 2.32900, 0.10541, 0.19136}, //
188  {2.220, 2.38330, 0.11237, 0.17130}, //
189  {2.240, 2.39770, 0.12271, 0.18956}, //
190  {2.260, 2.41770, 0.13251, 0.20254}, //
191  {2.280, 2.47180, 0.13879, 0.18454}, //
192  {2.300, 2.50910, 0.14658, 0.18261} //
193 };
194 
195 constexpr size_t kNumRecords =
196  sizeof(kPrecomputedVariables) / sizeof(kPrecomputedVariables[0]);
197 constexpr Scalar kMinRatio = kPrecomputedVariables[0][0];
198 constexpr Scalar kMaxRatio = kPrecomputedVariables[kNumRecords - 1][0];
199 constexpr Scalar kRatioStep =
200  kPrecomputedVariables[1][0] - kPrecomputedVariables[0][0];
201 
202 // Linear interpolation for `kPrecomputedVariables`.
203 //
204 // The `column` is a 0-based index that decides the target variable, where 1
205 // corresponds to the 2nd element of each row, etc.
206 //
207 // The `ratio` corresponds to column 0, on which the lerp is calculated.
208 Scalar LerpPrecomputedVariable(size_t column, Scalar ratio) {
209  Scalar steps =
210  std::clamp<Scalar>((ratio - kMinRatio) / kRatioStep, 0, kNumRecords - 1);
211  size_t left = std::clamp<size_t>(static_cast<size_t>(std::floor(steps)), 0,
212  kNumRecords - 2);
213  Scalar frac = steps - left;
214 
215  return (1 - frac) * kPrecomputedVariables[left][column] +
216  frac * kPrecomputedVariables[left + 1][column];
217 }
218 
219 // The max angular step that the algorithm will traverse a quadrant of the
220 // curve.
221 //
222 // This limits the max number of points of the curve.
223 constexpr Scalar kMaxQuadrantSteps = 40;
224 
225 // Calculates the angular step size for a smooth curve.
226 //
227 // Returns the angular step needed to ensure a curve appears smooth
228 // based on the smallest dimension of a shape. Smaller dimensions require
229 // larger steps as less detail is needed for smoothness.
230 //
231 // The `minDimension` is the smallest dimension (e.g., width or height) of the
232 // shape.
233 //
234 // The `fullAngle` is the total angular range to traverse.
235 Scalar CalculateStep(Scalar minDimension, Scalar fullAngle) {
236  constexpr Scalar kMinAngleStep = kPiOver2 / kMaxQuadrantSteps;
237 
238  // Assumes at least 1 point is needed per pixel to achieve sufficient
239  // smoothness.
240  constexpr Scalar pointsPerPixel = 1.0;
241  size_t pointsByDimension =
242  static_cast<size_t>(std::ceil(minDimension * pointsPerPixel));
243  Scalar angleByDimension = fullAngle / pointsByDimension;
244 
245  return std::min(kMinAngleStep, angleByDimension);
246 }
247 
248 // A factor used to calculate the "gap", defined as the distance from the
249 // midpoint of the curved corners to the nearest sides of the bounding box.
250 //
251 // When the corner radius is symmetrical on both dimensions, the midpoint of the
252 // corner is where the circular arc intersects its quadrant bisector. When the
253 // corner radius is asymmetrical, since the corner can be considered "elongated"
254 // from a symmetrical corner, the midpoint is transformed in the same way.
255 //
256 // Experiments indicate that the gap is linear with respect to the corner
257 // radius on that dimension.
258 //
259 // The formula should be kept in sync with a few files, as documented in
260 // `CalculateGap` in round_superellipse_geometry.cc.
261 constexpr Scalar kGapFactor = 0.2924066406;
262 
263 // Return the value that splits the range from `left` to `right` into two
264 // portions whose ratio equals to `ratio_left` : `ratio_right`.
265 static Scalar Split(Scalar left,
266  Scalar right,
267  Scalar ratio_left,
268  Scalar ratio_right) {
269  return (left * ratio_right + right * ratio_left) / (ratio_left + ratio_right);
270 }
271 
272 // Draw a circular arc from `start` to `end` with a radius of `r`.
273 //
274 // It is assumed that `start` is north-west to `end`, and the center of the
275 // circle is south-west to both points. If `reverse` is true, then the curve
276 // goes from `end` to `start` instead.
277 //
278 // The resulting points, after applying `transform`, are appended to `output`
279 // and include the effective starting point but exclude the effective ending
280 // point.
281 //
282 // Returns the number of generated points.
283 size_t DrawCircularArc(Point* output,
284  Point start,
285  Point end,
286  Scalar r,
287  bool reverse,
288  const Matrix& transform) {
289  /* Denote the middle point of S and E as M. The key is to find the center of
290  * the circle.
291  * S --__
292  * / ⟍ `、
293  * / M ⟍\
294  * / ⟋ E
295  * / ⟋ ↗
296  * / ⟋
297  * / ⟋ r
298  * C ᜱ ↙
299  */
300 
301  Point s_to_e = end - start;
302  Point m = (start + end) / 2;
303  Point c_to_m = Point(-s_to_e.y, s_to_e.x);
304  Scalar distance_sm = s_to_e.GetLength() / 2;
305  Scalar distance_cm = sqrt(r * r - distance_sm * distance_sm);
306  Point c = m - distance_cm * c_to_m.Normalize();
307  Scalar angle_sce = asinf(distance_sm / r) * 2;
308  Point c_to_s = start - c;
309  Matrix full_transform = transform * Matrix::MakeTranslation(c);
310 
311  Point* next = output;
312  Scalar angle = reverse ? angle_sce : 0.0f;
313  Scalar step =
314  (reverse ? -1 : 1) * CalculateStep(std::abs(s_to_e.y), angle_sce);
315  Scalar end_angle = reverse ? 0.0f : angle_sce;
316 
317  while ((angle < end_angle) != reverse) {
318  *(next++) = full_transform * c_to_s.Rotate(Radians(-angle));
319  angle += step;
320  }
321  return next - output;
322 }
323 
324 // Draw a superellipsoid arc.
325 //
326 // The superellipse is centered at the origin and has degree `n` and both
327 // semi-axes equal to `a`. The arc starts from positive Y axis and spans from 0
328 // to `max_theta` radiance clockwise if `reverse` is false, or from `max_theta`
329 // to 0 otherwise.
330 //
331 // The resulting points, after applying `transform`, are appended to `output`
332 // and include the starting point but exclude the ending point.
333 //
334 // Returns the number of generated points.
335 size_t DrawSuperellipsoidArc(Point* output,
336  Scalar a,
337  Scalar n,
338  Scalar max_theta,
339  bool reverse,
340  const Matrix& transform) {
341  Point* next = output;
342  Scalar angle = reverse ? max_theta : 0.0f;
343  Scalar step =
344  (reverse ? -1 : 1) *
345  CalculateStep(a - a * pow(abs(cosf(max_theta)), 2 / n), max_theta);
346  Scalar end = reverse ? 0.0f : max_theta;
347  while ((angle < end) != reverse) {
348  Scalar x = a * pow(abs(sinf(angle)), 2 / n);
349  Scalar y = a * pow(abs(cosf(angle)), 2 / n);
350  *(next++) = transform * Point(x, y);
351  angle += step;
352  }
353  return next - output;
354 }
355 
356 // Draws an arc representing the top 1/8 segment of a square-like rounded
357 // superellipse centered at the origin.
358 //
359 // The square-like rounded superellipse that this arc belongs to has a width and
360 // height specified by `size` and features rounded corners determined by
361 // `corner_radius`. The `corner_radius` corresponds to the `cornerRadius`
362 // parameter in SwiftUI, rather than the literal radius of corner circles.
363 //
364 // If `reverse` is false, the resulting arc spans from 0 (inclusive) to pi/4
365 // (exclusive), moving clockwise starting from the positive Y-axis. If `reverse`
366 // is true, the curve spans from pi/4 (inclusive) to 0 (inclusive)
367 // counterclockwise instead.
368 //
369 // Returns the number of points generated.
370 size_t DrawOctantSquareLikeSquircle(Point* output,
371  Scalar size,
372  Scalar corner_radius,
373  bool reverse,
374  const Matrix& transform) {
375  /* The following figure shows the first quadrant of a square-like rounded
376  * superellipse. The target arc consists of the "stretch" (AB), a
377  * superellipsoid arc (BJ), and a circular arc (JM).
378  *
379  * straight superelipse
380  * ↓ ↓
381  * A B J circular arc
382  * ---------...._ ↙
383  * | | / `⟍ M
384  * | | / ⟋ ⟍
385  * | | / ⟋ \
386  * | | / ⟋ |
387  * | | ᜱD |
388  * | | / |
389  * ↑ +----+ S |
390  * s | | |
391  * ↓ +----+---------------| A'
392  * O
393  * ← s →
394  * ←------ size/2 ------→
395  *
396  * Define gap (g) as the distance between point M and the bounding box,
397  * therefore point M is at (size/2 - g, size/2 - g).
398  *
399  * The superellipsoid curve can be drawn with an implicit parameter θ:
400  * x = a * sinθ ^ (2/n)
401  * y = a * cosθ ^ (2/n)
402  * https://math.stackexchange.com/questions/2573746/superellipse-parametric-equation
403  *
404  * Define thetaJ as the θ at point J.
405  */
406 
407  Scalar ratio = {std::min(size / corner_radius, kMaxRatio)};
408  Scalar a = ratio * corner_radius / 2;
409  Scalar s = size / 2 - a;
410  Scalar g = kGapFactor * corner_radius;
411 
412  Scalar n = LerpPrecomputedVariable(1, ratio);
413  Scalar d = LerpPrecomputedVariable(2, ratio) * a;
414  Scalar thetaJ = LerpPrecomputedVariable(3, ratio);
415 
416  Scalar R = (a - d - g) * sqrt(2);
417 
418  Point pointA{0, size / 2};
419  Point pointM{size / 2 - g, size / 2 - g};
420  Point pointS{s, s};
421  Point pointJ =
422  Point{pow(abs(sinf(thetaJ)), 2 / n), pow(abs(cosf(thetaJ)), 2 / n)} * a +
423  pointS;
424  Matrix translationS = Matrix::MakeTranslation(pointS);
425 
426  Point* next = output;
427  if (!reverse) {
428  // Point A
429  *(next++) = transform * pointA;
430  // Arc [B, J)
431  next += DrawSuperellipsoidArc(next, a, n, thetaJ, reverse,
432  transform * translationS);
433  // Arc [J, M)
434  next += DrawCircularArc(next, pointJ, pointM, R, reverse, transform);
435  } else {
436  // Arc [M, J)
437  next += DrawCircularArc(next, pointJ, pointM, R, reverse, transform);
438  // Arc [J, B)
439  next += DrawSuperellipsoidArc(next, a, n, thetaJ, reverse,
440  transform * translationS);
441  // Point B
442  *(next++) = transform * Point{s, size / 2};
443  // Point A
444  *(next++) = transform * pointA;
445  }
446  return next - output;
447 }
448 
449 // Draw a quadrant curve, both ends included.
450 //
451 // Returns the number of points.
452 //
453 // The eact quadrant is specified by the direction of `outer` relative to
454 // `center`. The curve goes from the X axis to the Y axis.
455 static size_t DrawQuadrant(Point* output,
456  Point center,
457  Point outer,
458  Size radii) {
459  if (radii.width == 0 || radii.height == 0) {
460  // Degrade to rectangle. (A zero radius causes error below.)
461  output[0] = {center.x, outer.y};
462  output[1] = outer;
463  output[2] = {outer.x, center.y};
464  return 3;
465  }
466  // Normalize sizes and radii into symmetrical radius by scaling the longer of
467  // `radii` to the shorter. For example, to draw a RSE with size (200, 300)
468  // and radii (20, 10), this function draws one with size (100, 300) and radii
469  // (10, 10) and then scales it by (2x, 1x).
470  Scalar norm_radius = radii.MinDimension();
471  Size radius_scale = radii / norm_radius;
472  Point signed_size = (outer - center) * 2;
473  Point norm_size = signed_size.Abs() / radius_scale;
474  Point signed_scale = signed_size / norm_size;
475 
476  // Each quadrant curve is composed of two octant curves, each of which belongs
477  // to a square-like rounded rectangle. When `norm_size`'s width != height, the
478  // centers of such square-like rounded rectangles are offset from the origin
479  // by a distance denoted as `c`.
480  Scalar c = (norm_size.x - norm_size.y) / 2;
481 
482  Point* next = output;
483 
484  next += DrawOctantSquareLikeSquircle(
485  next, norm_size.x, norm_radius, /*reverse=*/false,
486  Matrix::MakeTranslateScale(signed_scale, center) *
487  Matrix::MakeTranslation(Size{0, -c}));
488 
489  next += DrawOctantSquareLikeSquircle(
490  next, norm_size.y, norm_radius, /*reverse=*/true,
491  Matrix::MakeTranslateScale(signed_scale, center) *
492  Matrix::MakeTranslation(Size{c, 0}) * kFlip);
493 
494  return next - output;
495 }
496 
497 } // namespace
498 
500  const RoundingRadii& radii)
501  : bounds_(bounds.GetPositive()), radii_(radii.Scaled(bounds_)) {}
502 
504  float corner_radius)
505  : RoundSuperellipseGeometry(bounds,
506  RoundingRadii::MakeRadius(corner_radius)) {}
507 
509 
510 GeometryResult RoundSuperellipseGeometry::GetPositionBuffer(
511  const ContentContext& renderer,
512  const Entity& entity,
513  RenderPass& pass) const {
514  Point* cache = renderer.GetTessellator().GetStrokePointCache().data();
515 
516  // The memory size (in units of Points) allocated to store each quadrants.
517  constexpr size_t kMaxQuadSize = kPointArenaSize / 4;
518  // Since the curve is traversed in steps bounded by kMaxQuadrantSteps, the
519  // curving part will have fewer points than kMaxQuadrantSteps. Multiply it by
520  // 2 for storing other sporatic points (an extremely conservative estimate).
521  static_assert(kMaxQuadSize > 2 * kMaxQuadrantSteps);
522 
523  ConvexRearranger* rearranger;
524  std::variant<std::monostate, MirroredQuadrantRearranger,
525  UnevenQuadrantsRearranger>
526  rearranger_holder;
527 
528  if (radii_.AreAllCornersSame()) {
529  rearranger_holder.emplace<MirroredQuadrantRearranger>(bounds_.GetCenter(),
530  cache);
531  auto& t = std::get<MirroredQuadrantRearranger>(rearranger_holder);
532  rearranger = &t;
533 
534  // The quadrant must be drawn at the origin so that it can be rotated later.
535  t.QuadSize() = DrawQuadrant(cache, Point(),
536  bounds_.GetRightTop() - bounds_.GetCenter(),
537  radii_.top_right);
538  } else {
539  rearranger_holder.emplace<UnevenQuadrantsRearranger>(cache, kMaxQuadSize);
540  auto& t = std::get<UnevenQuadrantsRearranger>(rearranger_holder);
541  rearranger = &t;
542 
543  Scalar top_split = Split(bounds_.GetLeft(), bounds_.GetRight(),
544  radii_.top_left.width, radii_.top_right.width);
545  Scalar right_split =
546  Split(bounds_.GetTop(), bounds_.GetBottom(), radii_.top_right.height,
547  radii_.bottom_right.height);
548  Scalar bottom_split =
549  Split(bounds_.GetLeft(), bounds_.GetRight(), radii_.bottom_left.width,
550  radii_.bottom_right.width);
551  Scalar left_split =
552  Split(bounds_.GetTop(), bounds_.GetBottom(), radii_.top_left.height,
553  radii_.bottom_left.height);
554 
555  t.QuadSize(0) = DrawQuadrant(t.QuadCache(0), Point{top_split, right_split},
556  bounds_.GetRightTop(), radii_.top_right);
557  t.QuadSize(1) =
558  DrawQuadrant(t.QuadCache(1), Point{bottom_split, right_split},
559  bounds_.GetRightBottom(), radii_.bottom_right);
560  t.QuadSize(2) =
561  DrawQuadrant(t.QuadCache(2), Point{bottom_split, left_split},
562  bounds_.GetLeftBottom(), radii_.bottom_left);
563  t.QuadSize(3) = DrawQuadrant(t.QuadCache(3), Point{top_split, left_split},
564  bounds_.GetLeftTop(), radii_.top_left);
565  }
566 
567  size_t contour_length = rearranger->ContourLength();
568  BufferView vertex_buffer = renderer.GetTransientsBuffer().Emplace(
569  nullptr, sizeof(Point) * contour_length, alignof(Point));
570  Point* vertex_data =
571  reinterpret_cast<Point*>(vertex_buffer.GetBuffer()->OnGetContents() +
572  vertex_buffer.GetRange().offset);
573  rearranger->RearrangeIntoTriangleStrip(vertex_data);
574 
575  return GeometryResult{
577  .vertex_buffer =
578  {
579  .vertex_buffer = vertex_buffer,
580  .vertex_count = contour_length,
581  .index_type = IndexType::kNone,
582  },
583  .transform = entity.GetShaderTransform(pass),
584  };
585 }
586 
587 std::optional<Rect> RoundSuperellipseGeometry::GetCoverage(
588  const Matrix& transform) const {
589  return bounds_.TransformBounds(transform);
590 }
591 
593  const Rect& rect) const {
594  if (!transform.IsTranslationScaleOnly()) {
595  return false;
596  }
597  Scalar left_inset = std::max(radii_.top_left.width, radii_.bottom_left.width);
598  Scalar right_inset =
599  std::max(radii_.top_right.width, radii_.bottom_right.width);
600  Scalar top_inset = std::max(radii_.top_left.height, radii_.top_right.height);
601  Scalar bottom_inset =
602  std::max(radii_.bottom_left.height, radii_.bottom_right.height);
603  Rect coverage =
604  Rect::MakeLTRB(bounds_.GetLeft() + left_inset * kGapFactor,
605  bounds_.GetTop() + top_inset * kGapFactor,
606  bounds_.GetRight() - right_inset * kGapFactor,
607  bounds_.GetBottom() - bottom_inset * kGapFactor);
608  return coverage.TransformBounds(transform).Contains(rect);
609 }
610 
612  return false;
613 }
614 
615 } // namespace impeller
HostBuffer & GetTransientsBuffer() const
Retrieve the currnent host buffer for transient storage.
Tessellator & GetTessellator() const
Matrix GetShaderTransform(const RenderPass &pass) const
Get the vertex shader transform used for drawing this Entity.
Definition: entity.cc:48
BufferView Emplace(const BufferType &buffer, size_t alignment=0)
Emplace non-uniform data (like contiguous vertices) onto the host buffer.
Definition: host_buffer.h:93
Render passes encode render commands directed as one specific render target into an underlying comman...
Definition: render_pass.h:30
bool CoversArea(const Matrix &transform, const Rect &rect) const override
Determines if this geometry, transformed by the given transform, will completely cover all surface ar...
RoundSuperellipseGeometry(const Rect &bounds, const RoundingRadii &radii)
std::vector< Point > & GetStrokePointCache()
Retrieve a pre-allocated arena of kPointArenaSize points.
Definition: tessellator.cc:24
int32_t x
@ kNone
Does not use the index buffer.
float Scalar
Definition: scalar.h:18
TPoint< Scalar > Point
Definition: point.h:327
constexpr float kPiOver2
Definition: constants.h:32
TSize< Scalar > Size
Definition: size.h:171
static constexpr size_t kPointArenaSize
The size of the point arena buffer stored on the tessellator.
Definition: tessellator.h:22
A 4x4 matrix using column-major storage.
Definition: matrix.h:37
static constexpr Matrix MakeTranslation(const Vector3 &t)
Definition: matrix.h:95
static constexpr Matrix MakeTranslateScale(const Vector3 &s, const Vector3 &t)
Definition: matrix.h:113
constexpr bool AreAllCornersSame(Scalar tolerance=kEhCloseEnough) const
constexpr TPoint Abs() const
Definition: point.h:216
constexpr TPoint Normalize() const
Definition: point.h:208
constexpr auto GetBottom() const
Definition: rect.h:361
constexpr TRect TransformBounds(const Matrix &transform) const
Creates a new bounding box that contains this transformed rectangle.
Definition: rect.h:476
constexpr auto GetTop() const
Definition: rect.h:357
constexpr bool Contains(const TPoint< Type > &p) const
Returns true iff the provided point |p| is inside the half-open interior of this rectangle.
Definition: rect.h:235
constexpr auto GetLeft() const
Definition: rect.h:355
constexpr auto GetRight() const
Definition: rect.h:359
constexpr TPoint< T > GetLeftBottom() const
Definition: rect.h:371
constexpr TPoint< T > GetRightTop() const
Definition: rect.h:367
constexpr TPoint< T > GetRightBottom() const
Definition: rect.h:375
constexpr Point GetCenter() const
Get the center point as a |Point|.
Definition: rect.h:386
constexpr TPoint< T > GetLeftTop() const
Definition: rect.h:363
constexpr static TRect MakeLTRB(Type left, Type top, Type right, Type bottom)
Definition: rect.h:129
Type height
Definition: size.h:29
Type width
Definition: size.h:28