SkPathPriv.h 11 KB

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