Public Member Functions | |
StrokePathSegmentReceiver (Tessellator &tessellator, PositionWriter &vtx_builder, const StrokeParameters &stroke, const Scalar scale) | |
Protected Member Functions | |
void | BeginContour (Point origin, bool will_be_closed) override |
void | RecordLine (Point p1, Point p2) override |
void | RecordQuad (Point p1, Point cp, Point p2) override |
void | RecordConic (Point p1, Point cp, Point p2, Scalar weight) override |
void | RecordCubic (Point p1, Point cp1, Point cp2, Point p2) override |
template<typename Curve > | |
void | RecordCurve (const Curve &curve) |
void | RecordCurveSegment (const SeparatedVector2 &prev_perpendicular, const Point cur, const SeparatedVector2 &cur_perpendicular) |
void | EndContour (Point origin, bool with_close) override |
void | RecordArc (const Arc &arc, const Point center, const Size radii) override |
StrokePathSegmentReceiver converts path segments (fed by PathTessellator) into a vertex strip that covers the outline of the stroked version of the path and feeds those vertices, expressed in the form of a vertex strip into the supplied PositionWriter.
The general procedure follows the following basic methodology:
Every path segment is represented by a box with two starting vertices perpendicular to its start point and two vertices perpendicular to its end point, all perpendiculars of length (stroke_width * 0.5).
Joins will connect the ending "box" perpendiculars of the previous segment to the starting "box" perpendiculars of the following segment. If the two boxes are so aligned that their adjacent perpendiculars are less than a threshold distance apart (kJoinPixelThreshold), the join will just be elided so that the end of one box becomes the start of the next box. If the join process does add decorations, it assumes that the ending perpendicular vertices from the prior segment are the last vertices added and ensures that it appends the two vertices for the starting perpendiculars of the new segment's "box". Thus every join either adds nothing and the end perpendiculars of the previous segment become the start perpendiculars of the next segment, or it makes sure its geometry fills in the gap and ends with the start perpendiculars for the new segment.
Prior to the start of an unclosed contour we insert a cap and also the starting perpendicular segments for the first segment. Prior to the start of a closed contour, we just insert the starting perpendiculars for the first segment. Either way, we've initialized the path with the starting perpendiculars of the first segment.
After the last segment in an unclosed contour we insert a cap which can assume that the last segment has already inserted its closing perpendicular segments. After the last segment in a closed contour, we insert a join back to the very first segment in that contour.
Connecting any two contours we insert an infinitely thin connecting thread by inserting the last point of the previous contour twice and then inserting the first point of the next contour twice. This ensures that there are no non-empty triangles between the two contours.
Finally, inserting a line segment can assume that the starting perpendiculars have already been inserted by the preceding cap, join, or prior segment, so all it needs to do is to insert the ending perpendiculars which set the process up for the subsequent cap, join, or future segment.
Inserting curve segments acts like a series of line segments except that the opening perpendicular is taken from the curve rather than the direction between the starting point and the first sample point. This ensures that any cap or join will be aligned with the curve and not tilted by the first approximating segment. The same is true of the ending perpendicular which is taken from the curve and not the last approximated segment. Between each approximated segment of the curve, we insert only Cap::kRound joins so as not to polygonize a curve when it turns very sharply. We also skip these joins for any change of direction which is smaller than the first sample point of a round join for performance reasons.
To facilitate all of that work we maintain variables containing SeparatedVector2 values that, by convention, point 90 degrees to the right of the given path direction. This facilitates a quick add/subtract from the point on the path to insert the necessary perpendicular points of a segment's box. These values contain both a unit vector for direction and a magnitude for length.
SeparatedVector2 values also allow us to quickly test limits on when to include joins by using a simple dot product on the previous and next perpendiculars at a given path point which should match the dot product of the path's direction itself at the same point since both perpendiculars have been rotated identically to the same side of the path. The SeparatedVector2 will perform the dot product on the unit-length vectors so that the result is exactly the cosine of the angle between the segments - also the angle by which the path turned at a given path point.
Definition at line 131 of file stroke_path_geometry.cc.
|
inline |
Definition at line 133 of file stroke_path_geometry.cc.
References impeller::Tessellator::Trigs::end(), and impeller::Tessellator::Trigs::size().
|
inlineoverrideprotectedvirtual |
Every set of path segments will be surrounded by a Begin/EndContour pair with the same origin point.
Implements impeller::PathTessellator::SegmentReceiver.
Definition at line 157 of file stroke_path_geometry.cc.
|
inlineoverrideprotectedvirtual |
Every set of path segments will be surrounded by a Begin/EndContour pair with the same origin point. The boolean indicates if the path was closed as the result of an explicit PathReceiver::Close invocation which tells a stroking sub-class whether to use end caps or a "join to first segment". Contours which are closed by a MoveTo will supply "false".
Implements impeller::PathTessellator::SegmentReceiver.
Definition at line 258 of file stroke_path_geometry.cc.
References impeller::SeparatedVector2::GetVector(), impeller::kButt, impeller::kRound, and impeller::kSquare.
|
inlineoverrideprotectedvirtual |
Implements impeller::PathAndArcSegmentReceiver.
Definition at line 287 of file stroke_path_geometry.cc.
References impeller::Arc::Iteration::Quadrant::axis, impeller::Arc::ComputeIterations(), impeller::Arc::Iteration::end, end, impeller::Arc::Iteration::Quadrant::end_index, impeller::Tessellator::Trigs::GetSteps(), impeller::Tessellator::GetTrigsForDeviceRadius(), impeller::TSize< T >::MaxDimension(), impeller::Arc::Iteration::quadrant_count, impeller::Arc::Iteration::quadrants, RecordCurveSegment(), impeller::Arc::Iteration::start, impeller::Arc::Iteration::Quadrant::start_index, impeller::TPoint< T >::x, and impeller::TPoint< T >::y.
|
inlineoverrideprotectedvirtual |
Guaranteed to be non-degenerate (not a quad or line) p1 will always be the last recorded point.
Implements impeller::PathTessellator::SegmentReceiver.
Definition at line 191 of file stroke_path_geometry.cc.
|
inlineoverrideprotectedvirtual |
Guaranteed to be trivially non-degenerate (not all 4 points the same). p1 will always be the last recorded point.
Implements impeller::PathTessellator::SegmentReceiver.
Definition at line 196 of file stroke_path_geometry.cc.
|
inlineprotected |
|
inlineprotected |
Definition at line 245 of file stroke_path_geometry.cc.
References impeller::SeparatedVector2::GetAlignment(), and impeller::kRound.
Referenced by RecordArc(), and RecordCurve().
|
inlineoverrideprotectedvirtual |
Guaranteed to be non-degenerate except in the single case of stroking where we have a MoveTo followed by any number of degenerate (single point, going nowhere) path segments. p1 will always be the last recorded point.
Implements impeller::PathTessellator::SegmentReceiver.
Definition at line 173 of file stroke_path_geometry.cc.
|
inlineoverrideprotectedvirtual |
Guaranteed to be non-degenerate (not a line). p1 will always be the last recorded point.
Implements impeller::PathTessellator::SegmentReceiver.
Definition at line 186 of file stroke_path_geometry.cc.