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