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 "SkBuffer.h"
14
15SkCornerPathEffect::SkCornerPathEffect(SkScalar radius) : fRadius(radius)
16{
17}
18
19SkCornerPathEffect::~SkCornerPathEffect()
20{
21}
22
23static bool ComputeStep(const SkPoint& a, const SkPoint& b, SkScalar radius,
24                        SkPoint* step) {
25    SkScalar dist = SkPoint::Distance(a, b);
26
27    step->set(b.fX - a.fX, b.fY - a.fY);
28
29    if (dist <= radius * 2) {
30        step->scale(SK_ScalarHalf);
31        return false;
32    } else {
33        step->scale(SkScalarDiv(radius, dist));
34        return true;
35    }
36}
37
38bool SkCornerPathEffect::filterPath(SkPath* dst, const SkPath& src,
39                                    SkScalar* width) {
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    moveTo.set(0, 0);
55    firstStep.set(0, 0);
56    lastCorner.set(0, 0);
57
58    for (;;) {
59        switch (verb = iter.next(pts)) {
60            case SkPath::kMove_Verb:
61                    // close out the previous (open) contour
62                if (SkPath::kLine_Verb == prevVerb) {
63                    dst->lineTo(lastCorner);
64                }
65                closed = iter.isClosedContour();
66                if (closed) {
67                    moveTo = pts[0];
68                    prevIsValid = false;
69                } else {
70                    dst->moveTo(pts[0]);
71                    prevIsValid = true;
72                }
73                break;
74            case SkPath::kLine_Verb: {
75                bool drawSegment = ComputeStep(pts[0], pts[1], fRadius, &step);
76                // prev corner
77                if (!prevIsValid) {
78                    dst->moveTo(moveTo + step);
79                    prevIsValid = true;
80                } else {
81                    dst->quadTo(pts[0].fX, pts[0].fY, pts[0].fX + step.fX,
82                                pts[0].fY + step.fY);
83                }
84                if (drawSegment) {
85                    dst->lineTo(pts[1].fX - step.fX, pts[1].fY - step.fY);
86                }
87                lastCorner = pts[1];
88                prevIsValid = true;
89                break;
90            }
91            case SkPath::kQuad_Verb:
92                // TBD - just replicate the curve for now
93                if (!prevIsValid) {
94                    dst->moveTo(pts[0]);
95                    prevIsValid = true;
96                }
97                dst->quadTo(pts[1], pts[2]);
98                lastCorner = pts[2];
99                firstStep.set(0, 0);
100                break;
101            case SkPath::kCubic_Verb:
102                if (!prevIsValid) {
103                    dst->moveTo(pts[0]);
104                    prevIsValid = true;
105                }
106                // TBD - just replicate the curve for now
107                dst->cubicTo(pts[1], pts[2], pts[3]);
108                lastCorner = pts[3];
109                firstStep.set(0, 0);
110                break;
111            case SkPath::kClose_Verb:
112                if (firstStep.fX || firstStep.fY) {
113                    dst->quadTo(lastCorner.fX, lastCorner.fY,
114                                lastCorner.fX + firstStep.fX,
115                                lastCorner.fY + firstStep.fY);
116                    }
117                dst->close();
118                break;
119            case SkPath::kDone_Verb:
120                goto DONE;
121        }
122
123        if (SkPath::kMove_Verb == prevVerb) {
124            firstStep = step;
125        }
126        prevVerb = verb;
127    }
128DONE:
129    return true;
130}
131
132SkFlattenable::Factory SkCornerPathEffect::getFactory() {
133    return CreateProc;
134}
135
136void SkCornerPathEffect::flatten(SkFlattenableWriteBuffer& buffer) {
137    buffer.writeScalar(fRadius);
138}
139
140SkFlattenable* SkCornerPathEffect::CreateProc(SkFlattenableReadBuffer& buffer) {
141    return SkNEW_ARGS(SkCornerPathEffect, (buffer));
142}
143
144SkCornerPathEffect::SkCornerPathEffect(SkFlattenableReadBuffer& buffer) {
145    fRadius = buffer.readScalar();
146}
147
148SK_DEFINE_FLATTENABLE_REGISTRAR(SkCornerPathEffect)
149
150