Home | History | Annotate | Download | only in effects
      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 "gl/GrGLEffect.h"
     11 #include "gl/GrGLSL.h"
     12 #include "GrConvexPolyEffect.h"
     13 #include "GrOvalEffect.h"
     14 #include "GrTBackendEffectFactory.h"
     15 
     16 #include "SkRRect.h"
     17 
     18 // The effects defined here only handle rrect radii >= kRadiusMin.
     19 static const SkScalar kRadiusMin = SK_ScalarHalf;
     20 
     21 //////////////////////////////////////////////////////////////////////////////
     22 
     23 class GLCircularRRectEffect;
     24 
     25 class CircularRRectEffect : public GrEffect {
     26 public:
     27 
     28     enum CornerFlags {
     29         kTopLeft_CornerFlag     = (1 << SkRRect::kUpperLeft_Corner),
     30         kTopRight_CornerFlag    = (1 << SkRRect::kUpperRight_Corner),
     31         kBottomRight_CornerFlag = (1 << SkRRect::kLowerRight_Corner),
     32         kBottomLeft_CornerFlag  = (1 << SkRRect::kLowerLeft_Corner),
     33 
     34         kLeft_CornerFlags   = kTopLeft_CornerFlag    | kBottomLeft_CornerFlag,
     35         kTop_CornerFlags    = kTopLeft_CornerFlag    | kTopRight_CornerFlag,
     36         kRight_CornerFlags  = kTopRight_CornerFlag   | kBottomRight_CornerFlag,
     37         kBottom_CornerFlags = kBottomLeft_CornerFlag | kBottomRight_CornerFlag,
     38 
     39         kAll_CornerFlags = kTopLeft_CornerFlag    | kTopRight_CornerFlag |
     40                            kBottomLeft_CornerFlag | kBottomRight_CornerFlag,
     41 
     42         kNone_CornerFlags = 0
     43     };
     44 
     45     // The flags are used to indicate which corners are circluar (unflagged corners are assumed to
     46     // be square).
     47     static GrEffectRef* Create(GrEffectEdgeType, uint32_t circularCornerFlags, const SkRRect&);
     48 
     49     virtual ~CircularRRectEffect() {};
     50     static const char* Name() { return "CircularRRect"; }
     51 
     52     const SkRRect& getRRect() const { return fRRect; }
     53 
     54     uint32_t getCircularCornerFlags() const { return fCircularCornerFlags; }
     55 
     56     GrEffectEdgeType getEdgeType() const { return fEdgeType; }
     57 
     58     typedef GLCircularRRectEffect GLEffect;
     59 
     60     virtual void getConstantColorComponents(GrColor* color, uint32_t* validFlags) const SK_OVERRIDE;
     61 
     62     virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE;
     63 
     64 private:
     65     CircularRRectEffect(GrEffectEdgeType, uint32_t circularCornerFlags, const SkRRect&);
     66 
     67     virtual bool onIsEqual(const GrEffect& other) const SK_OVERRIDE;
     68 
     69     SkRRect             fRRect;
     70     GrEffectEdgeType    fEdgeType;
     71     uint32_t            fCircularCornerFlags;
     72 
     73     GR_DECLARE_EFFECT_TEST;
     74 
     75     typedef GrEffect INHERITED;
     76 };
     77 
     78 GrEffectRef* CircularRRectEffect::Create(GrEffectEdgeType edgeType,
     79                                  uint32_t circularCornerFlags,
     80                                  const SkRRect& rrect) {
     81     if (kFillAA_GrEffectEdgeType != edgeType && kInverseFillAA_GrEffectEdgeType != edgeType) {
     82         return NULL;
     83     }
     84     return CreateEffectRef(AutoEffectUnref(SkNEW_ARGS(CircularRRectEffect,
     85                                                       (edgeType, circularCornerFlags, rrect))));
     86 }
     87 
     88 void CircularRRectEffect::getConstantColorComponents(GrColor* color, uint32_t* validFlags) const {
     89     *validFlags = 0;
     90 }
     91 
     92 const GrBackendEffectFactory& CircularRRectEffect::getFactory() const {
     93     return GrTBackendEffectFactory<CircularRRectEffect>::getInstance();
     94 }
     95 
     96 CircularRRectEffect::CircularRRectEffect(GrEffectEdgeType edgeType, uint32_t circularCornerFlags,
     97                          const SkRRect& rrect)
     98     : fRRect(rrect)
     99     , fEdgeType(edgeType)
    100     , fCircularCornerFlags(circularCornerFlags) {
    101     this->setWillReadFragmentPosition();
    102 }
    103 
    104 bool CircularRRectEffect::onIsEqual(const GrEffect& other) const {
    105     const CircularRRectEffect& crre = CastEffect<CircularRRectEffect>(other);
    106     // The corner flags are derived from fRRect, so no need to check them.
    107     return fEdgeType == crre.fEdgeType && fRRect == crre.fRRect;
    108 }
    109 
    110 //////////////////////////////////////////////////////////////////////////////
    111 
    112 GR_DEFINE_EFFECT_TEST(CircularRRectEffect);
    113 
    114 GrEffectRef* CircularRRectEffect::TestCreate(SkRandom* random,
    115                                      GrContext*,
    116                                      const GrDrawTargetCaps& caps,
    117                                      GrTexture*[]) {
    118     SkScalar w = random->nextRangeScalar(20.f, 1000.f);
    119     SkScalar h = random->nextRangeScalar(20.f, 1000.f);
    120     SkScalar r = random->nextRangeF(kRadiusMin, 9.f);
    121     SkRRect rrect;
    122     rrect.setRectXY(SkRect::MakeWH(w, h), r, r);
    123     GrEffectRef* effect;
    124     do {
    125         GrEffectEdgeType et = (GrEffectEdgeType)random->nextULessThan(kGrEffectEdgeTypeCnt);
    126         effect = GrRRectEffect::Create(et, rrect);
    127     } while (NULL == effect);
    128     return effect;
    129 }
    130 
    131 //////////////////////////////////////////////////////////////////////////////
    132 
    133 class GLCircularRRectEffect : public GrGLEffect {
    134 public:
    135     GLCircularRRectEffect(const GrBackendEffectFactory&, const GrDrawEffect&);
    136 
    137     virtual void emitCode(GrGLShaderBuilder* builder,
    138                           const GrDrawEffect& drawEffect,
    139                           EffectKey key,
    140                           const char* outputColor,
    141                           const char* inputColor,
    142                           const TransformedCoordsArray&,
    143                           const TextureSamplerArray&) SK_OVERRIDE;
    144 
    145     static inline EffectKey GenKey(const GrDrawEffect&, const GrGLCaps&);
    146 
    147     virtual void setData(const GrGLUniformManager&, const GrDrawEffect&) SK_OVERRIDE;
    148 
    149 private:
    150     GrGLUniformManager::UniformHandle   fInnerRectUniform;
    151     GrGLUniformManager::UniformHandle   fRadiusPlusHalfUniform;
    152     SkRRect                             fPrevRRect;
    153     typedef GrGLEffect INHERITED;
    154 };
    155 
    156 GLCircularRRectEffect::GLCircularRRectEffect(const GrBackendEffectFactory& factory,
    157                              const GrDrawEffect& drawEffect)
    158     : INHERITED (factory) {
    159     fPrevRRect.setEmpty();
    160 }
    161 
    162 void GLCircularRRectEffect::emitCode(GrGLShaderBuilder* builder,
    163                              const GrDrawEffect& drawEffect,
    164                              EffectKey key,
    165                              const char* outputColor,
    166                              const char* inputColor,
    167                              const TransformedCoordsArray&,
    168                              const TextureSamplerArray& samplers) {
    169     const CircularRRectEffect& crre = drawEffect.castEffect<CircularRRectEffect>();
    170     const char *rectName;
    171     const char *radiusPlusHalfName;
    172     // The inner rect is the rrect bounds inset by the radius. Its left, top, right, and bottom
    173     // edges correspond to components x, y, z, and w, respectively. When a side of the rrect has
    174     // only rectangular corners, that side's value corresponds to the rect edge's value outset by
    175     // half a pixel.
    176     fInnerRectUniform = builder->addUniform(GrGLShaderBuilder::kFragment_Visibility,
    177                                             kVec4f_GrSLType,
    178                                             "innerRect",
    179                                             &rectName);
    180     fRadiusPlusHalfUniform = builder->addUniform(GrGLShaderBuilder::kFragment_Visibility,
    181                                                  kFloat_GrSLType,
    182                                                  "radiusPlusHalf",
    183                                                  &radiusPlusHalfName);
    184     const char* fragmentPos = builder->fragmentPosition();
    185     // At each quarter-circle corner we compute a vector that is the offset of the fragment position
    186     // from the circle center. The vector is pinned in x and y to be in the quarter-plane relevant
    187     // to that corner. This means that points near the interior near the rrect top edge will have
    188     // a vector that points straight up for both the TL left and TR corners. Computing an
    189     // alpha from this vector at either the TR or TL corner will give the correct result. Similarly,
    190     // fragments near the other three edges will get the correct AA. Fragments in the interior of
    191     // the rrect will have a (0,0) vector at all four corners. So long as the radius > 0.5 they will
    192     // correctly produce an alpha value of 1 at all four corners. We take the min of all the alphas.
    193     // The code below is a simplified version of the above that performs maxs on the vector
    194     // components before computing distances and alpha values so that only one distance computation
    195     // need be computed to determine the min alpha.
    196     //
    197     // For the cases where one half of the rrect is rectangular we drop one of the x or y
    198     // computations, compute a separate rect edge alpha for the rect side, and mul the two computed
    199     // alphas together.
    200     switch (crre.getCircularCornerFlags()) {
    201         case CircularRRectEffect::kAll_CornerFlags:
    202             builder->fsCodeAppendf("\t\tvec2 dxy0 = %s.xy - %s;\n", rectName, fragmentPos);
    203             builder->fsCodeAppendf("\t\tvec2 dxy1 = %s - %s.zw;\n", fragmentPos, rectName);
    204             builder->fsCodeAppend("\t\tvec2 dxy = max(max(dxy0, dxy1), 0.0);\n");
    205             builder->fsCodeAppendf("\t\tfloat alpha = clamp(%s - length(dxy), 0.0, 1.0);\n",
    206                                    radiusPlusHalfName);
    207             break;
    208         case CircularRRectEffect::kTopLeft_CornerFlag:
    209             builder->fsCodeAppendf("\t\tvec2 dxy = max(%s.xy - %s.xy, 0.0);\n",
    210                                    rectName, fragmentPos);
    211             builder->fsCodeAppendf("\t\tfloat rightAlpha = clamp(%s.z - %s.x, 0.0, 1.0);\n",
    212                                     rectName, fragmentPos);
    213             builder->fsCodeAppendf("\t\tfloat bottomAlpha = clamp(%s.w - %s.y, 0.0, 1.0);\n",
    214                                     rectName, fragmentPos);
    215             builder->fsCodeAppendf("\t\tfloat alpha = bottomAlpha * rightAlpha * clamp(%s - length(dxy), 0.0, 1.0);\n",
    216                                    radiusPlusHalfName);
    217             break;
    218         case CircularRRectEffect::kTopRight_CornerFlag:
    219             builder->fsCodeAppendf("\t\tvec2 dxy = max(vec2(%s.x - %s.z, %s.y - %s.y), 0.0);\n",
    220                                    fragmentPos, rectName, rectName, fragmentPos);
    221             builder->fsCodeAppendf("\t\tfloat leftAlpha = clamp(%s.x - %s.x, 0.0, 1.0);\n",
    222                                    fragmentPos, rectName);
    223             builder->fsCodeAppendf("\t\tfloat bottomAlpha = clamp(%s.w - %s.y, 0.0, 1.0);\n",
    224                                     rectName, fragmentPos);
    225             builder->fsCodeAppendf("\t\tfloat alpha = bottomAlpha * leftAlpha * clamp(%s - length(dxy), 0.0, 1.0);\n",
    226                                    radiusPlusHalfName);
    227             break;
    228         case CircularRRectEffect::kBottomRight_CornerFlag:
    229             builder->fsCodeAppendf("\t\tvec2 dxy = max(%s.xy - %s.zw, 0.0);\n",
    230                                    fragmentPos, rectName);
    231             builder->fsCodeAppendf("\t\tfloat leftAlpha = clamp(%s.x - %s.x, 0.0, 1.0);\n",
    232                                    fragmentPos, rectName);
    233             builder->fsCodeAppendf("\t\tfloat topAlpha = clamp(%s.y - %s.y, 0.0, 1.0);\n",
    234                                    fragmentPos, rectName);
    235             builder->fsCodeAppendf("\t\tfloat alpha = topAlpha * leftAlpha * clamp(%s - length(dxy), 0.0, 1.0);\n",
    236                                    radiusPlusHalfName);
    237             break;
    238         case CircularRRectEffect::kBottomLeft_CornerFlag:
    239             builder->fsCodeAppendf("\t\tvec2 dxy = max(vec2(%s.x - %s.x, %s.y - %s.w), 0.0);\n",
    240                                    rectName, fragmentPos, fragmentPos, rectName);
    241             builder->fsCodeAppendf("\t\tfloat rightAlpha = clamp(%s.z - %s.x, 0.0, 1.0);\n",
    242                                     rectName, fragmentPos);
    243             builder->fsCodeAppendf("\t\tfloat topAlpha = clamp(%s.y - %s.y, 0.0, 1.0);\n",
    244                                    fragmentPos, rectName);
    245             builder->fsCodeAppendf("\t\tfloat alpha = topAlpha * rightAlpha * clamp(%s - length(dxy), 0.0, 1.0);\n",
    246                                    radiusPlusHalfName);
    247             break;
    248         case CircularRRectEffect::kLeft_CornerFlags:
    249             builder->fsCodeAppendf("\t\tvec2 dxy0 = %s.xy - %s.xy;\n", rectName, fragmentPos);
    250             builder->fsCodeAppendf("\t\tfloat dy1 = %s.y - %s.w;\n", fragmentPos, rectName);
    251             builder->fsCodeAppend("\t\tvec2 dxy = max(vec2(dxy0.x, max(dxy0.y, dy1)), 0.0);\n");
    252             builder->fsCodeAppendf("\t\tfloat rightAlpha = clamp(%s.z - %s.x, 0.0, 1.0);\n",
    253                                     rectName, fragmentPos);
    254             builder->fsCodeAppendf("\t\tfloat alpha = rightAlpha * clamp(%s - length(dxy), 0.0, 1.0);\n",
    255                                    radiusPlusHalfName);
    256             break;
    257         case CircularRRectEffect::kTop_CornerFlags:
    258             builder->fsCodeAppendf("\t\tvec2 dxy0 = %s.xy - %s.xy;\n", rectName, fragmentPos);
    259             builder->fsCodeAppendf("\t\tfloat dx1 = %s.x - %s.z;\n", fragmentPos, rectName);
    260             builder->fsCodeAppend("\t\tvec2 dxy = max(vec2(max(dxy0.x, dx1), dxy0.y), 0.0);\n");
    261             builder->fsCodeAppendf("\t\tfloat bottomAlpha = clamp(%s.w - %s.y, 0.0, 1.0);\n",
    262                                    rectName, fragmentPos);
    263             builder->fsCodeAppendf("\t\tfloat alpha = bottomAlpha * clamp(%s - length(dxy), 0.0, 1.0);\n",
    264                                    radiusPlusHalfName);
    265             break;
    266         case CircularRRectEffect::kRight_CornerFlags:
    267             builder->fsCodeAppendf("\t\tfloat dy0 = %s.y - %s.y;\n", rectName, fragmentPos);
    268             builder->fsCodeAppendf("\t\tvec2 dxy1 = %s.xy - %s.zw;\n", fragmentPos, rectName);
    269             builder->fsCodeAppend("\t\tvec2 dxy = max(vec2(dxy1.x, max(dy0, dxy1.y)), 0.0);\n");
    270             builder->fsCodeAppendf("\t\tfloat leftAlpha = clamp(%s.x - %s.x, 0.0, 1.0);\n",
    271                                    fragmentPos, rectName);
    272             builder->fsCodeAppendf("\t\tfloat alpha = leftAlpha * clamp(%s - length(dxy), 0.0, 1.0);\n",
    273                                    radiusPlusHalfName);
    274             break;
    275         case CircularRRectEffect::kBottom_CornerFlags:
    276             builder->fsCodeAppendf("\t\tfloat dx0 = %s.x - %s.x;\n", rectName, fragmentPos);
    277             builder->fsCodeAppendf("\t\tvec2 dxy1 = %s.xy - %s.zw;\n", fragmentPos, rectName);
    278             builder->fsCodeAppend("\t\tvec2 dxy = max(vec2(max(dx0, dxy1.x), dxy1.y), 0.0);\n");
    279             builder->fsCodeAppendf("\t\tfloat topAlpha = clamp(%s.y - %s.y, 0.0, 1.0);\n",
    280                                    fragmentPos, rectName);
    281             builder->fsCodeAppendf("\t\tfloat alpha = topAlpha * clamp(%s - length(dxy), 0.0, 1.0);\n",
    282                                    radiusPlusHalfName);
    283             break;
    284     }
    285 
    286     if (kInverseFillAA_GrEffectEdgeType == crre.getEdgeType()) {
    287         builder->fsCodeAppend("\t\talpha = 1.0 - alpha;\n");
    288     }
    289 
    290     builder->fsCodeAppendf("\t\t%s = %s;\n", outputColor,
    291                            (GrGLSLExpr4(inputColor) * GrGLSLExpr1("alpha")).c_str());
    292 }
    293 
    294 GrGLEffect::EffectKey GLCircularRRectEffect::GenKey(const GrDrawEffect& drawEffect,
    295                                                     const GrGLCaps&) {
    296     const CircularRRectEffect& crre = drawEffect.castEffect<CircularRRectEffect>();
    297     GR_STATIC_ASSERT(kGrEffectEdgeTypeCnt <= 8);
    298     return (crre.getCircularCornerFlags() << 3) | crre.getEdgeType();
    299 }
    300 
    301 void GLCircularRRectEffect::setData(const GrGLUniformManager& uman,
    302                                     const GrDrawEffect& drawEffect) {
    303     const CircularRRectEffect& crre = drawEffect.castEffect<CircularRRectEffect>();
    304     const SkRRect& rrect = crre.getRRect();
    305     if (rrect != fPrevRRect) {
    306         SkRect rect = rrect.getBounds();
    307         SkScalar radius = 0;
    308         switch (crre.getCircularCornerFlags()) {
    309             case CircularRRectEffect::kAll_CornerFlags:
    310                 SkASSERT(rrect.isSimpleCircular());
    311                 radius = rrect.getSimpleRadii().fX;
    312                 SkASSERT(radius >= kRadiusMin);
    313                 rect.inset(radius, radius);
    314                 break;
    315             case CircularRRectEffect::kTopLeft_CornerFlag:
    316                 radius = rrect.radii(SkRRect::kUpperLeft_Corner).fX;
    317                 rect.fLeft += radius;
    318                 rect.fTop += radius;
    319                 rect.fRight += 0.5f;
    320                 rect.fBottom += 0.5f;
    321                 break;
    322             case CircularRRectEffect::kTopRight_CornerFlag:
    323                 radius = rrect.radii(SkRRect::kUpperRight_Corner).fX;
    324                 rect.fLeft -= 0.5f;
    325                 rect.fTop += radius;
    326                 rect.fRight -= radius;
    327                 rect.fBottom += 0.5f;
    328                 break;
    329             case CircularRRectEffect::kBottomRight_CornerFlag:
    330                 radius = rrect.radii(SkRRect::kLowerRight_Corner).fX;
    331                 rect.fLeft -= 0.5f;
    332                 rect.fTop -= 0.5f;
    333                 rect.fRight -= radius;
    334                 rect.fBottom -= radius;
    335                 break;
    336             case CircularRRectEffect::kBottomLeft_CornerFlag:
    337                 radius = rrect.radii(SkRRect::kLowerLeft_Corner).fX;
    338                 rect.fLeft += radius;
    339                 rect.fTop -= 0.5f;
    340                 rect.fRight += 0.5f;
    341                 rect.fBottom -= radius;
    342                 break;
    343             case CircularRRectEffect::kLeft_CornerFlags:
    344                 radius = rrect.radii(SkRRect::kUpperLeft_Corner).fX;
    345                 rect.fLeft += radius;
    346                 rect.fTop += radius;
    347                 rect.fRight += 0.5f;
    348                 rect.fBottom -= radius;
    349                 break;
    350             case CircularRRectEffect::kTop_CornerFlags:
    351                 radius = rrect.radii(SkRRect::kUpperLeft_Corner).fX;
    352                 rect.fLeft += radius;
    353                 rect.fTop += radius;
    354                 rect.fRight -= radius;
    355                 rect.fBottom += 0.5f;
    356                 break;
    357             case CircularRRectEffect::kRight_CornerFlags:
    358                 radius = rrect.radii(SkRRect::kUpperRight_Corner).fX;
    359                 rect.fLeft -= 0.5f;
    360                 rect.fTop += radius;
    361                 rect.fRight -= radius;
    362                 rect.fBottom -= radius;
    363                 break;
    364             case CircularRRectEffect::kBottom_CornerFlags:
    365                 radius = rrect.radii(SkRRect::kLowerLeft_Corner).fX;
    366                 rect.fLeft += radius;
    367                 rect.fTop -= 0.5f;
    368                 rect.fRight -= radius;
    369                 rect.fBottom -= radius;
    370                 break;
    371             default:
    372                 SkFAIL("Should have been one of the above cases.");
    373         }
    374         uman.set4f(fInnerRectUniform, rect.fLeft, rect.fTop, rect.fRight, rect.fBottom);
    375         uman.set1f(fRadiusPlusHalfUniform, radius + 0.5f);
    376         fPrevRRect = rrect;
    377     }
    378 }
    379 
    380 //////////////////////////////////////////////////////////////////////////////
    381 
    382 class GLEllipticalRRectEffect;
    383 
    384 class EllipticalRRectEffect : public GrEffect {
    385 public:
    386     static GrEffectRef* Create(GrEffectEdgeType, const SkRRect&);
    387 
    388     virtual ~EllipticalRRectEffect() {};
    389     static const char* Name() { return "EllipticalRRect"; }
    390 
    391     const SkRRect& getRRect() const { return fRRect; }
    392 
    393 
    394     GrEffectEdgeType getEdgeType() const { return fEdgeType; }
    395 
    396     typedef GLEllipticalRRectEffect GLEffect;
    397 
    398     virtual void getConstantColorComponents(GrColor* color, uint32_t* validFlags) const SK_OVERRIDE;
    399 
    400     virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE;
    401 
    402 private:
    403     EllipticalRRectEffect(GrEffectEdgeType, const SkRRect&);
    404 
    405     virtual bool onIsEqual(const GrEffect& other) const SK_OVERRIDE;
    406 
    407     SkRRect             fRRect;
    408     GrEffectEdgeType    fEdgeType;
    409 
    410     GR_DECLARE_EFFECT_TEST;
    411 
    412     typedef GrEffect INHERITED;
    413 };
    414 
    415 GrEffectRef* EllipticalRRectEffect::Create(GrEffectEdgeType edgeType, const SkRRect& rrect) {
    416     if (kFillAA_GrEffectEdgeType != edgeType && kInverseFillAA_GrEffectEdgeType != edgeType) {
    417         return NULL;
    418     }
    419     return CreateEffectRef(AutoEffectUnref(SkNEW_ARGS(EllipticalRRectEffect, (edgeType, rrect))));
    420 }
    421 
    422 void EllipticalRRectEffect::getConstantColorComponents(GrColor* color, uint32_t* validFlags) const {
    423     *validFlags = 0;
    424 }
    425 
    426 const GrBackendEffectFactory& EllipticalRRectEffect::getFactory() const {
    427     return GrTBackendEffectFactory<EllipticalRRectEffect>::getInstance();
    428 }
    429 
    430 EllipticalRRectEffect::EllipticalRRectEffect(GrEffectEdgeType edgeType, const SkRRect& rrect)
    431     : fRRect(rrect)
    432     , fEdgeType(edgeType){
    433     this->setWillReadFragmentPosition();
    434 }
    435 
    436 bool EllipticalRRectEffect::onIsEqual(const GrEffect& other) const {
    437     const EllipticalRRectEffect& erre = CastEffect<EllipticalRRectEffect>(other);
    438     return fEdgeType == erre.fEdgeType && fRRect == erre.fRRect;
    439 }
    440 
    441 //////////////////////////////////////////////////////////////////////////////
    442 
    443 GR_DEFINE_EFFECT_TEST(EllipticalRRectEffect);
    444 
    445 GrEffectRef* EllipticalRRectEffect::TestCreate(SkRandom* random,
    446                                                GrContext*,
    447                                                const GrDrawTargetCaps& caps,
    448                                                GrTexture*[]) {
    449     SkScalar w = random->nextRangeScalar(20.f, 1000.f);
    450     SkScalar h = random->nextRangeScalar(20.f, 1000.f);
    451     SkVector r[4];
    452     r[SkRRect::kUpperLeft_Corner].fX = random->nextRangeF(kRadiusMin, 9.f);
    453     // ensure at least one corner really is elliptical
    454     do {
    455         r[SkRRect::kUpperLeft_Corner].fY = random->nextRangeF(kRadiusMin, 9.f);
    456     } while (r[SkRRect::kUpperLeft_Corner].fY == r[SkRRect::kUpperLeft_Corner].fX);
    457 
    458     SkRRect rrect;
    459     if (random->nextBool()) {
    460         // half the time create a four-radii rrect.
    461         r[SkRRect::kLowerRight_Corner].fX = random->nextRangeF(kRadiusMin, 9.f);
    462         r[SkRRect::kLowerRight_Corner].fY = random->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     GrEffectRef* effect;
    476     do {
    477         GrEffectEdgeType et = (GrEffectEdgeType)random->nextULessThan(kGrEffectEdgeTypeCnt);
    478         effect = GrRRectEffect::Create(et, rrect);
    479     } while (NULL == effect);
    480     return effect;
    481 }
    482 
    483 //////////////////////////////////////////////////////////////////////////////
    484 
    485 class GLEllipticalRRectEffect : public GrGLEffect {
    486 public:
    487     GLEllipticalRRectEffect(const GrBackendEffectFactory&, const GrDrawEffect&);
    488 
    489     virtual void emitCode(GrGLShaderBuilder* builder,
    490                           const GrDrawEffect& drawEffect,
    491                           EffectKey key,
    492                           const char* outputColor,
    493                           const char* inputColor,
    494                           const TransformedCoordsArray&,
    495                           const TextureSamplerArray&) SK_OVERRIDE;
    496 
    497     static inline EffectKey GenKey(const GrDrawEffect&, const GrGLCaps&);
    498 
    499     virtual void setData(const GrGLUniformManager&, const GrDrawEffect&) SK_OVERRIDE;
    500 
    501 private:
    502     GrGLUniformManager::UniformHandle   fInnerRectUniform;
    503     GrGLUniformManager::UniformHandle   fInvRadiiSqdUniform;
    504     SkRRect                             fPrevRRect;
    505     typedef GrGLEffect INHERITED;
    506 };
    507 
    508 GLEllipticalRRectEffect::GLEllipticalRRectEffect(const GrBackendEffectFactory& factory,
    509                              const GrDrawEffect& drawEffect)
    510     : INHERITED (factory) {
    511     fPrevRRect.setEmpty();
    512 }
    513 
    514 void GLEllipticalRRectEffect::emitCode(GrGLShaderBuilder* builder,
    515                                        const GrDrawEffect& drawEffect,
    516                                        EffectKey key,
    517                                        const char* outputColor,
    518                                        const char* inputColor,
    519                                        const TransformedCoordsArray&,
    520                                        const TextureSamplerArray& samplers) {
    521     const EllipticalRRectEffect& erre = drawEffect.castEffect<EllipticalRRectEffect>();
    522     const char *rectName;
    523     // The inner rect is the rrect bounds inset by the x/y radii
    524     fInnerRectUniform = builder->addUniform(GrGLShaderBuilder::kFragment_Visibility,
    525                                             kVec4f_GrSLType,
    526                                             "innerRect",
    527                                             &rectName);
    528     const char* fragmentPos = builder->fragmentPosition();
    529     // At each quarter-ellipse corner we compute a vector that is the offset of the fragment pos
    530     // to the ellipse center. The vector is pinned in x and y to be in the quarter-plane relevant
    531     // to that corner. This means that points near the interior near the rrect top edge will have
    532     // a vector that points straight up for both the TL left and TR corners. Computing an
    533     // alpha from this vector at either the TR or TL corner will give the correct result. Similarly,
    534     // fragments near the other three edges will get the correct AA. Fragments in the interior of
    535     // the rrect will have a (0,0) vector at all four corners. So long as the radii > 0.5 they will
    536     // correctly produce an alpha value of 1 at all four corners. We take the min of all the alphas.
    537     // The code below is a simplified version of the above that performs maxs on the vector
    538     // components before computing distances and alpha values so that only one distance computation
    539     // need be computed to determine the min alpha.
    540     builder->fsCodeAppendf("\t\tvec2 dxy0 = %s.xy - %s;\n", rectName, fragmentPos);
    541     builder->fsCodeAppendf("\t\tvec2 dxy1 = %s - %s.zw;\n", fragmentPos, rectName);
    542     switch (erre.getRRect().getType()) {
    543         case SkRRect::kSimple_Type: {
    544             const char *invRadiiXYSqdName;
    545             fInvRadiiSqdUniform = builder->addUniform(GrGLShaderBuilder::kFragment_Visibility,
    546                                                       kVec2f_GrSLType,
    547                                                       "invRadiiXY",
    548                                                       &invRadiiXYSqdName);
    549             builder->fsCodeAppend("\t\tvec2 dxy = max(max(dxy0, dxy1), 0.0);\n");
    550             // Z is the x/y offsets divided by squared radii.
    551             builder->fsCodeAppendf("\t\tvec2 Z = dxy * %s;\n", invRadiiXYSqdName);
    552             break;
    553         }
    554         case SkRRect::kNinePatch_Type: {
    555             const char *invRadiiLTRBSqdName;
    556             fInvRadiiSqdUniform = builder->addUniform(GrGLShaderBuilder::kFragment_Visibility,
    557                                                       kVec4f_GrSLType,
    558                                                       "invRadiiLTRB",
    559                                                       &invRadiiLTRBSqdName);
    560             builder->fsCodeAppend("\t\tvec2 dxy = max(max(dxy0, dxy1), 0.0);\n");
    561             // Z is the x/y offsets divided by squared radii. We only care about the (at most) one
    562             // corner where both the x and y offsets are positive, hence the maxes. (The inverse
    563             // squared radii will always be positive.)
    564             builder->fsCodeAppendf("\t\tvec2 Z = max(max(dxy0 * %s.xy, dxy1 * %s.zw), 0.0);\n",
    565                                    invRadiiLTRBSqdName, invRadiiLTRBSqdName);
    566             break;
    567         }
    568         default:
    569             SkFAIL("RRect should always be simple or nine-patch.");
    570     }
    571     // implicit is the evaluation of (x/a)^2 + (y/b)^2 - 1.
    572     builder->fsCodeAppend("\t\tfloat implicit = dot(Z, dxy) - 1.0;\n");
    573     // grad_dot is the squared length of the gradient of the implicit.
    574     builder->fsCodeAppendf("\t\tfloat grad_dot = 4.0 * dot(Z, Z);\n");
    575     // avoid calling inversesqrt on zero.
    576     builder->fsCodeAppend("\t\tgrad_dot = max(grad_dot, 1.0e-4);\n");
    577     builder->fsCodeAppendf("\t\tfloat approx_dist = implicit * inversesqrt(grad_dot);\n");
    578 
    579     if (kFillAA_GrEffectEdgeType == erre.getEdgeType()) {
    580         builder->fsCodeAppend("\t\tfloat alpha = clamp(0.5 - approx_dist, 0.0, 1.0);\n");
    581     } else {
    582         builder->fsCodeAppend("\t\tfloat alpha = clamp(0.5 + approx_dist, 0.0, 1.0);\n");
    583     }
    584 
    585     builder->fsCodeAppendf("\t\t%s = %s;\n", outputColor,
    586                            (GrGLSLExpr4(inputColor) * GrGLSLExpr1("alpha")).c_str());
    587 }
    588 
    589 GrGLEffect::EffectKey GLEllipticalRRectEffect::GenKey(const GrDrawEffect& drawEffect,
    590                                                       const GrGLCaps&) {
    591     const EllipticalRRectEffect& erre = drawEffect.castEffect<EllipticalRRectEffect>();
    592     GR_STATIC_ASSERT(kLast_GrEffectEdgeType < (1 << 3));
    593     return erre.getRRect().getType() | erre.getEdgeType() << 3;
    594 }
    595 
    596 void GLEllipticalRRectEffect::setData(const GrGLUniformManager& uman,
    597                                       const GrDrawEffect& drawEffect) {
    598     const EllipticalRRectEffect& erre = drawEffect.castEffect<EllipticalRRectEffect>();
    599     const SkRRect& rrect = erre.getRRect();
    600     if (rrect != fPrevRRect) {
    601         SkRect rect = rrect.getBounds();
    602         const SkVector& r0 = rrect.radii(SkRRect::kUpperLeft_Corner);
    603         SkASSERT(r0.fX >= kRadiusMin);
    604         SkASSERT(r0.fY >= kRadiusMin);
    605         switch (erre.getRRect().getType()) {
    606             case SkRRect::kSimple_Type:
    607                 rect.inset(r0.fX, r0.fY);
    608                 uman.set2f(fInvRadiiSqdUniform, 1.f / (r0.fX * r0.fX),
    609                                                 1.f / (r0.fY * r0.fY));
    610                 break;
    611             case SkRRect::kNinePatch_Type: {
    612                 const SkVector& r1 = rrect.radii(SkRRect::kLowerRight_Corner);
    613                 SkASSERT(r1.fX >= kRadiusMin);
    614                 SkASSERT(r1.fY >= kRadiusMin);
    615                 rect.fLeft += r0.fX;
    616                 rect.fTop += r0.fY;
    617                 rect.fRight -= r1.fX;
    618                 rect.fBottom -= r1.fY;
    619                 uman.set4f(fInvRadiiSqdUniform, 1.f / (r0.fX * r0.fX),
    620                                                 1.f / (r0.fY * r0.fY),
    621                                                 1.f / (r1.fX * r1.fX),
    622                                                 1.f / (r1.fY * r1.fY));
    623                 break;
    624             }
    625         default:
    626             SkFAIL("RRect should always be simple or nine-patch.");
    627         }
    628         uman.set4f(fInnerRectUniform, rect.fLeft, rect.fTop, rect.fRight, rect.fBottom);
    629         fPrevRRect = rrect;
    630     }
    631 }
    632 
    633 //////////////////////////////////////////////////////////////////////////////
    634 
    635 GrEffectRef* GrRRectEffect::Create(GrEffectEdgeType edgeType, const SkRRect& rrect) {
    636     if (rrect.isRect()) {
    637         return GrConvexPolyEffect::Create(edgeType, rrect.getBounds());
    638     }
    639 
    640     if (rrect.isOval()) {
    641         return GrOvalEffect::Create(edgeType, rrect.getBounds());
    642     }
    643 
    644     if (rrect.isSimple()) {
    645         if (rrect.getSimpleRadii().fX < kRadiusMin || rrect.getSimpleRadii().fY < kRadiusMin) {
    646             // In this case the corners are extremely close to rectangular and we collapse the
    647             // clip to a rectangular clip.
    648             return GrConvexPolyEffect::Create(edgeType, rrect.getBounds());
    649         }
    650         if (rrect.getSimpleRadii().fX == rrect.getSimpleRadii().fY) {
    651             return CircularRRectEffect::Create(edgeType, CircularRRectEffect::kAll_CornerFlags,
    652                                                rrect);
    653         } else {
    654             return EllipticalRRectEffect::Create(edgeType, rrect);
    655         }
    656     }
    657 
    658     if (rrect.isComplex() || rrect.isNinePatch()) {
    659         // Check for the "tab" cases - two adjacent circular corners and two square corners.
    660         SkScalar circularRadius = 0;
    661         uint32_t cornerFlags  = 0;
    662 
    663         SkVector radii[4];
    664         bool squashedRadii = false;
    665         for (int c = 0; c < 4; ++c) {
    666             radii[c] = rrect.radii((SkRRect::Corner)c);
    667             SkASSERT((0 == radii[c].fX) == (0 == radii[c].fY));
    668             if (0 == radii[c].fX) {
    669                 // The corner is square, so no need to squash or flag as circular.
    670                 continue;
    671             }
    672             if (radii[c].fX < kRadiusMin || radii[c].fY < kRadiusMin) {
    673                 radii[c].set(0, 0);
    674                 squashedRadii = true;
    675                 continue;
    676             }
    677             if (radii[c].fX != radii[c].fY) {
    678                 cornerFlags = ~0U;
    679                 break;
    680             }
    681             if (!cornerFlags) {
    682                 circularRadius = radii[c].fX;
    683                 cornerFlags = 1 << c;
    684             } else {
    685                 if (radii[c].fX != circularRadius) {
    686                    cornerFlags = ~0U;
    687                    break;
    688                 }
    689                 cornerFlags |= 1 << c;
    690             }
    691         }
    692 
    693         switch (cornerFlags) {
    694             case CircularRRectEffect::kAll_CornerFlags:
    695                 // This rrect should have been caught in the simple case above. Though, it would
    696                 // be correctly handled in the fallthrough code.
    697                 SkASSERT(false);
    698             case CircularRRectEffect::kTopLeft_CornerFlag:
    699             case CircularRRectEffect::kTopRight_CornerFlag:
    700             case CircularRRectEffect::kBottomRight_CornerFlag:
    701             case CircularRRectEffect::kBottomLeft_CornerFlag:
    702             case CircularRRectEffect::kLeft_CornerFlags:
    703             case CircularRRectEffect::kTop_CornerFlags:
    704             case CircularRRectEffect::kRight_CornerFlags:
    705             case CircularRRectEffect::kBottom_CornerFlags: {
    706                 SkTCopyOnFirstWrite<SkRRect> rr(rrect);
    707                 if (squashedRadii) {
    708                     rr.writable()->setRectRadii(rrect.getBounds(), radii);
    709                 }
    710                 return CircularRRectEffect::Create(edgeType, cornerFlags, *rr);
    711             }
    712             case CircularRRectEffect::kNone_CornerFlags:
    713                 return GrConvexPolyEffect::Create(edgeType, rrect.getBounds());
    714             default: {
    715                 if (squashedRadii) {
    716                     // If we got here then we squashed some but not all the radii to zero. (If all
    717                     // had been squashed cornerFlags would be 0.) The elliptical effect doesn't
    718                     // support some rounded and some square corners.
    719                     return NULL;
    720                 }
    721                 if (rrect.isNinePatch()) {
    722                     return EllipticalRRectEffect::Create(edgeType, rrect);
    723                 }
    724                 return NULL;
    725             }
    726         }
    727     }
    728 
    729     return NULL;
    730 }
    731