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 
SkCornerPathEffect(SkScalar radius)16 SkCornerPathEffect::SkCornerPathEffect(SkScalar radius) : fRadius(radius) {}
~SkCornerPathEffect()17 SkCornerPathEffect::~SkCornerPathEffect() {}
18 
ComputeStep(const SkPoint & a,const SkPoint & b,SkScalar radius,SkPoint * step)19 static 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 
filterPath(SkPath * dst,const SkPath & src,SkStrokeRec *,const SkRect *) const33 bool 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     }
138 DONE:
139     return true;
140 }
141 
CreateProc(SkReadBuffer & buffer)142 SkFlattenable* SkCornerPathEffect::CreateProc(SkReadBuffer& buffer) {
143     return SkCornerPathEffect::Create(buffer.readScalar());
144 }
145 
flatten(SkWriteBuffer & buffer) const146 void SkCornerPathEffect::flatten(SkWriteBuffer& buffer) const {
147     buffer.writeScalar(fRadius);
148 }
149 
150 #ifndef SK_IGNORE_TO_STRING
toString(SkString * str) const151 void SkCornerPathEffect::toString(SkString* str) const {
152     str->appendf("SkCornerPathEffect: (");
153     str->appendf("radius: %.2f", fRadius);
154     str->appendf(")");
155 }
156 #endif
157