Home | History | Annotate | Download | only in effects
      1 /*
      2  * Copyright 2012 The Android Open Source Project
      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 "SkMatrixConvolutionImageFilter.h"
      9 #include "SkBitmap.h"
     10 #include "SkColorPriv.h"
     11 #include "SkFlattenableBuffers.h"
     12 #include "SkRect.h"
     13 #include "SkUnPreMultiply.h"
     14 
     15 #if SK_SUPPORT_GPU
     16 #include "gl/GrGLEffect.h"
     17 #include "effects/GrSingleTextureEffect.h"
     18 #include "GrTBackendEffectFactory.h"
     19 #include "GrTexture.h"
     20 #include "SkMatrix.h"
     21 #endif
     22 
     23 static bool tile_mode_is_valid(SkMatrixConvolutionImageFilter::TileMode tileMode) {
     24     switch (tileMode) {
     25     case SkMatrixConvolutionImageFilter::kClamp_TileMode:
     26     case SkMatrixConvolutionImageFilter::kRepeat_TileMode:
     27     case SkMatrixConvolutionImageFilter::kClampToBlack_TileMode:
     28         return true;
     29     default:
     30         break;
     31     }
     32     return false;
     33 }
     34 
     35 SkMatrixConvolutionImageFilter::SkMatrixConvolutionImageFilter(
     36     const SkISize& kernelSize,
     37     const SkScalar* kernel,
     38     SkScalar gain,
     39     SkScalar bias,
     40     const SkIPoint& target,
     41     TileMode tileMode,
     42     bool convolveAlpha,
     43     SkImageFilter* input,
     44     const CropRect* cropRect)
     45   : INHERITED(input, cropRect),
     46     fKernelSize(kernelSize),
     47     fGain(gain),
     48     fBias(bias),
     49     fTarget(target),
     50     fTileMode(tileMode),
     51     fConvolveAlpha(convolveAlpha) {
     52     uint32_t size = fKernelSize.fWidth * fKernelSize.fHeight;
     53     fKernel = SkNEW_ARRAY(SkScalar, size);
     54     memcpy(fKernel, kernel, size * sizeof(SkScalar));
     55     SkASSERT(kernelSize.fWidth >= 1 && kernelSize.fHeight >= 1);
     56     SkASSERT(target.fX >= 0 && target.fX < kernelSize.fWidth);
     57     SkASSERT(target.fY >= 0 && target.fY < kernelSize.fHeight);
     58 }
     59 
     60 SkMatrixConvolutionImageFilter::SkMatrixConvolutionImageFilter(SkFlattenableReadBuffer& buffer)
     61     : INHERITED(1, buffer) {
     62     // We need to be able to read at most SK_MaxS32 bytes, so divide that
     63     // by the size of a scalar to know how many scalars we can read.
     64     static const int32_t kMaxSize = SK_MaxS32 / sizeof(SkScalar);
     65     fKernelSize.fWidth = buffer.readInt();
     66     fKernelSize.fHeight = buffer.readInt();
     67     if ((fKernelSize.fWidth >= 1) && (fKernelSize.fHeight >= 1) &&
     68         // Make sure size won't be larger than a signed int,
     69         // which would still be extremely large for a kernel,
     70         // but we don't impose a hard limit for kernel size
     71         (kMaxSize / fKernelSize.fWidth >= fKernelSize.fHeight)) {
     72         size_t size = fKernelSize.fWidth * fKernelSize.fHeight;
     73         fKernel = SkNEW_ARRAY(SkScalar, size);
     74         SkDEBUGCODE(bool success =) buffer.readScalarArray(fKernel, size);
     75         SkASSERT(success);
     76     } else {
     77         fKernel = 0;
     78     }
     79     fGain = buffer.readScalar();
     80     fBias = buffer.readScalar();
     81     fTarget.fX = buffer.readInt();
     82     fTarget.fY = buffer.readInt();
     83     fTileMode = (TileMode) buffer.readInt();
     84     fConvolveAlpha = buffer.readBool();
     85     buffer.validate((fKernel != 0) &&
     86                     SkScalarIsFinite(fGain) &&
     87                     SkScalarIsFinite(fBias) &&
     88                     tile_mode_is_valid(fTileMode));
     89 }
     90 
     91 void SkMatrixConvolutionImageFilter::flatten(SkFlattenableWriteBuffer& buffer) const {
     92     this->INHERITED::flatten(buffer);
     93     buffer.writeInt(fKernelSize.fWidth);
     94     buffer.writeInt(fKernelSize.fHeight);
     95     buffer.writeScalarArray(fKernel, fKernelSize.fWidth * fKernelSize.fHeight);
     96     buffer.writeScalar(fGain);
     97     buffer.writeScalar(fBias);
     98     buffer.writeInt(fTarget.fX);
     99     buffer.writeInt(fTarget.fY);
    100     buffer.writeInt((int) fTileMode);
    101     buffer.writeBool(fConvolveAlpha);
    102 }
    103 
    104 SkMatrixConvolutionImageFilter::~SkMatrixConvolutionImageFilter() {
    105     delete[] fKernel;
    106 }
    107 
    108 class UncheckedPixelFetcher {
    109 public:
    110     static inline SkPMColor fetch(const SkBitmap& src, int x, int y, const SkIRect& bounds) {
    111         return *src.getAddr32(x, y);
    112     }
    113 };
    114 
    115 class ClampPixelFetcher {
    116 public:
    117     static inline SkPMColor fetch(const SkBitmap& src, int x, int y, const SkIRect& bounds) {
    118         x = SkPin32(x, bounds.fLeft, bounds.fRight - 1);
    119         y = SkPin32(y, bounds.fTop, bounds.fBottom - 1);
    120         return *src.getAddr32(x, y);
    121     }
    122 };
    123 
    124 class RepeatPixelFetcher {
    125 public:
    126     static inline SkPMColor fetch(const SkBitmap& src, int x, int y, const SkIRect& bounds) {
    127         x = (x - bounds.left()) % bounds.width() + bounds.left();
    128         y = (y - bounds.top()) % bounds.height() + bounds.top();
    129         if (x < bounds.left()) {
    130             x += bounds.width();
    131         }
    132         if (y < bounds.top()) {
    133             y += bounds.height();
    134         }
    135         return *src.getAddr32(x, y);
    136     }
    137 };
    138 
    139 class ClampToBlackPixelFetcher {
    140 public:
    141     static inline SkPMColor fetch(const SkBitmap& src, int x, int y, const SkIRect& bounds) {
    142         if (x < bounds.fLeft || x >= bounds.fRight || y < bounds.fTop || y >= bounds.fBottom) {
    143             return 0;
    144         } else {
    145             return *src.getAddr32(x, y);
    146         }
    147     }
    148 };
    149 
    150 template<class PixelFetcher, bool convolveAlpha>
    151 void SkMatrixConvolutionImageFilter::filterPixels(const SkBitmap& src,
    152                                                   SkBitmap* result,
    153                                                   const SkIRect& rect,
    154                                                   const SkIRect& bounds) {
    155     for (int y = rect.fTop; y < rect.fBottom; ++y) {
    156         SkPMColor* dptr = result->getAddr32(rect.fLeft - bounds.fLeft, y - bounds.fTop);
    157         for (int x = rect.fLeft; x < rect.fRight; ++x) {
    158             SkScalar sumA = 0, sumR = 0, sumG = 0, sumB = 0;
    159             for (int cy = 0; cy < fKernelSize.fHeight; cy++) {
    160                 for (int cx = 0; cx < fKernelSize.fWidth; cx++) {
    161                     SkPMColor s = PixelFetcher::fetch(src,
    162                                                       x + cx - fTarget.fX,
    163                                                       y + cy - fTarget.fY,
    164                                                       bounds);
    165                     SkScalar k = fKernel[cy * fKernelSize.fWidth + cx];
    166                     if (convolveAlpha) {
    167                         sumA += SkScalarMul(SkIntToScalar(SkGetPackedA32(s)), k);
    168                     }
    169                     sumR += SkScalarMul(SkIntToScalar(SkGetPackedR32(s)), k);
    170                     sumG += SkScalarMul(SkIntToScalar(SkGetPackedG32(s)), k);
    171                     sumB += SkScalarMul(SkIntToScalar(SkGetPackedB32(s)), k);
    172                 }
    173             }
    174             int a = convolveAlpha
    175                   ? SkClampMax(SkScalarFloorToInt(SkScalarMul(sumA, fGain) + fBias), 255)
    176                   : 255;
    177             int r = SkClampMax(SkScalarFloorToInt(SkScalarMul(sumR, fGain) + fBias), a);
    178             int g = SkClampMax(SkScalarFloorToInt(SkScalarMul(sumG, fGain) + fBias), a);
    179             int b = SkClampMax(SkScalarFloorToInt(SkScalarMul(sumB, fGain) + fBias), a);
    180             if (!convolveAlpha) {
    181                 a = SkGetPackedA32(PixelFetcher::fetch(src, x, y, bounds));
    182                 *dptr++ = SkPreMultiplyARGB(a, r, g, b);
    183             } else {
    184                 *dptr++ = SkPackARGB32(a, r, g, b);
    185             }
    186         }
    187     }
    188 }
    189 
    190 template<class PixelFetcher>
    191 void SkMatrixConvolutionImageFilter::filterPixels(const SkBitmap& src,
    192                                                   SkBitmap* result,
    193                                                   const SkIRect& rect,
    194                                                   const SkIRect& bounds) {
    195     if (fConvolveAlpha) {
    196         filterPixels<PixelFetcher, true>(src, result, rect, bounds);
    197     } else {
    198         filterPixels<PixelFetcher, false>(src, result, rect, bounds);
    199     }
    200 }
    201 
    202 void SkMatrixConvolutionImageFilter::filterInteriorPixels(const SkBitmap& src,
    203                                                           SkBitmap* result,
    204                                                           const SkIRect& rect,
    205                                                           const SkIRect& bounds) {
    206     filterPixels<UncheckedPixelFetcher>(src, result, rect, bounds);
    207 }
    208 
    209 void SkMatrixConvolutionImageFilter::filterBorderPixels(const SkBitmap& src,
    210                                                         SkBitmap* result,
    211                                                         const SkIRect& rect,
    212                                                         const SkIRect& bounds) {
    213     switch (fTileMode) {
    214         case kClamp_TileMode:
    215             filterPixels<ClampPixelFetcher>(src, result, rect, bounds);
    216             break;
    217         case kRepeat_TileMode:
    218             filterPixels<RepeatPixelFetcher>(src, result, rect, bounds);
    219             break;
    220         case kClampToBlack_TileMode:
    221             filterPixels<ClampToBlackPixelFetcher>(src, result, rect, bounds);
    222             break;
    223     }
    224 }
    225 
    226 // FIXME:  This should be refactored to SkImageFilterUtils for
    227 // use by other filters.  For now, we assume the input is always
    228 // premultiplied and unpremultiply it
    229 static SkBitmap unpremultiplyBitmap(const SkBitmap& src)
    230 {
    231     SkAutoLockPixels alp(src);
    232     if (!src.getPixels()) {
    233         return SkBitmap();
    234     }
    235     SkBitmap result;
    236     result.setConfig(src.config(), src.width(), src.height());
    237     result.allocPixels();
    238     if (!result.getPixels()) {
    239         return SkBitmap();
    240     }
    241     for (int y = 0; y < src.height(); ++y) {
    242         const uint32_t* srcRow = src.getAddr32(0, y);
    243         uint32_t* dstRow = result.getAddr32(0, y);
    244         for (int x = 0; x < src.width(); ++x) {
    245             dstRow[x] = SkUnPreMultiply::PMColorToColor(srcRow[x]);
    246         }
    247     }
    248     return result;
    249 }
    250 
    251 bool SkMatrixConvolutionImageFilter::onFilterImage(Proxy* proxy,
    252                                                    const SkBitmap& source,
    253                                                    const SkMatrix& matrix,
    254                                                    SkBitmap* result,
    255                                                    SkIPoint* loc) {
    256     SkBitmap src = source;
    257     if (getInput(0) && !getInput(0)->filterImage(proxy, source, matrix, &src, loc)) {
    258         return false;
    259     }
    260 
    261     if (src.config() != SkBitmap::kARGB_8888_Config) {
    262         return false;
    263     }
    264 
    265     SkIRect bounds;
    266     src.getBounds(&bounds);
    267     if (!this->applyCropRect(&bounds, matrix)) {
    268         return false;
    269     }
    270 
    271     if (!fConvolveAlpha && !src.isOpaque()) {
    272         src = unpremultiplyBitmap(src);
    273     }
    274 
    275     SkAutoLockPixels alp(src);
    276     if (!src.getPixels()) {
    277         return false;
    278     }
    279 
    280     result->setConfig(src.config(), bounds.width(), bounds.height());
    281     result->allocPixels();
    282     if (!result->getPixels()) {
    283         return false;
    284     }
    285 
    286     SkIRect interior = SkIRect::MakeXYWH(bounds.left() + fTarget.fX,
    287                                          bounds.top() + fTarget.fY,
    288                                          bounds.width() - fKernelSize.fWidth + 1,
    289                                          bounds.height() - fKernelSize.fHeight + 1);
    290     SkIRect top = SkIRect::MakeLTRB(bounds.left(), bounds.top(), bounds.right(), interior.top());
    291     SkIRect bottom = SkIRect::MakeLTRB(bounds.left(), interior.bottom(),
    292                                        bounds.right(), bounds.bottom());
    293     SkIRect left = SkIRect::MakeLTRB(bounds.left(), interior.top(),
    294                                      interior.left(), interior.bottom());
    295     SkIRect right = SkIRect::MakeLTRB(interior.right(), interior.top(),
    296                                       bounds.right(), interior.bottom());
    297     filterBorderPixels(src, result, top, bounds);
    298     filterBorderPixels(src, result, left, bounds);
    299     filterInteriorPixels(src, result, interior, bounds);
    300     filterBorderPixels(src, result, right, bounds);
    301     filterBorderPixels(src, result, bottom, bounds);
    302     loc->fX += bounds.fLeft;
    303     loc->fY += bounds.fTop;
    304     return true;
    305 }
    306 
    307 #if SK_SUPPORT_GPU
    308 
    309 ///////////////////////////////////////////////////////////////////////////////
    310 
    311 class GrGLMatrixConvolutionEffect;
    312 
    313 class GrMatrixConvolutionEffect : public GrSingleTextureEffect {
    314 public:
    315     typedef SkMatrixConvolutionImageFilter::TileMode TileMode;
    316     static GrEffectRef* Create(GrTexture* texture,
    317                                const SkIRect& bounds,
    318                                const SkISize& kernelSize,
    319                                const SkScalar* kernel,
    320                                SkScalar gain,
    321                                SkScalar bias,
    322                                const SkIPoint& target,
    323                                TileMode tileMode,
    324                                bool convolveAlpha) {
    325         AutoEffectUnref effect(SkNEW_ARGS(GrMatrixConvolutionEffect, (texture,
    326                                                                       bounds,
    327                                                                       kernelSize,
    328                                                                       kernel,
    329                                                                       gain,
    330                                                                       bias,
    331                                                                       target,
    332                                                                       tileMode,
    333                                                                       convolveAlpha)));
    334         return CreateEffectRef(effect);
    335     }
    336     virtual ~GrMatrixConvolutionEffect();
    337 
    338     virtual void getConstantColorComponents(GrColor* color,
    339                                             uint32_t* validFlags) const SK_OVERRIDE {
    340         // TODO: Try to do better?
    341         *validFlags = 0;
    342     }
    343 
    344     static const char* Name() { return "MatrixConvolution"; }
    345     const SkIRect& bounds() const { return fBounds; }
    346     const SkISize& kernelSize() const { return fKernelSize; }
    347     const float* target() const { return fTarget; }
    348     const float* kernel() const { return fKernel; }
    349     float gain() const { return fGain; }
    350     float bias() const { return fBias; }
    351     TileMode tileMode() const { return fTileMode; }
    352     bool convolveAlpha() const { return fConvolveAlpha; }
    353 
    354     typedef GrGLMatrixConvolutionEffect GLEffect;
    355 
    356     virtual const GrBackendEffectFactory& getFactory() const SK_OVERRIDE;
    357 
    358 private:
    359     GrMatrixConvolutionEffect(GrTexture*,
    360                               const SkIRect& bounds,
    361                               const SkISize& kernelSize,
    362                               const SkScalar* kernel,
    363                               SkScalar gain,
    364                               SkScalar bias,
    365                               const SkIPoint& target,
    366                               TileMode tileMode,
    367                               bool convolveAlpha);
    368 
    369     virtual bool onIsEqual(const GrEffect&) const SK_OVERRIDE;
    370 
    371     SkIRect  fBounds;
    372     SkISize  fKernelSize;
    373     float   *fKernel;
    374     float    fGain;
    375     float    fBias;
    376     float    fTarget[2];
    377     TileMode fTileMode;
    378     bool     fConvolveAlpha;
    379 
    380     GR_DECLARE_EFFECT_TEST;
    381 
    382     typedef GrSingleTextureEffect INHERITED;
    383 };
    384 
    385 class GrGLMatrixConvolutionEffect : public GrGLEffect {
    386 public:
    387     GrGLMatrixConvolutionEffect(const GrBackendEffectFactory& factory,
    388                                 const GrDrawEffect& effect);
    389     virtual void emitCode(GrGLShaderBuilder*,
    390                           const GrDrawEffect&,
    391                           EffectKey,
    392                           const char* outputColor,
    393                           const char* inputColor,
    394                           const TransformedCoordsArray&,
    395                           const TextureSamplerArray&) SK_OVERRIDE;
    396 
    397     static inline EffectKey GenKey(const GrDrawEffect&, const GrGLCaps&);
    398 
    399     virtual void setData(const GrGLUniformManager&, const GrDrawEffect&) SK_OVERRIDE;
    400 
    401 private:
    402     typedef GrGLUniformManager::UniformHandle        UniformHandle;
    403     typedef SkMatrixConvolutionImageFilter::TileMode TileMode;
    404     SkISize             fKernelSize;
    405     TileMode            fTileMode;
    406     bool                fConvolveAlpha;
    407 
    408     UniformHandle       fBoundsUni;
    409     UniformHandle       fKernelUni;
    410     UniformHandle       fImageIncrementUni;
    411     UniformHandle       fTargetUni;
    412     UniformHandle       fGainUni;
    413     UniformHandle       fBiasUni;
    414 
    415     typedef GrGLEffect INHERITED;
    416 };
    417 
    418 GrGLMatrixConvolutionEffect::GrGLMatrixConvolutionEffect(const GrBackendEffectFactory& factory,
    419                                                          const GrDrawEffect& drawEffect)
    420     : INHERITED(factory) {
    421     const GrMatrixConvolutionEffect& m = drawEffect.castEffect<GrMatrixConvolutionEffect>();
    422     fKernelSize = m.kernelSize();
    423     fTileMode = m.tileMode();
    424     fConvolveAlpha = m.convolveAlpha();
    425 }
    426 
    427 static void appendTextureLookup(GrGLShaderBuilder* builder,
    428                                 const GrGLShaderBuilder::TextureSampler& sampler,
    429                                 const char* coord,
    430                                 const char* bounds,
    431                                 SkMatrixConvolutionImageFilter::TileMode tileMode) {
    432     SkString clampedCoord;
    433     switch (tileMode) {
    434         case SkMatrixConvolutionImageFilter::kClamp_TileMode:
    435             clampedCoord.printf("clamp(%s, %s.xy, %s.zw)", coord, bounds, bounds);
    436             coord = clampedCoord.c_str();
    437             break;
    438         case SkMatrixConvolutionImageFilter::kRepeat_TileMode:
    439             clampedCoord.printf("mod(%s - %s.xy, %s.zw - %s.xy) + %s.xy", coord, bounds, bounds, bounds, bounds);
    440             coord = clampedCoord.c_str();
    441             break;
    442         case SkMatrixConvolutionImageFilter::kClampToBlack_TileMode:
    443             builder->fsCodeAppendf("clamp(%s, %s.xy, %s.zw) != %s ? vec4(0, 0, 0, 0) : ", coord, bounds, bounds, coord);
    444             break;
    445     }
    446     builder->fsAppendTextureLookup(sampler, coord);
    447 }
    448 
    449 void GrGLMatrixConvolutionEffect::emitCode(GrGLShaderBuilder* builder,
    450                                            const GrDrawEffect&,
    451                                            EffectKey key,
    452                                            const char* outputColor,
    453                                            const char* inputColor,
    454                                            const TransformedCoordsArray& coords,
    455                                            const TextureSamplerArray& samplers) {
    456     sk_ignore_unused_variable(inputColor);
    457     SkString coords2D = builder->ensureFSCoords2D(coords, 0);
    458     fBoundsUni = builder->addUniform(GrGLShaderBuilder::kFragment_Visibility,
    459                                      kVec4f_GrSLType, "Bounds");
    460     fImageIncrementUni = builder->addUniform(GrGLShaderBuilder::kFragment_Visibility,
    461                                              kVec2f_GrSLType, "ImageIncrement");
    462     fKernelUni = builder->addUniformArray(GrGLShaderBuilder::kFragment_Visibility,
    463                                              kFloat_GrSLType,
    464                                              "Kernel",
    465                                              fKernelSize.width() * fKernelSize.height());
    466     fTargetUni = builder->addUniform(GrGLShaderBuilder::kFragment_Visibility,
    467                                              kVec2f_GrSLType, "Target");
    468     fGainUni = builder->addUniform(GrGLShaderBuilder::kFragment_Visibility,
    469                                    kFloat_GrSLType, "Gain");
    470     fBiasUni = builder->addUniform(GrGLShaderBuilder::kFragment_Visibility,
    471                                    kFloat_GrSLType, "Bias");
    472 
    473     const char* bounds = builder->getUniformCStr(fBoundsUni);
    474     const char* target = builder->getUniformCStr(fTargetUni);
    475     const char* imgInc = builder->getUniformCStr(fImageIncrementUni);
    476     const char* kernel = builder->getUniformCStr(fKernelUni);
    477     const char* gain = builder->getUniformCStr(fGainUni);
    478     const char* bias = builder->getUniformCStr(fBiasUni);
    479     int kWidth = fKernelSize.width();
    480     int kHeight = fKernelSize.height();
    481 
    482     builder->fsCodeAppend("\t\tvec4 sum = vec4(0, 0, 0, 0);\n");
    483     builder->fsCodeAppendf("\t\tvec2 coord = %s - %s * %s;\n", coords2D.c_str(), target, imgInc);
    484     builder->fsCodeAppendf("\t\tfor (int y = 0; y < %d; y++) {\n", kHeight);
    485     builder->fsCodeAppendf("\t\t\tfor (int x = 0; x < %d; x++) {\n", kWidth);
    486     builder->fsCodeAppendf("\t\t\t\tfloat k = %s[y * %d + x];\n", kernel, kWidth);
    487     builder->fsCodeAppendf("\t\t\t\tvec2 coord2 = coord + vec2(x, y) * %s;\n", imgInc);
    488     builder->fsCodeAppend("\t\t\t\tvec4 c = ");
    489     appendTextureLookup(builder, samplers[0], "coord2", bounds, fTileMode);
    490     builder->fsCodeAppend(";\n");
    491     if (!fConvolveAlpha) {
    492         builder->fsCodeAppend("\t\t\t\tc.rgb /= c.a;\n");
    493     }
    494     builder->fsCodeAppend("\t\t\t\tsum += c * k;\n");
    495     builder->fsCodeAppend("\t\t\t}\n");
    496     builder->fsCodeAppend("\t\t}\n");
    497     if (fConvolveAlpha) {
    498         builder->fsCodeAppendf("\t\t%s = sum * %s + %s;\n", outputColor, gain, bias);
    499         builder->fsCodeAppendf("\t\t%s.rgb = clamp(%s.rgb, 0.0, %s.a);\n",
    500             outputColor, outputColor, outputColor);
    501     } else {
    502         builder->fsCodeAppend("\t\tvec4 c = ");
    503         appendTextureLookup(builder, samplers[0], coords2D.c_str(), bounds, fTileMode);
    504         builder->fsCodeAppend(";\n");
    505         builder->fsCodeAppendf("\t\t%s.a = c.a;\n", outputColor);
    506         builder->fsCodeAppendf("\t\t%s.rgb = sum.rgb * %s + %s;\n", outputColor, gain, bias);
    507         builder->fsCodeAppendf("\t\t%s.rgb *= %s.a;\n", outputColor, outputColor);
    508     }
    509 }
    510 
    511 namespace {
    512 
    513 int encodeXY(int x, int y) {
    514     SkASSERT(x >= 1 && y >= 1 && x * y <= 32);
    515     if (y < x)
    516         return 0x40 | encodeXY(y, x);
    517     else
    518         return (0x40 >> x) | (y - x);
    519 }
    520 
    521 };
    522 
    523 GrGLEffect::EffectKey GrGLMatrixConvolutionEffect::GenKey(const GrDrawEffect& drawEffect,
    524                                                           const GrGLCaps&) {
    525     const GrMatrixConvolutionEffect& m = drawEffect.castEffect<GrMatrixConvolutionEffect>();
    526     EffectKey key = encodeXY(m.kernelSize().width(), m.kernelSize().height());
    527     key |= m.tileMode() << 7;
    528     key |= m.convolveAlpha() ? 1 << 9 : 0;
    529     return key;
    530 }
    531 
    532 void GrGLMatrixConvolutionEffect::setData(const GrGLUniformManager& uman,
    533                                           const GrDrawEffect& drawEffect) {
    534     const GrMatrixConvolutionEffect& conv = drawEffect.castEffect<GrMatrixConvolutionEffect>();
    535     GrTexture& texture = *conv.texture(0);
    536     // the code we generated was for a specific kernel size
    537     SkASSERT(conv.kernelSize() == fKernelSize);
    538     SkASSERT(conv.tileMode() == fTileMode);
    539     float imageIncrement[2];
    540     float ySign = texture.origin() == kTopLeft_GrSurfaceOrigin ? 1.0f : -1.0f;
    541     imageIncrement[0] = 1.0f / texture.width();
    542     imageIncrement[1] = ySign / texture.height();
    543     uman.set2fv(fImageIncrementUni, 1, imageIncrement);
    544     uman.set2fv(fTargetUni, 1, conv.target());
    545     uman.set1fv(fKernelUni, fKernelSize.width() * fKernelSize.height(), conv.kernel());
    546     uman.set1f(fGainUni, conv.gain());
    547     uman.set1f(fBiasUni, conv.bias());
    548     const SkIRect& bounds = conv.bounds();
    549     float left = (float) bounds.left() / texture.width();
    550     float top = (float) bounds.top() / texture.height();
    551     float right = (float) bounds.right() / texture.width();
    552     float bottom = (float) bounds.bottom() / texture.height();
    553     if (texture.origin() == kBottomLeft_GrSurfaceOrigin) {
    554         uman.set4f(fBoundsUni, left, 1.0f - bottom, right, 1.0f - top);
    555     } else {
    556         uman.set4f(fBoundsUni, left, top, right, bottom);
    557     }
    558 }
    559 
    560 GrMatrixConvolutionEffect::GrMatrixConvolutionEffect(GrTexture* texture,
    561                                                      const SkIRect& bounds,
    562                                                      const SkISize& kernelSize,
    563                                                      const SkScalar* kernel,
    564                                                      SkScalar gain,
    565                                                      SkScalar bias,
    566                                                      const SkIPoint& target,
    567                                                      TileMode tileMode,
    568                                                      bool convolveAlpha)
    569   : INHERITED(texture, MakeDivByTextureWHMatrix(texture)),
    570     fBounds(bounds),
    571     fKernelSize(kernelSize),
    572     fGain(SkScalarToFloat(gain)),
    573     fBias(SkScalarToFloat(bias) / 255.0f),
    574     fTileMode(tileMode),
    575     fConvolveAlpha(convolveAlpha) {
    576     fKernel = new float[kernelSize.width() * kernelSize.height()];
    577     for (int i = 0; i < kernelSize.width() * kernelSize.height(); i++) {
    578         fKernel[i] = SkScalarToFloat(kernel[i]);
    579     }
    580     fTarget[0] = static_cast<float>(target.x());
    581     fTarget[1] = static_cast<float>(target.y());
    582     this->setWillNotUseInputColor();
    583 }
    584 
    585 GrMatrixConvolutionEffect::~GrMatrixConvolutionEffect() {
    586     delete[] fKernel;
    587 }
    588 
    589 const GrBackendEffectFactory& GrMatrixConvolutionEffect::getFactory() const {
    590     return GrTBackendEffectFactory<GrMatrixConvolutionEffect>::getInstance();
    591 }
    592 
    593 bool GrMatrixConvolutionEffect::onIsEqual(const GrEffect& sBase) const {
    594     const GrMatrixConvolutionEffect& s = CastEffect<GrMatrixConvolutionEffect>(sBase);
    595     return this->texture(0) == s.texture(0) &&
    596            fKernelSize == s.kernelSize() &&
    597            !memcmp(fKernel, s.kernel(),
    598                    fKernelSize.width() * fKernelSize.height() * sizeof(float)) &&
    599            fGain == s.gain() &&
    600            fBias == s.bias() &&
    601            fTarget == s.target() &&
    602            fTileMode == s.tileMode() &&
    603            fConvolveAlpha == s.convolveAlpha();
    604 }
    605 
    606 GR_DEFINE_EFFECT_TEST(GrMatrixConvolutionEffect);
    607 
    608 // A little bit less than the minimum # uniforms required by DX9SM2 (32).
    609 // Allows for a 5x5 kernel (or 25x1, for that matter).
    610 #define MAX_KERNEL_SIZE 25
    611 
    612 GrEffectRef* GrMatrixConvolutionEffect::TestCreate(SkRandom* random,
    613                                                    GrContext* context,
    614                                                    const GrDrawTargetCaps&,
    615                                                    GrTexture* textures[]) {
    616     int texIdx = random->nextBool() ? GrEffectUnitTest::kSkiaPMTextureIdx :
    617                                       GrEffectUnitTest::kAlphaTextureIdx;
    618     int width = random->nextRangeU(1, MAX_KERNEL_SIZE);
    619     int height = random->nextRangeU(1, MAX_KERNEL_SIZE / width);
    620     SkISize kernelSize = SkISize::Make(width, height);
    621     SkAutoTDeleteArray<SkScalar> kernel(new SkScalar[width * height]);
    622     for (int i = 0; i < width * height; i++) {
    623         kernel.get()[i] = random->nextSScalar1();
    624     }
    625     SkScalar gain = random->nextSScalar1();
    626     SkScalar bias = random->nextSScalar1();
    627     SkIPoint target = SkIPoint::Make(random->nextRangeU(0, kernelSize.width()),
    628                                      random->nextRangeU(0, kernelSize.height()));
    629     SkIRect bounds = SkIRect::MakeXYWH(random->nextRangeU(0, textures[texIdx]->width()),
    630                                        random->nextRangeU(0, textures[texIdx]->height()),
    631                                        random->nextRangeU(0, textures[texIdx]->width()),
    632                                        random->nextRangeU(0, textures[texIdx]->height()));
    633     TileMode tileMode = static_cast<TileMode>(random->nextRangeU(0, 2));
    634     bool convolveAlpha = random->nextBool();
    635     return GrMatrixConvolutionEffect::Create(textures[texIdx],
    636                                              bounds,
    637                                              kernelSize,
    638                                              kernel.get(),
    639                                              gain,
    640                                              bias,
    641                                              target,
    642                                              tileMode,
    643                                              convolveAlpha);
    644 }
    645 
    646 bool SkMatrixConvolutionImageFilter::asNewEffect(GrEffectRef** effect,
    647                                                  GrTexture* texture,
    648                                                  const SkMatrix&,
    649                                                  const SkIRect& bounds
    650                                                  ) const {
    651     if (!effect) {
    652         return fKernelSize.width() * fKernelSize.height() <= MAX_KERNEL_SIZE;
    653     }
    654     SkASSERT(fKernelSize.width() * fKernelSize.height() <= MAX_KERNEL_SIZE);
    655     *effect = GrMatrixConvolutionEffect::Create(texture,
    656                                                 bounds,
    657                                                 fKernelSize,
    658                                                 fKernel,
    659                                                 fGain,
    660                                                 fBias,
    661                                                 fTarget,
    662                                                 fTileMode,
    663                                                 fConvolveAlpha);
    664     return true;
    665 }
    666 
    667 ///////////////////////////////////////////////////////////////////////////////
    668 
    669 #endif
    670