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 37 of file rect_geometry.h.

Constructor & Destructor Documentation

◆ StrokeRectGeometry()

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

Definition at line 50 of file rect_geometry.cc.

52  : rect_(rect),
53  stroke_width_(stroke.width),
54  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 337 of file rect_geometry.cc.

338  {
339  return rect_.TransformBounds(transform);
340 }
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 58 of file rect_geometry.cc.

61  {
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 }
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:327
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: