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