Flutter Impeller
rect_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 
6 
7 namespace impeller {
8 
10 
12 
14  return rect_;
15 }
16 
18  padding_pixels_ = padding;
19 }
20 
22  return padding_pixels_;
23 }
24 
26  const ContentContext& renderer,
27  const Entity& entity,
28  RenderPass& pass) const {
29  auto& data_host_buffer = renderer.GetTransientsDataBuffer();
30  Scalar max_basis = entity.GetTransform().GetMaxBasisLengthXY();
31  Scalar padding = max_basis == 0 ? 0 : padding_pixels_ / max_basis;
32  Rect expanded_rect = rect_.Expand(padding);
33  return GeometryResult{
35  .vertex_buffer =
36  {
37  .vertex_buffer =
38  data_host_buffer.Emplace(expanded_rect.GetPoints().data(),
39  8 * sizeof(float), alignof(float)),
40  .vertex_count = 4,
41  .index_type = IndexType::kNone,
42  },
43  .transform = entity.GetShaderTransform(pass),
45  };
46 }
47 
48 std::optional<Rect> FillRectGeometry::GetCoverage(
49  const Matrix& transform) const {
50  Scalar max_basis = transform.GetMaxBasisLengthXY();
51  Scalar padding = max_basis == 0 ? 0 : padding_pixels_ / max_basis;
53 }
54 
56  const Rect& rect) const {
57  if (!transform.IsTranslationScaleOnly()) {
58  return false;
59  }
60  Rect coverage = rect_.TransformBounds(transform);
61  return coverage.Contains(rect);
62 }
63 
65  return true;
66 }
67 
69  const StrokeParameters& stroke)
70  : rect_(rect),
71  stroke_width_(stroke.width),
72  stroke_join_(AdjustStrokeJoin(stroke)) {}
73 
75 
77  const ContentContext& renderer,
78  const Entity& entity,
79  RenderPass& pass) const {
80  if (stroke_width_ < 0.0) {
81  return {};
82  }
83  Scalar max_basis = entity.GetTransform().GetMaxBasisLengthXY();
84  if (max_basis == 0) {
85  return {};
86  }
87 
88  Scalar min_size = kMinStrokeSize / max_basis;
89  Scalar stroke_width = std::max(stroke_width_, min_size);
90  Scalar half_stroke_width = stroke_width * 0.5f;
91 
92  auto& data_host_buffer = renderer.GetTransientsDataBuffer();
93  const Rect& rect = rect_;
94  bool interior_filled = (stroke_width >= rect.GetSize().MinDimension());
95 
96  switch (stroke_join_) {
97  case Join::kRound: {
98  Tessellator::Trigs trigs =
99  renderer.GetTessellator().GetTrigsForDeviceRadius(half_stroke_width *
100  max_basis);
101 
102  FML_DCHECK(trigs.size() >= 2u);
103 
104  auto vertex_count = trigs.size() * 4;
105  if (!interior_filled) {
106  // If there is a hole in the interior (as with most stroked rects
107  // unless the stroke width is really really wide) then we need
108  // to perform some surgery to generate the hollowed-out interior.
109  vertex_count += 12;
110  }
111 
112  return GeometryResult{
114  .vertex_buffer =
115  {
116  .vertex_buffer = data_host_buffer.Emplace(
117  vertex_count * sizeof(Point), alignof(Point),
118  [hsw = half_stroke_width, &rect, vertex_count, &trigs,
119  interior_filled](uint8_t* buffer) {
120  Scalar left = rect.GetLeft();
121  Scalar top = rect.GetTop();
122  Scalar right = rect.GetRight();
123  Scalar bottom = rect.GetBottom();
124 
125  auto vertices = reinterpret_cast<Point*>(buffer);
126  [[maybe_unused]]
127  auto vertices_end = vertices + vertex_count;
128 
129  // Traverse top down, left to right across slices.
130 
131  // Slice 1: Draw across between top pair of round joins.
132  for (auto trig : trigs) {
133  // trig.sin goes from 0 to 1
134  // trig.cos goes from 1 to 0
135  *vertices++ = Point(left - trig.sin * hsw,
136  top - trig.cos * hsw);
137  *vertices++ = Point(right + trig.sin * hsw,
138  top - trig.cos * hsw);
139  }
140  // Ends up with vertices that draw across the bottom
141  // of the top curved section (left - hsw, top) to
142  // (right + hsw, top). This is the starting pair of
143  // vertices for the following square section.
144 
145  if (interior_filled) {
146  // If interior is filled, we can just let the bottom
147  // pair of vertices of the top edge connect to the
148  // top pair of vertices of the bottom edge generated
149  // in slice 5 below. They both go left-right so they
150  // will create a proper zig-zag box to connect the
151  // 2 sections.
152  } else {
153  // Slice 2: Draw the inner part of the top stroke.
154  // Simply extend down from the last horizontal pair
155  // of vertices to (top + hsw).
156  *vertices++ = Point(left - hsw, top + hsw);
157  *vertices++ = Point(right + hsw, top + hsw);
158 
159  // Slice 3: Draw the left and right edges.
160 
161  // Slice 3a: Draw the right edge first.
162  // Since we are already at the right edge from the
163  // previous slice, we just have to add 2 vertices
164  // to get to the bottom of that right edge, but we
165  // have to start with an additional vertex that
166  // connects to (right - hsw) instead of the left
167  // side of the rectangle to avoid a big triangle
168  // through the hollow interior section.
169  *vertices++ = Point(right - hsw, top + hsw);
170  *vertices++ = Point(right + hsw, bottom - hsw);
171  *vertices++ = Point(right - hsw, bottom - hsw);
172 
173  // Now we need to jump up for the left edge, but we
174  // need to dupliate the last point and the next point
175  // to avoid drawing anything connecting them. These
176  // 2 vertices end up generating 2 empty triangles.
177  *vertices++ = Point(right - hsw, bottom - hsw);
178  *vertices++ = Point(left + hsw, top + hsw);
179 
180  // Slice 3b: Now draw the left edge.
181  // We draw this in a specific zig zag order so that
182  // we end up at (left - hsw, bottom - hsw) to connect
183  // properly to the next section.
184  *vertices++ = Point(left + hsw, top + hsw);
185  *vertices++ = Point(left - hsw, top + hsw);
186  *vertices++ = Point(left + hsw, bottom - hsw);
187  *vertices++ = Point(left - hsw, bottom - hsw);
188 
189  // Slice 4: Draw the inner part of the bottom stroke.
190  // Since the next section starts by drawing across
191  // the width of the rect at Y=bottom, we simple have
192  // to make sure that we presently have a pair of
193  // vertices that span the top of that section. The
194  // last point was (left - hsw, bottom - hsw), so we
195  // just have to add its right side partner which
196  // is not the same as the vertex before that. This
197  // extra vertex ends up defining an empty triangle,
198  // but sets us up for the final slice to complete
199  // this interior part of the bottom stroke.
200  *vertices++ = Point(right + hsw, bottom - hsw);
201  // Now the first pair of vertices below will
202  // "complete the zig-zag box" for the inner part
203  // of the bottom stroke.
204  }
205 
206  // Slice 5: Draw between bottom pair of round joins.
207  for (auto trig : trigs) {
208  // trig.sin goes from 0 to 1
209  // trig.cos goes from 1 to 0
210  *vertices++ = Point(left - trig.cos * hsw,
211  bottom + trig.sin * hsw);
212  *vertices++ = Point(right + trig.cos * hsw,
213  bottom + trig.sin * hsw);
214  }
215 
216  // Make sure our estimate is always up to date.
217  FML_DCHECK(vertices == vertices_end);
218  }),
219  .vertex_count = vertex_count,
220  .index_type = IndexType::kNone,
221  },
222  .transform = entity.GetShaderTransform(pass),
224  };
225  }
226 
227  case Join::kBevel: {
228  if (interior_filled) {
229  return GeometryResult{
231  .vertex_buffer =
232  {
233  .vertex_buffer = data_host_buffer.Emplace(
234  8 * sizeof(Point), alignof(Point),
235  [hsw = half_stroke_width, &rect](uint8_t* buffer) {
236  Scalar left = rect.GetLeft();
237  Scalar top = rect.GetTop();
238  Scalar right = rect.GetRight();
239  Scalar bottom = rect.GetBottom();
240  auto vertices = reinterpret_cast<Point*>(buffer);
241  vertices[0] = Point(left, top - hsw);
242  vertices[1] = Point(right, top - hsw);
243  vertices[2] = Point(left - hsw, top);
244  vertices[3] = Point(right + hsw, top);
245  vertices[4] = Point(left - hsw, bottom);
246  vertices[5] = Point(right + hsw, bottom);
247  vertices[6] = Point(left, bottom + hsw);
248  vertices[7] = Point(right, bottom + hsw);
249  }),
250  .vertex_count = 8u,
251  .index_type = IndexType::kNone,
252  },
253  .transform = entity.GetShaderTransform(pass),
255  };
256  }
257  return GeometryResult{
259  .vertex_buffer =
260  {
261  .vertex_buffer = data_host_buffer.Emplace(
262  17 * sizeof(Point), alignof(Point),
263  [hsw = half_stroke_width, &rect](uint8_t* buffer) {
264  Scalar left = rect.GetLeft();
265  Scalar top = rect.GetTop();
266  Scalar right = rect.GetRight();
267  Scalar bottom = rect.GetBottom();
268  auto vertices = reinterpret_cast<Point*>(buffer);
269  vertices[0] = Point(left - hsw, top);
270  vertices[1] = Point(left + hsw, top + hsw);
271  vertices[2] = Point(left, top - hsw);
272  vertices[3] = Point(right - hsw, top + hsw);
273  vertices[4] = Point(right, top - hsw);
274  vertices[5] = Point(right - hsw, top + hsw);
275  vertices[6] = Point(right + hsw, top);
276  vertices[7] = Point(right - hsw, bottom - hsw);
277  vertices[8] = Point(right + hsw, bottom);
278  vertices[9] = Point(right - hsw, bottom - hsw);
279  vertices[10] = Point(right, bottom + hsw);
280  vertices[11] = Point(left + hsw, bottom - hsw);
281  vertices[12] = Point(left, bottom + hsw);
282  vertices[13] = Point(left + hsw, bottom - hsw);
283  vertices[14] = Point(left - hsw, bottom);
284  vertices[15] = Point(left + hsw, top + hsw);
285  vertices[16] = Point(left - hsw, top);
286  }),
287  .vertex_count = 17u,
288  .index_type = IndexType::kNone,
289  },
290  .transform = entity.GetShaderTransform(pass),
292  };
293  }
294 
295  case Join::kMiter: {
296  if (interior_filled) {
297  return GeometryResult{
299  .vertex_buffer =
300  {
301  .vertex_buffer = data_host_buffer.Emplace(
302  4 * sizeof(Point), alignof(Point),
303  [hsw = half_stroke_width, &rect](uint8_t* buffer) {
304  Scalar left = rect.GetLeft();
305  Scalar top = rect.GetTop();
306  Scalar right = rect.GetRight();
307  Scalar bottom = rect.GetBottom();
308  auto vertices = reinterpret_cast<Point*>(buffer);
309 
310  vertices[0] = Point(left - hsw, top - hsw);
311  vertices[1] = Point(right + hsw, top - hsw);
312  vertices[2] = Point(left - hsw, bottom + hsw);
313  vertices[3] = Point(right + hsw, bottom + hsw);
314  }),
315  .vertex_count = 4u,
316  .index_type = IndexType::kNone,
317  },
318  .transform = entity.GetShaderTransform(pass),
320  };
321  }
322  return GeometryResult{
324  .vertex_buffer =
325  {
326  .vertex_buffer = data_host_buffer.Emplace(
327  10 * sizeof(Point), alignof(Point),
328  [hsw = half_stroke_width, &rect](uint8_t* buffer) {
329  Scalar left = rect.GetLeft();
330  Scalar top = rect.GetTop();
331  Scalar right = rect.GetRight();
332  Scalar bottom = rect.GetBottom();
333  auto vertices = reinterpret_cast<Point*>(buffer);
334  vertices[0] = Point(left - hsw, top - hsw);
335  vertices[1] = Point(left + hsw, top + hsw);
336  vertices[2] = Point(right + hsw, top - hsw);
337  vertices[3] = Point(right - hsw, top + hsw);
338  vertices[4] = Point(right + hsw, bottom + hsw);
339  vertices[5] = Point(right - hsw, bottom - hsw);
340  vertices[6] = Point(left - hsw, bottom + hsw);
341  vertices[7] = Point(left + hsw, bottom - hsw);
342  vertices[8] = Point(left - hsw, top - hsw);
343  vertices[9] = Point(left + hsw, top + hsw);
344  }),
345  .vertex_count = 10u,
346  .index_type = IndexType::kNone,
347  },
348  .transform = entity.GetShaderTransform(pass),
350  };
351  }
352  }
353 }
354 
356  const Matrix& transform) const {
357  return rect_.TransformBounds(transform);
358 }
359 
360 Join StrokeRectGeometry::AdjustStrokeJoin(const StrokeParameters& stroke) {
361  return (stroke.join == Join::kMiter && stroke.miter_limit < kSqrt2)
362  ? Join::kBevel
363  : stroke.join;
364 }
365 
366 } // namespace impeller
HostBuffer & GetTransientsDataBuffer() const
Retrieve the current host buffer for transient storage of other non-index data.
Tessellator & GetTessellator() const
Matrix GetShaderTransform(const RenderPass &pass) const
Definition: entity.cc:50
const Matrix & GetTransform() const
Get the global transform matrix for this Entity.
Definition: entity.cc:46
Scalar GetAntialiasPadding() const
void SetAntialiasPadding(Scalar padding)
std::optional< Rect > GetCoverage(const Matrix &transform) const override
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...
const Rect & GetRect() const
bool IsAxisAlignedRect() const override
GeometryResult GetPositionBuffer(const ContentContext &renderer, const Entity &entity, RenderPass &pass) const override
Render passes encode render commands directed as one specific render target into an underlying comman...
Definition: render_pass.h:30
std::optional< Rect > GetCoverage(const Matrix &transform) const override
StrokeRectGeometry(const Rect &rect, const StrokeParameters &stroke)
GeometryResult GetPositionBuffer(const ContentContext &renderer, const Entity &entity, RenderPass &pass) const override
Trigs GetTrigsForDeviceRadius(Scalar pixel_radius)
Definition: tessellator.cc:425
Vector2 padding
The halo padding in source space.
Join
An enum that describes ways to join two segments of a path.
float Scalar
Definition: scalar.h:19
@ kNone
Does not use the index buffer.
TPoint< Scalar > Point
Definition: point.h:426
constexpr float kSqrt2
Definition: constants.h:47
static constexpr Scalar kMinStrokeSize
Definition: geometry.h:19
PrimitiveType type
Definition: geometry.h:37
@ kNormal
The geometry has no overlapping triangles.
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
A structure to store all of the parameters related to stroking a path or basic geometry object.
constexpr TRect< T > Expand(T left, T top, T right, T bottom) const
Returns a rectangle with expanded edges. Negative expansion results in shrinking.
Definition: rect.h:618
constexpr auto GetBottom() const
Definition: rect.h:357
constexpr TRect TransformBounds(const Matrix &transform) const
Creates a new bounding box that contains this transformed rectangle.
Definition: rect.h:472
constexpr auto GetTop() const
Definition: rect.h:353
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:231
constexpr std::array< TPoint< T >, 4 > GetPoints() const
Get the points that represent the 4 corners of this rectangle in a Z order that is compatible with tr...
Definition: rect.h:414
constexpr auto GetLeft() const
Definition: rect.h:351
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 auto GetRight() const
Definition: rect.h:355
constexpr TRect TransformAndClipBounds(const Matrix &transform) const
Creates a new bounding box that contains this transformed rectangle, clipped against the near clippin...
Definition: rect.h:438