| 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
5 #include "impeller/entity/geometry/rect_geometry.h"
6
7 namespace impeller {
8
9 FillRectGeometry::FillRectGeometry(Rect rect) : rect_(rect) {}
10
11 FillRectGeometry::~FillRectGeometry() = default;
12
13 GeometryResult FillRectGeometry::GetPositionBuffer(
14 const ContentContext& renderer,
15 const Entity& entity,
16 RenderPass& pass) const {
17 auto& data_host_buffer = renderer.GetTransientsDataBuffer();
18 return GeometryResult{
19 .type = PrimitiveType::kTriangleStrip,
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),
28 .mode = GeometryResult::Mode::kNormal,
29 };
30 }
31
32 std::optional<Rect> FillRectGeometry::GetCoverage(
33 const Matrix& transform) const {
34 return rect_.TransformBounds(transform);
35 }
36
37 bool FillRectGeometry::CoversArea(const Matrix& transform,
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
46 bool FillRectGeometry::IsAxisAlignedRect() const {
47 return true;
48 }
49
50 StrokeRectGeometry::StrokeRectGeometry(const Rect& rect,
51 const StrokeParameters& stroke)
52 : rect_(rect),
53 stroke_width_(stroke.width),
54 stroke_join_(AdjustStrokeJoin(stroke)) {}
55
56 StrokeRectGeometry::~StrokeRectGeometry() = default;
57
58 GeometryResult StrokeRectGeometry::GetPositionBuffer(
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{
95 .type = PrimitiveType::kTriangleStrip,
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),
205 .mode = GeometryResult::Mode::kNormal,
206 };
207 }
208
209 case Join::kBevel: {
210 if (interior_filled) {
211 return GeometryResult{
212 .type = PrimitiveType::kTriangleStrip,
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),
236 .mode = GeometryResult::Mode::kNormal,
237 };
238 }
239 return GeometryResult{
240 .type = PrimitiveType::kTriangleStrip,
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),
273 .mode = GeometryResult::Mode::kNormal,
274 };
275 }
276
277 case Join::kMiter: {
278 if (interior_filled) {
279 return GeometryResult{
280 .type = PrimitiveType::kTriangleStrip,
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),
301 .mode = GeometryResult::Mode::kNormal,
302 };
303 }
304 return GeometryResult{
305 .type = PrimitiveType::kTriangleStrip,
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),
331 .mode = GeometryResult::Mode::kNormal,
332 };
333 }
334 }
335 }
336
337 std::optional<Rect> StrokeRectGeometry::GetCoverage(
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
impeller::ContentContext
Definition: content_context.h:138
impeller::ContentContext::GetTransientsDataBuffer
HostBuffer & GetTransientsDataBuffer() const
Retrieve the current host buffer for transient storage of other non-index data.
Definition: content_context.h:311
impeller::ContentContext::GetTessellator
Tessellator & GetTessellator() const
Definition: content_context.cc:949
impeller::Entity
Definition: entity.h:26
impeller::Entity::GetShaderTransform
Matrix GetShaderTransform(const RenderPass &pass) const
Definition: entity.cc:48
impeller::Entity::GetTransform
const Matrix & GetTransform() const
Get the global transform matrix for this Entity.
Definition: entity.cc:44
impeller::FillRectGeometry::FillRectGeometry
FillRectGeometry(Rect rect)
Definition: rect_geometry.cc:9
impeller::FillRectGeometry::GetCoverage
std::optional< Rect > GetCoverage(const Matrix &transform) const override
Definition: rect_geometry.cc:32
impeller::FillRectGeometry::CoversArea
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...
Definition: rect_geometry.cc:37
impeller::FillRectGeometry::IsAxisAlignedRect
bool IsAxisAlignedRect() const override
Definition: rect_geometry.cc:46
impeller::FillRectGeometry::GetPositionBuffer
GeometryResult GetPositionBuffer(const ContentContext &renderer, const Entity &entity, RenderPass &pass) const override
Definition: rect_geometry.cc:13
impeller::FillRectGeometry::~FillRectGeometry
~FillRectGeometry() override
impeller::RenderPass
Render passes encode render commands directed as one specific render target into an underlying comman...
Definition: render_pass.h:30
impeller::StrokeRectGeometry::GetCoverage
std::optional< Rect > GetCoverage(const Matrix &transform) const override
Definition: rect_geometry.cc:337
impeller::StrokeRectGeometry::StrokeRectGeometry
StrokeRectGeometry(const Rect &rect, const StrokeParameters &stroke)
Definition: rect_geometry.cc:50
impeller::StrokeRectGeometry::~StrokeRectGeometry
~StrokeRectGeometry() override
impeller::StrokeRectGeometry::GetPositionBuffer
GeometryResult GetPositionBuffer(const ContentContext &renderer, const Entity &entity, RenderPass &pass) const override
Definition: rect_geometry.cc:58
impeller::Tessellator::Trigs
Definition: tessellator.h:48
impeller::Tessellator::Trigs::size
size_t size() const
Definition: tessellator.h:53
impeller::Tessellator::GetTrigsForDeviceRadius
Trigs GetTrigsForDeviceRadius(Scalar pixel_radius)
Definition: tessellator.cc:310
transform
Matrix transform
Definition: gaussian_blur_filter_contents.cc:214
impeller
Definition: allocation.cc:12
impeller::Join
Join
An enum that describes ways to join two segments of a path.
Definition: stroke_parameters.h:20
impeller::Join::kRound
@ kRound
impeller::Join::kMiter
@ kMiter
impeller::Join::kBevel
@ kBevel
impeller::PrimitiveType::kTriangleStrip
@ kTriangleStrip
impeller::Scalar
float Scalar
Definition: scalar.h:19
impeller::IndexType::kNone
@ kNone
Does not use the index buffer.
impeller::Point
TPoint< Scalar > Point
Definition: point.h:327
impeller::kSqrt2
constexpr float kSqrt2
Definition: constants.h:47
impeller::kMinStrokeSize
static constexpr Scalar kMinStrokeSize
Definition: geometry.h:19
rect_geometry.h
impeller::GeometryResult
Definition: geometry.h:21
impeller::GeometryResult::type
PrimitiveType type
Definition: geometry.h:37
impeller::GeometryResult::Mode::kNormal
@ kNormal
The geometry has no overlapping triangles.
impeller::Matrix
A 4x4 matrix using column-major storage.
Definition: matrix.h:37
impeller::Matrix::GetMaxBasisLengthXY
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
impeller::StrokeParameters
A structure to store all of the parameters related to stroking a path or basic geometry object.
Definition: stroke_parameters.h:28
impeller::StrokeParameters::join
Join join
Definition: stroke_parameters.h:31
impeller::StrokeParameters::miter_limit
Scalar miter_limit
Definition: stroke_parameters.h:32
impeller::TPoint< Scalar >
impeller::TRect< Scalar >
impeller::TRect::GetBottom
constexpr auto GetBottom() const
Definition: rect.h:357
impeller::TRect::TransformBounds
constexpr TRect TransformBounds(const Matrix &transform) const
Creates a new bounding box that contains this transformed rectangle.
Definition: rect.h:472
impeller::TRect::GetTop
constexpr auto GetTop() const
Definition: rect.h:353
impeller::TRect::Contains
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
impeller::TRect::GetPoints
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
impeller::TRect::GetLeft
constexpr auto GetLeft() const
Definition: rect.h:351
impeller::TRect::GetSize
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
impeller::TRect::GetRight
constexpr auto GetRight() const
Definition: rect.h:355