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