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