24 explicit PositionWriter(std::vector<Point>&
points)
25 : points_(
points), oversized_() {
29 void AppendVertex(
const Point& point) {
31 oversized_.push_back(point);
33 points_[offset_++] = point;
39 std::pair<size_t, size_t> GetUsedSize()
const {
40 return std::make_pair(offset_, oversized_.size());
43 bool HasOversizedBuffer()
const {
return !oversized_.empty(); }
45 const std::vector<Point>& GetOversizedBuffer()
const {
return oversized_; }
48 std::vector<Point>& points_;
49 std::vector<Point> oversized_;
134 PositionWriter& vtx_builder,
137 : tessellator_(tessellator),
138 vtx_builder_(vtx_builder),
139 half_stroke_width_(stroke.
width * 0.5f),
140 maximum_join_cosine_(
141 ComputeMaximumJoinCosine(scale, half_stroke_width_)),
142 minimum_miter_cosine_(ComputeMinimumMiterCosine(stroke.miter_limit)),
146 trigs_(MakeTrigs(tessellator, scale, half_stroke_width_)) {
158 if (has_prior_contour_ && origin != last_point_) {
160 vtx_builder_.AppendVertex(last_point_);
161 vtx_builder_.AppendVertex(last_point_);
162 vtx_builder_.AppendVertex(origin);
163 vtx_builder_.AppendVertex(origin);
165 has_prior_contour_ =
true;
166 has_prior_segment_ =
false;
167 contour_needs_cap_ = !will_be_closed;
168 last_point_ = origin;
169 origin_point_ = origin;
177 HandlePreviousJoin(current_perpendicular);
178 AppendVertices(p2, current_perpendicular);
180 last_perpendicular_ = current_perpendicular;
187 RecordCurve<PathTessellator::Quad>({p1, cp, p2});
192 RecordCurve<PathTessellator::Conic>({p1, cp, p2, weight});
197 RecordCurve<PathTessellator::Cubic>({p1, cp1, cp2, p2});
201 template <
typename Curve>
203 std::optional<Point> start_direction = curve.GetStartDirection();
204 std::optional<Point> end_direction = curve.GetEndDirection();
208 FML_DCHECK(start_direction.has_value() && end_direction.has_value());
211 if (start_direction.has_value() && end_direction.has_value()) {
214 PerpendicularFromUnitDirection(-start_direction.value());
216 PerpendicularFromUnitDirection(end_direction.value());
221 HandlePreviousJoin(start_perpendicular);
224 std::ceilf(curve.SubdivisionCount(scale_ * half_stroke_width_));
226 Point prev = curve.p1;
230 for (
int i = 1;
i < count;
i++) {
231 Point cur = curve.Solve(
i / count);
235 prev_perpendicular = cur_perpendicular;
240 last_perpendicular_ = end_perpendicular;
241 last_point_ = curve.p2;
248 if (prev_perpendicular.
GetAlignment(cur_perpendicular) < trigs_[1].cos) {
251 AppendVertices(cur, prev_perpendicular);
252 AddJoin(
Join::kRound, cur, prev_perpendicular, cur_perpendicular);
254 AppendVertices(cur, cur_perpendicular);
260 if (!has_prior_segment_) {
265 Vector2 perpendicular = {-half_stroke_width_, 0};
266 AddCap(cap, origin, perpendicular,
true);
269 AppendVertices(origin, perpendicular);
271 AddCap(cap, origin, perpendicular,
false);
272 }
else if (with_close) {
276 AddJoin(join_, origin, last_perpendicular_, origin_perpendicular_);
278 last_perpendicular_ = origin_perpendicular_;
279 last_point_ = origin;
281 AddCap(cap_, last_point_, last_perpendicular_.
GetVector(),
false);
283 has_prior_segment_ =
false;
289 const Size radii)
override {
295 PerpendicularFromUnitDirection({-iterator.
start.
y, iterator.
start.
x});
296 HandlePreviousJoin(prev_perpendicular);
302 Point cur = center + direction * radii;
304 PerpendicularFromUnitDirection({-direction.
y, direction.
x});
306 prev_perpendicular = cur_perpendicular;
311 PerpendicularFromUnitDirection({-iterator.
end.
y, iterator.
end.
x});
315 last_perpendicular_ = end_perpendicular;
321 PositionWriter& vtx_builder_;
322 const Scalar half_stroke_width_;
323 const Scalar maximum_join_cosine_;
324 const Scalar minimum_miter_cosine_;
334 bool has_prior_contour_ =
false;
335 bool has_prior_segment_ =
false;
336 bool contour_needs_cap_ =
false;
340 Scalar half_stroke_width) {
345 static constexpr Scalar kJoinPixelThreshold = 0.25f;
356 Scalar half_stroke_width) {
378 Scalar hypotenuse = scale * half_stroke_width;
379 if (hypotenuse <= kJoinPixelThreshold) {
384 Scalar bisector = std::sqrt(hypotenuse * hypotenuse -
385 kJoinPixelThreshold * kJoinPixelThreshold);
386 Scalar half_cosine = bisector / hypotenuse;
387 Scalar cosine = 2.0f * half_cosine * half_cosine - 1;
403 static Scalar ComputeMinimumMiterCosine(
Scalar miter_limit) {
404 if (miter_limit <= 1.0f) {
429 Scalar half_cosine = 1 / miter_limit;
430 Scalar cosine = 2.0f * half_cosine * half_cosine - 1;
434 inline SeparatedVector2 PerpendicularFromPoints(
const Point from,
435 const Point to)
const {
436 return PerpendicularFromUnitDirection((to - from).Normalize());
439 inline SeparatedVector2 PerpendicularFromUnitDirection(
440 const Vector2 direction)
const {
441 return SeparatedVector2(
Vector2{-direction.
y, direction.x},
445 inline void AppendVertices(
const Point curve_point,
Vector2 offset) {
446 vtx_builder_.AppendVertex(curve_point + offset);
447 vtx_builder_.AppendVertex(curve_point - offset);
450 inline void AppendVertices(
const Point curve_point,
451 SeparatedVector2 perpendicular) {
452 return AppendVertices(curve_point, perpendicular.GetVector());
455 inline void HandlePreviousJoin(SeparatedVector2 new_perpendicular) {
457 if (has_prior_segment_) {
458 AddJoin(join_, last_point_, last_perpendicular_, new_perpendicular);
460 has_prior_segment_ =
true;
461 Vector2 perpendicular_vector = new_perpendicular.GetVector();
462 if (contour_needs_cap_) {
463 AddCap(cap_, last_point_, perpendicular_vector,
true);
467 AppendVertices(last_point_, perpendicular_vector);
468 origin_perpendicular_ = new_perpendicular;
489 bool contour_start) {
494 Point along(perpendicular.y, -perpendicular.x);
497 vtx_builder_.AppendVertex(path_point - along);
502 for (
size_t i = trigs_.
size() - 2u;
i > 0u; --
i) {
504 Vector2 offset = perpendicular * trigs_[
i].cos;
506 AppendVertices(center, offset);
512 size_t end = trigs_.
size() - 1u;
513 for (
size_t i = 1u;
i <
end; ++
i) {
515 Vector2 offset = perpendicular * trigs_[
i].cos;
517 AppendVertices(center, offset);
521 vtx_builder_.AppendVertex(path_point + along);
526 Point along(perpendicular.y, -perpendicular.x);
527 Point square_center = contour_start
529 : path_point + along;
530 AppendVertices(square_center, perpendicular);
536 void AddJoin(
Join join,
538 SeparatedVector2 old_perpendicular,
539 SeparatedVector2 new_perpendicular) {
540 Scalar cosine = old_perpendicular.GetAlignment(new_perpendicular);
541 if (cosine >= maximum_join_cosine_) {
558 if (cosine >= minimum_miter_cosine_) {
560 (old_perpendicular.GetVector() + new_perpendicular.GetVector()) /
562 if (old_perpendicular.Cross(new_perpendicular) < 0) {
563 vtx_builder_.AppendVertex(path_point + miter_vector);
565 vtx_builder_.AppendVertex(path_point - miter_vector);
573 if (cosine >= trigs_[1].cos) {
579 if (cosine < -trigs_[1].cos) {
587 AddCap(
Cap::kRound, path_point, old_perpendicular.GetVector(),
false);
599 Vector2 from_vector, to_vector;
600 bool begin_end_crossed;
601 Scalar turning = old_perpendicular.
Cross(new_perpendicular);
607 from_vector = -old_perpendicular.GetVector();
608 to_vector = -new_perpendicular.GetVector();
612 begin_end_crossed =
false;
618 from_vector = new_perpendicular.GetVector();
619 to_vector = old_perpendicular.GetVector();
623 begin_end_crossed =
true;
627 if (begin_end_crossed) {
628 vtx_builder_.AppendVertex(path_point + from_vector);
634 bool visit_center =
false;
640 Point middle_vector = (from_vector + to_vector);
649 size_t end = trigs_.
size() - 1u;
650 for (
size_t i = 1u;
i <
end; ++
i) {
651 Point p = trigs_[
i] * from_vector;
652 if (p.Cross(middle_vector) <= 0) {
661 vtx_builder_.AppendVertex(path_point);
662 visit_center =
false;
666 vtx_builder_.AppendVertex(path_point + p);
676 vtx_builder_.AppendVertex(path_point);
677 visit_center =
false;
681 vtx_builder_.AppendVertex(path_point + p);
684 if (begin_end_crossed) {
685 vtx_builder_.AppendVertex(path_point + to_vector);
694 AppendVertices(path_point, new_perpendicular);
699std::vector<Point> StrokeSegmentsGeometry::GenerateSolidStrokeVertices(
700 Tessellator& tessellator,
701 const PathSource& source,
702 const StrokeParameters& stroke,
704 std::vector<Point>
points(4096);
705 PositionWriter vtx_builder(
points);
706 StrokePathSegmentReceiver receiver(tessellator, vtx_builder, stroke, scale);
708 auto [arena, extra] = vtx_builder.GetUsedSize();
720 return stroke_.
width;
744 if (stroke_.
width < 0.0) {
748 if (max_basis == 0) {
753 StrokeParameters adjusted_stroke = stroke_;
754 adjusted_stroke.
width = std::max(stroke_.
width, min_size);
760 PositionWriter position_writer(tessellator.GetStrokePointCache());
761 StrokePathSegmentReceiver receiver(tessellator, position_writer,
762 adjusted_stroke, scale);
763 Dispatch(receiver, tessellator, scale);
765 const auto [arena_length, oversized_length] = position_writer.GetUsedSize();
766 if (!position_writer.HasOversizedBuffer()) {
768 data_host_buffer.Emplace(tessellator.GetStrokePointCache().data(),
769 arena_length *
sizeof(
Point),
alignof(
Point));
775 .vertex_count = arena_length,
781 const std::vector<Point>& oversized_data =
782 position_writer.GetOversizedBuffer();
785 (arena_length + oversized_length) *
sizeof(
Point),
790 tessellator.GetStrokePointCache().data(),
791 arena_length *
sizeof(
Point)
795 oversized_data.data(),
796 oversized_data.size() *
sizeof(
Point)
804 .vertex_count = arena_length + oversized_length,
817 const Rect& path_bounds)
const {
824 max_radius = max_radius *
kSqrt2;
827 max_radius = std::max(max_radius, stroke_.
miter_limit * 0.5f);
830 if (max_basis == 0) {
835 max_radius *= std::max(stroke_.
width, min_size);
886 if (include_center) {
911 source_(p0, p1, on_length, off_length) {}
void Dispatch(PathAndArcSegmentReceiver &receiver, Tessellator &tessellator, Scalar scale) const override
ArcStrokeGeometry(const Arc &arc, const StrokeParameters ¶meters)
std::optional< Rect > GetCoverage(const Matrix &transform) const override
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
const Matrix & GetTransform() const
Get the global transform matrix for this Entity.
static Scalar ComputeStrokeAlphaCoverage(const Matrix &entity, Scalar stroke_width)
Compute an alpha value to simulate lower coverage of fractional pixel strokes.
A |SegmentReceiver| that also accepts Arc segments for optimal handling. A path or |PathSource| will ...
virtual void RecordArc(const Arc &arc, const Point center, const Size radii)=0
virtual void RecordLine(Point p1, Point p2)=0
virtual void EndContour(Point origin, bool with_close)=0
virtual void BeginContour(Point origin, bool will_be_closed)=0
static void PathToStrokedSegments(const PathSource &source, SegmentReceiver &receiver)
Render passes encode render commands directed as one specific render target into an underlying comman...
const PathSource & GetSource() const override
StrokeDashedLineGeometry(Point p0, Point p1, Scalar on_length, Scalar off_length, const StrokeParameters ¶meters)
StrokeDiffRoundRectGeometry(const RoundRect &outer, const RoundRect &inner, const StrokeParameters ¶meters)
const PathSource & GetSource() const override
StrokePathGeometry(const flutter::DlPath &path, const StrokeParameters ¶meters)
const PathSource & GetSource() const override
void RecordCurve(const Curve &curve)
void BeginContour(Point origin, bool will_be_closed) override
void RecordArc(const Arc &arc, const Point center, const Size radii) override
void EndContour(Point origin, bool with_close) override
void RecordQuad(Point p1, Point cp, Point p2) override
StrokePathSegmentReceiver(Tessellator &tessellator, PositionWriter &vtx_builder, const StrokeParameters &stroke, const Scalar scale)
void RecordConic(Point p1, Point cp, Point p2, Scalar weight) override
void RecordCubic(Point p1, Point cp1, Point cp2, Point p2) override
void RecordCurveSegment(const SeparatedVector2 &prev_perpendicular, const Point cur, const SeparatedVector2 &cur_perpendicular)
void RecordLine(Point p1, Point p2) override
An abstract Geometry base class that produces fillable vertices representing the stroked outline from...
virtual const PathSource & GetSource() const =0
std::optional< Rect > GetCoverage(const Matrix &transform) const override
StrokePathSourceGeometry(const StrokeParameters ¶meters)
void Dispatch(PathAndArcSegmentReceiver &receiver, Tessellator &tessellator, Scalar scale) const override
An abstract Geometry base class that produces fillable vertices representing the stroked outline of t...
virtual void Dispatch(PathAndArcSegmentReceiver &receiver, Tessellator &tessellator, Scalar scale) const =0
Join GetStrokeJoin() const
Scalar GetStrokeWidth() const
Scalar GetMiterLimit() const
Scalar ComputeAlphaCoverage(const Matrix &transform) const override
std::optional< Rect > GetStrokeCoverage(const Matrix &transform, const Rect &segment_bounds) const
StrokeSegmentsGeometry(const StrokeParameters ¶meters)
~StrokeSegmentsGeometry() override
std::vector< Trig >::iterator end() const
A utility that generates triangles of the specified fill type given a polyline. This happens on the C...
Trigs GetTrigsForDeviceRadius(Scalar pixel_radius)
#define FML_DCHECK(condition)
Join
An enum that describes ways to join two segments of a path.
@ kNone
Does not use the index buffer.
Cap
An enum that describes ways to decorate the end of a path contour.
static constexpr size_t kPointArenaSize
The size of the point arena buffer stored on the tessellator.
static constexpr Scalar kMinStrokeSize
Iteration ComputeIterations(size_t step_count, bool simplify_360=true) const
Rect GetTightArcBounds() const
constexpr bool IncludeCenter() const
const Size GetOvalSize() const
Returns the size of the oval bounds.
const Point GetOvalCenter() const
Returns the center of the oval bounds.
A 4x4 matrix using column-major storage.
Scalar GetMaxBasisLengthXY() const
Return the maximum scale applied specifically to either the X axis or Y axis unit vectors (the bases)...
A Vector2, broken down as a separate magnitude and direction. Assumes that the direction given is nor...
Scalar GetAlignment(const SeparatedVector2 &other) const
Vector2 GetVector() const
Returns the vector representation of the vector.
A structure to store all of the parameters related to stroking a path or basic geometry object.
constexpr Type Cross(const TPoint &p) const
constexpr TRect TransformBounds(const Matrix &transform) const
Creates a new bounding box that contains this transformed rectangle.
constexpr bool IsEmpty() const
Returns true if either of the width or height are 0, negative, or NaN.
constexpr TRect< T > Expand(T left, T top, T right, T bottom) const
Returns a rectangle with expanded edges. Negative expansion results in shrinking.
constexpr Type MaxDimension() const
std::vector< Point > points