xref: /aosp_15_r20/external/skia/include/core/SkContourMeasure.h (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1 /*
2  * Copyright 2018 Google Inc.
3  *
4  * Use of this source code is governed by a BSD-style license that can be
5  * found in the LICENSE file.
6  */
7 
8 #ifndef SkContourMeasure_DEFINED
9 #define SkContourMeasure_DEFINED
10 
11 #include "include/core/SkPoint.h"
12 #include "include/core/SkRefCnt.h"
13 #include "include/core/SkScalar.h"
14 #include "include/core/SkSpan.h"
15 #include "include/private/base/SkAPI.h"
16 #include "include/private/base/SkAssert.h"
17 #include "include/private/base/SkTDArray.h"
18 
19 #include <cstddef>
20 #include <memory>
21 
22 class SkMatrix;
23 class SkPath;
24 enum class SkPathVerb;
25 
26 class SK_API SkContourMeasure : public SkRefCnt {
27 public:
28     /** Return the length of the contour.
29      */
length()30     SkScalar length() const { return fLength; }
31 
32     /** Pins distance to 0 <= distance <= length(), and then computes the corresponding
33      *  position and tangent.
34      */
35     [[nodiscard]] bool getPosTan(SkScalar distance, SkPoint* position, SkVector* tangent) const;
36 
37     enum MatrixFlags {
38         kGetPosition_MatrixFlag     = 0x01,
39         kGetTangent_MatrixFlag      = 0x02,
40         kGetPosAndTan_MatrixFlag    = kGetPosition_MatrixFlag | kGetTangent_MatrixFlag
41     };
42 
43     /** Pins distance to 0 <= distance <= getLength(), and then computes
44      the corresponding matrix (by calling getPosTan).
45      Returns false if there is no path, or a zero-length path was specified, in which case
46      matrix is unchanged.
47      */
48     [[nodiscard]] bool getMatrix(SkScalar distance, SkMatrix* matrix,
49                                  MatrixFlags flags = kGetPosAndTan_MatrixFlag) const;
50 
51     /** Given a start and stop distance, return in dst the intervening segment(s).
52      If the segment is zero-length, return false, else return true.
53      startD and stopD are pinned to legal values (0..getLength()). If startD > stopD
54      then return false (and leave dst untouched).
55      Begin the segment with a moveTo if startWithMoveTo is true
56      */
57     [[nodiscard]] bool getSegment(SkScalar startD, SkScalar stopD, SkPath* dst,
58                                   bool startWithMoveTo) const;
59 
60     /** Return true if the contour is closed()
61      */
isClosed()62     bool isClosed() const { return fIsClosed; }
63 
64     /** Measurement data for individual verbs.
65      */
66     struct VerbMeasure {
67         SkScalar              fDistance; // Cumulative distance along the current contour.
68         SkPathVerb            fVerb;     // Verb type.
69         SkSpan<const SkPoint> fPts;      // Verb points.
70     };
71 
72 private:
73     struct Segment;
74 
75 public:
76     /** Utility for iterating over the current contour verbs:
77      *
78      *   for (const auto verb_measure : contour_measure) {
79      *     ...
80      *   }
81      */
82     class ForwardVerbIterator final {
83     public:
84         VerbMeasure operator*() const;
85 
86         ForwardVerbIterator& operator++() {
87             SkASSERT(!fSegments.empty());
88 
89             fSegments = LastSegForCurrentVerb(fSegments.subspan(1));
90 
91             return *this;
92         }
93 
94         bool operator==(const ForwardVerbIterator& other) {
95             SkASSERT(fSegments.data() != other.fSegments.data() ||
96                      fSegments.size() == other.fSegments.size());
97             return fSegments.data() == other.fSegments.data();
98         }
99 
100         bool operator!=(const ForwardVerbIterator& other) {
101             return !((*this) == other);
102         }
103 
104     private:
105         friend class SkContourMeasure;
106 
ForwardVerbIterator(SkSpan<const Segment> segs,SkSpan<const SkPoint> pts)107         ForwardVerbIterator(SkSpan<const Segment> segs, SkSpan<const SkPoint> pts)
108             : fSegments(LastSegForCurrentVerb(segs))
109             , fPts(pts) {}
110 
LastSegForCurrentVerb(const SkSpan<const Segment> & segs)111         static SkSpan<const Segment> LastSegForCurrentVerb(const SkSpan<const Segment>& segs) {
112             size_t i = 1;
113             while (i < segs.size() && segs[0].fPtIndex == segs[i].fPtIndex) {
114                 ++i;
115             }
116 
117             return segs.subspan(i - 1);
118         }
119 
120         // Remaining segments for forward iteration. The first segment in the span is
121         // adjusted to always point to the last segment of the current verb, such that its distance
122         // corresponds to the verb distance.
123         SkSpan<const Segment> fSegments;
124 
125         // All path points (indexed in segments).
126         SkSpan<const SkPoint> fPts;
127     };
128 
begin()129     ForwardVerbIterator begin() const {
130         return ForwardVerbIterator(fSegments, fPts);
131     }
end()132     ForwardVerbIterator end() const {
133         return ForwardVerbIterator(SkSpan(fSegments.end(), 0), fPts);
134     }
135 
136 private:
137     struct Segment {
138         SkScalar    fDistance;  // total distance up to this point
139         unsigned    fPtIndex; // index into the fPts array
140         unsigned    fTValue : 30;
141         unsigned    fType : 2;  // actually the enum SkSegType
142         // See SkPathMeasurePriv.h
143 
144         SkScalar getScalarT() const;
145 
NextSegment146         static const Segment* Next(const Segment* seg) {
147             unsigned ptIndex = seg->fPtIndex;
148             do {
149                 ++seg;
150             } while (seg->fPtIndex == ptIndex);
151             return seg;
152         }
153 
154     };
155 
156     const SkTDArray<Segment>  fSegments;
157     const SkTDArray<SkPoint>  fPts; // Points used to define the segments
158 
159     const SkScalar fLength;
160     const bool fIsClosed;
161 
162     SkContourMeasure(SkTDArray<Segment>&& segs, SkTDArray<SkPoint>&& pts,
163                      SkScalar length, bool isClosed);
~SkContourMeasure()164     ~SkContourMeasure() override {}
165 
166     const Segment* distanceToSegment(SkScalar distance, SkScalar* t) const;
167 
168     friend class SkContourMeasureIter;
169     friend class SkPathMeasurePriv;
170 };
171 
172 class SK_API SkContourMeasureIter {
173 public:
174     SkContourMeasureIter();
175     /**
176      *  Initialize the Iter with a path.
177      *  The parts of the path that are needed are copied, so the client is free to modify/delete
178      *  the path after this call.
179      *
180      *  resScale controls the precision of the measure. values > 1 increase the
181      *  precision (and possibly slow down the computation).
182      */
183     SkContourMeasureIter(const SkPath& path, bool forceClosed, SkScalar resScale = 1);
184     ~SkContourMeasureIter();
185 
186     SkContourMeasureIter(SkContourMeasureIter&&);
187     SkContourMeasureIter& operator=(SkContourMeasureIter&&);
188 
189     /**
190      *  Reset the Iter with a path.
191      *  The parts of the path that are needed are copied, so the client is free to modify/delete
192      *  the path after this call.
193      */
194     void reset(const SkPath& path, bool forceClosed, SkScalar resScale = 1);
195 
196     /**
197      *  Iterates through contours in path, returning a contour-measure object for each contour
198      *  in the path. Returns null when it is done.
199      *
200      *  This only returns non-zero length contours, where a contour is the segments between
201      *  a kMove_Verb and either ...
202      *      - the next kMove_Verb
203      *      - kClose_Verb (1 or more)
204      *      - kDone_Verb
205      *  If it encounters a zero-length contour, it is skipped.
206      */
207     sk_sp<SkContourMeasure> next();
208 
209 private:
210     class Impl;
211 
212     std::unique_ptr<Impl> fImpl;
213 };
214 
215 #endif
216