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);
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);
272 FML_DCHECK(origin == origin_point_);
273 if (!has_prior_segment_) {
275 FML_DCHECK(last_point_ == origin);
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) {
287 FML_DCHECK(origin == origin_point_);
288 FML_DCHECK(last_point_ == origin);
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) {
469 FML_DCHECK(has_prior_contour_);
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) {
516 Point center = path_point - along * trigs_[i].sin;
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) {
527 Point center = path_point + along * trigs_[i].sin;
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;
638 FML_DCHECK(from_vector.Cross(to_vector) > 0);
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);
712 std::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();
722 FML_DCHECK(extra == 0u);
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)
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 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