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