/src/skia/src/effects/SkCornerPathEffect.cpp
Line | Count | Source (jump to first uncovered line) |
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 | | |
26 | | static bool ComputeStep(const SkPoint& a, const SkPoint& b, SkScalar radius, |
27 | 2.32M | SkPoint* step) { |
28 | 2.32M | SkScalar dist = SkPoint::Distance(a, b); |
29 | | |
30 | 2.32M | *step = b - a; |
31 | 2.32M | if (dist <= radius * 2) { |
32 | 1.61M | *step *= SK_ScalarHalf; |
33 | 1.61M | return false; |
34 | 1.61M | } else { |
35 | 709k | *step *= radius / dist; |
36 | 709k | return true; |
37 | 709k | } |
38 | 2.32M | } |
39 | | |
40 | | class SkCornerPathEffectImpl : public SkPathEffectBase { |
41 | | public: |
42 | 1.05M | explicit SkCornerPathEffectImpl(SkScalar radius) : fRadius(radius) { |
43 | 1.05M | SkASSERT(radius > 0); |
44 | 1.05M | } |
45 | | |
46 | | bool onFilterPath(SkPath* dst, const SkPath& src, SkStrokeRec*, const SkRect*, |
47 | 1.06M | const SkMatrix&) const override { |
48 | 1.06M | if (fRadius <= 0) { |
49 | 0 | return false; |
50 | 0 | } |
51 | | |
52 | 1.06M | SkPath::Iter iter(src, false); |
53 | 1.06M | SkPath::Verb verb, prevVerb = SkPath::kDone_Verb; |
54 | 1.06M | SkPoint pts[4]; |
55 | | |
56 | 1.06M | bool closed; |
57 | 1.06M | SkPoint moveTo, lastCorner; |
58 | 1.06M | SkVector firstStep, step; |
59 | 1.06M | bool prevIsValid = true; |
60 | | |
61 | | // to avoid warnings |
62 | 1.06M | step.set(0, 0); |
63 | 1.06M | moveTo.set(0, 0); |
64 | 1.06M | firstStep.set(0, 0); |
65 | 1.06M | lastCorner.set(0, 0); |
66 | | |
67 | 9.83M | for (;;) { |
68 | 9.83M | switch (verb = iter.next(pts)) { |
69 | 2.13M | case SkPath::kMove_Verb: |
70 | | // close out the previous (open) contour |
71 | 2.13M | if (SkPath::kLine_Verb == prevVerb) { |
72 | 275k | dst->lineTo(lastCorner); |
73 | 275k | } |
74 | 2.13M | closed = iter.isClosedContour(); |
75 | 2.13M | if (closed) { |
76 | 432k | moveTo = pts[0]; |
77 | 432k | prevIsValid = false; |
78 | 1.70M | } else { |
79 | 1.70M | dst->moveTo(pts[0]); |
80 | 1.70M | prevIsValid = true; |
81 | 1.70M | } |
82 | 2.13M | break; |
83 | 2.32M | case SkPath::kLine_Verb: { |
84 | 2.32M | bool drawSegment = ComputeStep(pts[0], pts[1], fRadius, &step); |
85 | | // prev corner |
86 | 2.32M | if (!prevIsValid) { |
87 | 115k | dst->moveTo(moveTo + step); |
88 | 115k | prevIsValid = true; |
89 | 2.20M | } else { |
90 | 2.20M | dst->quadTo(pts[0].fX, pts[0].fY, pts[0].fX + step.fX, |
91 | 2.20M | pts[0].fY + step.fY); |
92 | 2.20M | } |
93 | 2.32M | if (drawSegment) { |
94 | 709k | dst->lineTo(pts[1].fX - step.fX, pts[1].fY - step.fY); |
95 | 709k | } |
96 | 2.32M | lastCorner = pts[1]; |
97 | 2.32M | prevIsValid = true; |
98 | 2.32M | break; |
99 | 0 | } |
100 | 2.61M | case SkPath::kQuad_Verb: |
101 | | // TBD - just replicate the curve for now |
102 | 2.61M | if (!prevIsValid) { |
103 | 82.6k | dst->moveTo(pts[0]); |
104 | 82.6k | prevIsValid = true; |
105 | 82.6k | } |
106 | 2.61M | dst->quadTo(pts[1], pts[2]); |
107 | 2.61M | lastCorner = pts[2]; |
108 | 2.61M | firstStep.set(0, 0); |
109 | 2.61M | break; |
110 | 957k | case SkPath::kConic_Verb: |
111 | | // TBD - just replicate the curve for now |
112 | 957k | if (!prevIsValid) { |
113 | 87.8k | dst->moveTo(pts[0]); |
114 | 87.8k | prevIsValid = true; |
115 | 87.8k | } |
116 | 957k | dst->conicTo(pts[1], pts[2], iter.conicWeight()); |
117 | 957k | lastCorner = pts[2]; |
118 | 957k | firstStep.set(0, 0); |
119 | 957k | break; |
120 | 355k | case SkPath::kCubic_Verb: |
121 | 355k | if (!prevIsValid) { |
122 | 62.2k | dst->moveTo(pts[0]); |
123 | 62.2k | prevIsValid = true; |
124 | 62.2k | } |
125 | | // TBD - just replicate the curve for now |
126 | 355k | dst->cubicTo(pts[1], pts[2], pts[3]); |
127 | 355k | lastCorner = pts[3]; |
128 | 355k | firstStep.set(0, 0); |
129 | 355k | break; |
130 | 383k | case SkPath::kClose_Verb: |
131 | 383k | if (firstStep.fX || firstStep.fY) { |
132 | 71.8k | dst->quadTo(lastCorner.fX, lastCorner.fY, |
133 | 71.8k | lastCorner.fX + firstStep.fX, |
134 | 71.8k | lastCorner.fY + firstStep.fY); |
135 | 71.8k | } |
136 | 383k | dst->close(); |
137 | 383k | prevIsValid = false; |
138 | 383k | break; |
139 | 1.06M | case SkPath::kDone_Verb: |
140 | 1.06M | if (prevIsValid) { |
141 | 944k | dst->lineTo(lastCorner); |
142 | 944k | } |
143 | 1.06M | return true; |
144 | 0 | default: |
145 | 0 | SkDEBUGFAIL("default should not be reached"); |
146 | 0 | return false; |
147 | 9.83M | } |
148 | | |
149 | 8.76M | if (SkPath::kMove_Verb == prevVerb) { |
150 | 2.13M | firstStep = step; |
151 | 2.13M | } |
152 | 8.76M | prevVerb = verb; |
153 | 8.76M | } |
154 | 1.06M | } |
155 | | |
156 | 11.9k | 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 | 11.9k | return true; |
160 | 11.9k | } |
161 | | |
162 | 12 | static sk_sp<SkFlattenable> CreateProc(SkReadBuffer& buffer) { |
163 | 12 | return SkCornerPathEffect::Make(buffer.readScalar()); |
164 | 12 | } |
165 | | |
166 | 1.69k | void flatten(SkWriteBuffer& buffer) const override { |
167 | 1.69k | buffer.writeScalar(fRadius); |
168 | 1.69k | } |
169 | | |
170 | 1.69k | Factory getFactory() const override { return CreateProc; } |
171 | 1.69k | const char* getTypeName() const override { return "SkCornerPathEffect"; } |
172 | | |
173 | | private: |
174 | | const SkScalar fRadius; |
175 | | |
176 | | using INHERITED = SkPathEffectBase; |
177 | | }; |
178 | | |
179 | | ////////////////////////////////////////////////////////////////////////////////////////////////// |
180 | | |
181 | 1.29M | sk_sp<SkPathEffect> SkCornerPathEffect::Make(SkScalar radius) { |
182 | 1.29M | return SkIsFinite(radius) && (radius > 0) ? |
183 | 1.05M | sk_sp<SkPathEffect>(new SkCornerPathEffectImpl(radius)) : nullptr; |
184 | 1.29M | } |
185 | | |
186 | 3 | void SkCornerPathEffect::RegisterFlattenables() { |
187 | 3 | SkFlattenable::Register("SkCornerPathEffect", SkCornerPathEffectImpl::CreateProc); |
188 | 3 | } |