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);
234 Scalar stroke_scale = scale_ * std::max(1.0f, half_stroke_width_);
235 Scalar count = std::ceilf(curve.SubdivisionCount(stroke_scale));
237 Point prev = curve.p1;
241 for (
int i = 1;
i < count;
i++) {
242 Point cur = curve.Solve(
i / count);
246 prev_perpendicular = cur_perpendicular;
251 last_perpendicular_ = end_perpendicular;
252 last_point_ = curve.p2;
259 if (prev_perpendicular.
GetAlignment(cur_perpendicular) < trigs_[1].cos) {
264 AppendVertices(cur, prev_perpendicular);
265 AddJoin(
Join::kRound, cur, prev_perpendicular, cur_perpendicular);
267 AppendVertices(cur, cur_perpendicular);
273 if (!has_prior_segment_) {
278 Vector2 perpendicular = {-half_stroke_width_, 0};
279 AddCap(cap, origin, perpendicular,
true);
282 AppendVertices(origin, perpendicular);
284 AddCap(cap, origin, perpendicular,
false);
285 }
else if (with_close) {
289 AddJoin(join_, origin, last_perpendicular_, origin_perpendicular_);
291 last_perpendicular_ = origin_perpendicular_;
292 last_point_ = origin;
294 AddCap(cap_, last_point_, last_perpendicular_.
GetVector(),
false);
296 has_prior_segment_ =
false;
302 const Size radii)
override {
308 PerpendicularFromUnitDirection({-iterator.
start.
y, iterator.
start.
x});
309 HandlePreviousJoin(prev_perpendicular);
315 Point cur = center + direction * radii;
317 PerpendicularFromUnitDirection({-direction.
y, direction.
x});
319 prev_perpendicular = cur_perpendicular;
324 PerpendicularFromUnitDirection({-iterator.
end.
y, iterator.
end.
x});
328 last_perpendicular_ = end_perpendicular;
334 PositionWriter& vtx_builder_;
335 const Scalar half_stroke_width_;
336 const Scalar maximum_join_cosine_;
337 const Scalar minimum_miter_cosine_;
347 bool has_prior_contour_ =
false;
348 bool has_prior_segment_ =
false;
349 bool contour_needs_cap_ =
false;
353 Scalar half_stroke_width) {
358 static constexpr Scalar kJoinPixelThreshold = 0.25f;
369 Scalar half_stroke_width) {
391 Scalar hypotenuse = scale * half_stroke_width;
392 if (hypotenuse <= kJoinPixelThreshold) {
397 Scalar bisector = std::sqrt(hypotenuse * hypotenuse -
398 kJoinPixelThreshold * kJoinPixelThreshold);
399 Scalar half_cosine = bisector / hypotenuse;
400 Scalar cosine = 2.0f * half_cosine * half_cosine - 1;
416 static Scalar ComputeMinimumMiterCosine(
Scalar miter_limit) {
417 if (miter_limit <= 1.0f) {
442 Scalar half_cosine = 1 / miter_limit;
443 Scalar cosine = 2.0f * half_cosine * half_cosine - 1;
447 inline SeparatedVector2 PerpendicularFromPoints(
const Point from,
448 const Point to)
const {
449 return PerpendicularFromUnitDirection((to - from).Normalize());
452 inline SeparatedVector2 PerpendicularFromUnitDirection(
453 const Vector2 direction)
const {
454 return SeparatedVector2(
Vector2{-direction.
y, direction.x},
458 inline void AppendVertices(
const Point curve_point,
Vector2 offset) {
459 vtx_builder_.AppendVertex(curve_point + offset);
460 vtx_builder_.AppendVertex(curve_point - offset);
463 inline void AppendVertices(
const Point curve_point,
464 SeparatedVector2 perpendicular) {
465 return AppendVertices(curve_point, perpendicular.GetVector());
468 inline void HandlePreviousJoin(SeparatedVector2 new_perpendicular) {
470 if (has_prior_segment_) {
471 AddJoin(join_, last_point_, last_perpendicular_, new_perpendicular);
473 has_prior_segment_ =
true;
474 Vector2 perpendicular_vector = new_perpendicular.GetVector();
475 if (contour_needs_cap_) {
476 AddCap(cap_, last_point_, perpendicular_vector,
true);
480 AppendVertices(last_point_, perpendicular_vector);
481 origin_perpendicular_ = new_perpendicular;
502 bool contour_start) {
507 Point along(perpendicular.y, -perpendicular.x);
510 vtx_builder_.AppendVertex(path_point - along);
515 for (
size_t i = trigs_.
size() - 2u;
i > 0u; --
i) {
517 Vector2 offset = perpendicular * trigs_[
i].cos;
519 AppendVertices(center, offset);
525 size_t end = trigs_.
size() - 1u;
526 for (
size_t i = 1u;
i <
end; ++
i) {
528 Vector2 offset = perpendicular * trigs_[
i].cos;
530 AppendVertices(center, offset);
534 vtx_builder_.AppendVertex(path_point + along);
539 Point along(perpendicular.y, -perpendicular.x);
540 Point square_center = contour_start
542 : path_point + along;
543 AppendVertices(square_center, perpendicular);
549 void AddJoin(
Join join,
551 SeparatedVector2 old_perpendicular,
552 SeparatedVector2 new_perpendicular) {
553 Scalar cosine = old_perpendicular.GetAlignment(new_perpendicular);
554 if (cosine >= maximum_join_cosine_) {
571 if (cosine >= minimum_miter_cosine_) {
573 (old_perpendicular.GetVector() + new_perpendicular.GetVector()) /
575 if (old_perpendicular.Cross(new_perpendicular) < 0) {
576 vtx_builder_.AppendVertex(path_point + miter_vector);
578 vtx_builder_.AppendVertex(path_point - miter_vector);
586 if (cosine >= trigs_[1].cos) {
592 if (cosine < -trigs_[1].cos) {
600 AddCap(
Cap::kRound, path_point, old_perpendicular.GetVector(),
false);
612 Vector2 from_vector, to_vector;
613 bool begin_end_crossed;
614 Scalar turning = old_perpendicular.
Cross(new_perpendicular);
620 from_vector = -old_perpendicular.GetVector();
621 to_vector = -new_perpendicular.GetVector();
625 begin_end_crossed =
false;
631 from_vector = new_perpendicular.GetVector();
632 to_vector = old_perpendicular.GetVector();
636 begin_end_crossed =
true;
640 if (begin_end_crossed) {
641 vtx_builder_.AppendVertex(path_point + from_vector);
647 bool visit_center =
false;
653 Point middle_vector = (from_vector + to_vector);
662 size_t end = trigs_.
size() - 1u;
663 for (
size_t i = 1u;
i <
end; ++
i) {
664 Point p = trigs_[
i] * from_vector;
665 if (p.Cross(middle_vector) <= 0) {
674 vtx_builder_.AppendVertex(path_point);
675 visit_center =
false;
679 vtx_builder_.AppendVertex(path_point + p);
689 vtx_builder_.AppendVertex(path_point);
690 visit_center =
false;
694 vtx_builder_.AppendVertex(path_point + p);
697 if (begin_end_crossed) {
698 vtx_builder_.AppendVertex(path_point + to_vector);
707 AppendVertices(path_point, new_perpendicular);
712std::vector<Point> StrokeSegmentsGeometry::GenerateSolidStrokeVertices(
713 Tessellator& tessellator,
714 const PathSource& source,
715 const StrokeParameters& stroke,
717 std::vector<Point>
points(4096);
718 PositionWriter vtx_builder(
points);
719 StrokePathSegmentReceiver receiver(tessellator, vtx_builder, stroke, scale);
721 auto [arena, extra] = vtx_builder.GetUsedSize();
733 return stroke_.
width;
757 if (stroke_.
width < 0.0) {
761 if (max_basis == 0) {
766 StrokeParameters adjusted_stroke = stroke_;
767 adjusted_stroke.
width = std::max(stroke_.
width, min_size);
773 PositionWriter position_writer(tessellator.GetStrokePointCache());
774 StrokePathSegmentReceiver receiver(tessellator, position_writer,
775 adjusted_stroke, scale);
776 Dispatch(receiver, tessellator, scale);
778 const auto [arena_length, oversized_length] = position_writer.GetUsedSize();
779 if (!position_writer.HasOversizedBuffer()) {
781 data_host_buffer.Emplace(tessellator.GetStrokePointCache().data(),
782 arena_length *
sizeof(
Point),
alignof(
Point));
788 .vertex_count = arena_length,
794 const std::vector<Point>& oversized_data =
795 position_writer.GetOversizedBuffer();
798 (arena_length + oversized_length) *
sizeof(
Point),
803 tessellator.GetStrokePointCache().data(),
804 arena_length *
sizeof(
Point)
808 oversized_data.data(),
809 oversized_data.size() *
sizeof(
Point)
817 .vertex_count = arena_length + oversized_length,
830 const Rect& path_bounds)
const {
837 max_radius = max_radius *
kSqrt2;
840 max_radius = std::max(max_radius, stroke_.
miter_limit * 0.5f);
843 if (max_basis == 0) {
848 max_radius *= std::max(stroke_.
width, min_size);
899 if (include_center) {
924 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