GrRRectEffect.cpp revision 587e08f361ee3e775a6bbc6dca761dbba82e422c
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 "GrShaderCaps.h"
15#include "SkRRect.h"
16#include "SkTLazy.h"
17#include "glsl/GrGLSLFragmentProcessor.h"
18#include "glsl/GrGLSLFragmentShaderBuilder.h"
19#include "glsl/GrGLSLProgramDataManager.h"
20#include "glsl/GrGLSLUniformHandler.h"
21
22// The effects defined here only handle rrect radii >= kRadiusMin.
23static const SkScalar kRadiusMin = SK_ScalarHalf;
24
25//////////////////////////////////////////////////////////////////////////////
26
27class CircularRRectEffect : public GrFragmentProcessor {
28public:
29
30    enum CornerFlags {
31        kTopLeft_CornerFlag     = (1 << SkRRect::kUpperLeft_Corner),
32        kTopRight_CornerFlag    = (1 << SkRRect::kUpperRight_Corner),
33        kBottomRight_CornerFlag = (1 << SkRRect::kLowerRight_Corner),
34        kBottomLeft_CornerFlag  = (1 << SkRRect::kLowerLeft_Corner),
35
36        kLeft_CornerFlags   = kTopLeft_CornerFlag    | kBottomLeft_CornerFlag,
37        kTop_CornerFlags    = kTopLeft_CornerFlag    | kTopRight_CornerFlag,
38        kRight_CornerFlags  = kTopRight_CornerFlag   | kBottomRight_CornerFlag,
39        kBottom_CornerFlags = kBottomLeft_CornerFlag | kBottomRight_CornerFlag,
40
41        kAll_CornerFlags = kTopLeft_CornerFlag    | kTopRight_CornerFlag |
42                           kBottomLeft_CornerFlag | kBottomRight_CornerFlag,
43
44        kNone_CornerFlags = 0
45    };
46
47    // The flags are used to indicate which corners are circluar (unflagged corners are assumed to
48    // be square).
49    static sk_sp<GrFragmentProcessor> Make(GrPrimitiveEdgeType, uint32_t circularCornerFlags,
50                                           const SkRRect&);
51
52    virtual ~CircularRRectEffect() {}
53
54    const char* name() const override { return "CircularRRect"; }
55
56    const SkRRect& getRRect() const { return fRRect; }
57
58    uint32_t getCircularCornerFlags() const { return fCircularCornerFlags; }
59
60    GrPrimitiveEdgeType getEdgeType() const { return fEdgeType; }
61
62private:
63    CircularRRectEffect(GrPrimitiveEdgeType, uint32_t circularCornerFlags, const SkRRect&);
64
65    GrGLSLFragmentProcessor* onCreateGLSLInstance() const override;
66
67    void onGetGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const override;
68
69    bool onIsEqual(const GrFragmentProcessor& other) const override;
70
71    void onComputeInvariantOutput(GrInvariantOutput* inout) const override;
72
73    SkRRect                fRRect;
74    GrPrimitiveEdgeType    fEdgeType;
75    uint32_t               fCircularCornerFlags;
76
77    GR_DECLARE_FRAGMENT_PROCESSOR_TEST;
78
79    typedef GrFragmentProcessor INHERITED;
80};
81
82sk_sp<GrFragmentProcessor> CircularRRectEffect::Make(GrPrimitiveEdgeType edgeType,
83                                                     uint32_t circularCornerFlags,
84                                                     const SkRRect& rrect) {
85    if (kFillAA_GrProcessorEdgeType != edgeType && kInverseFillAA_GrProcessorEdgeType != edgeType) {
86        return nullptr;
87    }
88    return sk_sp<GrFragmentProcessor>(
89        new CircularRRectEffect(edgeType, circularCornerFlags, rrect));
90}
91
92void CircularRRectEffect::onComputeInvariantOutput(GrInvariantOutput* inout) const {
93    inout->mulByUnknownSingleComponent();
94}
95
96CircularRRectEffect::CircularRRectEffect(GrPrimitiveEdgeType edgeType, uint32_t circularCornerFlags,
97                                         const SkRRect& rrect)
98        : INHERITED(kModulatesInput_OptimizationFlag)
99        , fRRect(rrect)
100        , fEdgeType(edgeType)
101        , fCircularCornerFlags(circularCornerFlags) {
102    this->initClassID<CircularRRectEffect>();
103}
104
105bool CircularRRectEffect::onIsEqual(const GrFragmentProcessor& other) const {
106    const CircularRRectEffect& crre = other.cast<CircularRRectEffect>();
107    // The corner flags are derived from fRRect, so no need to check them.
108    return fEdgeType == crre.fEdgeType && fRRect == crre.fRRect;
109}
110
111//////////////////////////////////////////////////////////////////////////////
112
113GR_DEFINE_FRAGMENT_PROCESSOR_TEST(CircularRRectEffect);
114
115sk_sp<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    sk_sp<GrFragmentProcessor> fp;
122    do {
123        GrPrimitiveEdgeType et =
124                (GrPrimitiveEdgeType)d->fRandom->nextULessThan(kGrProcessorEdgeTypeCnt);
125        fp = GrRRectEffect::Make(et, rrect);
126    } while (nullptr == fp);
127    return fp;
128}
129
130//////////////////////////////////////////////////////////////////////////////
131
132class GLCircularRRectEffect : public GrGLSLFragmentProcessor {
133public:
134    GLCircularRRectEffect() {
135        fPrevRRect.setEmpty();
136    }
137
138    virtual void emitCode(EmitArgs&) override;
139
140    static inline void GenKey(const GrProcessor&, const GrShaderCaps&, GrProcessorKeyBuilder*);
141
142protected:
143    void onSetData(const GrGLSLProgramDataManager&, const GrProcessor&) override;
144
145private:
146    GrGLSLProgramDataManager::UniformHandle fInnerRectUniform;
147    GrGLSLProgramDataManager::UniformHandle fRadiusPlusHalfUniform;
148    SkRRect                                 fPrevRRect;
149    typedef GrGLSLFragmentProcessor INHERITED;
150};
151
152void GLCircularRRectEffect::emitCode(EmitArgs& args) {
153    const CircularRRectEffect& crre = args.fFp.cast<CircularRRectEffect>();
154    GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
155    const char *rectName;
156    const char *radiusPlusHalfName;
157    // The inner rect is the rrect bounds inset by the radius. Its left, top, right, and bottom
158    // edges correspond to components x, y, z, and w, respectively. When a side of the rrect has
159    // only rectangular corners, that side's value corresponds to the rect edge's value outset by
160    // half a pixel.
161    fInnerRectUniform = uniformHandler->addUniform(kFragment_GrShaderFlag,
162                                                   kVec4f_GrSLType, kDefault_GrSLPrecision,
163                                                   "innerRect",
164                                                   &rectName);
165    // x is (r + .5) and y is 1/(r + .5)
166    fRadiusPlusHalfUniform = uniformHandler->addUniform(kFragment_GrShaderFlag,
167                                                        kVec2f_GrSLType, kDefault_GrSLPrecision,
168                                                        "radiusPlusHalf",
169                                                        &radiusPlusHalfName);
170
171    // If we're on a device with a "real" mediump then the length calculation could overflow.
172    SkString clampedCircleDistance;
173    if (args.fShaderCaps->floatPrecisionVaries()) {
174        clampedCircleDistance.printf("clamp(%s.x * (1.0 - length(dxy * %s.y)), 0.0, 1.0);",
175                                     radiusPlusHalfName, radiusPlusHalfName);
176    } else {
177        clampedCircleDistance.printf("clamp(%s.x - length(dxy), 0.0, 1.0);", radiusPlusHalfName);
178    }
179
180    GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
181    // At each quarter-circle corner we compute a vector that is the offset of the fragment position
182    // from the circle center. The vector is pinned in x and y to be in the quarter-plane relevant
183    // to that corner. This means that points near the interior near the rrect top edge will have
184    // a vector that points straight up for both the TL left and TR corners. Computing an
185    // alpha from this vector at either the TR or TL corner will give the correct result. Similarly,
186    // fragments near the other three edges will get the correct AA. Fragments in the interior of
187    // the rrect will have a (0,0) vector at all four corners. So long as the radius > 0.5 they will
188    // correctly produce an alpha value of 1 at all four corners. We take the min of all the alphas.
189    // The code below is a simplified version of the above that performs maxs on the vector
190    // components before computing distances and alpha values so that only one distance computation
191    // need be computed to determine the min alpha.
192    //
193    // For the cases where one half of the rrect is rectangular we drop one of the x or y
194    // computations, compute a separate rect edge alpha for the rect side, and mul the two computed
195    // alphas together.
196    switch (crre.getCircularCornerFlags()) {
197        case CircularRRectEffect::kAll_CornerFlags:
198            fragBuilder->codeAppendf("vec2 dxy0 = %s.xy - sk_FragCoord.xy;", rectName);
199            fragBuilder->codeAppendf("vec2 dxy1 = sk_FragCoord.xy - %s.zw;", rectName);
200            fragBuilder->codeAppend("vec2 dxy = max(max(dxy0, dxy1), 0.0);");
201            fragBuilder->codeAppendf("float alpha = %s;", clampedCircleDistance.c_str());
202            break;
203        case CircularRRectEffect::kTopLeft_CornerFlag:
204            fragBuilder->codeAppendf("vec2 dxy = max(%s.xy - sk_FragCoord.xy, 0.0);",
205                                     rectName);
206            fragBuilder->codeAppendf("float rightAlpha = clamp(%s.z - sk_FragCoord.x, 0.0, 1.0);",
207                                     rectName);
208            fragBuilder->codeAppendf("float bottomAlpha = clamp(%s.w - sk_FragCoord.y, 0.0, 1.0);",
209                                     rectName);
210            fragBuilder->codeAppendf("float alpha = bottomAlpha * rightAlpha * %s;",
211                                     clampedCircleDistance.c_str());
212            break;
213        case CircularRRectEffect::kTopRight_CornerFlag:
214            fragBuilder->codeAppendf("vec2 dxy = max(vec2(sk_FragCoord.x - %s.z, "
215                                                         "%s.y - sk_FragCoord.y), 0.0);",
216                                     rectName, rectName);
217            fragBuilder->codeAppendf("float leftAlpha = clamp(sk_FragCoord.x - %s.x, 0.0, 1.0);",
218                                     rectName);
219            fragBuilder->codeAppendf("float bottomAlpha = clamp(%s.w - sk_FragCoord.y, 0.0, 1.0);",
220                                     rectName);
221            fragBuilder->codeAppendf("float alpha = bottomAlpha * leftAlpha * %s;",
222                                     clampedCircleDistance.c_str());
223            break;
224        case CircularRRectEffect::kBottomRight_CornerFlag:
225            fragBuilder->codeAppendf("vec2 dxy = max(sk_FragCoord.xy - %s.zw, 0.0);",
226                                     rectName);
227            fragBuilder->codeAppendf("float leftAlpha = clamp(sk_FragCoord.x - %s.x, 0.0, 1.0);",
228                                     rectName);
229            fragBuilder->codeAppendf("float topAlpha = clamp(sk_FragCoord.y - %s.y, 0.0, 1.0);",
230                                     rectName);
231            fragBuilder->codeAppendf("float alpha = topAlpha * leftAlpha * %s;",
232                                     clampedCircleDistance.c_str());
233            break;
234        case CircularRRectEffect::kBottomLeft_CornerFlag:
235            fragBuilder->codeAppendf("vec2 dxy = max(vec2(%s.x - sk_FragCoord.x, sk_FragCoord.y - "
236                                     "%s.w), 0.0);",
237                                     rectName, rectName);
238            fragBuilder->codeAppendf("float rightAlpha = clamp(%s.z - sk_FragCoord.x, 0.0, 1.0);",
239                                     rectName);
240            fragBuilder->codeAppendf("float topAlpha = clamp(sk_FragCoord.y - %s.y, 0.0, 1.0);",
241                                     rectName);
242            fragBuilder->codeAppendf("float alpha = topAlpha * rightAlpha * %s;",
243                                     clampedCircleDistance.c_str());
244            break;
245        case CircularRRectEffect::kLeft_CornerFlags:
246            fragBuilder->codeAppendf("vec2 dxy0 = %s.xy - sk_FragCoord.xy;", rectName);
247            fragBuilder->codeAppendf("float dy1 = sk_FragCoord.y - %s.w;", rectName);
248            fragBuilder->codeAppend("vec2 dxy = max(vec2(dxy0.x, max(dxy0.y, dy1)), 0.0);");
249            fragBuilder->codeAppendf("float rightAlpha = clamp(%s.z - sk_FragCoord.x, 0.0, 1.0);",
250                                     rectName);
251            fragBuilder->codeAppendf("float alpha = rightAlpha * %s;",
252                                     clampedCircleDistance.c_str());
253            break;
254        case CircularRRectEffect::kTop_CornerFlags:
255            fragBuilder->codeAppendf("vec2 dxy0 = %s.xy - sk_FragCoord.xy;", rectName);
256            fragBuilder->codeAppendf("float dx1 = sk_FragCoord.x - %s.z;", rectName);
257            fragBuilder->codeAppend("vec2 dxy = max(vec2(max(dxy0.x, dx1), dxy0.y), 0.0);");
258            fragBuilder->codeAppendf("float bottomAlpha = clamp(%s.w - sk_FragCoord.y, 0.0, 1.0);",
259                                     rectName);
260            fragBuilder->codeAppendf("float alpha = bottomAlpha * %s;",
261                                     clampedCircleDistance.c_str());
262            break;
263        case CircularRRectEffect::kRight_CornerFlags:
264            fragBuilder->codeAppendf("float dy0 = %s.y - sk_FragCoord.y;", rectName);
265            fragBuilder->codeAppendf("vec2 dxy1 = sk_FragCoord.xy - %s.zw;", rectName);
266            fragBuilder->codeAppend("vec2 dxy = max(vec2(dxy1.x, max(dy0, dxy1.y)), 0.0);");
267            fragBuilder->codeAppendf("float leftAlpha = clamp(sk_FragCoord.x - %s.x, 0.0, 1.0);",
268                                     rectName);
269            fragBuilder->codeAppendf("float alpha = leftAlpha * %s;",
270                                     clampedCircleDistance.c_str());
271            break;
272        case CircularRRectEffect::kBottom_CornerFlags:
273            fragBuilder->codeAppendf("float dx0 = %s.x - sk_FragCoord.x;", rectName);
274            fragBuilder->codeAppendf("vec2 dxy1 = sk_FragCoord.xy - %s.zw;", rectName);
275            fragBuilder->codeAppend("vec2 dxy = max(vec2(max(dx0, dxy1.x), dxy1.y), 0.0);");
276            fragBuilder->codeAppendf("float topAlpha = clamp(sk_FragCoord.y - %s.y, 0.0, 1.0);",
277                                     rectName);
278            fragBuilder->codeAppendf("float alpha = topAlpha * %s;",
279                                     clampedCircleDistance.c_str());
280            break;
281    }
282
283    if (kInverseFillAA_GrProcessorEdgeType == crre.getEdgeType()) {
284        fragBuilder->codeAppend("alpha = 1.0 - alpha;");
285    }
286
287    fragBuilder->codeAppendf("%s = %s;", args.fOutputColor,
288                             (GrGLSLExpr4(args.fInputColor) * GrGLSLExpr1("alpha")).c_str());
289}
290
291void GLCircularRRectEffect::GenKey(const GrProcessor& processor, const GrShaderCaps&,
292                                   GrProcessorKeyBuilder* b) {
293    const CircularRRectEffect& crre = processor.cast<CircularRRectEffect>();
294    GR_STATIC_ASSERT(kGrProcessorEdgeTypeCnt <= 8);
295    b->add32((crre.getCircularCornerFlags() << 3) | crre.getEdgeType());
296}
297
298void GLCircularRRectEffect::onSetData(const GrGLSLProgramDataManager& pdman,
299                                      const GrProcessor& processor) {
300    const CircularRRectEffect& crre = processor.cast<CircularRRectEffect>();
301    const SkRRect& rrect = crre.getRRect();
302    if (rrect != fPrevRRect) {
303        SkRect rect = rrect.getBounds();
304        SkScalar radius = 0;
305        switch (crre.getCircularCornerFlags()) {
306            case CircularRRectEffect::kAll_CornerFlags:
307                SkASSERT(rrect.isSimpleCircular());
308                radius = rrect.getSimpleRadii().fX;
309                SkASSERT(radius >= kRadiusMin);
310                rect.inset(radius, radius);
311                break;
312            case CircularRRectEffect::kTopLeft_CornerFlag:
313                radius = rrect.radii(SkRRect::kUpperLeft_Corner).fX;
314                rect.fLeft += radius;
315                rect.fTop += radius;
316                rect.fRight += 0.5f;
317                rect.fBottom += 0.5f;
318                break;
319            case CircularRRectEffect::kTopRight_CornerFlag:
320                radius = rrect.radii(SkRRect::kUpperRight_Corner).fX;
321                rect.fLeft -= 0.5f;
322                rect.fTop += radius;
323                rect.fRight -= radius;
324                rect.fBottom += 0.5f;
325                break;
326            case CircularRRectEffect::kBottomRight_CornerFlag:
327                radius = rrect.radii(SkRRect::kLowerRight_Corner).fX;
328                rect.fLeft -= 0.5f;
329                rect.fTop -= 0.5f;
330                rect.fRight -= radius;
331                rect.fBottom -= radius;
332                break;
333            case CircularRRectEffect::kBottomLeft_CornerFlag:
334                radius = rrect.radii(SkRRect::kLowerLeft_Corner).fX;
335                rect.fLeft += radius;
336                rect.fTop -= 0.5f;
337                rect.fRight += 0.5f;
338                rect.fBottom -= radius;
339                break;
340            case CircularRRectEffect::kLeft_CornerFlags:
341                radius = rrect.radii(SkRRect::kUpperLeft_Corner).fX;
342                rect.fLeft += radius;
343                rect.fTop += radius;
344                rect.fRight += 0.5f;
345                rect.fBottom -= radius;
346                break;
347            case CircularRRectEffect::kTop_CornerFlags:
348                radius = rrect.radii(SkRRect::kUpperLeft_Corner).fX;
349                rect.fLeft += radius;
350                rect.fTop += radius;
351                rect.fRight -= radius;
352                rect.fBottom += 0.5f;
353                break;
354            case CircularRRectEffect::kRight_CornerFlags:
355                radius = rrect.radii(SkRRect::kUpperRight_Corner).fX;
356                rect.fLeft -= 0.5f;
357                rect.fTop += radius;
358                rect.fRight -= radius;
359                rect.fBottom -= radius;
360                break;
361            case CircularRRectEffect::kBottom_CornerFlags:
362                radius = rrect.radii(SkRRect::kLowerLeft_Corner).fX;
363                rect.fLeft += radius;
364                rect.fTop -= 0.5f;
365                rect.fRight -= radius;
366                rect.fBottom -= radius;
367                break;
368            default:
369                SkFAIL("Should have been one of the above cases.");
370        }
371        pdman.set4f(fInnerRectUniform, rect.fLeft, rect.fTop, rect.fRight, rect.fBottom);
372        radius += 0.5f;
373        pdman.set2f(fRadiusPlusHalfUniform, radius, 1.f / radius);
374        fPrevRRect = rrect;
375    }
376}
377
378////////////////////////////////////////////////////////////////////////////////////////////////////
379
380void CircularRRectEffect::onGetGLSLProcessorKey(const GrShaderCaps& caps,
381                                                GrProcessorKeyBuilder* b) const {
382    GLCircularRRectEffect::GenKey(*this, caps, b);
383}
384
385GrGLSLFragmentProcessor* CircularRRectEffect::onCreateGLSLInstance() const  {
386    return new GLCircularRRectEffect;
387}
388
389//////////////////////////////////////////////////////////////////////////////
390
391class EllipticalRRectEffect : public GrFragmentProcessor {
392public:
393    static sk_sp<GrFragmentProcessor> Make(GrPrimitiveEdgeType, const SkRRect&);
394
395    virtual ~EllipticalRRectEffect() {}
396
397    const char* name() const override { return "EllipticalRRect"; }
398
399    const SkRRect& getRRect() const { return fRRect; }
400
401    GrPrimitiveEdgeType getEdgeType() const { return fEdgeType; }
402
403private:
404    EllipticalRRectEffect(GrPrimitiveEdgeType, const SkRRect&);
405
406    GrGLSLFragmentProcessor* onCreateGLSLInstance() const override;
407
408    void onGetGLSLProcessorKey(const GrShaderCaps&, GrProcessorKeyBuilder*) const override;
409
410    bool onIsEqual(const GrFragmentProcessor& other) const override;
411
412    void onComputeInvariantOutput(GrInvariantOutput* inout) const override;
413
414    SkRRect fRRect;
415    GrPrimitiveEdgeType fEdgeType;
416
417    GR_DECLARE_FRAGMENT_PROCESSOR_TEST;
418
419    typedef GrFragmentProcessor INHERITED;
420};
421
422sk_sp<GrFragmentProcessor>
423EllipticalRRectEffect::Make(GrPrimitiveEdgeType edgeType, const SkRRect& rrect) {
424    if (kFillAA_GrProcessorEdgeType != edgeType && kInverseFillAA_GrProcessorEdgeType != edgeType) {
425        return nullptr;
426    }
427    return sk_sp<GrFragmentProcessor>(new EllipticalRRectEffect(edgeType, rrect));
428}
429
430void EllipticalRRectEffect::onComputeInvariantOutput(GrInvariantOutput* inout) const {
431    inout->mulByUnknownSingleComponent();
432}
433
434EllipticalRRectEffect::EllipticalRRectEffect(GrPrimitiveEdgeType edgeType, const SkRRect& rrect)
435        : INHERITED(kModulatesInput_OptimizationFlag), fRRect(rrect), fEdgeType(edgeType) {
436    this->initClassID<EllipticalRRectEffect>();
437}
438
439bool EllipticalRRectEffect::onIsEqual(const GrFragmentProcessor& other) const {
440    const EllipticalRRectEffect& erre = other.cast<EllipticalRRectEffect>();
441    return fEdgeType == erre.fEdgeType && fRRect == erre.fRRect;
442}
443
444//////////////////////////////////////////////////////////////////////////////
445
446GR_DEFINE_FRAGMENT_PROCESSOR_TEST(EllipticalRRectEffect);
447
448sk_sp<GrFragmentProcessor> EllipticalRRectEffect::TestCreate(GrProcessorTestData* d) {
449    SkScalar w = d->fRandom->nextRangeScalar(20.f, 1000.f);
450    SkScalar h = d->fRandom->nextRangeScalar(20.f, 1000.f);
451    SkVector r[4];
452    r[SkRRect::kUpperLeft_Corner].fX = d->fRandom->nextRangeF(kRadiusMin, 9.f);
453    // ensure at least one corner really is elliptical
454    do {
455        r[SkRRect::kUpperLeft_Corner].fY = d->fRandom->nextRangeF(kRadiusMin, 9.f);
456    } while (r[SkRRect::kUpperLeft_Corner].fY == r[SkRRect::kUpperLeft_Corner].fX);
457
458    SkRRect rrect;
459    if (d->fRandom->nextBool()) {
460        // half the time create a four-radii rrect.
461        r[SkRRect::kLowerRight_Corner].fX = d->fRandom->nextRangeF(kRadiusMin, 9.f);
462        r[SkRRect::kLowerRight_Corner].fY = d->fRandom->nextRangeF(kRadiusMin, 9.f);
463
464        r[SkRRect::kUpperRight_Corner].fX = r[SkRRect::kLowerRight_Corner].fX;
465        r[SkRRect::kUpperRight_Corner].fY = r[SkRRect::kUpperLeft_Corner].fY;
466
467        r[SkRRect::kLowerLeft_Corner].fX = r[SkRRect::kUpperLeft_Corner].fX;
468        r[SkRRect::kLowerLeft_Corner].fY = r[SkRRect::kLowerRight_Corner].fY;
469
470        rrect.setRectRadii(SkRect::MakeWH(w, h), r);
471    } else {
472        rrect.setRectXY(SkRect::MakeWH(w, h), r[SkRRect::kUpperLeft_Corner].fX,
473                                              r[SkRRect::kUpperLeft_Corner].fY);
474    }
475    sk_sp<GrFragmentProcessor> fp;
476    do {
477        GrPrimitiveEdgeType et =
478                (GrPrimitiveEdgeType)d->fRandom->nextULessThan(kGrProcessorEdgeTypeCnt);
479        fp = GrRRectEffect::Make(et, rrect);
480    } while (nullptr == fp);
481    return fp;
482}
483
484//////////////////////////////////////////////////////////////////////////////
485
486class GLEllipticalRRectEffect : public GrGLSLFragmentProcessor {
487public:
488    GLEllipticalRRectEffect() {
489        fPrevRRect.setEmpty();
490    }
491
492    void emitCode(EmitArgs&) override;
493
494    static inline void GenKey(const GrProcessor&, const GrShaderCaps&, GrProcessorKeyBuilder*);
495
496protected:
497    void onSetData(const GrGLSLProgramDataManager&, const GrProcessor&) override;
498
499private:
500    GrGLSLProgramDataManager::UniformHandle fInnerRectUniform;
501    GrGLSLProgramDataManager::UniformHandle fInvRadiiSqdUniform;
502    GrGLSLProgramDataManager::UniformHandle fScaleUniform;
503    SkRRect                                 fPrevRRect;
504    typedef GrGLSLFragmentProcessor INHERITED;
505};
506
507void GLEllipticalRRectEffect::emitCode(EmitArgs& args) {
508    const EllipticalRRectEffect& erre = args.fFp.cast<EllipticalRRectEffect>();
509    GrGLSLUniformHandler* uniformHandler = args.fUniformHandler;
510    const char *rectName;
511    // The inner rect is the rrect bounds inset by the x/y radii
512    fInnerRectUniform = uniformHandler->addUniform(kFragment_GrShaderFlag,
513                                                   kVec4f_GrSLType, kDefault_GrSLPrecision,
514                                                   "innerRect",
515                                                   &rectName);
516
517    GrGLSLFPFragmentBuilder* fragBuilder = args.fFragBuilder;
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 - sk_FragCoord.xy;", rectName);
531    fragBuilder->codeAppendf("vec2 dxy1 = sk_FragCoord.xy - %s.zw;", 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.fShaderCaps->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
604void GLEllipticalRRectEffect::GenKey(const GrProcessor& effect, const GrShaderCaps&,
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
611void 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
672void EllipticalRRectEffect::onGetGLSLProcessorKey(const GrShaderCaps& caps,
673                                                  GrProcessorKeyBuilder* b) const {
674    GLEllipticalRRectEffect::GenKey(*this, caps, b);
675}
676
677GrGLSLFragmentProcessor* EllipticalRRectEffect::onCreateGLSLInstance() const  {
678    return new GLEllipticalRRectEffect;
679}
680
681//////////////////////////////////////////////////////////////////////////////
682
683sk_sp<GrFragmentProcessor> GrRRectEffect::Make(GrPrimitiveEdgeType edgeType, const SkRRect& rrect) {
684    if (rrect.isRect()) {
685        return GrConvexPolyEffect::Make(edgeType, rrect.getBounds());
686    }
687
688    if (rrect.isOval()) {
689        return GrOvalEffect::Make(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::Make(edgeType, rrect.getBounds());
697        }
698        if (rrect.getSimpleRadii().fX == rrect.getSimpleRadii().fY) {
699            return CircularRRectEffect::Make(edgeType, CircularRRectEffect::kAll_CornerFlags,
700                                               rrect);
701        } else {
702            return EllipticalRRectEffect::Make(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::Make(edgeType, cornerFlags, *rr);
759            }
760            case CircularRRectEffect::kNone_CornerFlags:
761                return GrConvexPolyEffect::Make(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::Make(edgeType, rrect);
771                }
772                return nullptr;
773            }
774        }
775    }
776
777    return nullptr;
778}
779