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 
SkCornerPathEffect(SkScalar radius)15 SkCornerPathEffect::SkCornerPathEffect(SkScalar radius) {
16     // check with ! to catch NaNs
17     if (!(radius > 0)) {
18         radius = 0;
19     }
20     fRadius = radius;
21 }
~SkCornerPathEffect()22 SkCornerPathEffect::~SkCornerPathEffect() {}
23 
ComputeStep(const SkPoint & a,const SkPoint & b,SkScalar radius,SkPoint * step)24 static 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 
filterPath(SkPath * dst,const SkPath & src,SkStrokeRec *,const SkRect *) const38 bool 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     }
143 DONE:
144     return true;
145 }
146 
CreateProc(SkReadBuffer & buffer)147 sk_sp<SkFlattenable> SkCornerPathEffect::CreateProc(SkReadBuffer& buffer) {
148     return SkCornerPathEffect::Make(buffer.readScalar());
149 }
150 
flatten(SkWriteBuffer & buffer) const151 void SkCornerPathEffect::flatten(SkWriteBuffer& buffer) const {
152     buffer.writeScalar(fRadius);
153 }
154 
155 #ifndef SK_IGNORE_TO_STRING
toString(SkString * str) const156 void SkCornerPathEffect::toString(SkString* str) const {
157     str->appendf("SkCornerPathEffect: (");
158     str->appendf("radius: %.2f", fRadius);
159     str->appendf(")");
160 }
161 #endif
162