7 #include "flutter/display_list/geometry/dl_path.h"
22 class PositionWriter {
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_)) {
148 FML_DCHECK(trigs_.
size() >= 2);
149 FML_DCHECK(trigs_[0].cos == 1.0f);
150 FML_DCHECK(trigs_[0].sin == 0.0f);
151 FML_DCHECK(trigs_.
end()[-1].cos == 0.0f);
152 FML_DCHECK(trigs_.
end()[-1].sin == 1.0f);
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);
259 FML_DCHECK(origin == origin_point_);
260 if (!has_prior_segment_) {
262 FML_DCHECK(last_point_ == origin);
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) {
274 FML_DCHECK(origin == origin_point_);
275 FML_DCHECK(last_point_ == origin);
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) {
456 FML_DCHECK(has_prior_contour_);
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) {
503 Point center = path_point - along * trigs_[i].sin;
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) {
514 Point center = path_point + along * trigs_[i].sin;
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;
625 FML_DCHECK(from_vector.Cross(to_vector) > 0);
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);
699 std::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();
709 FML_DCHECK(extra == 0u);
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 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 & GetTransientsBuffer() const
Retrieve the currnent host buffer for transient storage.
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)
@ kNone
Does not use the index buffer.
Join
An enum that describes ways to join two segments of a path.
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
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 TRect< T > Expand(T left, T top, T right, T bottom) const
Returns a rectangle with expanded edges. Negative expansion results in shrinking.
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 Type MaxDimension() const
std::vector< Point > points