xref: /aosp_15_r20/external/skia/src/effects/SkCornerPathEffect.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
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