1 /* 2 * Copyright 2015 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 SkPathPriv_DEFINED 9 #define SkPathPriv_DEFINED 10 11 #include "include/core/SkPath.h" 12 13 class SkPathPriv { 14 public: 15 #ifdef SK_BUILD_FOR_ANDROID_FRAMEWORK 16 static const int kPathRefGenIDBitCnt = 30; // leave room for the fill type (skbug.com/1762) 17 #else 18 static const int kPathRefGenIDBitCnt = 32; 19 #endif 20 21 enum FirstDirection : int { 22 kCW_FirstDirection, // == SkPath::kCW_Direction 23 kCCW_FirstDirection, // == SkPath::kCCW_Direction 24 kUnknown_FirstDirection, 25 }; 26 AsFirstDirection(SkPath::Direction dir)27 static FirstDirection AsFirstDirection(SkPath::Direction dir) { 28 // since we agree numerically for the values in Direction, we can just cast. 29 return (FirstDirection)dir; 30 } 31 32 /** 33 * Return the opposite of the specified direction. kUnknown is its own 34 * opposite. 35 */ OppositeFirstDirection(FirstDirection dir)36 static FirstDirection OppositeFirstDirection(FirstDirection dir) { 37 static const FirstDirection gOppositeDir[] = { 38 kCCW_FirstDirection, kCW_FirstDirection, kUnknown_FirstDirection, 39 }; 40 return gOppositeDir[dir]; 41 } 42 43 /** 44 * Tries to quickly compute the direction of the first non-degenerate 45 * contour. If it can be computed, return true and set dir to that 46 * direction. If it cannot be (quickly) determined, return false and ignore 47 * the dir parameter. If the direction was determined, it is cached to make 48 * subsequent calls return quickly. 49 */ 50 static bool CheapComputeFirstDirection(const SkPath&, FirstDirection* dir); 51 52 /** 53 * Returns true if the path's direction can be computed via 54 * cheapComputDirection() and if that computed direction matches the 55 * specified direction. If dir is kUnknown, returns true if the direction 56 * cannot be computed. 57 */ CheapIsFirstDirection(const SkPath & path,FirstDirection dir)58 static bool CheapIsFirstDirection(const SkPath& path, FirstDirection dir) { 59 FirstDirection computedDir = kUnknown_FirstDirection; 60 (void)CheapComputeFirstDirection(path, &computedDir); 61 return computedDir == dir; 62 } 63 IsClosedSingleContour(const SkPath & path)64 static bool IsClosedSingleContour(const SkPath& path) { 65 int verbCount = path.countVerbs(); 66 if (verbCount == 0) 67 return false; 68 int moveCount = 0; 69 auto verbs = path.fPathRef->verbs(); 70 for (int i = 0; i < verbCount; i++) { 71 switch (verbs[~i]) { // verbs are stored backwards; we use [~i] to get the i'th verb 72 case SkPath::Verb::kMove_Verb: 73 moveCount += 1; 74 if (moveCount > 1) { 75 return false; 76 } 77 break; 78 case SkPath::Verb::kClose_Verb: 79 if (i == verbCount - 1) { 80 return true; 81 } 82 return false; 83 default: break; 84 } 85 } 86 return false; 87 } 88 AddGenIDChangeListener(const SkPath & path,sk_sp<SkPathRef::GenIDChangeListener> listener)89 static void AddGenIDChangeListener(const SkPath& path, 90 sk_sp<SkPathRef::GenIDChangeListener> listener) { 91 path.fPathRef->addGenIDChangeListener(std::move(listener)); 92 } 93 94 /** 95 * This returns true for a rect that begins and ends at the same corner and has either a move 96 * followed by four lines or a move followed by 3 lines and a close. None of the parameters are 97 * optional. This does not permit degenerate line or point rectangles. 98 */ 99 static bool IsSimpleClosedRect(const SkPath& path, SkRect* rect, SkPath::Direction* direction, 100 unsigned* start); 101 102 /** 103 * Creates a path from arc params using the semantics of SkCanvas::drawArc. This function 104 * assumes empty ovals and zero sweeps have already been filtered out. 105 */ 106 static void CreateDrawArcPath(SkPath* path, const SkRect& oval, SkScalar startAngle, 107 SkScalar sweepAngle, bool useCenter, bool isFillNoPathEffect); 108 109 /** 110 * Determines whether an arc produced by CreateDrawArcPath will be convex. Assumes a non-empty 111 * oval. 112 */ 113 static bool DrawArcIsConvex(SkScalar sweepAngle, bool useCenter, bool isFillNoPathEffect); 114 115 /** 116 * Returns a C++11-iterable object that traverses a path's verbs in order. e.g: 117 * 118 * for (SkPath::Verb verb : SkPathPriv::Verbs(path)) { 119 * ... 120 * } 121 */ 122 struct Verbs { 123 public: VerbsVerbs124 Verbs(const SkPath& path) : fPathRef(path.fPathRef.get()) {} 125 struct Iter { 126 void operator++() { --fVerb; } // verbs are laid out backwards in memory. 127 bool operator!=(const Iter& b) { return fVerb != b.fVerb; } 128 SkPath::Verb operator*() { return static_cast<SkPath::Verb>(*fVerb); } 129 const uint8_t* fVerb; 130 }; beginVerbs131 Iter begin() { return Iter{fPathRef->verbs() - 1}; } endVerbs132 Iter end() { return Iter{fPathRef->verbs() - fPathRef->countVerbs() - 1}; } 133 private: 134 Verbs(const Verbs&) = delete; 135 Verbs& operator=(const Verbs&) = delete; 136 SkPathRef* fPathRef; 137 }; 138 139 /** 140 * Returns a pointer to the verb data. Note that the verbs are stored backwards in memory and 141 * thus the returned pointer is the last verb. 142 */ VerbData(const SkPath & path)143 static const uint8_t* VerbData(const SkPath& path) { 144 return path.fPathRef->verbsMemBegin(); 145 } 146 147 /** Returns a raw pointer to the path points */ PointData(const SkPath & path)148 static const SkPoint* PointData(const SkPath& path) { 149 return path.fPathRef->points(); 150 } 151 152 /** Returns the number of conic weights in the path */ ConicWeightCnt(const SkPath & path)153 static int ConicWeightCnt(const SkPath& path) { 154 return path.fPathRef->countWeights(); 155 } 156 157 /** Returns a raw pointer to the path conic weights. */ ConicWeightData(const SkPath & path)158 static const SkScalar* ConicWeightData(const SkPath& path) { 159 return path.fPathRef->conicWeights(); 160 } 161 162 #ifndef SK_LEGACY_PATH_CONVEXITY 163 /** Returns true if path formed by pts is convex. 164 165 @param pts SkPoint array of path 166 @param count number of entries in array 167 168 @return true if pts represent a convex geometry 169 */ 170 static bool IsConvex(const SkPoint pts[], int count); 171 #endif 172 173 /** Returns true if the underlying SkPathRef has one single owner. */ TestingOnly_unique(const SkPath & path)174 static bool TestingOnly_unique(const SkPath& path) { 175 return path.fPathRef->unique(); 176 } 177 178 /** Returns true if constructed by addCircle(), addOval(); and in some cases, 179 addRoundRect(), addRRect(). SkPath constructed with conicTo() or rConicTo() will not 180 return true though SkPath draws oval. 181 182 rect receives bounds of oval. 183 dir receives SkPath::Direction of oval: kCW_Direction if clockwise, kCCW_Direction if 184 counterclockwise. 185 start receives start of oval: 0 for top, 1 for right, 2 for bottom, 3 for left. 186 187 rect, dir, and start are unmodified if oval is not found. 188 189 Triggers performance optimizations on some GPU surface implementations. 190 191 @param rect storage for bounding SkRect of oval; may be nullptr 192 @param dir storage for SkPath::Direction; may be nullptr 193 @param start storage for start of oval; may be nullptr 194 @return true if SkPath was constructed by method that reduces to oval 195 */ IsOval(const SkPath & path,SkRect * rect,SkPath::Direction * dir,unsigned * start)196 static bool IsOval(const SkPath& path, SkRect* rect, SkPath::Direction* dir, unsigned* start) { 197 bool isCCW = false; 198 bool result = path.fPathRef->isOval(rect, &isCCW, start); 199 if (dir && result) { 200 *dir = isCCW ? SkPath::kCCW_Direction : SkPath::kCW_Direction; 201 } 202 return result; 203 } 204 205 /** Returns true if constructed by addRoundRect(), addRRect(); and if construction 206 is not empty, not SkRect, and not oval. SkPath constructed with other calls 207 will not return true though SkPath draws SkRRect. 208 209 rrect receives bounds of SkRRect. 210 dir receives SkPath::Direction of oval: kCW_Direction if clockwise, kCCW_Direction if 211 counterclockwise. 212 start receives start of SkRRect: 0 for top, 1 for right, 2 for bottom, 3 for left. 213 214 rrect, dir, and start are unmodified if SkRRect is not found. 215 216 Triggers performance optimizations on some GPU surface implementations. 217 218 @param rrect storage for bounding SkRect of SkRRect; may be nullptr 219 @param dir storage for SkPath::Direction; may be nullptr 220 @param start storage for start of SkRRect; may be nullptr 221 @return true if SkPath contains only SkRRect 222 */ IsRRect(const SkPath & path,SkRRect * rrect,SkPath::Direction * dir,unsigned * start)223 static bool IsRRect(const SkPath& path, SkRRect* rrect, SkPath::Direction* dir, 224 unsigned* start) { 225 bool isCCW = false; 226 bool result = path.fPathRef->isRRect(rrect, &isCCW, start); 227 if (dir && result) { 228 *dir = isCCW ? SkPath::kCCW_Direction : SkPath::kCW_Direction; 229 } 230 return result; 231 } 232 233 /** 234 * Sometimes in the drawing pipeline, we have to perform math on path coordinates, even after 235 * the path is in device-coordinates. Tessellation and clipping are two examples. Usually this 236 * is pretty modest, but it can involve subtracting/adding coordinates, or multiplying by 237 * small constants (e.g. 2,3,4). To try to preflight issues where these optionations could turn 238 * finite path values into infinities (or NaNs), we allow the upper drawing code to reject 239 * the path if its bounds (in device coordinates) is too close to max float. 240 */ TooBigForMath(const SkRect & bounds)241 static bool TooBigForMath(const SkRect& bounds) { 242 // This value is just a guess. smaller is safer, but we don't want to reject largish paths 243 // that we don't have to. 244 constexpr SkScalar scale_down_to_allow_for_small_multiplies = 0.25f; 245 constexpr SkScalar max = SK_ScalarMax * scale_down_to_allow_for_small_multiplies; 246 247 // use ! expression so we return true if bounds contains NaN 248 return !(bounds.fLeft >= -max && bounds.fTop >= -max && 249 bounds.fRight <= max && bounds.fBottom <= max); 250 } TooBigForMath(const SkPath & path)251 static bool TooBigForMath(const SkPath& path) { 252 return TooBigForMath(path.getBounds()); 253 } 254 255 // Returns number of valid points for each SkPath::Iter verb PtsInIter(unsigned verb)256 static int PtsInIter(unsigned verb) { 257 static const uint8_t gPtsInVerb[] = { 258 1, // kMove pts[0] 259 2, // kLine pts[0..1] 260 3, // kQuad pts[0..2] 261 3, // kConic pts[0..2] 262 4, // kCubic pts[0..3] 263 0, // kClose 264 0 // kDone 265 }; 266 267 SkASSERT(verb < SK_ARRAY_COUNT(gPtsInVerb)); 268 return gPtsInVerb[verb]; 269 } 270 IsAxisAligned(const SkPath & path)271 static bool IsAxisAligned(const SkPath& path) { 272 SkRect tmp; 273 return (path.fPathRef->fIsRRect | path.fPathRef->fIsOval) || path.isRect(&tmp); 274 } 275 AllPointsEq(const SkPoint pts[],int count)276 static bool AllPointsEq(const SkPoint pts[], int count) { 277 for (int i = 1; i < count; ++i) { 278 if (pts[0] != pts[i]) { 279 return false; 280 } 281 } 282 return true; 283 } 284 }; 285 286 #endif 287