SkStrokerPriv.cpp 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235
  1. /*
  2. * Copyright 2006 The Android Open Source Project
  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. #include "include/core/SkPath.h"
  8. #include "src/core/SkGeometry.h"
  9. #include "src/core/SkPointPriv.h"
  10. #include "src/core/SkStrokerPriv.h"
  11. #include <utility>
  12. static void ButtCapper(SkPath* path, const SkPoint& pivot, const SkVector& normal,
  13. const SkPoint& stop, SkPath*) {
  14. path->lineTo(stop.fX, stop.fY);
  15. }
  16. static void RoundCapper(SkPath* path, const SkPoint& pivot, const SkVector& normal,
  17. const SkPoint& stop, SkPath*) {
  18. SkVector parallel;
  19. SkPointPriv::RotateCW(normal, &parallel);
  20. SkPoint projectedCenter = pivot + parallel;
  21. path->conicTo(projectedCenter + normal, projectedCenter, SK_ScalarRoot2Over2);
  22. path->conicTo(projectedCenter - normal, stop, SK_ScalarRoot2Over2);
  23. }
  24. static void SquareCapper(SkPath* path, const SkPoint& pivot, const SkVector& normal,
  25. const SkPoint& stop, SkPath* otherPath) {
  26. SkVector parallel;
  27. SkPointPriv::RotateCW(normal, &parallel);
  28. if (otherPath) {
  29. path->setLastPt(pivot.fX + normal.fX + parallel.fX, pivot.fY + normal.fY + parallel.fY);
  30. path->lineTo(pivot.fX - normal.fX + parallel.fX, pivot.fY - normal.fY + parallel.fY);
  31. } else {
  32. path->lineTo(pivot.fX + normal.fX + parallel.fX, pivot.fY + normal.fY + parallel.fY);
  33. path->lineTo(pivot.fX - normal.fX + parallel.fX, pivot.fY - normal.fY + parallel.fY);
  34. path->lineTo(stop.fX, stop.fY);
  35. }
  36. }
  37. /////////////////////////////////////////////////////////////////////////////
  38. static bool is_clockwise(const SkVector& before, const SkVector& after) {
  39. return before.fX * after.fY > before.fY * after.fX;
  40. }
  41. enum AngleType {
  42. kNearly180_AngleType,
  43. kSharp_AngleType,
  44. kShallow_AngleType,
  45. kNearlyLine_AngleType
  46. };
  47. static AngleType Dot2AngleType(SkScalar dot) {
  48. // need more precise fixed normalization
  49. // SkASSERT(SkScalarAbs(dot) <= SK_Scalar1 + SK_ScalarNearlyZero);
  50. if (dot >= 0) { // shallow or line
  51. return SkScalarNearlyZero(SK_Scalar1 - dot) ? kNearlyLine_AngleType : kShallow_AngleType;
  52. } else { // sharp or 180
  53. return SkScalarNearlyZero(SK_Scalar1 + dot) ? kNearly180_AngleType : kSharp_AngleType;
  54. }
  55. }
  56. static void HandleInnerJoin(SkPath* inner, const SkPoint& pivot, const SkVector& after) {
  57. #if 1
  58. /* In the degenerate case that the stroke radius is larger than our segments
  59. just connecting the two inner segments may "show through" as a funny
  60. diagonal. To pseudo-fix this, we go through the pivot point. This adds
  61. an extra point/edge, but I can't see a cheap way to know when this is
  62. not needed :(
  63. */
  64. inner->lineTo(pivot.fX, pivot.fY);
  65. #endif
  66. inner->lineTo(pivot.fX - after.fX, pivot.fY - after.fY);
  67. }
  68. static void BluntJoiner(SkPath* outer, SkPath* inner, const SkVector& beforeUnitNormal,
  69. const SkPoint& pivot, const SkVector& afterUnitNormal,
  70. SkScalar radius, SkScalar invMiterLimit, bool, bool) {
  71. SkVector after;
  72. afterUnitNormal.scale(radius, &after);
  73. if (!is_clockwise(beforeUnitNormal, afterUnitNormal)) {
  74. using std::swap;
  75. swap(outer, inner);
  76. after.negate();
  77. }
  78. outer->lineTo(pivot.fX + after.fX, pivot.fY + after.fY);
  79. HandleInnerJoin(inner, pivot, after);
  80. }
  81. static void RoundJoiner(SkPath* outer, SkPath* inner, const SkVector& beforeUnitNormal,
  82. const SkPoint& pivot, const SkVector& afterUnitNormal,
  83. SkScalar radius, SkScalar invMiterLimit, bool, bool) {
  84. SkScalar dotProd = SkPoint::DotProduct(beforeUnitNormal, afterUnitNormal);
  85. AngleType angleType = Dot2AngleType(dotProd);
  86. if (angleType == kNearlyLine_AngleType)
  87. return;
  88. SkVector before = beforeUnitNormal;
  89. SkVector after = afterUnitNormal;
  90. SkRotationDirection dir = kCW_SkRotationDirection;
  91. if (!is_clockwise(before, after)) {
  92. using std::swap;
  93. swap(outer, inner);
  94. before.negate();
  95. after.negate();
  96. dir = kCCW_SkRotationDirection;
  97. }
  98. SkMatrix matrix;
  99. matrix.setScale(radius, radius);
  100. matrix.postTranslate(pivot.fX, pivot.fY);
  101. SkConic conics[SkConic::kMaxConicsForArc];
  102. int count = SkConic::BuildUnitArc(before, after, dir, &matrix, conics);
  103. if (count > 0) {
  104. for (int i = 0; i < count; ++i) {
  105. outer->conicTo(conics[i].fPts[1], conics[i].fPts[2], conics[i].fW);
  106. }
  107. after.scale(radius);
  108. HandleInnerJoin(inner, pivot, after);
  109. }
  110. }
  111. #define kOneOverSqrt2 (0.707106781f)
  112. static void MiterJoiner(SkPath* outer, SkPath* inner, const SkVector& beforeUnitNormal,
  113. const SkPoint& pivot, const SkVector& afterUnitNormal,
  114. SkScalar radius, SkScalar invMiterLimit,
  115. bool prevIsLine, bool currIsLine) {
  116. // negate the dot since we're using normals instead of tangents
  117. SkScalar dotProd = SkPoint::DotProduct(beforeUnitNormal, afterUnitNormal);
  118. AngleType angleType = Dot2AngleType(dotProd);
  119. SkVector before = beforeUnitNormal;
  120. SkVector after = afterUnitNormal;
  121. SkVector mid;
  122. SkScalar sinHalfAngle;
  123. bool ccw;
  124. if (angleType == kNearlyLine_AngleType) {
  125. return;
  126. }
  127. if (angleType == kNearly180_AngleType) {
  128. currIsLine = false;
  129. goto DO_BLUNT;
  130. }
  131. ccw = !is_clockwise(before, after);
  132. if (ccw) {
  133. using std::swap;
  134. swap(outer, inner);
  135. before.negate();
  136. after.negate();
  137. }
  138. /* Before we enter the world of square-roots and divides,
  139. check if we're trying to join an upright right angle
  140. (common case for stroking rectangles). If so, special case
  141. that (for speed an accuracy).
  142. Note: we only need to check one normal if dot==0
  143. */
  144. if (0 == dotProd && invMiterLimit <= kOneOverSqrt2) {
  145. mid = (before + after) * radius;
  146. goto DO_MITER;
  147. }
  148. /* midLength = radius / sinHalfAngle
  149. if (midLength > miterLimit * radius) abort
  150. if (radius / sinHalf > miterLimit * radius) abort
  151. if (1 / sinHalf > miterLimit) abort
  152. if (1 / miterLimit > sinHalf) abort
  153. My dotProd is opposite sign, since it is built from normals and not tangents
  154. hence 1 + dot instead of 1 - dot in the formula
  155. */
  156. sinHalfAngle = SkScalarSqrt(SkScalarHalf(SK_Scalar1 + dotProd));
  157. if (sinHalfAngle < invMiterLimit) {
  158. currIsLine = false;
  159. goto DO_BLUNT;
  160. }
  161. // choose the most accurate way to form the initial mid-vector
  162. if (angleType == kSharp_AngleType) {
  163. mid.set(after.fY - before.fY, before.fX - after.fX);
  164. if (ccw) {
  165. mid.negate();
  166. }
  167. } else {
  168. mid.set(before.fX + after.fX, before.fY + after.fY);
  169. }
  170. mid.setLength(radius / sinHalfAngle);
  171. DO_MITER:
  172. if (prevIsLine) {
  173. outer->setLastPt(pivot.fX + mid.fX, pivot.fY + mid.fY);
  174. } else {
  175. outer->lineTo(pivot.fX + mid.fX, pivot.fY + mid.fY);
  176. }
  177. DO_BLUNT:
  178. after.scale(radius);
  179. if (!currIsLine) {
  180. outer->lineTo(pivot.fX + after.fX, pivot.fY + after.fY);
  181. }
  182. HandleInnerJoin(inner, pivot, after);
  183. }
  184. /////////////////////////////////////////////////////////////////////////////
  185. SkStrokerPriv::CapProc SkStrokerPriv::CapFactory(SkPaint::Cap cap) {
  186. const SkStrokerPriv::CapProc gCappers[] = {
  187. ButtCapper, RoundCapper, SquareCapper
  188. };
  189. SkASSERT((unsigned)cap < SkPaint::kCapCount);
  190. return gCappers[cap];
  191. }
  192. SkStrokerPriv::JoinProc SkStrokerPriv::JoinFactory(SkPaint::Join join) {
  193. const SkStrokerPriv::JoinProc gJoiners[] = {
  194. MiterJoiner, RoundJoiner, BluntJoiner
  195. };
  196. SkASSERT((unsigned)join < SkPaint::kJoinCount);
  197. return gJoiners[join];
  198. }