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 #include "include/effects/SkCornerPathEffect.h"
9
10 #include "include/core/SkFlattenable.h"
11 #include "include/core/SkPath.h"
12 #include "include/core/SkPathEffect.h"
13 #include "include/core/SkPoint.h"
14 #include "include/core/SkRefCnt.h"
15 #include "include/core/SkScalar.h"
16 #include "include/core/SkTypes.h"
17 #include "include/private/base/SkFloatingPoint.h"
18 #include "src/core/SkPathEffectBase.h"
19 #include "src/core/SkReadBuffer.h"
20 #include "src/core/SkWriteBuffer.h"
21
22 class SkMatrix;
23 class SkStrokeRec;
24 struct SkRect;
25
ComputeStep(const SkPoint & a,const SkPoint & b,SkScalar radius,SkPoint * step)26 static bool ComputeStep(const SkPoint& a, const SkPoint& b, SkScalar radius,
27 SkPoint* step) {
28 SkScalar dist = SkPoint::Distance(a, b);
29
30 *step = b - a;
31 if (dist <= radius * 2) {
32 *step *= SK_ScalarHalf;
33 return false;
34 } else {
35 *step *= radius / dist;
36 return true;
37 }
38 }
39
40 class SkCornerPathEffectImpl : public SkPathEffectBase {
41 public:
SkCornerPathEffectImpl(SkScalar radius)42 explicit SkCornerPathEffectImpl(SkScalar radius) : fRadius(radius) {
43 SkASSERT(radius > 0);
44 }
45
onFilterPath(SkPath * dst,const SkPath & src,SkStrokeRec *,const SkRect *,const SkMatrix &) const46 bool onFilterPath(SkPath* dst, const SkPath& src, SkStrokeRec*, const SkRect*,
47 const SkMatrix&) const override {
48 if (fRadius <= 0) {
49 return false;
50 }
51
52 SkPath::Iter iter(src, false);
53 SkPath::Verb verb, prevVerb = SkPath::kDone_Verb;
54 SkPoint pts[4];
55
56 bool closed;
57 SkPoint moveTo, lastCorner;
58 SkVector firstStep, step;
59 bool prevIsValid = true;
60
61 // to avoid warnings
62 step.set(0, 0);
63 moveTo.set(0, 0);
64 firstStep.set(0, 0);
65 lastCorner.set(0, 0);
66
67 for (;;) {
68 switch (verb = iter.next(pts)) {
69 case SkPath::kMove_Verb:
70 // close out the previous (open) contour
71 if (SkPath::kLine_Verb == prevVerb) {
72 dst->lineTo(lastCorner);
73 }
74 closed = iter.isClosedContour();
75 if (closed) {
76 moveTo = pts[0];
77 prevIsValid = false;
78 } else {
79 dst->moveTo(pts[0]);
80 prevIsValid = true;
81 }
82 break;
83 case SkPath::kLine_Verb: {
84 bool drawSegment = ComputeStep(pts[0], pts[1], fRadius, &step);
85 // prev corner
86 if (!prevIsValid) {
87 dst->moveTo(moveTo + step);
88 prevIsValid = true;
89 } else {
90 dst->quadTo(pts[0].fX, pts[0].fY, pts[0].fX + step.fX,
91 pts[0].fY + step.fY);
92 }
93 if (drawSegment) {
94 dst->lineTo(pts[1].fX - step.fX, pts[1].fY - step.fY);
95 }
96 lastCorner = pts[1];
97 prevIsValid = true;
98 break;
99 }
100 case SkPath::kQuad_Verb:
101 // TBD - just replicate the curve for now
102 if (!prevIsValid) {
103 dst->moveTo(pts[0]);
104 prevIsValid = true;
105 }
106 dst->quadTo(pts[1], pts[2]);
107 lastCorner = pts[2];
108 firstStep.set(0, 0);
109 break;
110 case SkPath::kConic_Verb:
111 // TBD - just replicate the curve for now
112 if (!prevIsValid) {
113 dst->moveTo(pts[0]);
114 prevIsValid = true;
115 }
116 dst->conicTo(pts[1], pts[2], iter.conicWeight());
117 lastCorner = pts[2];
118 firstStep.set(0, 0);
119 break;
120 case SkPath::kCubic_Verb:
121 if (!prevIsValid) {
122 dst->moveTo(pts[0]);
123 prevIsValid = true;
124 }
125 // TBD - just replicate the curve for now
126 dst->cubicTo(pts[1], pts[2], pts[3]);
127 lastCorner = pts[3];
128 firstStep.set(0, 0);
129 break;
130 case SkPath::kClose_Verb:
131 if (firstStep.fX || firstStep.fY) {
132 dst->quadTo(lastCorner.fX, lastCorner.fY,
133 lastCorner.fX + firstStep.fX,
134 lastCorner.fY + firstStep.fY);
135 }
136 dst->close();
137 prevIsValid = false;
138 break;
139 case SkPath::kDone_Verb:
140 if (prevIsValid) {
141 dst->lineTo(lastCorner);
142 }
143 return true;
144 default:
145 SkDEBUGFAIL("default should not be reached");
146 return false;
147 }
148
149 if (SkPath::kMove_Verb == prevVerb) {
150 firstStep = step;
151 }
152 prevVerb = verb;
153 }
154 }
155
computeFastBounds(SkRect *) const156 bool computeFastBounds(SkRect*) const override {
157 // Rounding sharp corners within a path produces a new path that is still contained within
158 // the original's bounds, so leave 'bounds' unmodified.
159 return true;
160 }
161
CreateProc(SkReadBuffer & buffer)162 static sk_sp<SkFlattenable> CreateProc(SkReadBuffer& buffer) {
163 return SkCornerPathEffect::Make(buffer.readScalar());
164 }
165
flatten(SkWriteBuffer & buffer) const166 void flatten(SkWriteBuffer& buffer) const override {
167 buffer.writeScalar(fRadius);
168 }
169
getFactory() const170 Factory getFactory() const override { return CreateProc; }
getTypeName() const171 const char* getTypeName() const override { return "SkCornerPathEffect"; }
172
173 private:
174 const SkScalar fRadius;
175
176 using INHERITED = SkPathEffectBase;
177 };
178
179 //////////////////////////////////////////////////////////////////////////////////////////////////
180
Make(SkScalar radius)181 sk_sp<SkPathEffect> SkCornerPathEffect::Make(SkScalar radius) {
182 return SkIsFinite(radius) && (radius > 0) ?
183 sk_sp<SkPathEffect>(new SkCornerPathEffectImpl(radius)) : nullptr;
184 }
185
RegisterFlattenables()186 void SkCornerPathEffect::RegisterFlattenables() {
187 SkFlattenable::Register("SkCornerPathEffect", SkCornerPathEffectImpl::CreateProc);
188 }
189