Home | History | Annotate | Download | only in effects
      1 /*
      2  * Copyright 2013 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 "SkDisplacementMapEffect.h"
      9 #include "SkFlattenableBuffers.h"
     10 #include "SkUnPreMultiply.h"
     11 #include "SkColorPriv.h"
     12 #if SK_SUPPORT_GPU
     13 #include "GrContext.h"
     14 #include "gl/GrGLEffect.h"
     15 #include "gl/GrGLEffectMatrix.h"
     16 #include "GrTBackendEffectFactory.h"
     17 #include "SkImageFilterUtils.h"
     18 #endif
     19 
     20 namespace {
     21 
     22 template<SkDisplacementMapEffect::ChannelSelectorType type>
     23 uint32_t getValue(SkColor, const SkUnPreMultiply::Scale*) {
     24     SkASSERT(!"Unknown channel selector");
     25     return 0;
     26 }
     27 
     28 template<> uint32_t getValue<SkDisplacementMapEffect::kR_ChannelSelectorType>(
     29     SkColor l, const SkUnPreMultiply::Scale* table) {
     30     return SkUnPreMultiply::ApplyScale(table[SkGetPackedA32(l)], SkGetPackedR32(l));
     31 }
     32 
     33 template<> uint32_t getValue<SkDisplacementMapEffect::kG_ChannelSelectorType>(
     34     SkColor l, const SkUnPreMultiply::Scale* table) {
     35     return SkUnPreMultiply::ApplyScale(table[SkGetPackedA32(l)], SkGetPackedG32(l));
     36 }
     37 
     38 template<> uint32_t getValue<SkDisplacementMapEffect::kB_ChannelSelectorType>(
     39     SkColor l, const SkUnPreMultiply::Scale* table) {
     40     return SkUnPreMultiply::ApplyScale(table[SkGetPackedA32(l)], SkGetPackedB32(l));
     41 }
     42 
     43 template<> uint32_t getValue<SkDisplacementMapEffect::kA_ChannelSelectorType>(
     44     SkColor l, const SkUnPreMultiply::Scale*) {
     45     return SkGetPackedA32(l);
     46 }
     47 
     48 template<SkDisplacementMapEffect::ChannelSelectorType typeX,
     49          SkDisplacementMapEffect::ChannelSelectorType typeY>
     50 void computeDisplacement(SkScalar scale, SkBitmap* dst, SkBitmap* displ, SkBitmap* src)
     51 {
     52     static const SkScalar Inv8bit = SkScalarDiv(SK_Scalar1, SkFloatToScalar(255.0f));
     53     static const SkScalar Half8bit = SkFloatToScalar(255.0f * 0.5f);
     54     const int dstW = displ->width();
     55     const int dstH = displ->height();
     56     const int srcW = src->width();
     57     const int srcH = src->height();
     58     const SkScalar scaleX = SkScalarMul(SkScalarMul(scale, SkIntToScalar(dstW)), Inv8bit);
     59     const SkScalar scaleY = SkScalarMul(SkScalarMul(scale, SkIntToScalar(dstH)), Inv8bit);
     60     const SkUnPreMultiply::Scale* table = SkUnPreMultiply::GetScaleTable();
     61     for (int y = 0; y < dstH; ++y) {
     62         const SkPMColor* displPtr = displ->getAddr32(0, y);
     63         SkPMColor* dstPtr = dst->getAddr32(0, y);
     64         for (int x = 0; x < dstW; ++x, ++displPtr, ++dstPtr) {
     65             const SkScalar displX =
     66                 SkScalarMul(scaleX, SkIntToScalar(getValue<typeX>(*displPtr, table))-Half8bit);
     67             const SkScalar displY =
     68                 SkScalarMul(scaleY, SkIntToScalar(getValue<typeY>(*displPtr, table))-Half8bit);
     69             const int coordX = x + SkScalarRoundToInt(displX);
     70             const int coordY = y + SkScalarRoundToInt(displY);
     71             *dstPtr = ((coordX < 0) || (coordX >= srcW) || (coordY < 0) || (coordY >= srcH)) ?
     72                       0 : *(src->getAddr32(coordX, coordY));
     73         }
     74     }
     75 }
     76 
     77 template<SkDisplacementMapEffect::ChannelSelectorType typeX>
     78 void computeDisplacement(SkDisplacementMapEffect::ChannelSelectorType yChannelSelector,
     79                          SkScalar scale, SkBitmap* dst, SkBitmap* displ, SkBitmap* src)
     80 {
     81     switch (yChannelSelector) {
     82       case SkDisplacementMapEffect::kR_ChannelSelectorType:
     83         computeDisplacement<typeX, SkDisplacementMapEffect::kR_ChannelSelectorType>(
     84             scale, dst, displ, src);
     85         break;
     86       case SkDisplacementMapEffect::kG_ChannelSelectorType:
     87         computeDisplacement<typeX, SkDisplacementMapEffect::kG_ChannelSelectorType>(
     88             scale, dst, displ, src);
     89         break;
     90       case SkDisplacementMapEffect::kB_ChannelSelectorType:
     91         computeDisplacement<typeX, SkDisplacementMapEffect::kB_ChannelSelectorType>(
     92             scale, dst, displ, src);
     93         break;
     94       case SkDisplacementMapEffect::kA_ChannelSelectorType:
     95         computeDisplacement<typeX, SkDisplacementMapEffect::kA_ChannelSelectorType>(
     96             scale, dst, displ, src);
     97         break;
     98       case SkDisplacementMapEffect::kUnknown_ChannelSelectorType:
     99       default:
    100         SkASSERT(!"Unknown Y channel selector");
    101     }
    102 }
    103 
    104 void computeDisplacement(SkDisplacementMapEffect::ChannelSelectorType xChannelSelector,
    105                          SkDisplacementMapEffect::ChannelSelectorType yChannelSelector,
    106                          SkScalar scale, SkBitmap* dst, SkBitmap* displ, SkBitmap* src)
    107 {
    108     switch (xChannelSelector) {
    109       case SkDisplacementMapEffect::kR_ChannelSelectorType:
    110         computeDisplacement<SkDisplacementMapEffect::kR_ChannelSelectorType>(
    111             yChannelSelector, scale, dst, displ, src);
    112         break;
    113       case SkDisplacementMapEffect::kG_ChannelSelectorType:
    114         computeDisplacement<SkDisplacementMapEffect::kG_ChannelSelectorType>(
    115             yChannelSelector, scale, dst, displ, src);
    116         break;
    117       case SkDisplacementMapEffect::kB_ChannelSelectorType:
    118         computeDisplacement<SkDisplacementMapEffect::kB_ChannelSelectorType>(
    119             yChannelSelector, scale, dst, displ, src);
    120         break;
    121       case SkDisplacementMapEffect::kA_ChannelSelectorType:
    122         computeDisplacement<SkDisplacementMapEffect::kA_ChannelSelectorType>(
    123             yChannelSelector, scale, dst, displ, src);
    124         break;
    125       case SkDisplacementMapEffect::kUnknown_ChannelSelectorType:
    126       default:
    127         SkASSERT(!"Unknown X channel selector");
    128     }
    129 }
    130 
    131 } // end namespace
    132 
    133 ///////////////////////////////////////////////////////////////////////////////
    134 
    135 SkDisplacementMapEffect::SkDisplacementMapEffect(ChannelSelectorType xChannelSelector,
    136                                                  ChannelSelectorType yChannelSelector,
    137                                                  SkScalar scale,
    138                                                  SkImageFilter* displacement,
    139                                                  SkImageFilter* color)
    140   : INHERITED(displacement, color)
    141   , fXChannelSelector(xChannelSelector)
    142   , fYChannelSelector(yChannelSelector)
    143   , fScale(scale)
    144 {
    145 }
    146 
    147 SkDisplacementMapEffect::~SkDisplacementMapEffect() {
    148 }
    149 
    150 SkDisplacementMapEffect::SkDisplacementMapEffect(SkFlattenableReadBuffer& buffer)
    151   : INHERITED(buffer)
    152 {
    153     fXChannelSelector = (SkDisplacementMapEffect::ChannelSelectorType) buffer.readInt();
    154     fYChannelSelector = (SkDisplacementMapEffect::ChannelSelectorType) buffer.readInt();
    155     fScale            = buffer.readScalar();
    156 }
    157 
    158 void SkDisplacementMapEffect::flatten(SkFlattenableWriteBuffer& buffer) const {
    159     this->INHERITED::flatten(buffer);
    160     buffer.writeInt((int) fXChannelSelector);
    161     buffer.writeInt((int) fYChannelSelector);
    162     buffer.writeScalar(fScale);
    163 }
    164 
    165 bool SkDisplacementMapEffect::onFilterImage(Proxy* proxy,
    166                                             const SkBitmap& src,
    167                                             const SkMatrix& ctm,
    168                                             SkBitmap* dst,
    169                                             SkIPoint* offset) {
    170     SkBitmap displ, color = src;
    171     SkImageFilter* colorInput = getColorInput();
    172     SkImageFilter* displacementInput = getDisplacementInput();
    173     SkASSERT(NULL != displacementInput);
    174     if ((colorInput && !colorInput->filterImage(proxy, src, ctm, &color, offset)) ||
    175         !displacementInput->filterImage(proxy, src, ctm, &displ, offset)) {
    176         return false;
    177     }
    178     if ((displ.config() != SkBitmap::kARGB_8888_Config) ||
    179         (color.config() != SkBitmap::kARGB_8888_Config)) {
    180         return false;
    181     }
    182 
    183     SkAutoLockPixels alp_displacement(displ), alp_color(color);
    184     if (!displ.getPixels() || !color.getPixels()) {
    185         return false;
    186     }
    187     dst->setConfig(displ.config(), displ.width(), displ.height());
    188     dst->allocPixels();
    189     if (!dst->getPixels()) {
    190         return false;
    191     }
    192 
    193     computeDisplacement(fXChannelSelector, fYChannelSelector, fScale, dst, &displ, &color);
    194 
    195     return true;
    196 }
    197 
    198 ///////////////////////////////////////////////////////////////////////////////
    199 
    200 #if SK_SUPPORT_GPU
    201 class GrGLDisplacementMapEffect : public GrGLEffect {
    202 public:
    203     GrGLDisplacementMapEffect(const GrBackendEffectFactory& factory,
    204                               const GrEffectRef& effect);
    205     virtual ~GrGLDisplacementMapEffect();
    206 
    207     virtual void emitCode(GrGLShaderBuilder*,
    208                           const GrEffectStage&,
    209                           EffectKey,
    210                           const char* vertexCoords,
    211                           const char* outputColor,
    212                           const char* inputColor,
    213                           const TextureSamplerArray&) SK_OVERRIDE;
    214 
    215     static inline EffectKey GenKey(const GrEffectStage&, const GrGLCaps&);
    216 
    217     virtual void setData(const GrGLUniformManager&, const GrEffectStage&);
    218 
    219 private:
    220     SkDisplacementMapEffect::ChannelSelectorType fXChannelSelector;
    221     SkDisplacementMapEffect::ChannelSelectorType fYChannelSelector;
    222     GrGLEffectMatrix fDisplacementEffectMatrix;
    223     GrGLEffectMatrix fColorEffectMatrix;
    224     GrGLUniformManager::UniformHandle fScaleUni;
    225     GrGLUniformManager::UniformHandle fYSignColor;
    226     GrGLUniformManager::UniformHandle fYSignDispl;
    227 
    228     typedef GrGLEffect INHERITED;
    229 };
    230 
    231 ///////////////////////////////////////////////////////////////////////////////
    232 
    233 class GrDisplacementMapEffect : public GrEffect {
    234 public:
    235     static GrEffectRef* Create(SkDisplacementMapEffect::ChannelSelectorType xChannelSelector,
    236                                SkDisplacementMapEffect::ChannelSelectorType yChannelSelector,
    237                                SkScalar scale, GrTexture* displacement, GrTexture* color) {
    238         AutoEffectUnref effect(SkNEW_ARGS(GrDisplacementMapEffect, (xChannelSelector,
    239                                                                     yChannelSelector,
    240                                                                     scale,
    241                                                                     displacement,
    242                                                                     color)));
    243         return CreateEffectRef(effect);
    244     }
    245 
    246     virtual ~GrDisplacementMapEffect();
    247 
    248     const GrBackendEffectFactory& getFactory() const;
    249     SkDisplacementMapEffect::ChannelSelectorType xChannelSelector() const
    250         { return fXChannelSelector; }
    251     SkDisplacementMapEffect::ChannelSelectorType yChannelSelector() const
    252         { return fYChannelSelector; }
    253     SkScalar scale() const { return fScale; }
    254 
    255     typedef GrGLDisplacementMapEffect GLEffect;
    256     static const char* Name() { return "DisplacementMap"; }
    257 
    258     void getConstantColorComponents(GrColor* color, uint32_t* validFlags) const SK_OVERRIDE;
    259 
    260 private:
    261     virtual bool onIsEqual(const GrEffect&) const SK_OVERRIDE;
    262 
    263     GrDisplacementMapEffect(SkDisplacementMapEffect::ChannelSelectorType xChannelSelector,
    264                             SkDisplacementMapEffect::ChannelSelectorType yChannelSelector,
    265                             SkScalar scale, GrTexture* displacement, GrTexture* color);
    266 
    267     GR_DECLARE_EFFECT_TEST;
    268 
    269     GrTextureAccess             fDisplacementAccess;
    270     GrTextureAccess             fColorAccess;
    271     SkDisplacementMapEffect::ChannelSelectorType fXChannelSelector;
    272     SkDisplacementMapEffect::ChannelSelectorType fYChannelSelector;
    273     SkScalar fScale;
    274 
    275     typedef GrEffect INHERITED;
    276 };
    277 
    278 bool SkDisplacementMapEffect::filterImageGPU(Proxy* proxy, const SkBitmap& src, SkBitmap* result) {
    279     SkBitmap colorBM;
    280     if (!SkImageFilterUtils::GetInputResultGPU(getColorInput(), proxy, src, &colorBM)) {
    281         return false;
    282     }
    283     GrTexture* color = (GrTexture*) colorBM.getTexture();
    284     SkBitmap displacementBM;
    285     if (!SkImageFilterUtils::GetInputResultGPU(getDisplacementInput(), proxy, src, &displacementBM)) {
    286         return false;
    287     }
    288     GrTexture* displacement = (GrTexture*) displacementBM.getTexture();
    289     GrContext* context = color->getContext();
    290 
    291     GrTextureDesc desc;
    292     desc.fFlags = kRenderTarget_GrTextureFlagBit | kNoStencil_GrTextureFlagBit;
    293     desc.fWidth = src.width();
    294     desc.fHeight = src.height();
    295     desc.fConfig = kSkia8888_GrPixelConfig;
    296 
    297     GrAutoScratchTexture ast(context, desc);
    298     SkAutoTUnref<GrTexture> dst(ast.detach());
    299 
    300     GrContext::AutoRenderTarget art(context, dst->asRenderTarget());
    301 
    302     GrPaint paint;
    303     paint.colorStage(0)->setEffect(
    304         GrDisplacementMapEffect::Create(fXChannelSelector,
    305                                         fYChannelSelector,
    306                                         fScale,
    307                                         displacement,
    308                                         color))->unref();
    309     SkRect srcRect;
    310     src.getBounds(&srcRect);
    311     context->drawRect(paint, srcRect);
    312     return SkImageFilterUtils::WrapTexture(dst, src.width(), src.height(), result);
    313 }
    314 
    315 ///////////////////////////////////////////////////////////////////////////////
    316 
    317 GrDisplacementMapEffect::GrDisplacementMapEffect(
    318                              SkDisplacementMapEffect::ChannelSelectorType xChannelSelector,
    319                              SkDisplacementMapEffect::ChannelSelectorType yChannelSelector,
    320                              SkScalar scale,
    321                              GrTexture* displacement,
    322                              GrTexture* color)
    323     : fDisplacementAccess(displacement)
    324     , fColorAccess(color)
    325     , fXChannelSelector(xChannelSelector)
    326     , fYChannelSelector(yChannelSelector)
    327     , fScale(scale) {
    328     this->addTextureAccess(&fDisplacementAccess);
    329     this->addTextureAccess(&fColorAccess);
    330 }
    331 
    332 GrDisplacementMapEffect::~GrDisplacementMapEffect() {
    333 }
    334 
    335 bool GrDisplacementMapEffect::onIsEqual(const GrEffect& sBase) const {
    336     const GrDisplacementMapEffect& s = CastEffect<GrDisplacementMapEffect>(sBase);
    337     return fDisplacementAccess.getTexture() == s.fDisplacementAccess.getTexture() &&
    338            fColorAccess.getTexture() == s.fColorAccess.getTexture() &&
    339            fXChannelSelector == s.fXChannelSelector &&
    340            fYChannelSelector == s.fYChannelSelector &&
    341            fScale == s.fScale;
    342 }
    343 
    344 const GrBackendEffectFactory& GrDisplacementMapEffect::getFactory() const {
    345     return GrTBackendEffectFactory<GrDisplacementMapEffect>::getInstance();
    346 }
    347 
    348 void GrDisplacementMapEffect::getConstantColorComponents(GrColor* color,
    349                                                          uint32_t* validFlags) const {
    350     // Any displacement offset bringing a pixel out of bounds will output a color of (0,0,0,0),
    351     // so the only way we'd get a constant alpha is if the input color image has a constant alpha
    352     // and no displacement offset push any texture coordinates out of bounds OR if the constant
    353     // alpha is 0. Since this isn't trivial to compute at this point, let's assume the output is
    354     // not of constant color when a displacement effect is applied.
    355     *validFlags = 0;
    356 }
    357 
    358 ///////////////////////////////////////////////////////////////////////////////
    359 
    360 GR_DEFINE_EFFECT_TEST(GrDisplacementMapEffect);
    361 
    362 GrEffectRef* GrDisplacementMapEffect::TestCreate(SkRandom* random,
    363                                                  GrContext* context,
    364                                                  GrTexture* textures[]) {
    365     int texIdxDispl = random->nextBool() ? GrEffectUnitTest::kSkiaPMTextureIdx :
    366                                            GrEffectUnitTest::kAlphaTextureIdx;
    367     int texIdxColor = random->nextBool() ? GrEffectUnitTest::kSkiaPMTextureIdx :
    368                                            GrEffectUnitTest::kAlphaTextureIdx;
    369     static const int kMaxComponent = 4;
    370     SkDisplacementMapEffect::ChannelSelectorType xChannelSelector =
    371         static_cast<SkDisplacementMapEffect::ChannelSelectorType>(
    372         random->nextRangeU(1, kMaxComponent));
    373     SkDisplacementMapEffect::ChannelSelectorType yChannelSelector =
    374         static_cast<SkDisplacementMapEffect::ChannelSelectorType>(
    375         random->nextRangeU(1, kMaxComponent));
    376     SkScalar scale = random->nextUScalar1();
    377 
    378     return GrDisplacementMapEffect::Create(xChannelSelector, yChannelSelector, scale,
    379                                            textures[texIdxDispl], textures[texIdxColor]);
    380 }
    381 
    382 ///////////////////////////////////////////////////////////////////////////////
    383 
    384 GrGLDisplacementMapEffect::GrGLDisplacementMapEffect(const GrBackendEffectFactory& factory,
    385                                                      const GrEffectRef& effect)
    386     : INHERITED(factory)
    387     , fXChannelSelector(CastEffect<GrDisplacementMapEffect>(effect).xChannelSelector())
    388     , fYChannelSelector(CastEffect<GrDisplacementMapEffect>(effect).yChannelSelector()) {
    389 }
    390 
    391 GrGLDisplacementMapEffect::~GrGLDisplacementMapEffect() {
    392 }
    393 
    394 void GrGLDisplacementMapEffect::emitCode(GrGLShaderBuilder* builder,
    395                                const GrEffectStage&,
    396                                EffectKey key,
    397                                const char* vertexCoords,
    398                                const char* outputColor,
    399                                const char* inputColor,
    400                                const TextureSamplerArray& samplers) {
    401     fScaleUni = builder->addUniform(GrGLShaderBuilder::kFragment_ShaderType,
    402                                     kVec2f_GrSLType, "Scale");
    403     const char* scaleUni = builder->getUniformCStr(fScaleUni);
    404 
    405     const char* dCoordsIn;
    406     GrSLType dCoordsType = fDisplacementEffectMatrix.emitCode(
    407                                 builder, key, vertexCoords, &dCoordsIn, NULL, "DISPL");
    408     const char* cCoordsIn;
    409     GrSLType cCoordsType = fColorEffectMatrix.emitCode(
    410                                 builder, key, vertexCoords, &cCoordsIn, NULL, "COLOR");
    411 
    412     SkString* code = &builder->fFSCode;
    413     const char* dColor = "dColor";
    414     const char* cCoords = "cCoords";
    415     const char* nearZero = "1e-6"; // Since 6.10352e5 is the smallest half float, use
    416                                    // a number smaller than that to approximate 0, but
    417                                    // leave room for 32-bit float GPU rounding errors.
    418 
    419     code->appendf("\t\tvec4 %s = ", dColor);
    420     builder->appendTextureLookup(code, samplers[0], dCoordsIn, dCoordsType);
    421     code->append(";\n");
    422 
    423     // Unpremultiply the displacement
    424     code->appendf("\t\t%s.rgb = (%s.a < %s) ? vec3(0.0) : clamp(%s.rgb / %s.a, 0.0, 1.0);",
    425                   dColor, dColor, nearZero, dColor, dColor);
    426 
    427     code->appendf("\t\tvec2 %s = %s + %s*(%s.",
    428                   cCoords, cCoordsIn, scaleUni, dColor);
    429 
    430     switch (fXChannelSelector) {
    431       case SkDisplacementMapEffect::kR_ChannelSelectorType:
    432         code->append("r");
    433         break;
    434       case SkDisplacementMapEffect::kG_ChannelSelectorType:
    435         code->append("g");
    436         break;
    437       case SkDisplacementMapEffect::kB_ChannelSelectorType:
    438         code->append("b");
    439         break;
    440       case SkDisplacementMapEffect::kA_ChannelSelectorType:
    441         code->append("a");
    442         break;
    443       case SkDisplacementMapEffect::kUnknown_ChannelSelectorType:
    444       default:
    445         SkASSERT(!"Unknown X channel selector");
    446     }
    447 
    448     switch (fYChannelSelector) {
    449       case SkDisplacementMapEffect::kR_ChannelSelectorType:
    450         code->append("r");
    451         break;
    452       case SkDisplacementMapEffect::kG_ChannelSelectorType:
    453         code->append("g");
    454         break;
    455       case SkDisplacementMapEffect::kB_ChannelSelectorType:
    456         code->append("b");
    457         break;
    458       case SkDisplacementMapEffect::kA_ChannelSelectorType:
    459         code->append("a");
    460         break;
    461       case SkDisplacementMapEffect::kUnknown_ChannelSelectorType:
    462       default:
    463         SkASSERT(!"Unknown Y channel selector");
    464     }
    465     code->append("-vec2(0.5));\t\t");
    466 
    467     // FIXME : This can be achieved with a "clamp to border" texture repeat mode and
    468     //         a 0 border color instead of computing if cCoords is out of bounds here.
    469     code->appendf(
    470         "%s = any(greaterThan(vec4(vec2(0.0), %s), vec4(%s, vec2(1.0)))) ? vec4(0.0) : ",
    471         outputColor, cCoords, cCoords);
    472     builder->appendTextureLookup(code, samplers[1], cCoords, cCoordsType);
    473     code->append(";\n");
    474 }
    475 
    476 void GrGLDisplacementMapEffect::setData(const GrGLUniformManager& uman, const GrEffectStage& stage) {
    477     const GrDisplacementMapEffect& displacementMap = GetEffectFromStage<GrDisplacementMapEffect>(stage);
    478     GrTexture* displTex = displacementMap.texture(0);
    479     GrTexture* colorTex = displacementMap.texture(1);
    480     fDisplacementEffectMatrix.setData(uman,
    481                                      GrEffect::MakeDivByTextureWHMatrix(displTex),
    482                                      stage.getCoordChangeMatrix(),
    483                                      displTex);
    484     fColorEffectMatrix.setData(uman,
    485                                GrEffect::MakeDivByTextureWHMatrix(colorTex),
    486                                stage.getCoordChangeMatrix(),
    487                                colorTex);
    488 
    489     uman.set2f(fScaleUni, SkScalarToFloat(displacementMap.scale()),
    490                 colorTex->origin() == kTopLeft_GrSurfaceOrigin ?
    491                 SkScalarToFloat(displacementMap.scale()) :
    492                 SkScalarToFloat(-displacementMap.scale()));
    493 }
    494 
    495 GrGLEffect::EffectKey GrGLDisplacementMapEffect::GenKey(const GrEffectStage& stage,
    496                                                         const GrGLCaps&) {
    497     const GrDisplacementMapEffect& displacementMap =
    498         GetEffectFromStage<GrDisplacementMapEffect>(stage);
    499 
    500     GrTexture* displTex = displacementMap.texture(0);
    501     GrTexture* colorTex = displacementMap.texture(1);
    502 
    503     EffectKey displKey = GrGLEffectMatrix::GenKey(GrEffect::MakeDivByTextureWHMatrix(displTex),
    504                                                   stage.getCoordChangeMatrix(),
    505                                                   displTex);
    506 
    507     EffectKey colorKey = GrGLEffectMatrix::GenKey(GrEffect::MakeDivByTextureWHMatrix(colorTex),
    508                                                   stage.getCoordChangeMatrix(),
    509                                                   colorTex);
    510 
    511     colorKey <<= GrGLEffectMatrix::kKeyBits;
    512     EffectKey xKey = displacementMap.xChannelSelector() << (2 * GrGLEffectMatrix::kKeyBits);
    513     EffectKey yKey = displacementMap.yChannelSelector() << (2 * GrGLEffectMatrix::kKeyBits +
    514                                                             SkDisplacementMapEffect::kKeyBits);
    515 
    516     return xKey | yKey | displKey | colorKey;
    517 }
    518 #endif
    519