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