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