Flutter Engine
The Flutter Engine
GrRRectEffect.cpp
Go to the documentation of this file.
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
9
12#include "include/core/SkRect.h"
17#include "src/base/SkRandom.h"
18#include "src/base/SkTLazy.h"
21#include "src/gpu/KeyBuilder.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.
38
39//////////////////////////////////////////////////////////////////////////////
40
41namespace {
42class CircularRRectEffect : public GrFragmentProcessor {
43public:
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 ~CircularRRectEffect() override {}
67
68 const char* name() const override { return "CircularRRect"; }
69
70 std::unique_ptr<GrFragmentProcessor> clone() const override;
71
72private:
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
86 GrClipEdgeType fEdgeType;
87 uint32_t fCircularCornerFlags;
88
90
92};
93} // anonymous namespace
94
95GrFPResult CircularRRectEffect::Make(std::unique_ptr<GrFragmentProcessor> inputFP,
96 GrClipEdgeType edgeType,
97 uint32_t circularCornerFlags, const SkRRect& rrect) {
98 if (GrClipEdgeType::kFillAA != edgeType && GrClipEdgeType::kInverseFillAA != edgeType) {
99 return GrFPFailure(std::move(inputFP));
100 }
101 return GrFPSuccess(std::unique_ptr<GrFragmentProcessor>(
102 new CircularRRectEffect(std::move(inputFP), edgeType, circularCornerFlags, rrect)));
103}
104
105CircularRRectEffect::CircularRRectEffect(std::unique_ptr<GrFragmentProcessor> inputFP,
106 GrClipEdgeType edgeType,
107 uint32_t circularCornerFlags,
108 const SkRRect& rrect)
109 : INHERITED(kCircularRRectEffect_ClassID,
110 ProcessorOptimizationFlags(inputFP.get()) &
111 kCompatibleWithCoverageAsAlpha_OptimizationFlag)
112 , fRRect(rrect)
113 , fEdgeType(edgeType)
114 , fCircularCornerFlags(circularCornerFlags) {
115 this->registerChild(std::move(inputFP));
116}
117
118CircularRRectEffect::CircularRRectEffect(const CircularRRectEffect& that)
119 : INHERITED(that)
120 , fRRect(that.fRRect)
121 , fEdgeType(that.fEdgeType)
122 , fCircularCornerFlags(that.fCircularCornerFlags) {}
123
124std::unique_ptr<GrFragmentProcessor> CircularRRectEffect::clone() const {
125 return std::unique_ptr<GrFragmentProcessor>(new CircularRRectEffect(*this));
126}
127
128bool CircularRRectEffect::onIsEqual(const GrFragmentProcessor& other) const {
129 const CircularRRectEffect& crre = other.cast<CircularRRectEffect>();
130 // The corner flags are derived from fRRect, so no need to check them.
131 return fEdgeType == crre.fEdgeType && fRRect == crre.fRRect;
132}
133
134//////////////////////////////////////////////////////////////////////////////
135
136GR_DEFINE_FRAGMENT_PROCESSOR_TEST(CircularRRectEffect)
137
138#if defined(GR_TEST_UTILS)
139std::unique_ptr<GrFragmentProcessor> CircularRRectEffect::TestCreate(GrProcessorTestData* d) {
140 SkScalar w = d->fRandom->nextRangeScalar(20.f, 1000.f);
141 SkScalar h = d->fRandom->nextRangeScalar(20.f, 1000.f);
142 SkScalar r = d->fRandom->nextRangeF(kRadiusMin, 9.f);
145 std::unique_ptr<GrFragmentProcessor> fp = d->inputFP();
146 bool success;
147 do {
148 GrClipEdgeType et =
149 (GrClipEdgeType)d->fRandom->nextULessThan(kGrClipEdgeTypeCnt);
150 std::tie(success, fp) = GrRRectEffect::Make(std::move(fp), et, rrect,
151 *d->caps()->shaderCaps());
152 } while (!success);
153 return fp;
154}
155#endif
156
157//////////////////////////////////////////////////////////////////////////////
158
160public:
161 void emitCode(EmitArgs&) override;
162
163private:
164 void onSetData(const GrGLSLProgramDataManager&, const GrFragmentProcessor&) override;
165
166 GrGLSLProgramDataManager::UniformHandle fInnerRectUniform;
167 GrGLSLProgramDataManager::UniformHandle fRadiusPlusHalfUniform;
168 SkRRect fPrevRRect;
169};
170
172 const CircularRRectEffect& crre = args.fFp.cast<CircularRRectEffect>();
173 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
174 const char *rectName;
175 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 fInnerRectUniform = uniformHandler->addUniform(&crre, kFragment_GrShaderFlag, SkSLType::kFloat4,
181 "innerRect", &rectName);
182 // x is (r + .5) and y is 1/(r + .5)
183 fRadiusPlusHalfUniform = uniformHandler->addUniform(&crre, kFragment_GrShaderFlag,
184 SkSLType::kHalf2, "radiusPlusHalf",
185 &radiusPlusHalfName);
186
187 // If we're on a device where float != fp32 then the length calculation could overflow.
188 SkString clampedCircleDistance;
189 if (!args.fShaderCaps->fFloatIs32Bits) {
190 clampedCircleDistance.printf("saturate(%s.x * (1.0 - length(dxy * %s.y)))",
191 radiusPlusHalfName, radiusPlusHalfName);
192 } else {
193 clampedCircleDistance.printf("saturate(%s.x - length(dxy))", radiusPlusHalfName);
194 }
195
196 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 switch (crre.fCircularCornerFlags) {
213 case CircularRRectEffect::kAll_CornerFlags:
214 fragBuilder->codeAppendf("float2 dxy0 = %s.LT - sk_FragCoord.xy;", rectName);
215 fragBuilder->codeAppendf("float2 dxy1 = sk_FragCoord.xy - %s.RB;", rectName);
216 fragBuilder->codeAppend("float2 dxy = max(max(dxy0, dxy1), 0.0);");
217 fragBuilder->codeAppendf("half alpha = half(%s);", clampedCircleDistance.c_str());
218 break;
219 case CircularRRectEffect::kTopLeft_CornerFlag:
220 fragBuilder->codeAppendf("float2 dxy = max(%s.LT - sk_FragCoord.xy, 0.0);",
221 rectName);
222 fragBuilder->codeAppendf("half rightAlpha = half(saturate(%s.R - sk_FragCoord.x));",
223 rectName);
224 fragBuilder->codeAppendf("half bottomAlpha = half(saturate(%s.B - sk_FragCoord.y));",
225 rectName);
226 fragBuilder->codeAppendf("half alpha = bottomAlpha * rightAlpha * half(%s);",
227 clampedCircleDistance.c_str());
228 break;
229 case CircularRRectEffect::kTopRight_CornerFlag:
230 fragBuilder->codeAppendf("float2 dxy = max(float2(sk_FragCoord.x - %s.R, "
231 "%s.T - sk_FragCoord.y), 0.0);",
232 rectName, rectName);
233 fragBuilder->codeAppendf("half leftAlpha = half(saturate(sk_FragCoord.x - %s.L));",
234 rectName);
235 fragBuilder->codeAppendf("half bottomAlpha = half(saturate(%s.B - sk_FragCoord.y));",
236 rectName);
237 fragBuilder->codeAppendf("half alpha = bottomAlpha * leftAlpha * half(%s);",
238 clampedCircleDistance.c_str());
239 break;
240 case CircularRRectEffect::kBottomRight_CornerFlag:
241 fragBuilder->codeAppendf("float2 dxy = max(sk_FragCoord.xy - %s.RB, 0.0);",
242 rectName);
243 fragBuilder->codeAppendf("half leftAlpha = half(saturate(sk_FragCoord.x - %s.L));",
244 rectName);
245 fragBuilder->codeAppendf("half topAlpha = half(saturate(sk_FragCoord.y - %s.T));",
246 rectName);
247 fragBuilder->codeAppendf("half alpha = topAlpha * leftAlpha * half(%s);",
248 clampedCircleDistance.c_str());
249 break;
250 case CircularRRectEffect::kBottomLeft_CornerFlag:
251 fragBuilder->codeAppendf("float2 dxy = max(float2(%s.L - sk_FragCoord.x, "
252 "sk_FragCoord.y - %s.B), 0.0);",
253 rectName, rectName);
254 fragBuilder->codeAppendf("half rightAlpha = half(saturate(%s.R - sk_FragCoord.x));",
255 rectName);
256 fragBuilder->codeAppendf("half topAlpha = half(saturate(sk_FragCoord.y - %s.T));",
257 rectName);
258 fragBuilder->codeAppendf("half alpha = topAlpha * rightAlpha * half(%s);",
259 clampedCircleDistance.c_str());
260 break;
261 case CircularRRectEffect::kLeft_CornerFlags:
262 fragBuilder->codeAppendf("float2 dxy0 = %s.LT - sk_FragCoord.xy;", rectName);
263 fragBuilder->codeAppendf("float dy1 = sk_FragCoord.y - %s.B;", rectName);
264 fragBuilder->codeAppend("float2 dxy = max(float2(dxy0.x, max(dxy0.y, dy1)), 0.0);");
265 fragBuilder->codeAppendf("half rightAlpha = half(saturate(%s.R - sk_FragCoord.x));",
266 rectName);
267 fragBuilder->codeAppendf("half alpha = rightAlpha * half(%s);",
268 clampedCircleDistance.c_str());
269 break;
270 case CircularRRectEffect::kTop_CornerFlags:
271 fragBuilder->codeAppendf("float2 dxy0 = %s.LT - sk_FragCoord.xy;", rectName);
272 fragBuilder->codeAppendf("float dx1 = sk_FragCoord.x - %s.R;", rectName);
273 fragBuilder->codeAppend("float2 dxy = max(float2(max(dxy0.x, dx1), dxy0.y), 0.0);");
274 fragBuilder->codeAppendf("half bottomAlpha = half(saturate(%s.B - sk_FragCoord.y));",
275 rectName);
276 fragBuilder->codeAppendf("half alpha = bottomAlpha * half(%s);",
277 clampedCircleDistance.c_str());
278 break;
279 case CircularRRectEffect::kRight_CornerFlags:
280 fragBuilder->codeAppendf("float dy0 = %s.T - sk_FragCoord.y;", rectName);
281 fragBuilder->codeAppendf("float2 dxy1 = sk_FragCoord.xy - %s.RB;", rectName);
282 fragBuilder->codeAppend("float2 dxy = max(float2(dxy1.x, max(dy0, dxy1.y)), 0.0);");
283 fragBuilder->codeAppendf("half leftAlpha = half(saturate(sk_FragCoord.x - %s.L));",
284 rectName);
285 fragBuilder->codeAppendf("half alpha = leftAlpha * half(%s);",
286 clampedCircleDistance.c_str());
287 break;
288 case CircularRRectEffect::kBottom_CornerFlags:
289 fragBuilder->codeAppendf("float dx0 = %s.L - sk_FragCoord.x;", rectName);
290 fragBuilder->codeAppendf("float2 dxy1 = sk_FragCoord.xy - %s.RB;", rectName);
291 fragBuilder->codeAppend("float2 dxy = max(float2(max(dx0, dxy1.x), dxy1.y), 0.0);");
292 fragBuilder->codeAppendf("half topAlpha = half(saturate(sk_FragCoord.y - %s.T));",
293 rectName);
294 fragBuilder->codeAppendf("half alpha = topAlpha * half(%s);",
295 clampedCircleDistance.c_str());
296 break;
297 }
298
299 if (GrClipEdgeType::kInverseFillAA == crre.fEdgeType) {
300 fragBuilder->codeAppend("alpha = 1.0 - alpha;");
301 }
302
303 SkString inputSample = this->invokeChild(/*childIndex=*/0, args);
304
305 fragBuilder->codeAppendf("return %s * alpha;", inputSample.c_str());
306}
307
308void CircularRRectEffect::Impl::onSetData(const GrGLSLProgramDataManager& pdman,
309 const GrFragmentProcessor& processor) {
310 const CircularRRectEffect& crre = processor.cast<CircularRRectEffect>();
311 const SkRRect& rrect = crre.fRRect;
312 if (rrect != fPrevRRect) {
314 SkScalar radius = 0;
315 switch (crre.fCircularCornerFlags) {
316 case CircularRRectEffect::kAll_CornerFlags:
319 SkASSERT(radius >= kRadiusMin);
320 rect.inset(radius, radius);
321 break;
322 case CircularRRectEffect::kTopLeft_CornerFlag:
324 rect.fLeft += radius;
325 rect.fTop += radius;
326 rect.fRight += 0.5f;
327 rect.fBottom += 0.5f;
328 break;
329 case CircularRRectEffect::kTopRight_CornerFlag:
331 rect.fLeft -= 0.5f;
332 rect.fTop += radius;
333 rect.fRight -= radius;
334 rect.fBottom += 0.5f;
335 break;
336 case CircularRRectEffect::kBottomRight_CornerFlag:
338 rect.fLeft -= 0.5f;
339 rect.fTop -= 0.5f;
340 rect.fRight -= radius;
341 rect.fBottom -= radius;
342 break;
343 case CircularRRectEffect::kBottomLeft_CornerFlag:
345 rect.fLeft += radius;
346 rect.fTop -= 0.5f;
347 rect.fRight += 0.5f;
348 rect.fBottom -= radius;
349 break;
350 case CircularRRectEffect::kLeft_CornerFlags:
352 rect.fLeft += radius;
353 rect.fTop += radius;
354 rect.fRight += 0.5f;
355 rect.fBottom -= radius;
356 break;
357 case CircularRRectEffect::kTop_CornerFlags:
359 rect.fLeft += radius;
360 rect.fTop += radius;
361 rect.fRight -= radius;
362 rect.fBottom += 0.5f;
363 break;
364 case CircularRRectEffect::kRight_CornerFlags:
366 rect.fLeft -= 0.5f;
367 rect.fTop += radius;
368 rect.fRight -= radius;
369 rect.fBottom -= radius;
370 break;
371 case CircularRRectEffect::kBottom_CornerFlags:
373 rect.fLeft += radius;
374 rect.fTop -= 0.5f;
375 rect.fRight -= radius;
376 rect.fBottom -= radius;
377 break;
378 default:
379 SK_ABORT("Should have been one of the above cases.");
380 }
381 pdman.set4f(fInnerRectUniform, rect.fLeft, rect.fTop, rect.fRight, rect.fBottom);
382 radius += 0.5f;
383 pdman.set2f(fRadiusPlusHalfUniform, radius, 1.f / radius);
384 fPrevRRect = rrect;
385 }
386}
387
388////////////////////////////////////////////////////////////////////////////////////////////////////
389
390void CircularRRectEffect::onAddToKey(const GrShaderCaps& caps, skgpu::KeyBuilder* b) const {
391 static_assert(kGrClipEdgeTypeCnt <= 8);
392 b->add32((fCircularCornerFlags << 3) | static_cast<int>(fEdgeType));
393}
394
395std::unique_ptr<GrFragmentProcessor::ProgramImpl> CircularRRectEffect::onMakeProgramImpl() const {
396 return std::make_unique<Impl>();
397}
398
399//////////////////////////////////////////////////////////////////////////////
400
401namespace {
402class EllipticalRRectEffect : public GrFragmentProcessor {
403public:
404 static GrFPResult Make(std::unique_ptr<GrFragmentProcessor>, GrClipEdgeType, const SkRRect&);
405
406 ~EllipticalRRectEffect() override {}
407
408 const char* name() const override { return "EllipticalRRect"; }
409
410 std::unique_ptr<GrFragmentProcessor> clone() const override;
411
412private:
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
425 GrClipEdgeType fEdgeType;
426
428
430};
431
432GrFPResult EllipticalRRectEffect::Make(std::unique_ptr<GrFragmentProcessor> inputFP,
433 GrClipEdgeType edgeType,
434 const SkRRect& rrect) {
435 if (GrClipEdgeType::kFillAA != edgeType && GrClipEdgeType::kInverseFillAA != edgeType) {
436 return GrFPFailure(std::move(inputFP));
437 }
438 return GrFPSuccess(std::unique_ptr<GrFragmentProcessor>(
439 new EllipticalRRectEffect(std::move(inputFP), edgeType, rrect)));
440}
441
442EllipticalRRectEffect::EllipticalRRectEffect(std::unique_ptr<GrFragmentProcessor> inputFP,
443 GrClipEdgeType edgeType,
444 const SkRRect& rrect)
445 : INHERITED(kEllipticalRRectEffect_ClassID,
446 ProcessorOptimizationFlags(inputFP.get()) &
447 kCompatibleWithCoverageAsAlpha_OptimizationFlag)
448 , fRRect(rrect)
449 , fEdgeType(edgeType) {
450 this->registerChild(std::move(inputFP));
451}
452
453EllipticalRRectEffect::EllipticalRRectEffect(const EllipticalRRectEffect& that)
454 : INHERITED(that)
455 , fRRect(that.fRRect)
456 , fEdgeType(that.fEdgeType) {}
457
458std::unique_ptr<GrFragmentProcessor> EllipticalRRectEffect::clone() const {
459 return std::unique_ptr<GrFragmentProcessor>(new EllipticalRRectEffect(*this));
460}
461
462bool EllipticalRRectEffect::onIsEqual(const GrFragmentProcessor& other) const {
463 const EllipticalRRectEffect& erre = other.cast<EllipticalRRectEffect>();
464 return fEdgeType == erre.fEdgeType && fRRect == erre.fRRect;
465}
466} // anonymous namespace
467
468//////////////////////////////////////////////////////////////////////////////
469
470GR_DEFINE_FRAGMENT_PROCESSOR_TEST(EllipticalRRectEffect)
471
472#if defined(GR_TEST_UTILS)
473std::unique_ptr<GrFragmentProcessor> EllipticalRRectEffect::TestCreate(GrProcessorTestData* d) {
474 SkScalar w = d->fRandom->nextRangeScalar(20.f, 1000.f);
475 SkScalar h = d->fRandom->nextRangeScalar(20.f, 1000.f);
476 SkVector r[4];
477 r[SkRRect::kUpperLeft_Corner].fX = d->fRandom->nextRangeF(kRadiusMin, 9.f);
478 // ensure at least one corner really is elliptical
479 do {
480 r[SkRRect::kUpperLeft_Corner].fY = d->fRandom->nextRangeF(kRadiusMin, 9.f);
482
484 if (d->fRandom->nextBool()) {
485 // half the time create a four-radii rrect.
486 r[SkRRect::kLowerRight_Corner].fX = d->fRandom->nextRangeF(kRadiusMin, 9.f);
487 r[SkRRect::kLowerRight_Corner].fY = d->fRandom->nextRangeF(kRadiusMin, 9.f);
488
491
494
496 } else {
499 }
500 std::unique_ptr<GrFragmentProcessor> fp = d->inputFP();
501 bool success;
502 do {
503 GrClipEdgeType et = (GrClipEdgeType)d->fRandom->nextULessThan(kGrClipEdgeTypeCnt);
504 std::tie(success, fp) = GrRRectEffect::Make(std::move(fp), et, rrect,
505 *d->caps()->shaderCaps());
506 } while (!success);
507 return fp;
508}
509#endif
510
511//////////////////////////////////////////////////////////////////////////////
512
513static bool elliptical_effect_uses_scale(const GrShaderCaps& caps, const SkRRect& rrect) {
514 // Keep shaders consistent across varying radii when in reduced shader mode.
515 if (caps.fReducedShaderMode) {
516 return true;
517 }
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 if (!caps.fFloatIs32Bits) {
522 return true;
523 }
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
528 float maxRadius = std::max(std::max(r0.fX, r0.fY), std::max(r1.fX, r1.fY));
529 return SkScalarNearlyZero(1.f / (maxRadius * maxRadius));
530}
531
533public:
534 void emitCode(EmitArgs&) override;
535
536private:
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
546 const EllipticalRRectEffect& erre = args.fFp.cast<EllipticalRRectEffect>();
547 GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
548 const char *rectName;
549 // The inner rect is the rrect bounds inset by the x/y radii
550 fInnerRectUniform = uniformHandler->addUniform(&erre, kFragment_GrShaderFlag, SkSLType::kFloat4,
551 "innerRect", &rectName);
552
553 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 fragBuilder->codeAppendf("float2 dxy0 = %s.LT - sk_FragCoord.xy;", rectName);
567 fragBuilder->codeAppendf("float2 dxy1 = sk_FragCoord.xy - %s.RB;", rectName);
568
569
570 const char* scaleName = nullptr;
571 if (elliptical_effect_uses_scale(*args.fShaderCaps, erre.fRRect)) {
572 fScaleUniform = uniformHandler->addUniform(&erre, kFragment_GrShaderFlag, SkSLType::kHalf2,
573 "scale", &scaleName);
574 }
575
576 // The uniforms with the inv squared radii are highp to prevent underflow.
577 switch (erre.fRRect.getType()) {
579 const char *invRadiiXYSqdName;
580 fInvRadiiSqdUniform = uniformHandler->addUniform(&erre,
583 "invRadiiXY",
584 &invRadiiXYSqdName);
585 fragBuilder->codeAppend("float2 dxy = max(max(dxy0, dxy1), 0.0);");
586 if (scaleName) {
587 fragBuilder->codeAppendf("dxy *= %s.y;", scaleName);
588 }
589 // Z is the x/y offsets divided by squared radii.
590 fragBuilder->codeAppendf("float2 Z = dxy * %s.xy;", invRadiiXYSqdName);
591 break;
592 }
594 const char *invRadiiLTRBSqdName;
595 fInvRadiiSqdUniform = uniformHandler->addUniform(&erre,
598 "invRadiiLTRB",
599 &invRadiiLTRBSqdName);
600 if (scaleName) {
601 fragBuilder->codeAppendf("dxy0 *= %s.y;", scaleName);
602 fragBuilder->codeAppendf("dxy1 *= %s.y;", scaleName);
603 }
604 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 fragBuilder->codeAppendf("float2 Z = max(max(dxy0 * %s.xy, dxy1 * %s.zw), 0.0);",
609 invRadiiLTRBSqdName, invRadiiLTRBSqdName);
610
611 break;
612 }
613 default:
614 SK_ABORT("RRect should always be simple or nine-patch.");
615 }
616 // implicit is the evaluation of (x/a)^2 + (y/b)^2 - 1.
617 fragBuilder->codeAppend("half implicit = half(dot(Z, dxy) - 1.0);");
618 // grad_dot is the squared length of the gradient of the implicit.
619 fragBuilder->codeAppend("half grad_dot = half(4.0 * dot(Z, Z));");
620 // avoid calling inversesqrt on zero.
621 fragBuilder->codeAppend("grad_dot = max(grad_dot, 1.0e-4);");
622 fragBuilder->codeAppend("half approx_dist = implicit * half(inversesqrt(grad_dot));");
623 if (scaleName) {
624 fragBuilder->codeAppendf("approx_dist *= %s.x;", scaleName);
625 }
626
627 if (erre.fEdgeType == GrClipEdgeType::kFillAA) {
628 fragBuilder->codeAppend("half alpha = clamp(0.5 - approx_dist, 0.0, 1.0);");
629 } else {
630 fragBuilder->codeAppend("half alpha = clamp(0.5 + approx_dist, 0.0, 1.0);");
631 }
632
633 SkString inputSample = this->invokeChild(/*childIndex=*/0, args);
634
635 fragBuilder->codeAppendf("return %s * alpha;", inputSample.c_str());
636}
637
638void EllipticalRRectEffect::Impl::onSetData(const GrGLSLProgramDataManager& pdman,
639 const GrFragmentProcessor& effect) {
640 const EllipticalRRectEffect& erre = effect.cast<EllipticalRRectEffect>();
641 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 if (rrect != fPrevRRect) {
647 SkASSERT(r0.fX >= kRadiusMin);
648 SkASSERT(r0.fY >= kRadiusMin);
649 switch (rrect.getType()) {
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;
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
699void EllipticalRRectEffect::onAddToKey(const GrShaderCaps& caps, skgpu::KeyBuilder* b) const {
700 static_assert(kGrClipEdgeTypeCnt <= 4); // 2 bits
701 static_assert((int)SkRRect::kLastType + 1 <= 8); // 3 bits
702 b->addBits(2, static_cast<int>(fEdgeType), "edge_type");
703 b->addBits(3, static_cast<int>(fRRect.getType()), "rrect_type");
704 b->addBool(elliptical_effect_uses_scale(caps, fRRect), "scale_radii");
705}
706
707std::unique_ptr<GrFragmentProcessor::ProgramImpl> EllipticalRRectEffect::onMakeProgramImpl() const {
708 return std::make_unique<Impl>();
709}
710
711//////////////////////////////////////////////////////////////////////////////
712
713GrFPResult GrRRectEffect::Make(std::unique_ptr<GrFragmentProcessor> inputFP,
714 GrClipEdgeType edgeType, const SkRRect& rrect,
715 const GrShaderCaps& caps) {
716 if (rrect.isRect()) {
717 auto fp = GrFragmentProcessor::Rect(std::move(inputFP), edgeType, rrect.getBounds());
718 return GrFPSuccess(std::move(fp));
719 }
720
721 if (rrect.isOval()) {
722 return GrOvalEffect::Make(std::move(inputFP), edgeType, rrect.getBounds(), caps);
723 }
724
725 if (rrect.isSimple()) {
728 // In this case the corners are extremely close to rectangular and we collapse the
729 // clip to a rectangular clip.
730 auto fp = GrFragmentProcessor::Rect(std::move(inputFP), edgeType, rrect.getBounds());
731 return GrFPSuccess(std::move(fp));
732 }
734 return CircularRRectEffect::Make(std::move(inputFP), edgeType,
735 CircularRRectEffect::kAll_CornerFlags, rrect);
736 } else {
737 return EllipticalRRectEffect::Make(std::move(inputFP), edgeType, rrect);
738 }
739 }
740
741 if (rrect.isComplex() || rrect.isNinePatch()) {
742 // Check for the "tab" cases - two adjacent circular corners and two square corners.
743 SkScalar circularRadius = 0;
744 uint32_t cornerFlags = 0;
745
746 SkVector radii[4];
747 bool squashedRadii = false;
748 for (int c = 0; c < 4; ++c) {
749 radii[c] = rrect.radii((SkRRect::Corner)c);
750 // This effect can't handle a corner with both zero and non-zero radii
751 if ((0 == radii[c].fX) != (0 == radii[c].fY)) {
752 return GrFPFailure(std::move(inputFP));
753 }
754 if (0 == radii[c].fX) {
755 // The corner is square, so no need to squash or flag as circular.
756 continue;
757 }
758 if (radii[c].fX < kRadiusMin || radii[c].fY < kRadiusMin) {
759 radii[c].set(0, 0);
760 squashedRadii = true;
761 continue;
762 }
763 if (radii[c].fX != radii[c].fY) {
764 cornerFlags = ~0U;
765 break;
766 }
767 if (!cornerFlags) {
768 circularRadius = radii[c].fX;
769 cornerFlags = 1 << c;
770 } else {
771 if (radii[c].fX != circularRadius) {
772 cornerFlags = ~0U;
773 break;
774 }
775 cornerFlags |= 1 << c;
776 }
777 }
778
779 switch (cornerFlags) {
780 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 SkASSERT(false);
784 [[fallthrough]];
785 case CircularRRectEffect::kTopLeft_CornerFlag:
786 case CircularRRectEffect::kTopRight_CornerFlag:
787 case CircularRRectEffect::kBottomRight_CornerFlag:
788 case CircularRRectEffect::kBottomLeft_CornerFlag:
789 case CircularRRectEffect::kLeft_CornerFlags:
790 case CircularRRectEffect::kTop_CornerFlags:
791 case CircularRRectEffect::kRight_CornerFlags:
792 case CircularRRectEffect::kBottom_CornerFlags: {
794 if (squashedRadii) {
795 rr.writable()->setRectRadii(rrect.getBounds(), radii);
796 }
797 return CircularRRectEffect::Make(std::move(inputFP), edgeType, cornerFlags, *rr);
798 }
799 case CircularRRectEffect::kNone_CornerFlags: {
800 auto fp =
801 GrFragmentProcessor::Rect(std::move(inputFP), edgeType, rrect.getBounds());
802 return GrFPSuccess(std::move(fp));
803 }
804 default: {
807 if (rrect.isNinePatch() &&
808 ul.fX >= kRadiusMin &&
809 ul.fY >= kRadiusMin &&
810 lr.fX >= kRadiusMin &&
811 lr.fY >= kRadiusMin) {
812 return EllipticalRRectEffect::Make(std::move(inputFP), edgeType, rrect);
813 }
814 return GrFPFailure(std::move(inputFP));
815 }
816 }
817 }
818 return GrFPFailure(std::move(inputFP));
819}
SkRRect fRRect
static GrFPResult GrFPSuccess(std::unique_ptr< GrFragmentProcessor > fp)
std::tuple< bool, std::unique_ptr< GrFragmentProcessor > > GrFPResult
static GrFPResult GrFPFailure(std::unique_ptr< GrFragmentProcessor > fp)
#define GR_DEFINE_FRAGMENT_PROCESSOR_TEST(...)
#define GR_DECLARE_FRAGMENT_PROCESSOR_TEST
static const SkScalar kRadiusMin
static bool elliptical_effect_uses_scale(const GrShaderCaps &caps, const SkRRect &rrect)
static const int kGrClipEdgeTypeCnt
Definition: GrTypesPriv.h:369
@ kFragment_GrShaderFlag
Definition: GrTypesPriv.h:287
GrClipEdgeType
Definition: GrTypesPriv.h:361
#define SK_ABORT(message,...)
Definition: SkAssert.h:70
#define SkASSERT(cond)
Definition: SkAssert.h:116
#define INHERITED(method,...)
Definition: SkRecorder.cpp:128
static bool SkScalarNearlyZero(SkScalar x, SkScalar tolerance=SK_ScalarNearlyZero)
Definition: SkScalar.h:101
#define SK_ScalarHalf
Definition: SkScalar.h:19
void emitCode(EmitArgs &) override
void emitCode(EmitArgs &) override
SkString invokeChild(int childIndex, EmitArgs &parentArgs, std::string_view skslCoords={})
virtual std::unique_ptr< GrFragmentProcessor > clone() const =0
static std::unique_ptr< GrFragmentProcessor > Rect(std::unique_ptr< GrFragmentProcessor >, GrClipEdgeType, SkRect)
virtual void set2f(UniformHandle, float, float) const =0
virtual void set4f(UniformHandle, float, float, float, float) const =0
void codeAppend(const char *str)
void codeAppendf(const char format[],...) SK_PRINTF_LIKE(2
const T & cast() const
Definition: GrProcessor.h:127
virtual const char * name() const =0
static bool IsSimpleCircular(const SkRRect &rr)
Definition: SkRRectPriv.h:27
static SkVector GetSimpleRadii(const SkRRect &rr)
Definition: SkRRectPriv.h:22
bool isOval() const
Definition: SkRRect.h:85
Type getType() const
Definition: SkRRect.h:76
SkVector radii(Corner corner) const
Definition: SkRRect.h:271
@ kLastType
largest Type value
Definition: SkRRect.h:73
@ kSimple_Type
non-zero width and height with equal radii
Definition: SkRRect.h:70
@ kNinePatch_Type
non-zero width and height with axis-aligned radii
Definition: SkRRect.h:71
@ kUpperLeft_Corner
index of top-left corner radii
Definition: SkRRect.h:252
@ kLowerRight_Corner
index of bottom-right corner radii
Definition: SkRRect.h:254
@ kUpperRight_Corner
index of top-right corner radii
Definition: SkRRect.h:253
@ kLowerLeft_Corner
index of bottom-left corner radii
Definition: SkRRect.h:255
bool isNinePatch() const
Definition: SkRRect.h:87
bool isRect() const
Definition: SkRRect.h:84
void setRectRadii(const SkRect &rect, const SkVector radii[4])
Definition: SkRRect.cpp:189
void setRectXY(const SkRect &rect, SkScalar xRad, SkScalar yRad)
Definition: SkRRect.cpp:52
const SkRect & getBounds() const
Definition: SkRRect.h:279
bool isSimple() const
Definition: SkRRect.h:86
bool isComplex() const
Definition: SkRRect.h:88
void printf(const char format[],...) SK_PRINTF_LIKE(2
Definition: SkString.cpp:534
const char * c_str() const
Definition: SkString.h:133
VULKAN_HPP_DEFAULT_DISPATCH_LOADER_DYNAMIC_STORAGE auto & d
Definition: main.cc:19
float SkScalar
Definition: extension.cpp:12
static bool b
G_BEGIN_DECLS G_MODULE_EXPORT FlValue * args
static float max(float r, float g, float b)
Definition: hsl.cpp:49
GrFPResult Make(std::unique_ptr< GrFragmentProcessor >, GrClipEdgeType, const SkRect &, const GrShaderCaps &)
GrFPResult Make(std::unique_ptr< GrFragmentProcessor >, GrClipEdgeType, const SkRRect &, const GrShaderCaps &)
SK_API sk_sp< SkDocument > Make(SkWStream *dst, const SkSerialProcs *=nullptr, std::function< void(const SkPicture *)> onEndPage=nullptr)
SkRRect rrect
Definition: SkRecords.h:232
sk_sp< SkBlender > blender SkRect rect
Definition: SkRecords.h:350
const uint32_t fp
DEF_SWITCHES_START aot vmservice shared library name
Definition: switches.h:32
const myers::Point & get(const myers::Segment &)
SkScalar w
SkScalar h
const Scalar scale
bool fReducedShaderMode
Definition: GrShaderCaps.h:46
float fX
x-axis value
Definition: SkPoint_impl.h:164
void set(float x, float y)
Definition: SkPoint_impl.h:200
float fY
y-axis value
Definition: SkPoint_impl.h:165
static constexpr SkRect MakeWH(float w, float h)
Definition: SkRect.h:609
bool fFloatIs32Bits
Definition: SkSLUtil.h:100