Flutter Impeller
impeller::StrokeRectGeometry Class Referencefinal

#include <rect_geometry.h>

Inheritance diagram for impeller::StrokeRectGeometry:
impeller::Geometry

Public Member Functions

 StrokeRectGeometry (const Rect &rect, const StrokeParameters &stroke)
 
 ~StrokeRectGeometry () override
 
GeometryResult GetPositionBuffer (const ContentContext &renderer, const Entity &entity, RenderPass &pass) const override
 
std::optional< RectGetCoverage (const Matrix &transform) const override
 
- Public Member Functions inherited from impeller::Geometry
virtual ~Geometry ()
 
virtual GeometryResult::Mode GetResultMode () const
 
virtual bool CoversArea (const Matrix &transform, const Rect &rect) const
 Determines if this geometry, transformed by the given transform, will completely cover all surface area of the given rect. More...
 
virtual bool IsAxisAlignedRect () const
 
virtual bool CanApplyMaskFilter () const
 
virtual Scalar ComputeAlphaCoverage (const Matrix &transform) const
 

Additional Inherited Members

- Static Public Member Functions inherited from impeller::Geometry
static std::unique_ptr< GeometryMakeFillPath (const flutter::DlPath &path, std::optional< Rect > inner_rect=std::nullopt)
 
static std::unique_ptr< GeometryMakeStrokePath (const flutter::DlPath &path, const StrokeParameters &stroke={})
 
static std::unique_ptr< GeometryMakeCover ()
 
static std::unique_ptr< GeometryMakeRect (const Rect &rect)
 
static std::unique_ptr< GeometryMakeOval (const Rect &rect)
 
static std::unique_ptr< GeometryMakeLine (const Point &p0, const Point &p1, const StrokeParameters &stroke)
 
static std::unique_ptr< GeometryMakeCircle (const Point &center, Scalar radius)
 
static std::unique_ptr< GeometryMakeStrokedCircle (const Point &center, Scalar radius, Scalar stroke_width)
 
static std::unique_ptr< GeometryMakeFilledArc (const Rect &oval_bounds, Degrees start, Degrees sweep, bool include_center)
 
static std::unique_ptr< GeometryMakeStrokedArc (const Rect &oval_bounds, Degrees start, Degrees sweep, const StrokeParameters &stroke)
 
static std::unique_ptr< GeometryMakeRoundRect (const Rect &rect, const Size &radii)
 
static std::unique_ptr< GeometryMakeRoundSuperellipse (const Rect &rect, Scalar corner_radius)
 
static Scalar ComputeStrokeAlphaCoverage (const Matrix &entity, Scalar stroke_width)
 Compute an alpha value to simulate lower coverage of fractional pixel strokes. More...
 
static GeometryResult ComputePositionGeometry (const ContentContext &renderer, const Tessellator::VertexGenerator &generator, const Entity &entity, RenderPass &pass)
 

Detailed Description

Definition at line 44 of file rect_geometry.h.

Constructor & Destructor Documentation

◆ StrokeRectGeometry()

impeller::StrokeRectGeometry::StrokeRectGeometry ( const Rect rect,
const StrokeParameters stroke 
)
explicit

Definition at line 68 of file rect_geometry.cc.

70  : rect_(rect),
71  stroke_width_(stroke.width),
72  stroke_join_(AdjustStrokeJoin(stroke)) {}

◆ ~StrokeRectGeometry()

impeller::StrokeRectGeometry::~StrokeRectGeometry ( )
overridedefault

Member Function Documentation

◆ GetCoverage()

std::optional< Rect > impeller::StrokeRectGeometry::GetCoverage ( const Matrix transform) const
overridevirtual

Implements impeller::Geometry.

Definition at line 355 of file rect_geometry.cc.

356  {
357  return rect_.TransformBounds(transform);
358 }
constexpr TRect TransformBounds(const Matrix &transform) const
Creates a new bounding box that contains this transformed rectangle.
Definition: rect.h:472

References transform, and impeller::TRect< T >::TransformBounds().

◆ GetPositionBuffer()

GeometryResult impeller::StrokeRectGeometry::GetPositionBuffer ( const ContentContext renderer,
const Entity entity,
RenderPass pass 
) const
overridevirtual

Implements impeller::Geometry.

Definition at line 76 of file rect_geometry.cc.

79  {
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 }
float Scalar
Definition: scalar.h:19
@ kNone
Does not use the index buffer.
TRect< Scalar > Rect
Definition: rect.h:788
TPoint< Scalar > Point
Definition: point.h:426
static constexpr Scalar kMinStrokeSize
Definition: geometry.h:19
@ kNormal
The geometry has no overlapping triangles.

References impeller::TRect< T >::GetBottom(), impeller::TRect< T >::GetLeft(), impeller::Matrix::GetMaxBasisLengthXY(), impeller::TRect< T >::GetRight(), impeller::Entity::GetShaderTransform(), impeller::TRect< T >::GetSize(), impeller::ContentContext::GetTessellator(), impeller::TRect< T >::GetTop(), impeller::Entity::GetTransform(), impeller::ContentContext::GetTransientsDataBuffer(), impeller::Tessellator::GetTrigsForDeviceRadius(), impeller::kBevel, impeller::kMinStrokeSize, impeller::kMiter, impeller::kNone, impeller::GeometryResult::kNormal, impeller::kRound, impeller::kTriangleStrip, impeller::Tessellator::Trigs::size(), and impeller::GeometryResult::type.


The documentation for this class was generated from the following files: