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