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  const ContentContext& renderer,
15  const Entity& entity,
16  RenderPass& pass) const {
17  auto& data_host_buffer = renderer.GetTransientsDataBuffer();
18  return GeometryResult{
20  .vertex_buffer =
21  {
22  .vertex_buffer = data_host_buffer.Emplace(
23  rect_.GetPoints().data(), 8 * sizeof(float), alignof(float)),
24  .vertex_count = 4,
25  .index_type = IndexType::kNone,
26  },
27  .transform = entity.GetShaderTransform(pass),
29  };
30 }
31 
32 std::optional<Rect> FillRectGeometry::GetCoverage(
33  const Matrix& transform) const {
34  return rect_.TransformBounds(transform);
35 }
36 
38  const Rect& rect) const {
39  if (!transform.IsTranslationScaleOnly()) {
40  return false;
41  }
42  Rect coverage = rect_.TransformBounds(transform);
43  return coverage.Contains(rect);
44 }
45 
47  return true;
48 }
49 
51  const StrokeParameters& stroke)
52  : rect_(rect),
53  stroke_width_(stroke.width),
54  stroke_join_(AdjustStrokeJoin(stroke)) {}
55 
57 
59  const ContentContext& renderer,
60  const Entity& entity,
61  RenderPass& pass) const {
62  if (stroke_width_ < 0.0) {
63  return {};
64  }
65  Scalar max_basis = entity.GetTransform().GetMaxBasisLengthXY();
66  if (max_basis == 0) {
67  return {};
68  }
69 
70  Scalar min_size = kMinStrokeSize / max_basis;
71  Scalar stroke_width = std::max(stroke_width_, min_size);
72  Scalar half_stroke_width = stroke_width * 0.5f;
73 
74  auto& data_host_buffer = renderer.GetTransientsDataBuffer();
75  const Rect& rect = rect_;
76  bool interior_filled = (stroke_width >= rect.GetSize().MinDimension());
77 
78  switch (stroke_join_) {
79  case Join::kRound: {
80  Tessellator::Trigs trigs =
81  renderer.GetTessellator().GetTrigsForDeviceRadius(half_stroke_width *
82  max_basis);
83 
84  FML_DCHECK(trigs.size() >= 2u);
85 
86  auto vertex_count = trigs.size() * 4;
87  if (!interior_filled) {
88  // If there is a hole in the interior (as with most stroked rects
89  // unless the stroke width is really really wide) then we need
90  // to perform some surgery to generate the hollowed-out interior.
91  vertex_count += 12;
92  }
93 
94  return GeometryResult{
96  .vertex_buffer =
97  {
98  .vertex_buffer = data_host_buffer.Emplace(
99  vertex_count * sizeof(Point), alignof(Point),
100  [hsw = half_stroke_width, &rect, vertex_count, &trigs,
101  interior_filled](uint8_t* buffer) {
102  Scalar left = rect.GetLeft();
103  Scalar top = rect.GetTop();
104  Scalar right = rect.GetRight();
105  Scalar bottom = rect.GetBottom();
106 
107  auto vertices = reinterpret_cast<Point*>(buffer);
108  [[maybe_unused]]
109  auto vertices_end = vertices + vertex_count;
110 
111  // Traverse top down, left to right across slices.
112 
113  // Slice 1: Draw across between top pair of round joins.
114  for (auto trig : trigs) {
115  // trig.sin goes from 0 to 1
116  // trig.cos goes from 1 to 0
117  *vertices++ = Point(left - trig.sin * hsw,
118  top - trig.cos * hsw);
119  *vertices++ = Point(right + trig.sin * hsw,
120  top - trig.cos * hsw);
121  }
122  // Ends up with vertices that draw across the bottom
123  // of the top curved section (left - hsw, top) to
124  // (right + hsw, top). This is the starting pair of
125  // vertices for the following square section.
126 
127  if (interior_filled) {
128  // If interior is filled, we can just let the bottom
129  // pair of vertices of the top edge connect to the
130  // top pair of vertices of the bottom edge generated
131  // in slice 5 below. They both go left-right so they
132  // will create a proper zig-zag box to connect the
133  // 2 sections.
134  } else {
135  // Slice 2: Draw the inner part of the top stroke.
136  // Simply extend down from the last horizontal pair
137  // of vertices to (top + hsw).
138  *vertices++ = Point(left - hsw, top + hsw);
139  *vertices++ = Point(right + hsw, top + hsw);
140 
141  // Slice 3: Draw the left and right edges.
142 
143  // Slice 3a: Draw the right edge first.
144  // Since we are already at the right edge from the
145  // previous slice, we just have to add 2 vertices
146  // to get to the bottom of that right edge, but we
147  // have to start with an additional vertex that
148  // connects to (right - hsw) instead of the left
149  // side of the rectangle to avoid a big triangle
150  // through the hollow interior section.
151  *vertices++ = Point(right - hsw, top + hsw);
152  *vertices++ = Point(right + hsw, bottom - hsw);
153  *vertices++ = Point(right - hsw, bottom - hsw);
154 
155  // Now we need to jump up for the left edge, but we
156  // need to dupliate the last point and the next point
157  // to avoid drawing anything connecting them. These
158  // 2 vertices end up generating 2 empty triangles.
159  *vertices++ = Point(right - hsw, bottom - hsw);
160  *vertices++ = Point(left + hsw, top + hsw);
161 
162  // Slice 3b: Now draw the left edge.
163  // We draw this in a specific zig zag order so that
164  // we end up at (left - hsw, bottom - hsw) to connect
165  // properly to the next section.
166  *vertices++ = Point(left + hsw, top + hsw);
167  *vertices++ = Point(left - hsw, top + hsw);
168  *vertices++ = Point(left + hsw, bottom - hsw);
169  *vertices++ = Point(left - hsw, bottom - hsw);
170 
171  // Slice 4: Draw the inner part of the bottom stroke.
172  // Since the next section starts by drawing across
173  // the width of the rect at Y=bottom, we simple have
174  // to make sure that we presently have a pair of
175  // vertices that span the top of that section. The
176  // last point was (left - hsw, bottom - hsw), so we
177  // just have to add its right side partner which
178  // is not the same as the vertex before that. This
179  // extra vertex ends up defining an empty triangle,
180  // but sets us up for the final slice to complete
181  // this interior part of the bottom stroke.
182  *vertices++ = Point(right + hsw, bottom - hsw);
183  // Now the first pair of vertices below will
184  // "complete the zig-zag box" for the inner part
185  // of the bottom stroke.
186  }
187 
188  // Slice 5: Draw between bottom pair of round joins.
189  for (auto trig : trigs) {
190  // trig.sin goes from 0 to 1
191  // trig.cos goes from 1 to 0
192  *vertices++ = Point(left - trig.cos * hsw,
193  bottom + trig.sin * hsw);
194  *vertices++ = Point(right + trig.cos * hsw,
195  bottom + trig.sin * hsw);
196  }
197 
198  // Make sure our estimate is always up to date.
199  FML_DCHECK(vertices == vertices_end);
200  }),
201  .vertex_count = vertex_count,
202  .index_type = IndexType::kNone,
203  },
204  .transform = entity.GetShaderTransform(pass),
206  };
207  }
208 
209  case Join::kBevel: {
210  if (interior_filled) {
211  return GeometryResult{
213  .vertex_buffer =
214  {
215  .vertex_buffer = data_host_buffer.Emplace(
216  8 * sizeof(Point), alignof(Point),
217  [hsw = half_stroke_width, &rect](uint8_t* buffer) {
218  Scalar left = rect.GetLeft();
219  Scalar top = rect.GetTop();
220  Scalar right = rect.GetRight();
221  Scalar bottom = rect.GetBottom();
222  auto vertices = reinterpret_cast<Point*>(buffer);
223  vertices[0] = Point(left, top - hsw);
224  vertices[1] = Point(right, top - hsw);
225  vertices[2] = Point(left - hsw, top);
226  vertices[3] = Point(right + hsw, top);
227  vertices[4] = Point(left - hsw, bottom);
228  vertices[5] = Point(right + hsw, bottom);
229  vertices[6] = Point(left, bottom + hsw);
230  vertices[7] = Point(right, bottom + hsw);
231  }),
232  .vertex_count = 8u,
233  .index_type = IndexType::kNone,
234  },
235  .transform = entity.GetShaderTransform(pass),
237  };
238  }
239  return GeometryResult{
241  .vertex_buffer =
242  {
243  .vertex_buffer = data_host_buffer.Emplace(
244  17 * sizeof(Point), alignof(Point),
245  [hsw = half_stroke_width, &rect](uint8_t* buffer) {
246  Scalar left = rect.GetLeft();
247  Scalar top = rect.GetTop();
248  Scalar right = rect.GetRight();
249  Scalar bottom = rect.GetBottom();
250  auto vertices = reinterpret_cast<Point*>(buffer);
251  vertices[0] = Point(left - hsw, top);
252  vertices[1] = Point(left + hsw, top + hsw);
253  vertices[2] = Point(left, top - hsw);
254  vertices[3] = Point(right - hsw, top + hsw);
255  vertices[4] = Point(right, top - hsw);
256  vertices[5] = Point(right - hsw, top + hsw);
257  vertices[6] = Point(right + hsw, top);
258  vertices[7] = Point(right - hsw, bottom - hsw);
259  vertices[8] = Point(right + hsw, bottom);
260  vertices[9] = Point(right - hsw, bottom - hsw);
261  vertices[10] = Point(right, bottom + hsw);
262  vertices[11] = Point(left + hsw, bottom - hsw);
263  vertices[12] = Point(left, bottom + hsw);
264  vertices[13] = Point(left + hsw, bottom - hsw);
265  vertices[14] = Point(left - hsw, bottom);
266  vertices[15] = Point(left + hsw, top + hsw);
267  vertices[16] = Point(left - hsw, top);
268  }),
269  .vertex_count = 17u,
270  .index_type = IndexType::kNone,
271  },
272  .transform = entity.GetShaderTransform(pass),
274  };
275  }
276 
277  case Join::kMiter: {
278  if (interior_filled) {
279  return GeometryResult{
281  .vertex_buffer =
282  {
283  .vertex_buffer = data_host_buffer.Emplace(
284  4 * sizeof(Point), alignof(Point),
285  [hsw = half_stroke_width, &rect](uint8_t* buffer) {
286  Scalar left = rect.GetLeft();
287  Scalar top = rect.GetTop();
288  Scalar right = rect.GetRight();
289  Scalar bottom = rect.GetBottom();
290  auto vertices = reinterpret_cast<Point*>(buffer);
291 
292  vertices[0] = Point(left - hsw, top - hsw);
293  vertices[1] = Point(right + hsw, top - hsw);
294  vertices[2] = Point(left - hsw, bottom + hsw);
295  vertices[3] = Point(right + hsw, bottom + hsw);
296  }),
297  .vertex_count = 4u,
298  .index_type = IndexType::kNone,
299  },
300  .transform = entity.GetShaderTransform(pass),
302  };
303  }
304  return GeometryResult{
306  .vertex_buffer =
307  {
308  .vertex_buffer = data_host_buffer.Emplace(
309  10 * sizeof(Point), alignof(Point),
310  [hsw = half_stroke_width, &rect](uint8_t* buffer) {
311  Scalar left = rect.GetLeft();
312  Scalar top = rect.GetTop();
313  Scalar right = rect.GetRight();
314  Scalar bottom = rect.GetBottom();
315  auto vertices = reinterpret_cast<Point*>(buffer);
316  vertices[0] = Point(left - hsw, top - hsw);
317  vertices[1] = Point(left + hsw, top + hsw);
318  vertices[2] = Point(right + hsw, top - hsw);
319  vertices[3] = Point(right - hsw, top + hsw);
320  vertices[4] = Point(right + hsw, bottom + hsw);
321  vertices[5] = Point(right - hsw, bottom - hsw);
322  vertices[6] = Point(left - hsw, bottom + hsw);
323  vertices[7] = Point(left + hsw, bottom - hsw);
324  vertices[8] = Point(left - hsw, top - hsw);
325  vertices[9] = Point(left + hsw, top + hsw);
326  }),
327  .vertex_count = 10u,
328  .index_type = IndexType::kNone,
329  },
330  .transform = entity.GetShaderTransform(pass),
332  };
333  }
334  }
335 }
336 
338  const Matrix& transform) const {
339  return rect_.TransformBounds(transform);
340 }
341 
342 Join StrokeRectGeometry::AdjustStrokeJoin(const StrokeParameters& stroke) {
343  return (stroke.join == Join::kMiter && stroke.miter_limit < kSqrt2)
344  ? Join::kBevel
345  : stroke.join;
346 }
347 
348 } // 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:48
const Matrix & GetTransform() const
Get the global transform matrix for this Entity.
Definition: entity.cc:44
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...
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:310
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:327
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 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