• 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 
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