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 "SkReadBuffer.h"
     12 #include "SkWriteBuffer.h"
     13 #include "SkRect.h"
     14 #include "SkUnPreMultiply.h"
     15 
     16 #if SK_SUPPORT_GPU
     17 #include "effects/GrMatrixConvolutionEffect.h"
     18 #endif
     19 
     20 // We need to be able to read at most SK_MaxS32 bytes, so divide that
     21 // by the size of a scalar to know how many scalars we can read.
     22 static const int32_t gMaxKernelSize = SK_MaxS32 / sizeof(SkScalar);
     23 
     24 SkMatrixConvolutionImageFilter::SkMatrixConvolutionImageFilter(
     25     const SkISize& kernelSize,
     26     const SkScalar* kernel,
     27     SkScalar gain,
     28     SkScalar bias,
     29     const SkIPoint& kernelOffset,
     30     TileMode tileMode,
     31     bool convolveAlpha,
     32     SkImageFilter* input,
     33     const CropRect* cropRect,
     34     uint32_t uniqueID)
     35   : INHERITED(1, &input, cropRect, uniqueID),
     36     fKernelSize(kernelSize),
     37     fGain(gain),
     38     fBias(bias),
     39     fKernelOffset(kernelOffset),
     40     fTileMode(tileMode),
     41     fConvolveAlpha(convolveAlpha) {
     42     size_t size = (size_t) sk_64_mul(fKernelSize.width(), fKernelSize.height());
     43     fKernel = SkNEW_ARRAY(SkScalar, size);
     44     memcpy(fKernel, kernel, size * sizeof(SkScalar));
     45     SkASSERT(kernelSize.fWidth >= 1 && kernelSize.fHeight >= 1);
     46     SkASSERT(kernelOffset.fX >= 0 && kernelOffset.fX < kernelSize.fWidth);
     47     SkASSERT(kernelOffset.fY >= 0 && kernelOffset.fY < kernelSize.fHeight);
     48 }
     49 
     50 SkMatrixConvolutionImageFilter* SkMatrixConvolutionImageFilter::Create(
     51     const SkISize& kernelSize,
     52     const SkScalar* kernel,
     53     SkScalar gain,
     54     SkScalar bias,
     55     const SkIPoint& kernelOffset,
     56     TileMode tileMode,
     57     bool convolveAlpha,
     58     SkImageFilter* input,
     59     const CropRect* cropRect,
     60     uint32_t uniqueID) {
     61     if (kernelSize.width() < 1 || kernelSize.height() < 1) {
     62         return NULL;
     63     }
     64     if (gMaxKernelSize / kernelSize.fWidth < kernelSize.fHeight) {
     65         return NULL;
     66     }
     67     if (!kernel) {
     68         return NULL;
     69     }
     70     return SkNEW_ARGS(SkMatrixConvolutionImageFilter, (kernelSize, kernel, gain, bias,
     71                                                        kernelOffset, tileMode, convolveAlpha,
     72                                                        input, cropRect, uniqueID));
     73 }
     74 
     75 #ifdef SK_SUPPORT_LEGACY_DEEPFLATTENING
     76 static bool tile_mode_is_valid(SkMatrixConvolutionImageFilter::TileMode tileMode) {
     77     switch (tileMode) {
     78         case SkMatrixConvolutionImageFilter::kClamp_TileMode:
     79         case SkMatrixConvolutionImageFilter::kRepeat_TileMode:
     80         case SkMatrixConvolutionImageFilter::kClampToBlack_TileMode:
     81             return true;
     82         default:
     83             break;
     84     }
     85     return false;
     86 }
     87 
     88 SkMatrixConvolutionImageFilter::SkMatrixConvolutionImageFilter(SkReadBuffer& buffer)
     89     : INHERITED(1, buffer) {
     90     fKernelSize.fWidth = buffer.readInt();
     91     fKernelSize.fHeight = buffer.readInt();
     92     if ((fKernelSize.fWidth >= 1) && (fKernelSize.fHeight >= 1) &&
     93         // Make sure size won't be larger than a signed int,
     94         // which would still be extremely large for a kernel,
     95         // but we don't impose a hard limit for kernel size
     96         (gMaxKernelSize / fKernelSize.fWidth >= fKernelSize.fHeight)) {
     97         size_t size = fKernelSize.fWidth * fKernelSize.fHeight;
     98         fKernel = SkNEW_ARRAY(SkScalar, size);
     99         SkDEBUGCODE(bool success =) buffer.readScalarArray(fKernel, size);
    100         SkASSERT(success);
    101     } else {
    102         fKernel = 0;
    103     }
    104     fGain = buffer.readScalar();
    105     fBias = buffer.readScalar();
    106     fKernelOffset.fX = buffer.readInt();
    107     fKernelOffset.fY = buffer.readInt();
    108     fTileMode = (TileMode) buffer.readInt();
    109     fConvolveAlpha = buffer.readBool();
    110     buffer.validate((fKernel != 0) &&
    111                     SkScalarIsFinite(fGain) &&
    112                     SkScalarIsFinite(fBias) &&
    113                     tile_mode_is_valid(fTileMode) &&
    114                     (fKernelOffset.fX >= 0) && (fKernelOffset.fX < fKernelSize.fWidth) &&
    115                     (fKernelOffset.fY >= 0) && (fKernelOffset.fY < fKernelSize.fHeight));
    116 }
    117 #endif
    118 
    119 SkFlattenable* SkMatrixConvolutionImageFilter::CreateProc(SkReadBuffer& buffer) {
    120     SK_IMAGEFILTER_UNFLATTEN_COMMON(common, 1);
    121     SkISize kernelSize;
    122     kernelSize.fWidth = buffer.readInt();
    123     kernelSize.fHeight = buffer.readInt();
    124     const int count = buffer.getArrayCount();
    125 
    126     const int64_t kernelArea = sk_64_mul(kernelSize.width(), kernelSize.height());
    127     if (!buffer.validate(kernelArea == count)) {
    128         return NULL;
    129     }
    130     SkAutoSTArray<16, SkScalar> kernel(count);
    131     if (!buffer.readScalarArray(kernel.get(), count)) {
    132         return NULL;
    133     }
    134     SkScalar gain = buffer.readScalar();
    135     SkScalar bias = buffer.readScalar();
    136     SkIPoint kernelOffset;
    137     kernelOffset.fX = buffer.readInt();
    138     kernelOffset.fY = buffer.readInt();
    139     TileMode tileMode = (TileMode)buffer.readInt();
    140     bool convolveAlpha = buffer.readBool();
    141     return Create(kernelSize, kernel.get(), gain, bias, kernelOffset, tileMode, convolveAlpha,
    142                   common.getInput(0), &common.cropRect(), common.uniqueID());
    143 }
    144 
    145 void SkMatrixConvolutionImageFilter::flatten(SkWriteBuffer& buffer) const {
    146     this->INHERITED::flatten(buffer);
    147     buffer.writeInt(fKernelSize.fWidth);
    148     buffer.writeInt(fKernelSize.fHeight);
    149     buffer.writeScalarArray(fKernel, fKernelSize.fWidth * fKernelSize.fHeight);
    150     buffer.writeScalar(fGain);
    151     buffer.writeScalar(fBias);
    152     buffer.writeInt(fKernelOffset.fX);
    153     buffer.writeInt(fKernelOffset.fY);
    154     buffer.writeInt((int) fTileMode);
    155     buffer.writeBool(fConvolveAlpha);
    156 }
    157 
    158 SkMatrixConvolutionImageFilter::~SkMatrixConvolutionImageFilter() {
    159     delete[] fKernel;
    160 }
    161 
    162 class UncheckedPixelFetcher {
    163 public:
    164     static inline SkPMColor fetch(const SkBitmap& src, int x, int y, const SkIRect& bounds) {
    165         return *src.getAddr32(x, y);
    166     }
    167 };
    168 
    169 class ClampPixelFetcher {
    170 public:
    171     static inline SkPMColor fetch(const SkBitmap& src, int x, int y, const SkIRect& bounds) {
    172         x = SkPin32(x, bounds.fLeft, bounds.fRight - 1);
    173         y = SkPin32(y, bounds.fTop, bounds.fBottom - 1);
    174         return *src.getAddr32(x, y);
    175     }
    176 };
    177 
    178 class RepeatPixelFetcher {
    179 public:
    180     static inline SkPMColor fetch(const SkBitmap& src, int x, int y, const SkIRect& bounds) {
    181         x = (x - bounds.left()) % bounds.width() + bounds.left();
    182         y = (y - bounds.top()) % bounds.height() + bounds.top();
    183         if (x < bounds.left()) {
    184             x += bounds.width();
    185         }
    186         if (y < bounds.top()) {
    187             y += bounds.height();
    188         }
    189         return *src.getAddr32(x, y);
    190     }
    191 };
    192 
    193 class ClampToBlackPixelFetcher {
    194 public:
    195     static inline SkPMColor fetch(const SkBitmap& src, int x, int y, const SkIRect& bounds) {
    196         if (x < bounds.fLeft || x >= bounds.fRight || y < bounds.fTop || y >= bounds.fBottom) {
    197             return 0;
    198         } else {
    199             return *src.getAddr32(x, y);
    200         }
    201     }
    202 };
    203 
    204 template<class PixelFetcher, bool convolveAlpha>
    205 void SkMatrixConvolutionImageFilter::filterPixels(const SkBitmap& src,
    206                                                   SkBitmap* result,
    207                                                   const SkIRect& r,
    208                                                   const SkIRect& bounds) const {
    209     SkIRect rect(r);
    210     if (!rect.intersect(bounds)) {
    211         return;
    212     }
    213     for (int y = rect.fTop; y < rect.fBottom; ++y) {
    214         SkPMColor* dptr = result->getAddr32(rect.fLeft - bounds.fLeft, y - bounds.fTop);
    215         for (int x = rect.fLeft; x < rect.fRight; ++x) {
    216             SkScalar sumA = 0, sumR = 0, sumG = 0, sumB = 0;
    217             for (int cy = 0; cy < fKernelSize.fHeight; cy++) {
    218                 for (int cx = 0; cx < fKernelSize.fWidth; cx++) {
    219                     SkPMColor s = PixelFetcher::fetch(src,
    220                                                       x + cx - fKernelOffset.fX,
    221                                                       y + cy - fKernelOffset.fY,
    222                                                       bounds);
    223                     SkScalar k = fKernel[cy * fKernelSize.fWidth + cx];
    224                     if (convolveAlpha) {
    225                         sumA += SkScalarMul(SkIntToScalar(SkGetPackedA32(s)), k);
    226                     }
    227                     sumR += SkScalarMul(SkIntToScalar(SkGetPackedR32(s)), k);
    228                     sumG += SkScalarMul(SkIntToScalar(SkGetPackedG32(s)), k);
    229                     sumB += SkScalarMul(SkIntToScalar(SkGetPackedB32(s)), k);
    230                 }
    231             }
    232             int a = convolveAlpha
    233                   ? SkClampMax(SkScalarFloorToInt(SkScalarMul(sumA, fGain) + fBias), 255)
    234                   : 255;
    235             int r = SkClampMax(SkScalarFloorToInt(SkScalarMul(sumR, fGain) + fBias), a);
    236             int g = SkClampMax(SkScalarFloorToInt(SkScalarMul(sumG, fGain) + fBias), a);
    237             int b = SkClampMax(SkScalarFloorToInt(SkScalarMul(sumB, fGain) + fBias), a);
    238             if (!convolveAlpha) {
    239                 a = SkGetPackedA32(PixelFetcher::fetch(src, x, y, bounds));
    240                 *dptr++ = SkPreMultiplyARGB(a, r, g, b);
    241             } else {
    242                 *dptr++ = SkPackARGB32(a, r, g, b);
    243             }
    244         }
    245     }
    246 }
    247 
    248 template<class PixelFetcher>
    249 void SkMatrixConvolutionImageFilter::filterPixels(const SkBitmap& src,
    250                                                   SkBitmap* result,
    251                                                   const SkIRect& rect,
    252                                                   const SkIRect& bounds) const {
    253     if (fConvolveAlpha) {
    254         filterPixels<PixelFetcher, true>(src, result, rect, bounds);
    255     } else {
    256         filterPixels<PixelFetcher, false>(src, result, rect, bounds);
    257     }
    258 }
    259 
    260 void SkMatrixConvolutionImageFilter::filterInteriorPixels(const SkBitmap& src,
    261                                                           SkBitmap* result,
    262                                                           const SkIRect& rect,
    263                                                           const SkIRect& bounds) const {
    264     filterPixels<UncheckedPixelFetcher>(src, result, rect, bounds);
    265 }
    266 
    267 void SkMatrixConvolutionImageFilter::filterBorderPixels(const SkBitmap& src,
    268                                                         SkBitmap* result,
    269                                                         const SkIRect& rect,
    270                                                         const SkIRect& bounds) const {
    271     switch (fTileMode) {
    272         case kClamp_TileMode:
    273             filterPixels<ClampPixelFetcher>(src, result, rect, bounds);
    274             break;
    275         case kRepeat_TileMode:
    276             filterPixels<RepeatPixelFetcher>(src, result, rect, bounds);
    277             break;
    278         case kClampToBlack_TileMode:
    279             filterPixels<ClampToBlackPixelFetcher>(src, result, rect, bounds);
    280             break;
    281     }
    282 }
    283 
    284 // FIXME:  This should be refactored to SkImageFilterUtils for
    285 // use by other filters.  For now, we assume the input is always
    286 // premultiplied and unpremultiply it
    287 static SkBitmap unpremultiplyBitmap(const SkBitmap& src)
    288 {
    289     SkAutoLockPixels alp(src);
    290     if (!src.getPixels()) {
    291         return SkBitmap();
    292     }
    293     SkBitmap result;
    294     if (!result.tryAllocPixels(src.info())) {
    295         return SkBitmap();
    296     }
    297     for (int y = 0; y < src.height(); ++y) {
    298         const uint32_t* srcRow = src.getAddr32(0, y);
    299         uint32_t* dstRow = result.getAddr32(0, y);
    300         for (int x = 0; x < src.width(); ++x) {
    301             dstRow[x] = SkUnPreMultiply::PMColorToColor(srcRow[x]);
    302         }
    303     }
    304     return result;
    305 }
    306 
    307 bool SkMatrixConvolutionImageFilter::onFilterImage(Proxy* proxy,
    308                                                    const SkBitmap& source,
    309                                                    const Context& ctx,
    310                                                    SkBitmap* result,
    311                                                    SkIPoint* offset) const {
    312     SkBitmap src = source;
    313     SkIPoint srcOffset = SkIPoint::Make(0, 0);
    314     if (getInput(0) && !getInput(0)->filterImage(proxy, source, ctx, &src, &srcOffset)) {
    315         return false;
    316     }
    317 
    318     if (src.colorType() != kN32_SkColorType) {
    319         return false;
    320     }
    321 
    322     SkIRect bounds;
    323     if (!this->applyCropRect(ctx, proxy, src, &srcOffset, &bounds, &src)) {
    324         return false;
    325     }
    326 
    327     if (!fConvolveAlpha && !src.isOpaque()) {
    328         src = unpremultiplyBitmap(src);
    329     }
    330 
    331     SkAutoLockPixels alp(src);
    332     if (!src.getPixels()) {
    333         return false;
    334     }
    335 
    336     if (!result->tryAllocPixels(src.info().makeWH(bounds.width(), bounds.height()))) {
    337         return false;
    338     }
    339 
    340     offset->fX = bounds.fLeft;
    341     offset->fY = bounds.fTop;
    342     bounds.offset(-srcOffset);
    343     SkIRect interior = SkIRect::MakeXYWH(bounds.left() + fKernelOffset.fX,
    344                                          bounds.top() + fKernelOffset.fY,
    345                                          bounds.width() - fKernelSize.fWidth + 1,
    346                                          bounds.height() - fKernelSize.fHeight + 1);
    347     SkIRect top = SkIRect::MakeLTRB(bounds.left(), bounds.top(), bounds.right(), interior.top());
    348     SkIRect bottom = SkIRect::MakeLTRB(bounds.left(), interior.bottom(),
    349                                        bounds.right(), bounds.bottom());
    350     SkIRect left = SkIRect::MakeLTRB(bounds.left(), interior.top(),
    351                                      interior.left(), interior.bottom());
    352     SkIRect right = SkIRect::MakeLTRB(interior.right(), interior.top(),
    353                                       bounds.right(), interior.bottom());
    354     filterBorderPixels(src, result, top, bounds);
    355     filterBorderPixels(src, result, left, bounds);
    356     filterInteriorPixels(src, result, interior, bounds);
    357     filterBorderPixels(src, result, right, bounds);
    358     filterBorderPixels(src, result, bottom, bounds);
    359     return true;
    360 }
    361 
    362 bool SkMatrixConvolutionImageFilter::onFilterBounds(const SkIRect& src, const SkMatrix& ctm,
    363                                                     SkIRect* dst) const {
    364     SkIRect bounds = src;
    365     bounds.fRight += fKernelSize.width() - 1;
    366     bounds.fBottom += fKernelSize.height() - 1;
    367     bounds.offset(-fKernelOffset);
    368     if (getInput(0) && !getInput(0)->filterBounds(bounds, ctm, &bounds)) {
    369         return false;
    370     }
    371     *dst = bounds;
    372     return true;
    373 }
    374 
    375 #if SK_SUPPORT_GPU
    376 
    377 static GrTextureDomain::Mode convert_tilemodes(
    378         SkMatrixConvolutionImageFilter::TileMode tileMode) {
    379     switch (tileMode) {
    380         case SkMatrixConvolutionImageFilter::kClamp_TileMode:
    381             return GrTextureDomain::kClamp_Mode;
    382         case SkMatrixConvolutionImageFilter::kRepeat_TileMode:
    383             return GrTextureDomain::kRepeat_Mode;
    384         case SkMatrixConvolutionImageFilter::kClampToBlack_TileMode:
    385             return GrTextureDomain::kDecal_Mode;
    386         default:
    387             SkASSERT(false);
    388     }
    389     return GrTextureDomain::kIgnore_Mode;
    390 }
    391 
    392 bool SkMatrixConvolutionImageFilter::asFragmentProcessor(GrFragmentProcessor** fp,
    393                                                          GrTexture* texture,
    394                                                          const SkMatrix&,
    395                                                          const SkIRect& bounds) const {
    396     if (!fp) {
    397         return fKernelSize.width() * fKernelSize.height() <= MAX_KERNEL_SIZE;
    398     }
    399     SkASSERT(fKernelSize.width() * fKernelSize.height() <= MAX_KERNEL_SIZE);
    400     *fp = GrMatrixConvolutionEffect::Create(texture,
    401                                             bounds,
    402                                             fKernelSize,
    403                                             fKernel,
    404                                             fGain,
    405                                             fBias,
    406                                             fKernelOffset,
    407                                             convert_tilemodes(fTileMode),
    408                                             fConvolveAlpha);
    409     return true;
    410 }
    411 #endif
    412