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