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