/src/skia/src/gpu/ganesh/effects/GrRRectEffect.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Copyright 2014 Google Inc. |
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 "src/gpu/ganesh/effects/GrRRectEffect.h" |
9 | | |
10 | | #include "include/core/SkPoint.h" |
11 | | #include "include/core/SkRRect.h" |
12 | | #include "include/core/SkRect.h" |
13 | | #include "include/core/SkScalar.h" |
14 | | #include "include/core/SkString.h" |
15 | | #include "include/private/base/SkAssert.h" |
16 | | #include "include/private/gpu/ganesh/GrTypesPriv.h" |
17 | | #include "src/base/SkRandom.h" |
18 | | #include "src/base/SkTLazy.h" |
19 | | #include "src/core/SkRRectPriv.h" |
20 | | #include "src/core/SkSLTypeShared.h" |
21 | | #include "src/gpu/KeyBuilder.h" |
22 | | #include "src/gpu/ganesh/GrCaps.h" |
23 | | #include "src/gpu/ganesh/GrFragmentProcessor.h" |
24 | | #include "src/gpu/ganesh/GrProcessorUnitTest.h" |
25 | | #include "src/gpu/ganesh/GrShaderCaps.h" |
26 | | #include "src/gpu/ganesh/effects/GrOvalEffect.h" |
27 | | #include "src/gpu/ganesh/glsl/GrGLSLFragmentShaderBuilder.h" |
28 | | #include "src/gpu/ganesh/glsl/GrGLSLProgramDataManager.h" |
29 | | #include "src/gpu/ganesh/glsl/GrGLSLUniformHandler.h" |
30 | | |
31 | | #include <algorithm> |
32 | | #include <cstdint> |
33 | | #include <tuple> |
34 | | #include <utility> |
35 | | |
36 | | // The effects defined here only handle rrect radii >= kRadiusMin. |
37 | | static const SkScalar kRadiusMin = SK_ScalarHalf; |
38 | | |
39 | | ////////////////////////////////////////////////////////////////////////////// |
40 | | |
41 | | namespace { |
42 | | class CircularRRectEffect : public GrFragmentProcessor { |
43 | | public: |
44 | | enum CornerFlags { |
45 | | kTopLeft_CornerFlag = (1 << SkRRect::kUpperLeft_Corner), |
46 | | kTopRight_CornerFlag = (1 << SkRRect::kUpperRight_Corner), |
47 | | kBottomRight_CornerFlag = (1 << SkRRect::kLowerRight_Corner), |
48 | | kBottomLeft_CornerFlag = (1 << SkRRect::kLowerLeft_Corner), |
49 | | |
50 | | kLeft_CornerFlags = kTopLeft_CornerFlag | kBottomLeft_CornerFlag, |
51 | | kTop_CornerFlags = kTopLeft_CornerFlag | kTopRight_CornerFlag, |
52 | | kRight_CornerFlags = kTopRight_CornerFlag | kBottomRight_CornerFlag, |
53 | | kBottom_CornerFlags = kBottomLeft_CornerFlag | kBottomRight_CornerFlag, |
54 | | |
55 | | kAll_CornerFlags = kTopLeft_CornerFlag | kTopRight_CornerFlag | |
56 | | kBottomLeft_CornerFlag | kBottomRight_CornerFlag, |
57 | | |
58 | | kNone_CornerFlags = 0 |
59 | | }; |
60 | | |
61 | | // The flags are used to indicate which corners are circluar (unflagged corners are assumed to |
62 | | // be square). |
63 | | static GrFPResult Make(std::unique_ptr<GrFragmentProcessor>, GrClipEdgeType, |
64 | | uint32_t circularCornerFlags, const SkRRect&); |
65 | | |
66 | 0 | ~CircularRRectEffect() override {} |
67 | | |
68 | 0 | const char* name() const override { return "CircularRRect"; } |
69 | | |
70 | | std::unique_ptr<GrFragmentProcessor> clone() const override; |
71 | | |
72 | | private: |
73 | | class Impl; |
74 | | |
75 | | CircularRRectEffect(std::unique_ptr<GrFragmentProcessor> inputFP, |
76 | | GrClipEdgeType, uint32_t circularCornerFlags, const SkRRect&); |
77 | | CircularRRectEffect(const CircularRRectEffect& that); |
78 | | |
79 | | std::unique_ptr<ProgramImpl> onMakeProgramImpl() const override; |
80 | | |
81 | | void onAddToKey(const GrShaderCaps&, skgpu::KeyBuilder*) const override; |
82 | | |
83 | | bool onIsEqual(const GrFragmentProcessor& other) const override; |
84 | | |
85 | | SkRRect fRRect; |
86 | | GrClipEdgeType fEdgeType; |
87 | | uint32_t fCircularCornerFlags; |
88 | | |
89 | | GR_DECLARE_FRAGMENT_PROCESSOR_TEST |
90 | | |
91 | | using INHERITED = GrFragmentProcessor; |
92 | | }; |
93 | | } // anonymous namespace |
94 | | |
95 | | GrFPResult CircularRRectEffect::Make(std::unique_ptr<GrFragmentProcessor> inputFP, |
96 | | GrClipEdgeType edgeType, |
97 | 4.06k | uint32_t circularCornerFlags, const SkRRect& rrect) { |
98 | 4.06k | if (GrClipEdgeType::kFillAA != edgeType && GrClipEdgeType::kInverseFillAA != edgeType) { |
99 | 1.20k | return GrFPFailure(std::move(inputFP)); |
100 | 1.20k | } |
101 | 2.85k | return GrFPSuccess(std::unique_ptr<GrFragmentProcessor>( |
102 | 2.85k | new CircularRRectEffect(std::move(inputFP), edgeType, circularCornerFlags, rrect))); |
103 | 4.06k | } |
104 | | |
105 | | CircularRRectEffect::CircularRRectEffect(std::unique_ptr<GrFragmentProcessor> inputFP, |
106 | | GrClipEdgeType edgeType, |
107 | | uint32_t circularCornerFlags, |
108 | | const SkRRect& rrect) |
109 | 2.85k | : INHERITED(kCircularRRectEffect_ClassID, |
110 | 2.85k | ProcessorOptimizationFlags(inputFP.get()) & |
111 | 2.85k | kCompatibleWithCoverageAsAlpha_OptimizationFlag) |
112 | 2.85k | , fRRect(rrect) |
113 | 2.85k | , fEdgeType(edgeType) |
114 | 2.85k | , fCircularCornerFlags(circularCornerFlags) { |
115 | 2.85k | this->registerChild(std::move(inputFP)); |
116 | 2.85k | } |
117 | | |
118 | | CircularRRectEffect::CircularRRectEffect(const CircularRRectEffect& that) |
119 | 0 | : INHERITED(that) |
120 | 0 | , fRRect(that.fRRect) |
121 | 0 | , fEdgeType(that.fEdgeType) |
122 | 0 | , fCircularCornerFlags(that.fCircularCornerFlags) {} |
123 | | |
124 | 0 | std::unique_ptr<GrFragmentProcessor> CircularRRectEffect::clone() const { |
125 | 0 | return std::unique_ptr<GrFragmentProcessor>(new CircularRRectEffect(*this)); |
126 | 0 | } |
127 | | |
128 | 1.86k | bool CircularRRectEffect::onIsEqual(const GrFragmentProcessor& other) const { |
129 | 1.86k | const CircularRRectEffect& crre = other.cast<CircularRRectEffect>(); |
130 | | // The corner flags are derived from fRRect, so no need to check them. |
131 | 1.86k | return fEdgeType == crre.fEdgeType && fRRect == crre.fRRect; |
132 | 1.86k | } |
133 | | |
134 | | ////////////////////////////////////////////////////////////////////////////// |
135 | | |
136 | | GR_DEFINE_FRAGMENT_PROCESSOR_TEST(CircularRRectEffect) |
137 | | |
138 | | #if defined(GPU_TEST_UTILS) |
139 | 0 | std::unique_ptr<GrFragmentProcessor> CircularRRectEffect::TestCreate(GrProcessorTestData* d) { |
140 | 0 | SkScalar w = d->fRandom->nextRangeScalar(20.f, 1000.f); |
141 | 0 | SkScalar h = d->fRandom->nextRangeScalar(20.f, 1000.f); |
142 | 0 | SkScalar r = d->fRandom->nextRangeF(kRadiusMin, 9.f); |
143 | 0 | SkRRect rrect; |
144 | 0 | rrect.setRectXY(SkRect::MakeWH(w, h), r, r); |
145 | 0 | std::unique_ptr<GrFragmentProcessor> fp = d->inputFP(); |
146 | 0 | bool success; |
147 | 0 | do { |
148 | 0 | GrClipEdgeType et = |
149 | 0 | (GrClipEdgeType)d->fRandom->nextULessThan(kGrClipEdgeTypeCnt); |
150 | 0 | std::tie(success, fp) = GrRRectEffect::Make(std::move(fp), et, rrect, |
151 | 0 | *d->caps()->shaderCaps()); |
152 | 0 | } while (!success); |
153 | 0 | return fp; |
154 | 0 | } |
155 | | #endif |
156 | | |
157 | | ////////////////////////////////////////////////////////////////////////////// |
158 | | |
159 | | class CircularRRectEffect::Impl : public ProgramImpl { |
160 | | public: |
161 | | void emitCode(EmitArgs&) override; |
162 | | |
163 | | private: |
164 | | void onSetData(const GrGLSLProgramDataManager&, const GrFragmentProcessor&) override; |
165 | | |
166 | | GrGLSLProgramDataManager::UniformHandle fInnerRectUniform; |
167 | | GrGLSLProgramDataManager::UniformHandle fRadiusPlusHalfUniform; |
168 | | SkRRect fPrevRRect; |
169 | | }; |
170 | | |
171 | 0 | void CircularRRectEffect::Impl::emitCode(EmitArgs& args) { |
172 | 0 | const CircularRRectEffect& crre = args.fFp.cast<CircularRRectEffect>(); |
173 | 0 | GrGLSLUniformHandler* uniformHandler = args.fUniformHandler; |
174 | 0 | const char *rectName; |
175 | 0 | const char *radiusPlusHalfName; |
176 | | // The inner rect is the rrect bounds inset by the radius. Its left, top, right, and bottom |
177 | | // edges correspond to components x, y, z, and w, respectively. When a side of the rrect has |
178 | | // only rectangular corners, that side's value corresponds to the rect edge's value outset by |
179 | | // half a pixel. |
180 | 0 | fInnerRectUniform = uniformHandler->addUniform(&crre, kFragment_GrShaderFlag, SkSLType::kFloat4, |
181 | 0 | "innerRect", &rectName); |
182 | | // x is (r + .5) and y is 1/(r + .5) |
183 | 0 | fRadiusPlusHalfUniform = uniformHandler->addUniform(&crre, kFragment_GrShaderFlag, |
184 | 0 | SkSLType::kHalf2, "radiusPlusHalf", |
185 | 0 | &radiusPlusHalfName); |
186 | | |
187 | | // If we're on a device where float != fp32 then the length calculation could overflow. |
188 | 0 | SkString clampedCircleDistance; |
189 | 0 | if (!args.fShaderCaps->fFloatIs32Bits) { |
190 | 0 | clampedCircleDistance.printf("saturate(%s.x * (1.0 - length(dxy * %s.y)))", |
191 | 0 | radiusPlusHalfName, radiusPlusHalfName); |
192 | 0 | } else { |
193 | 0 | clampedCircleDistance.printf("saturate(%s.x - length(dxy))", radiusPlusHalfName); |
194 | 0 | } |
195 | |
|
196 | 0 | GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder; |
197 | | // At each quarter-circle corner we compute a vector that is the offset of the fragment position |
198 | | // from the circle center. The vector is pinned in x and y to be in the quarter-plane relevant |
199 | | // to that corner. This means that points near the interior near the rrect top edge will have |
200 | | // a vector that points straight up for both the TL left and TR corners. Computing an |
201 | | // alpha from this vector at either the TR or TL corner will give the correct result. Similarly, |
202 | | // fragments near the other three edges will get the correct AA. Fragments in the interior of |
203 | | // the rrect will have a (0,0) vector at all four corners. So long as the radius > 0.5 they will |
204 | | // correctly produce an alpha value of 1 at all four corners. We take the min of all the alphas. |
205 | | // The code below is a simplified version of the above that performs maxs on the vector |
206 | | // components before computing distances and alpha values so that only one distance computation |
207 | | // need be computed to determine the min alpha. |
208 | | // |
209 | | // For the cases where one half of the rrect is rectangular we drop one of the x or y |
210 | | // computations, compute a separate rect edge alpha for the rect side, and mul the two computed |
211 | | // alphas together. |
212 | 0 | switch (crre.fCircularCornerFlags) { |
213 | 0 | case CircularRRectEffect::kAll_CornerFlags: |
214 | 0 | fragBuilder->codeAppendf("float2 dxy0 = %s.LT - sk_FragCoord.xy;", rectName); |
215 | 0 | fragBuilder->codeAppendf("float2 dxy1 = sk_FragCoord.xy - %s.RB;", rectName); |
216 | 0 | fragBuilder->codeAppend("float2 dxy = max(max(dxy0, dxy1), 0.0);"); |
217 | 0 | fragBuilder->codeAppendf("half alpha = half(%s);", clampedCircleDistance.c_str()); |
218 | 0 | break; |
219 | 0 | case CircularRRectEffect::kTopLeft_CornerFlag: |
220 | 0 | fragBuilder->codeAppendf("float2 dxy = max(%s.LT - sk_FragCoord.xy, 0.0);", |
221 | 0 | rectName); |
222 | 0 | fragBuilder->codeAppendf("half rightAlpha = half(saturate(%s.R - sk_FragCoord.x));", |
223 | 0 | rectName); |
224 | 0 | fragBuilder->codeAppendf("half bottomAlpha = half(saturate(%s.B - sk_FragCoord.y));", |
225 | 0 | rectName); |
226 | 0 | fragBuilder->codeAppendf("half alpha = bottomAlpha * rightAlpha * half(%s);", |
227 | 0 | clampedCircleDistance.c_str()); |
228 | 0 | break; |
229 | 0 | case CircularRRectEffect::kTopRight_CornerFlag: |
230 | 0 | fragBuilder->codeAppendf("float2 dxy = max(float2(sk_FragCoord.x - %s.R, " |
231 | 0 | "%s.T - sk_FragCoord.y), 0.0);", |
232 | 0 | rectName, rectName); |
233 | 0 | fragBuilder->codeAppendf("half leftAlpha = half(saturate(sk_FragCoord.x - %s.L));", |
234 | 0 | rectName); |
235 | 0 | fragBuilder->codeAppendf("half bottomAlpha = half(saturate(%s.B - sk_FragCoord.y));", |
236 | 0 | rectName); |
237 | 0 | fragBuilder->codeAppendf("half alpha = bottomAlpha * leftAlpha * half(%s);", |
238 | 0 | clampedCircleDistance.c_str()); |
239 | 0 | break; |
240 | 0 | case CircularRRectEffect::kBottomRight_CornerFlag: |
241 | 0 | fragBuilder->codeAppendf("float2 dxy = max(sk_FragCoord.xy - %s.RB, 0.0);", |
242 | 0 | rectName); |
243 | 0 | fragBuilder->codeAppendf("half leftAlpha = half(saturate(sk_FragCoord.x - %s.L));", |
244 | 0 | rectName); |
245 | 0 | fragBuilder->codeAppendf("half topAlpha = half(saturate(sk_FragCoord.y - %s.T));", |
246 | 0 | rectName); |
247 | 0 | fragBuilder->codeAppendf("half alpha = topAlpha * leftAlpha * half(%s);", |
248 | 0 | clampedCircleDistance.c_str()); |
249 | 0 | break; |
250 | 0 | case CircularRRectEffect::kBottomLeft_CornerFlag: |
251 | 0 | fragBuilder->codeAppendf("float2 dxy = max(float2(%s.L - sk_FragCoord.x, " |
252 | 0 | "sk_FragCoord.y - %s.B), 0.0);", |
253 | 0 | rectName, rectName); |
254 | 0 | fragBuilder->codeAppendf("half rightAlpha = half(saturate(%s.R - sk_FragCoord.x));", |
255 | 0 | rectName); |
256 | 0 | fragBuilder->codeAppendf("half topAlpha = half(saturate(sk_FragCoord.y - %s.T));", |
257 | 0 | rectName); |
258 | 0 | fragBuilder->codeAppendf("half alpha = topAlpha * rightAlpha * half(%s);", |
259 | 0 | clampedCircleDistance.c_str()); |
260 | 0 | break; |
261 | 0 | case CircularRRectEffect::kLeft_CornerFlags: |
262 | 0 | fragBuilder->codeAppendf("float2 dxy0 = %s.LT - sk_FragCoord.xy;", rectName); |
263 | 0 | fragBuilder->codeAppendf("float dy1 = sk_FragCoord.y - %s.B;", rectName); |
264 | 0 | fragBuilder->codeAppend("float2 dxy = max(float2(dxy0.x, max(dxy0.y, dy1)), 0.0);"); |
265 | 0 | fragBuilder->codeAppendf("half rightAlpha = half(saturate(%s.R - sk_FragCoord.x));", |
266 | 0 | rectName); |
267 | 0 | fragBuilder->codeAppendf("half alpha = rightAlpha * half(%s);", |
268 | 0 | clampedCircleDistance.c_str()); |
269 | 0 | break; |
270 | 0 | case CircularRRectEffect::kTop_CornerFlags: |
271 | 0 | fragBuilder->codeAppendf("float2 dxy0 = %s.LT - sk_FragCoord.xy;", rectName); |
272 | 0 | fragBuilder->codeAppendf("float dx1 = sk_FragCoord.x - %s.R;", rectName); |
273 | 0 | fragBuilder->codeAppend("float2 dxy = max(float2(max(dxy0.x, dx1), dxy0.y), 0.0);"); |
274 | 0 | fragBuilder->codeAppendf("half bottomAlpha = half(saturate(%s.B - sk_FragCoord.y));", |
275 | 0 | rectName); |
276 | 0 | fragBuilder->codeAppendf("half alpha = bottomAlpha * half(%s);", |
277 | 0 | clampedCircleDistance.c_str()); |
278 | 0 | break; |
279 | 0 | case CircularRRectEffect::kRight_CornerFlags: |
280 | 0 | fragBuilder->codeAppendf("float dy0 = %s.T - sk_FragCoord.y;", rectName); |
281 | 0 | fragBuilder->codeAppendf("float2 dxy1 = sk_FragCoord.xy - %s.RB;", rectName); |
282 | 0 | fragBuilder->codeAppend("float2 dxy = max(float2(dxy1.x, max(dy0, dxy1.y)), 0.0);"); |
283 | 0 | fragBuilder->codeAppendf("half leftAlpha = half(saturate(sk_FragCoord.x - %s.L));", |
284 | 0 | rectName); |
285 | 0 | fragBuilder->codeAppendf("half alpha = leftAlpha * half(%s);", |
286 | 0 | clampedCircleDistance.c_str()); |
287 | 0 | break; |
288 | 0 | case CircularRRectEffect::kBottom_CornerFlags: |
289 | 0 | fragBuilder->codeAppendf("float dx0 = %s.L - sk_FragCoord.x;", rectName); |
290 | 0 | fragBuilder->codeAppendf("float2 dxy1 = sk_FragCoord.xy - %s.RB;", rectName); |
291 | 0 | fragBuilder->codeAppend("float2 dxy = max(float2(max(dx0, dxy1.x), dxy1.y), 0.0);"); |
292 | 0 | fragBuilder->codeAppendf("half topAlpha = half(saturate(sk_FragCoord.y - %s.T));", |
293 | 0 | rectName); |
294 | 0 | fragBuilder->codeAppendf("half alpha = topAlpha * half(%s);", |
295 | 0 | clampedCircleDistance.c_str()); |
296 | 0 | break; |
297 | 0 | } |
298 | | |
299 | 0 | if (GrClipEdgeType::kInverseFillAA == crre.fEdgeType) { |
300 | 0 | fragBuilder->codeAppend("alpha = 1.0 - alpha;"); |
301 | 0 | } |
302 | |
|
303 | 0 | SkString inputSample = this->invokeChild(/*childIndex=*/0, args); |
304 | |
|
305 | 0 | fragBuilder->codeAppendf("return %s * alpha;", inputSample.c_str()); |
306 | 0 | } |
307 | | |
308 | | void CircularRRectEffect::Impl::onSetData(const GrGLSLProgramDataManager& pdman, |
309 | 0 | const GrFragmentProcessor& processor) { |
310 | 0 | const CircularRRectEffect& crre = processor.cast<CircularRRectEffect>(); |
311 | 0 | const SkRRect& rrect = crre.fRRect; |
312 | 0 | if (rrect != fPrevRRect) { |
313 | 0 | SkRect rect = rrect.getBounds(); |
314 | 0 | SkScalar radius = 0; |
315 | 0 | switch (crre.fCircularCornerFlags) { |
316 | 0 | case CircularRRectEffect::kAll_CornerFlags: |
317 | 0 | SkASSERT(SkRRectPriv::IsSimpleCircular(rrect)); |
318 | 0 | radius = SkRRectPriv::GetSimpleRadii(rrect).fX; |
319 | 0 | SkASSERT(radius >= kRadiusMin); |
320 | 0 | rect.inset(radius, radius); |
321 | 0 | break; |
322 | 0 | case CircularRRectEffect::kTopLeft_CornerFlag: |
323 | 0 | radius = rrect.radii(SkRRect::kUpperLeft_Corner).fX; |
324 | 0 | rect.fLeft += radius; |
325 | 0 | rect.fTop += radius; |
326 | 0 | rect.fRight += 0.5f; |
327 | 0 | rect.fBottom += 0.5f; |
328 | 0 | break; |
329 | 0 | case CircularRRectEffect::kTopRight_CornerFlag: |
330 | 0 | radius = rrect.radii(SkRRect::kUpperRight_Corner).fX; |
331 | 0 | rect.fLeft -= 0.5f; |
332 | 0 | rect.fTop += radius; |
333 | 0 | rect.fRight -= radius; |
334 | 0 | rect.fBottom += 0.5f; |
335 | 0 | break; |
336 | 0 | case CircularRRectEffect::kBottomRight_CornerFlag: |
337 | 0 | radius = rrect.radii(SkRRect::kLowerRight_Corner).fX; |
338 | 0 | rect.fLeft -= 0.5f; |
339 | 0 | rect.fTop -= 0.5f; |
340 | 0 | rect.fRight -= radius; |
341 | 0 | rect.fBottom -= radius; |
342 | 0 | break; |
343 | 0 | case CircularRRectEffect::kBottomLeft_CornerFlag: |
344 | 0 | radius = rrect.radii(SkRRect::kLowerLeft_Corner).fX; |
345 | 0 | rect.fLeft += radius; |
346 | 0 | rect.fTop -= 0.5f; |
347 | 0 | rect.fRight += 0.5f; |
348 | 0 | rect.fBottom -= radius; |
349 | 0 | break; |
350 | 0 | case CircularRRectEffect::kLeft_CornerFlags: |
351 | 0 | radius = rrect.radii(SkRRect::kUpperLeft_Corner).fX; |
352 | 0 | rect.fLeft += radius; |
353 | 0 | rect.fTop += radius; |
354 | 0 | rect.fRight += 0.5f; |
355 | 0 | rect.fBottom -= radius; |
356 | 0 | break; |
357 | 0 | case CircularRRectEffect::kTop_CornerFlags: |
358 | 0 | radius = rrect.radii(SkRRect::kUpperLeft_Corner).fX; |
359 | 0 | rect.fLeft += radius; |
360 | 0 | rect.fTop += radius; |
361 | 0 | rect.fRight -= radius; |
362 | 0 | rect.fBottom += 0.5f; |
363 | 0 | break; |
364 | 0 | case CircularRRectEffect::kRight_CornerFlags: |
365 | 0 | radius = rrect.radii(SkRRect::kUpperRight_Corner).fX; |
366 | 0 | rect.fLeft -= 0.5f; |
367 | 0 | rect.fTop += radius; |
368 | 0 | rect.fRight -= radius; |
369 | 0 | rect.fBottom -= radius; |
370 | 0 | break; |
371 | 0 | case CircularRRectEffect::kBottom_CornerFlags: |
372 | 0 | radius = rrect.radii(SkRRect::kLowerLeft_Corner).fX; |
373 | 0 | rect.fLeft += radius; |
374 | 0 | rect.fTop -= 0.5f; |
375 | 0 | rect.fRight -= radius; |
376 | 0 | rect.fBottom -= radius; |
377 | 0 | break; |
378 | 0 | default: |
379 | 0 | SK_ABORT("Should have been one of the above cases."); |
380 | 0 | } |
381 | 0 | pdman.set4f(fInnerRectUniform, rect.fLeft, rect.fTop, rect.fRight, rect.fBottom); |
382 | 0 | radius += 0.5f; |
383 | 0 | pdman.set2f(fRadiusPlusHalfUniform, radius, 1.f / radius); |
384 | 0 | fPrevRRect = rrect; |
385 | 0 | } |
386 | 0 | } Unexecuted instantiation: GrRRectEffect.cpp:(anonymous namespace)::CircularRRectEffect::Impl::onSetData(GrGLSLProgramDataManager const&, GrFragmentProcessor const&) Unexecuted instantiation: GrRRectEffect.cpp:(anonymous namespace)::CircularRRectEffect::Impl::onSetData(GrGLSLProgramDataManager const&, GrFragmentProcessor const&) |
387 | | |
388 | | //////////////////////////////////////////////////////////////////////////////////////////////////// |
389 | | |
390 | 0 | void CircularRRectEffect::onAddToKey(const GrShaderCaps& caps, skgpu::KeyBuilder* b) const { |
391 | 0 | static_assert(kGrClipEdgeTypeCnt <= 8); |
392 | 0 | b->add32((fCircularCornerFlags << 3) | static_cast<int>(fEdgeType)); |
393 | 0 | } |
394 | | |
395 | 0 | std::unique_ptr<GrFragmentProcessor::ProgramImpl> CircularRRectEffect::onMakeProgramImpl() const { |
396 | 0 | return std::make_unique<Impl>(); |
397 | 0 | } |
398 | | |
399 | | ////////////////////////////////////////////////////////////////////////////// |
400 | | |
401 | | namespace { |
402 | | class EllipticalRRectEffect : public GrFragmentProcessor { |
403 | | public: |
404 | | static GrFPResult Make(std::unique_ptr<GrFragmentProcessor>, GrClipEdgeType, const SkRRect&); |
405 | | |
406 | 0 | ~EllipticalRRectEffect() override {} |
407 | | |
408 | 0 | const char* name() const override { return "EllipticalRRect"; } |
409 | | |
410 | | std::unique_ptr<GrFragmentProcessor> clone() const override; |
411 | | |
412 | | private: |
413 | | class Impl; |
414 | | |
415 | | EllipticalRRectEffect(std::unique_ptr<GrFragmentProcessor>, GrClipEdgeType, const SkRRect&); |
416 | | EllipticalRRectEffect(const EllipticalRRectEffect& that); |
417 | | |
418 | | std::unique_ptr<ProgramImpl> onMakeProgramImpl() const override; |
419 | | |
420 | | void onAddToKey(const GrShaderCaps&, skgpu::KeyBuilder*) const override; |
421 | | |
422 | | bool onIsEqual(const GrFragmentProcessor& other) const override; |
423 | | |
424 | | SkRRect fRRect; |
425 | | GrClipEdgeType fEdgeType; |
426 | | |
427 | | GR_DECLARE_FRAGMENT_PROCESSOR_TEST |
428 | | |
429 | | using INHERITED = GrFragmentProcessor; |
430 | | }; |
431 | | |
432 | | GrFPResult EllipticalRRectEffect::Make(std::unique_ptr<GrFragmentProcessor> inputFP, |
433 | | GrClipEdgeType edgeType, |
434 | 2.64k | const SkRRect& rrect) { |
435 | 2.64k | if (GrClipEdgeType::kFillAA != edgeType && GrClipEdgeType::kInverseFillAA != edgeType) { |
436 | 1.34k | return GrFPFailure(std::move(inputFP)); |
437 | 1.34k | } |
438 | 1.30k | return GrFPSuccess(std::unique_ptr<GrFragmentProcessor>( |
439 | 1.30k | new EllipticalRRectEffect(std::move(inputFP), edgeType, rrect))); |
440 | 2.64k | } |
441 | | |
442 | | EllipticalRRectEffect::EllipticalRRectEffect(std::unique_ptr<GrFragmentProcessor> inputFP, |
443 | | GrClipEdgeType edgeType, |
444 | | const SkRRect& rrect) |
445 | 1.30k | : INHERITED(kEllipticalRRectEffect_ClassID, |
446 | 1.30k | ProcessorOptimizationFlags(inputFP.get()) & |
447 | 1.30k | kCompatibleWithCoverageAsAlpha_OptimizationFlag) |
448 | 1.30k | , fRRect(rrect) |
449 | 1.30k | , fEdgeType(edgeType) { |
450 | 1.30k | this->registerChild(std::move(inputFP)); |
451 | 1.30k | } |
452 | | |
453 | | EllipticalRRectEffect::EllipticalRRectEffect(const EllipticalRRectEffect& that) |
454 | 0 | : INHERITED(that) |
455 | 0 | , fRRect(that.fRRect) |
456 | 0 | , fEdgeType(that.fEdgeType) {} |
457 | | |
458 | 0 | std::unique_ptr<GrFragmentProcessor> EllipticalRRectEffect::clone() const { |
459 | 0 | return std::unique_ptr<GrFragmentProcessor>(new EllipticalRRectEffect(*this)); |
460 | 0 | } |
461 | | |
462 | 1.39k | bool EllipticalRRectEffect::onIsEqual(const GrFragmentProcessor& other) const { |
463 | 1.39k | const EllipticalRRectEffect& erre = other.cast<EllipticalRRectEffect>(); |
464 | 1.39k | return fEdgeType == erre.fEdgeType && fRRect == erre.fRRect; |
465 | 1.39k | } |
466 | | } // anonymous namespace |
467 | | |
468 | | ////////////////////////////////////////////////////////////////////////////// |
469 | | |
470 | | GR_DEFINE_FRAGMENT_PROCESSOR_TEST(EllipticalRRectEffect) |
471 | | |
472 | | #if defined(GPU_TEST_UTILS) |
473 | 0 | std::unique_ptr<GrFragmentProcessor> EllipticalRRectEffect::TestCreate(GrProcessorTestData* d) { |
474 | 0 | SkScalar w = d->fRandom->nextRangeScalar(20.f, 1000.f); |
475 | 0 | SkScalar h = d->fRandom->nextRangeScalar(20.f, 1000.f); |
476 | 0 | SkVector r[4]; |
477 | 0 | r[SkRRect::kUpperLeft_Corner].fX = d->fRandom->nextRangeF(kRadiusMin, 9.f); |
478 | | // ensure at least one corner really is elliptical |
479 | 0 | do { |
480 | 0 | r[SkRRect::kUpperLeft_Corner].fY = d->fRandom->nextRangeF(kRadiusMin, 9.f); |
481 | 0 | } while (r[SkRRect::kUpperLeft_Corner].fY == r[SkRRect::kUpperLeft_Corner].fX); |
482 | |
|
483 | 0 | SkRRect rrect; |
484 | 0 | if (d->fRandom->nextBool()) { |
485 | | // half the time create a four-radii rrect. |
486 | 0 | r[SkRRect::kLowerRight_Corner].fX = d->fRandom->nextRangeF(kRadiusMin, 9.f); |
487 | 0 | r[SkRRect::kLowerRight_Corner].fY = d->fRandom->nextRangeF(kRadiusMin, 9.f); |
488 | |
|
489 | 0 | r[SkRRect::kUpperRight_Corner].fX = r[SkRRect::kLowerRight_Corner].fX; |
490 | 0 | r[SkRRect::kUpperRight_Corner].fY = r[SkRRect::kUpperLeft_Corner].fY; |
491 | |
|
492 | 0 | r[SkRRect::kLowerLeft_Corner].fX = r[SkRRect::kUpperLeft_Corner].fX; |
493 | 0 | r[SkRRect::kLowerLeft_Corner].fY = r[SkRRect::kLowerRight_Corner].fY; |
494 | |
|
495 | 0 | rrect.setRectRadii(SkRect::MakeWH(w, h), r); |
496 | 0 | } else { |
497 | 0 | rrect.setRectXY(SkRect::MakeWH(w, h), r[SkRRect::kUpperLeft_Corner].fX, |
498 | 0 | r[SkRRect::kUpperLeft_Corner].fY); |
499 | 0 | } |
500 | 0 | std::unique_ptr<GrFragmentProcessor> fp = d->inputFP(); |
501 | 0 | bool success; |
502 | 0 | do { |
503 | 0 | GrClipEdgeType et = (GrClipEdgeType)d->fRandom->nextULessThan(kGrClipEdgeTypeCnt); |
504 | 0 | std::tie(success, fp) = GrRRectEffect::Make(std::move(fp), et, rrect, |
505 | 0 | *d->caps()->shaderCaps()); |
506 | 0 | } while (!success); |
507 | 0 | return fp; |
508 | 0 | } |
509 | | #endif |
510 | | |
511 | | ////////////////////////////////////////////////////////////////////////////// |
512 | | |
513 | 0 | static bool elliptical_effect_uses_scale(const GrShaderCaps& caps, const SkRRect& rrect) { |
514 | | // Keep shaders consistent across varying radii when in reduced shader mode. |
515 | 0 | if (caps.fReducedShaderMode) { |
516 | 0 | return true; |
517 | 0 | } |
518 | | // If we're on a device where float != fp32 then we'll do the distance computation in a space |
519 | | // that is normalized by the largest radius. The scale uniform will be scale, 1/scale. The |
520 | | // radii uniform values are already in this normalized space. |
521 | 0 | if (!caps.fFloatIs32Bits) { |
522 | 0 | return true; |
523 | 0 | } |
524 | | // Additionally, even if we have fp32, large radii can underflow 1/radii^2 terms leading to |
525 | | // blurry coverage. This effect applies to simple and nine-patch, so only need to check TL+BR |
526 | 0 | const SkVector& r0 = rrect.radii(SkRRect::kUpperLeft_Corner); |
527 | 0 | const SkVector& r1 = rrect.radii(SkRRect::kLowerRight_Corner); |
528 | 0 | float maxRadius = std::max(std::max(r0.fX, r0.fY), std::max(r1.fX, r1.fY)); |
529 | 0 | return SkScalarNearlyZero(1.f / (maxRadius * maxRadius)); |
530 | 0 | } |
531 | | |
532 | | class EllipticalRRectEffect::Impl : public ProgramImpl { |
533 | | public: |
534 | | void emitCode(EmitArgs&) override; |
535 | | |
536 | | private: |
537 | | void onSetData(const GrGLSLProgramDataManager&, const GrFragmentProcessor&) override; |
538 | | |
539 | | GrGLSLProgramDataManager::UniformHandle fInnerRectUniform; |
540 | | GrGLSLProgramDataManager::UniformHandle fInvRadiiSqdUniform; |
541 | | GrGLSLProgramDataManager::UniformHandle fScaleUniform; |
542 | | SkRRect fPrevRRect; |
543 | | }; |
544 | | |
545 | 0 | void EllipticalRRectEffect::Impl::emitCode(EmitArgs& args) { |
546 | 0 | const EllipticalRRectEffect& erre = args.fFp.cast<EllipticalRRectEffect>(); |
547 | 0 | GrGLSLUniformHandler* uniformHandler = args.fUniformHandler; |
548 | 0 | const char *rectName; |
549 | | // The inner rect is the rrect bounds inset by the x/y radii |
550 | 0 | fInnerRectUniform = uniformHandler->addUniform(&erre, kFragment_GrShaderFlag, SkSLType::kFloat4, |
551 | 0 | "innerRect", &rectName); |
552 | |
|
553 | 0 | GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder; |
554 | | // At each quarter-ellipse corner we compute a vector that is the offset of the fragment pos |
555 | | // to the ellipse center. The vector is pinned in x and y to be in the quarter-plane relevant |
556 | | // to that corner. This means that points near the interior near the rrect top edge will have |
557 | | // a vector that points straight up for both the TL left and TR corners. Computing an |
558 | | // alpha from this vector at either the TR or TL corner will give the correct result. Similarly, |
559 | | // fragments near the other three edges will get the correct AA. Fragments in the interior of |
560 | | // the rrect will have a (0,0) vector at all four corners. So long as the radii > 0.5 they will |
561 | | // correctly produce an alpha value of 1 at all four corners. We take the min of all the alphas. |
562 | | // |
563 | | // The code below is a simplified version of the above that performs maxs on the vector |
564 | | // components before computing distances and alpha values so that only one distance computation |
565 | | // need be computed to determine the min alpha. |
566 | 0 | fragBuilder->codeAppendf("float2 dxy0 = %s.LT - sk_FragCoord.xy;", rectName); |
567 | 0 | fragBuilder->codeAppendf("float2 dxy1 = sk_FragCoord.xy - %s.RB;", rectName); |
568 | | |
569 | |
|
570 | 0 | const char* scaleName = nullptr; |
571 | 0 | if (elliptical_effect_uses_scale(*args.fShaderCaps, erre.fRRect)) { |
572 | 0 | fScaleUniform = uniformHandler->addUniform(&erre, kFragment_GrShaderFlag, SkSLType::kHalf2, |
573 | 0 | "scale", &scaleName); |
574 | 0 | } |
575 | | |
576 | | // The uniforms with the inv squared radii are highp to prevent underflow. |
577 | 0 | switch (erre.fRRect.getType()) { |
578 | 0 | case SkRRect::kSimple_Type: { |
579 | 0 | const char *invRadiiXYSqdName; |
580 | 0 | fInvRadiiSqdUniform = uniformHandler->addUniform(&erre, |
581 | 0 | kFragment_GrShaderFlag, |
582 | 0 | SkSLType::kFloat2, |
583 | 0 | "invRadiiXY", |
584 | 0 | &invRadiiXYSqdName); |
585 | 0 | fragBuilder->codeAppend("float2 dxy = max(max(dxy0, dxy1), 0.0);"); |
586 | 0 | if (scaleName) { |
587 | 0 | fragBuilder->codeAppendf("dxy *= %s.y;", scaleName); |
588 | 0 | } |
589 | | // Z is the x/y offsets divided by squared radii. |
590 | 0 | fragBuilder->codeAppendf("float2 Z = dxy * %s.xy;", invRadiiXYSqdName); |
591 | 0 | break; |
592 | 0 | } |
593 | 0 | case SkRRect::kNinePatch_Type: { |
594 | 0 | const char *invRadiiLTRBSqdName; |
595 | 0 | fInvRadiiSqdUniform = uniformHandler->addUniform(&erre, |
596 | 0 | kFragment_GrShaderFlag, |
597 | 0 | SkSLType::kFloat4, |
598 | 0 | "invRadiiLTRB", |
599 | 0 | &invRadiiLTRBSqdName); |
600 | 0 | if (scaleName) { |
601 | 0 | fragBuilder->codeAppendf("dxy0 *= %s.y;", scaleName); |
602 | 0 | fragBuilder->codeAppendf("dxy1 *= %s.y;", scaleName); |
603 | 0 | } |
604 | 0 | fragBuilder->codeAppend("float2 dxy = max(max(dxy0, dxy1), 0.0);"); |
605 | | // Z is the x/y offsets divided by squared radii. We only care about the (at most) one |
606 | | // corner where both the x and y offsets are positive, hence the maxes. (The inverse |
607 | | // squared radii will always be positive.) |
608 | 0 | fragBuilder->codeAppendf("float2 Z = max(max(dxy0 * %s.xy, dxy1 * %s.zw), 0.0);", |
609 | 0 | invRadiiLTRBSqdName, invRadiiLTRBSqdName); |
610 | |
|
611 | 0 | break; |
612 | 0 | } |
613 | 0 | default: |
614 | 0 | SK_ABORT("RRect should always be simple or nine-patch."); |
615 | 0 | } |
616 | | // implicit is the evaluation of (x/a)^2 + (y/b)^2 - 1. |
617 | 0 | fragBuilder->codeAppend("half implicit = half(dot(Z, dxy) - 1.0);"); |
618 | | // grad_dot is the squared length of the gradient of the implicit. |
619 | 0 | fragBuilder->codeAppend("half grad_dot = half(4.0 * dot(Z, Z));"); |
620 | | // avoid calling inversesqrt on zero. |
621 | 0 | fragBuilder->codeAppend("grad_dot = max(grad_dot, 1.0e-4);"); |
622 | 0 | fragBuilder->codeAppend("half approx_dist = implicit * half(inversesqrt(grad_dot));"); |
623 | 0 | if (scaleName) { |
624 | 0 | fragBuilder->codeAppendf("approx_dist *= %s.x;", scaleName); |
625 | 0 | } |
626 | |
|
627 | 0 | if (erre.fEdgeType == GrClipEdgeType::kFillAA) { |
628 | 0 | fragBuilder->codeAppend("half alpha = clamp(0.5 - approx_dist, 0.0, 1.0);"); |
629 | 0 | } else { |
630 | 0 | fragBuilder->codeAppend("half alpha = clamp(0.5 + approx_dist, 0.0, 1.0);"); |
631 | 0 | } |
632 | |
|
633 | 0 | SkString inputSample = this->invokeChild(/*childIndex=*/0, args); |
634 | |
|
635 | 0 | fragBuilder->codeAppendf("return %s * alpha;", inputSample.c_str()); |
636 | 0 | } |
637 | | |
638 | | void EllipticalRRectEffect::Impl::onSetData(const GrGLSLProgramDataManager& pdman, |
639 | 0 | const GrFragmentProcessor& effect) { |
640 | 0 | const EllipticalRRectEffect& erre = effect.cast<EllipticalRRectEffect>(); |
641 | 0 | const SkRRect& rrect = erre.fRRect; |
642 | | // If we're using a scale factor to work around precision issues, choose the largest radius |
643 | | // as the scale factor. The inv radii need to be pre-adjusted by the scale factor. |
644 | 0 | if (rrect != fPrevRRect) { |
645 | 0 | SkRect rect = rrect.getBounds(); |
646 | 0 | const SkVector& r0 = rrect.radii(SkRRect::kUpperLeft_Corner); |
647 | 0 | SkASSERT(r0.fX >= kRadiusMin); |
648 | 0 | SkASSERT(r0.fY >= kRadiusMin); |
649 | 0 | switch (rrect.getType()) { |
650 | 0 | case SkRRect::kSimple_Type: |
651 | 0 | rect.inset(r0.fX, r0.fY); |
652 | 0 | if (fScaleUniform.isValid()) { |
653 | 0 | if (r0.fX > r0.fY) { |
654 | 0 | pdman.set2f(fInvRadiiSqdUniform, 1.f, (r0.fX * r0.fX) / (r0.fY * r0.fY)); |
655 | 0 | pdman.set2f(fScaleUniform, r0.fX, 1.f / r0.fX); |
656 | 0 | } else { |
657 | 0 | pdman.set2f(fInvRadiiSqdUniform, (r0.fY * r0.fY) / (r0.fX * r0.fX), 1.f); |
658 | 0 | pdman.set2f(fScaleUniform, r0.fY, 1.f / r0.fY); |
659 | 0 | } |
660 | 0 | } else { |
661 | 0 | pdman.set2f(fInvRadiiSqdUniform, 1.f / (r0.fX * r0.fX), |
662 | 0 | 1.f / (r0.fY * r0.fY)); |
663 | 0 | } |
664 | 0 | break; |
665 | 0 | case SkRRect::kNinePatch_Type: { |
666 | 0 | const SkVector& r1 = rrect.radii(SkRRect::kLowerRight_Corner); |
667 | 0 | SkASSERT(r1.fX >= kRadiusMin); |
668 | 0 | SkASSERT(r1.fY >= kRadiusMin); |
669 | 0 | rect.fLeft += r0.fX; |
670 | 0 | rect.fTop += r0.fY; |
671 | 0 | rect.fRight -= r1.fX; |
672 | 0 | rect.fBottom -= r1.fY; |
673 | 0 | if (fScaleUniform.isValid()) { |
674 | 0 | float scale = std::max(std::max(r0.fX, r0.fY), std::max(r1.fX, r1.fY)); |
675 | 0 | float scaleSqd = scale * scale; |
676 | 0 | pdman.set4f(fInvRadiiSqdUniform, scaleSqd / (r0.fX * r0.fX), |
677 | 0 | scaleSqd / (r0.fY * r0.fY), |
678 | 0 | scaleSqd / (r1.fX * r1.fX), |
679 | 0 | scaleSqd / (r1.fY * r1.fY)); |
680 | 0 | pdman.set2f(fScaleUniform, scale, 1.f / scale); |
681 | 0 | } else { |
682 | 0 | pdman.set4f(fInvRadiiSqdUniform, 1.f / (r0.fX * r0.fX), |
683 | 0 | 1.f / (r0.fY * r0.fY), |
684 | 0 | 1.f / (r1.fX * r1.fX), |
685 | 0 | 1.f / (r1.fY * r1.fY)); |
686 | 0 | } |
687 | 0 | break; |
688 | 0 | } |
689 | 0 | default: |
690 | 0 | SK_ABORT("RRect should always be simple or nine-patch."); |
691 | 0 | } |
692 | 0 | pdman.set4f(fInnerRectUniform, rect.fLeft, rect.fTop, rect.fRight, rect.fBottom); |
693 | 0 | fPrevRRect = rrect; |
694 | 0 | } |
695 | 0 | } Unexecuted instantiation: GrRRectEffect.cpp:(anonymous namespace)::EllipticalRRectEffect::Impl::onSetData(GrGLSLProgramDataManager const&, GrFragmentProcessor const&) Unexecuted instantiation: GrRRectEffect.cpp:(anonymous namespace)::EllipticalRRectEffect::Impl::onSetData(GrGLSLProgramDataManager const&, GrFragmentProcessor const&) |
696 | | |
697 | | //////////////////////////////////////////////////////////////////////////////////////////////////// |
698 | | |
699 | 0 | void EllipticalRRectEffect::onAddToKey(const GrShaderCaps& caps, skgpu::KeyBuilder* b) const { |
700 | 0 | static_assert(kGrClipEdgeTypeCnt <= 4); // 2 bits |
701 | 0 | static_assert((int)SkRRect::kLastType + 1 <= 8); // 3 bits |
702 | 0 | b->addBits(2, static_cast<int>(fEdgeType), "edge_type"); |
703 | 0 | b->addBits(3, static_cast<int>(fRRect.getType()), "rrect_type"); |
704 | 0 | b->addBool(elliptical_effect_uses_scale(caps, fRRect), "scale_radii"); |
705 | 0 | } |
706 | | |
707 | 0 | std::unique_ptr<GrFragmentProcessor::ProgramImpl> EllipticalRRectEffect::onMakeProgramImpl() const { |
708 | 0 | return std::make_unique<Impl>(); |
709 | 0 | } |
710 | | |
711 | | ////////////////////////////////////////////////////////////////////////////// |
712 | | |
713 | | GrFPResult GrRRectEffect::Make(std::unique_ptr<GrFragmentProcessor> inputFP, |
714 | | GrClipEdgeType edgeType, const SkRRect& rrect, |
715 | 38.2k | const GrShaderCaps& caps) { |
716 | 38.2k | if (rrect.isRect()) { |
717 | 221 | auto fp = GrFragmentProcessor::Rect(std::move(inputFP), edgeType, rrect.getBounds()); |
718 | 221 | return GrFPSuccess(std::move(fp)); |
719 | 221 | } |
720 | | |
721 | 38.0k | if (rrect.isOval()) { |
722 | 1.06k | return GrOvalEffect::Make(std::move(inputFP), edgeType, rrect.getBounds(), caps); |
723 | 1.06k | } |
724 | | |
725 | 36.9k | if (rrect.isSimple()) { |
726 | 3.03k | if (SkRRectPriv::GetSimpleRadii(rrect).fX < kRadiusMin || |
727 | 3.03k | SkRRectPriv::GetSimpleRadii(rrect).fY < kRadiusMin) { |
728 | | // In this case the corners are extremely close to rectangular and we collapse the |
729 | | // clip to a rectangular clip. |
730 | 307 | auto fp = GrFragmentProcessor::Rect(std::move(inputFP), edgeType, rrect.getBounds()); |
731 | 307 | return GrFPSuccess(std::move(fp)); |
732 | 307 | } |
733 | 2.72k | if (SkRRectPriv::IsSimpleCircular(rrect)) { |
734 | 277 | return CircularRRectEffect::Make(std::move(inputFP), edgeType, |
735 | 277 | CircularRRectEffect::kAll_CornerFlags, rrect); |
736 | 2.44k | } else { |
737 | 2.44k | return EllipticalRRectEffect::Make(std::move(inputFP), edgeType, rrect); |
738 | 2.44k | } |
739 | 2.72k | } |
740 | | |
741 | 33.9k | if (rrect.isComplex() || rrect.isNinePatch()) { |
742 | | // Check for the "tab" cases - two adjacent circular corners and two square corners. |
743 | 33.9k | SkScalar circularRadius = 0; |
744 | 33.9k | uint32_t cornerFlags = 0; |
745 | | |
746 | 33.9k | SkVector radii[4]; |
747 | 33.9k | bool squashedRadii = false; |
748 | 94.6k | for (int c = 0; c < 4; ++c) { |
749 | 86.6k | radii[c] = rrect.radii((SkRRect::Corner)c); |
750 | | // This effect can't handle a corner with both zero and non-zero radii |
751 | 86.6k | if ((0 == radii[c].fX) != (0 == radii[c].fY)) { |
752 | 0 | return GrFPFailure(std::move(inputFP)); |
753 | 0 | } |
754 | 86.6k | if (0 == radii[c].fX) { |
755 | | // The corner is square, so no need to squash or flag as circular. |
756 | 43.6k | continue; |
757 | 43.6k | } |
758 | 43.0k | if (radii[c].fX < kRadiusMin || radii[c].fY < kRadiusMin) { |
759 | 12.5k | radii[c].set(0, 0); |
760 | 12.5k | squashedRadii = true; |
761 | 12.5k | continue; |
762 | 12.5k | } |
763 | 30.5k | if (radii[c].fX != radii[c].fY) { |
764 | 25.7k | cornerFlags = ~0U; |
765 | 25.7k | break; |
766 | 25.7k | } |
767 | 4.75k | if (!cornerFlags) { |
768 | 4.02k | circularRadius = radii[c].fX; |
769 | 4.02k | cornerFlags = 1 << c; |
770 | 4.02k | } else { |
771 | 731 | if (radii[c].fX != circularRadius) { |
772 | 232 | cornerFlags = ~0U; |
773 | 232 | break; |
774 | 232 | } |
775 | 499 | cornerFlags |= 1 << c; |
776 | 499 | } |
777 | 4.75k | } |
778 | | |
779 | 33.9k | switch (cornerFlags) { |
780 | 0 | case CircularRRectEffect::kAll_CornerFlags: |
781 | | // This rrect should have been caught in the simple case above. Though, it would |
782 | | // be correctly handled in the fallthrough code. |
783 | 0 | SkASSERT(false); |
784 | 0 | [[fallthrough]]; |
785 | 2.03k | case CircularRRectEffect::kTopLeft_CornerFlag: |
786 | 2.51k | case CircularRRectEffect::kTopRight_CornerFlag: |
787 | 2.85k | case CircularRRectEffect::kBottomRight_CornerFlag: |
788 | 3.28k | case CircularRRectEffect::kBottomLeft_CornerFlag: |
789 | 3.38k | case CircularRRectEffect::kLeft_CornerFlags: |
790 | 3.41k | case CircularRRectEffect::kTop_CornerFlags: |
791 | 3.62k | case CircularRRectEffect::kRight_CornerFlags: |
792 | 3.78k | case CircularRRectEffect::kBottom_CornerFlags: { |
793 | 3.78k | SkTCopyOnFirstWrite<SkRRect> rr(rrect); |
794 | 3.78k | if (squashedRadii) { |
795 | 814 | rr.writable()->setRectRadii(rrect.getBounds(), radii); |
796 | 814 | } |
797 | 3.78k | return CircularRRectEffect::Make(std::move(inputFP), edgeType, cornerFlags, *rr); |
798 | 3.62k | } |
799 | 4.17k | case CircularRRectEffect::kNone_CornerFlags: { |
800 | 4.17k | auto fp = |
801 | 4.17k | GrFragmentProcessor::Rect(std::move(inputFP), edgeType, rrect.getBounds()); |
802 | 4.17k | return GrFPSuccess(std::move(fp)); |
803 | 3.62k | } |
804 | 25.9k | default: { |
805 | 25.9k | const SkVector ul = rrect.radii(SkRRect::kUpperLeft_Corner); |
806 | 25.9k | const SkVector lr = rrect.radii(SkRRect::kLowerRight_Corner); |
807 | 25.9k | if (rrect.isNinePatch() && |
808 | 25.9k | ul.fX >= kRadiusMin && |
809 | 25.9k | ul.fY >= kRadiusMin && |
810 | 25.9k | lr.fX >= kRadiusMin && |
811 | 25.9k | lr.fY >= kRadiusMin) { |
812 | 200 | return EllipticalRRectEffect::Make(std::move(inputFP), edgeType, rrect); |
813 | 200 | } |
814 | 25.7k | return GrFPFailure(std::move(inputFP)); |
815 | 25.9k | } |
816 | 33.9k | } |
817 | 33.9k | } |
818 | 0 | return GrFPFailure(std::move(inputFP)); |
819 | 33.9k | } GrRRectEffect::Make(std::__1::unique_ptr<GrFragmentProcessor, std::__1::default_delete<GrFragmentProcessor> >, GrClipEdgeType, SkRRect const&, GrShaderCaps const&) Line | Count | Source | 715 | 38.2k | const GrShaderCaps& caps) { | 716 | 38.2k | if (rrect.isRect()) { | 717 | 221 | auto fp = GrFragmentProcessor::Rect(std::move(inputFP), edgeType, rrect.getBounds()); | 718 | 221 | return GrFPSuccess(std::move(fp)); | 719 | 221 | } | 720 | | | 721 | 38.0k | if (rrect.isOval()) { | 722 | 1.06k | return GrOvalEffect::Make(std::move(inputFP), edgeType, rrect.getBounds(), caps); | 723 | 1.06k | } | 724 | | | 725 | 36.9k | if (rrect.isSimple()) { | 726 | 3.03k | if (SkRRectPriv::GetSimpleRadii(rrect).fX < kRadiusMin || | 727 | 3.03k | SkRRectPriv::GetSimpleRadii(rrect).fY < kRadiusMin) { | 728 | | // In this case the corners are extremely close to rectangular and we collapse the | 729 | | // clip to a rectangular clip. | 730 | 307 | auto fp = GrFragmentProcessor::Rect(std::move(inputFP), edgeType, rrect.getBounds()); | 731 | 307 | return GrFPSuccess(std::move(fp)); | 732 | 307 | } | 733 | 2.72k | if (SkRRectPriv::IsSimpleCircular(rrect)) { | 734 | 277 | return CircularRRectEffect::Make(std::move(inputFP), edgeType, | 735 | 277 | CircularRRectEffect::kAll_CornerFlags, rrect); | 736 | 2.44k | } else { | 737 | 2.44k | return EllipticalRRectEffect::Make(std::move(inputFP), edgeType, rrect); | 738 | 2.44k | } | 739 | 2.72k | } | 740 | | | 741 | 33.9k | if (rrect.isComplex() || rrect.isNinePatch()) { | 742 | | // Check for the "tab" cases - two adjacent circular corners and two square corners. | 743 | 33.9k | SkScalar circularRadius = 0; | 744 | 33.9k | uint32_t cornerFlags = 0; | 745 | | | 746 | 33.9k | SkVector radii[4]; | 747 | 33.9k | bool squashedRadii = false; | 748 | 94.6k | for (int c = 0; c < 4; ++c) { | 749 | 86.6k | radii[c] = rrect.radii((SkRRect::Corner)c); | 750 | | // This effect can't handle a corner with both zero and non-zero radii | 751 | 86.6k | if ((0 == radii[c].fX) != (0 == radii[c].fY)) { | 752 | 0 | return GrFPFailure(std::move(inputFP)); | 753 | 0 | } | 754 | 86.6k | if (0 == radii[c].fX) { | 755 | | // The corner is square, so no need to squash or flag as circular. | 756 | 43.6k | continue; | 757 | 43.6k | } | 758 | 43.0k | if (radii[c].fX < kRadiusMin || radii[c].fY < kRadiusMin) { | 759 | 12.5k | radii[c].set(0, 0); | 760 | 12.5k | squashedRadii = true; | 761 | 12.5k | continue; | 762 | 12.5k | } | 763 | 30.5k | if (radii[c].fX != radii[c].fY) { | 764 | 25.7k | cornerFlags = ~0U; | 765 | 25.7k | break; | 766 | 25.7k | } | 767 | 4.75k | if (!cornerFlags) { | 768 | 4.02k | circularRadius = radii[c].fX; | 769 | 4.02k | cornerFlags = 1 << c; | 770 | 4.02k | } else { | 771 | 731 | if (radii[c].fX != circularRadius) { | 772 | 232 | cornerFlags = ~0U; | 773 | 232 | break; | 774 | 232 | } | 775 | 499 | cornerFlags |= 1 << c; | 776 | 499 | } | 777 | 4.75k | } | 778 | | | 779 | 33.9k | switch (cornerFlags) { | 780 | 0 | case CircularRRectEffect::kAll_CornerFlags: | 781 | | // This rrect should have been caught in the simple case above. Though, it would | 782 | | // be correctly handled in the fallthrough code. | 783 | 0 | SkASSERT(false); | 784 | 0 | [[fallthrough]]; | 785 | 2.03k | case CircularRRectEffect::kTopLeft_CornerFlag: | 786 | 2.51k | case CircularRRectEffect::kTopRight_CornerFlag: | 787 | 2.85k | case CircularRRectEffect::kBottomRight_CornerFlag: | 788 | 3.28k | case CircularRRectEffect::kBottomLeft_CornerFlag: | 789 | 3.38k | case CircularRRectEffect::kLeft_CornerFlags: | 790 | 3.41k | case CircularRRectEffect::kTop_CornerFlags: | 791 | 3.62k | case CircularRRectEffect::kRight_CornerFlags: | 792 | 3.78k | case CircularRRectEffect::kBottom_CornerFlags: { | 793 | 3.78k | SkTCopyOnFirstWrite<SkRRect> rr(rrect); | 794 | 3.78k | if (squashedRadii) { | 795 | 814 | rr.writable()->setRectRadii(rrect.getBounds(), radii); | 796 | 814 | } | 797 | 3.78k | return CircularRRectEffect::Make(std::move(inputFP), edgeType, cornerFlags, *rr); | 798 | 3.62k | } | 799 | 4.17k | case CircularRRectEffect::kNone_CornerFlags: { | 800 | 4.17k | auto fp = | 801 | 4.17k | GrFragmentProcessor::Rect(std::move(inputFP), edgeType, rrect.getBounds()); | 802 | 4.17k | return GrFPSuccess(std::move(fp)); | 803 | 3.62k | } | 804 | 25.9k | default: { | 805 | 25.9k | const SkVector ul = rrect.radii(SkRRect::kUpperLeft_Corner); | 806 | 25.9k | const SkVector lr = rrect.radii(SkRRect::kLowerRight_Corner); | 807 | 25.9k | if (rrect.isNinePatch() && | 808 | 25.9k | ul.fX >= kRadiusMin && | 809 | 25.9k | ul.fY >= kRadiusMin && | 810 | 25.9k | lr.fX >= kRadiusMin && | 811 | 25.9k | lr.fY >= kRadiusMin) { | 812 | 200 | return EllipticalRRectEffect::Make(std::move(inputFP), edgeType, rrect); | 813 | 200 | } | 814 | 25.7k | return GrFPFailure(std::move(inputFP)); | 815 | 25.9k | } | 816 | 33.9k | } | 817 | 33.9k | } | 818 | 0 | return GrFPFailure(std::move(inputFP)); | 819 | 33.9k | } |
Unexecuted instantiation: GrRRectEffect::Make(std::__1::unique_ptr<GrFragmentProcessor, std::__1::default_delete<GrFragmentProcessor> >, GrClipEdgeType, SkRRect const&, GrShaderCaps const&) |