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
9#include "SkCornerPathEffect.h"
10#include "SkPath.h"
11#include "SkPoint.h"
12#include "SkReadBuffer.h"
13#include "SkWriteBuffer.h"
14
15SkCornerPathEffect::SkCornerPathEffect(SkScalar radius) {
16    // check with ! to catch NaNs
17    if (!(radius > 0)) {
18        radius = 0;
19    }
20    fRadius = radius;
21}
22SkCornerPathEffect::~SkCornerPathEffect() {}
23
24static bool ComputeStep(const SkPoint& a, const SkPoint& b, SkScalar radius,
25                        SkPoint* step) {
26    SkScalar dist = SkPoint::Distance(a, b);
27
28    *step = b - a;
29    if (dist <= radius * 2) {
30        *step *= SK_ScalarHalf;
31        return false;
32    } else {
33        *step *= radius / dist;
34        return true;
35    }
36}
37
38bool SkCornerPathEffect::filterPath(SkPath* dst, const SkPath& src,
39                                    SkStrokeRec*, const SkRect*) const {
40    if (fRadius <= 0) {
41        return false;
42    }
43
44    SkPath::Iter    iter(src, false);
45    SkPath::Verb    verb, prevVerb = (SkPath::Verb)-1;
46    SkPoint         pts[4];
47
48    bool        closed;
49    SkPoint     moveTo, lastCorner;
50    SkVector    firstStep, step;
51    bool        prevIsValid = true;
52
53    // to avoid warnings
54    step.set(0, 0);
55    moveTo.set(0, 0);
56    firstStep.set(0, 0);
57    lastCorner.set(0, 0);
58
59    for (;;) {
60        switch (verb = iter.next(pts, false)) {
61            case SkPath::kMove_Verb:
62                    // close out the previous (open) contour
63                if (SkPath::kLine_Verb == prevVerb) {
64                    dst->lineTo(lastCorner);
65                }
66                closed = iter.isClosedContour();
67                if (closed) {
68                    moveTo = pts[0];
69                    prevIsValid = false;
70                } else {
71                    dst->moveTo(pts[0]);
72                    prevIsValid = true;
73                }
74                break;
75            case SkPath::kLine_Verb: {
76                bool drawSegment = ComputeStep(pts[0], pts[1], fRadius, &step);
77                // prev corner
78                if (!prevIsValid) {
79                    dst->moveTo(moveTo + step);
80                    prevIsValid = true;
81                } else {
82                    dst->quadTo(pts[0].fX, pts[0].fY, pts[0].fX + step.fX,
83                                pts[0].fY + step.fY);
84                }
85                if (drawSegment) {
86                    dst->lineTo(pts[1].fX - step.fX, pts[1].fY - step.fY);
87                }
88                lastCorner = pts[1];
89                prevIsValid = true;
90                break;
91            }
92            case SkPath::kQuad_Verb:
93                // TBD - just replicate the curve for now
94                if (!prevIsValid) {
95                    dst->moveTo(pts[0]);
96                    prevIsValid = true;
97                }
98                dst->quadTo(pts[1], pts[2]);
99                lastCorner = pts[2];
100                firstStep.set(0, 0);
101                break;
102            case SkPath::kConic_Verb:
103                // TBD - just replicate the curve for now
104                if (!prevIsValid) {
105                    dst->moveTo(pts[0]);
106                    prevIsValid = true;
107                }
108                dst->conicTo(pts[1], pts[2], iter.conicWeight());
109                lastCorner = pts[2];
110                firstStep.set(0, 0);
111                break;
112            case SkPath::kCubic_Verb:
113                if (!prevIsValid) {
114                    dst->moveTo(pts[0]);
115                    prevIsValid = true;
116                }
117                // TBD - just replicate the curve for now
118                dst->cubicTo(pts[1], pts[2], pts[3]);
119                lastCorner = pts[3];
120                firstStep.set(0, 0);
121                break;
122            case SkPath::kClose_Verb:
123                if (firstStep.fX || firstStep.fY) {
124                    dst->quadTo(lastCorner.fX, lastCorner.fY,
125                                lastCorner.fX + firstStep.fX,
126                                lastCorner.fY + firstStep.fY);
127                    }
128                dst->close();
129                prevIsValid = false;
130                break;
131            case SkPath::kDone_Verb:
132                if (prevIsValid) {
133                    dst->lineTo(lastCorner);
134                }
135                goto DONE;
136        }
137
138        if (SkPath::kMove_Verb == prevVerb) {
139            firstStep = step;
140        }
141        prevVerb = verb;
142    }
143DONE:
144    return true;
145}
146
147sk_sp<SkFlattenable> SkCornerPathEffect::CreateProc(SkReadBuffer& buffer) {
148    return SkCornerPathEffect::Make(buffer.readScalar());
149}
150
151void SkCornerPathEffect::flatten(SkWriteBuffer& buffer) const {
152    buffer.writeScalar(fRadius);
153}
154
155#ifndef SK_IGNORE_TO_STRING
156void SkCornerPathEffect::toString(SkString* str) const {
157    str->appendf("SkCornerPathEffect: (");
158    str->appendf("radius: %.2f", fRadius);
159    str->appendf(")");
160}
161#endif
162