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