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