Flutter Engine
 
Loading...
Searching...
No Matches
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
7namespace 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
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
342Join StrokeRectGeometry::AdjustStrokeJoin(const StrokeParameters& stroke) {
343 return (stroke.join == Join::kMiter && stroke.miter_limit < kSqrt2)
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)
#define FML_DCHECK(condition)
Definition logging.h:122
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
int32_t width
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 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 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 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 auto GetLeft() const
Definition rect.h:351
constexpr auto GetRight() const
Definition rect.h:355