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