Home | History | Annotate | Download | only in effects
      1 /*
      2  * Copyright 2011 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 "SkBitmap.h"
      9 #include "SkBlurImageFilter.h"
     10 #include "SkColorPriv.h"
     11 #include "SkFlattenableBuffers.h"
     12 #if SK_SUPPORT_GPU
     13 #include "GrContext.h"
     14 #include "SkImageFilterUtils.h"
     15 #endif
     16 
     17 SkBlurImageFilter::SkBlurImageFilter(SkFlattenableReadBuffer& buffer)
     18   : INHERITED(buffer) {
     19     fSigma.fWidth = buffer.readScalar();
     20     fSigma.fHeight = buffer.readScalar();
     21 }
     22 
     23 SkBlurImageFilter::SkBlurImageFilter(SkScalar sigmaX, SkScalar sigmaY, SkImageFilter* input)
     24     : INHERITED(input), fSigma(SkSize::Make(sigmaX, sigmaY)) {
     25     SkASSERT(sigmaX >= 0 && sigmaY >= 0);
     26 }
     27 
     28 void SkBlurImageFilter::flatten(SkFlattenableWriteBuffer& buffer) const {
     29     this->INHERITED::flatten(buffer);
     30     buffer.writeScalar(fSigma.fWidth);
     31     buffer.writeScalar(fSigma.fHeight);
     32 }
     33 
     34 static void boxBlurX(const SkBitmap& src, SkBitmap* dst, int kernelSize,
     35                      int leftOffset, int rightOffset)
     36 {
     37     int width = src.width(), height = src.height();
     38     int rightBorder = SkMin32(rightOffset + 1, width);
     39     for (int y = 0; y < height; ++y) {
     40         int sumA = 0, sumR = 0, sumG = 0, sumB = 0;
     41         SkPMColor* p = src.getAddr32(0, y);
     42         for (int i = 0; i < rightBorder; ++i) {
     43             sumA += SkGetPackedA32(*p);
     44             sumR += SkGetPackedR32(*p);
     45             sumG += SkGetPackedG32(*p);
     46             sumB += SkGetPackedB32(*p);
     47             p++;
     48         }
     49 
     50         const SkColor* sptr = src.getAddr32(0, y);
     51         SkColor* dptr = dst->getAddr32(0, y);
     52         for (int x = 0; x < width; ++x) {
     53             *dptr = SkPackARGB32(sumA / kernelSize,
     54                                  sumR / kernelSize,
     55                                  sumG / kernelSize,
     56                                  sumB / kernelSize);
     57             if (x >= leftOffset) {
     58                 SkColor l = *(sptr - leftOffset);
     59                 sumA -= SkGetPackedA32(l);
     60                 sumR -= SkGetPackedR32(l);
     61                 sumG -= SkGetPackedG32(l);
     62                 sumB -= SkGetPackedB32(l);
     63             }
     64             if (x + rightOffset + 1 < width) {
     65                 SkColor r = *(sptr + rightOffset + 1);
     66                 sumA += SkGetPackedA32(r);
     67                 sumR += SkGetPackedR32(r);
     68                 sumG += SkGetPackedG32(r);
     69                 sumB += SkGetPackedB32(r);
     70             }
     71             sptr++;
     72             dptr++;
     73         }
     74     }
     75 }
     76 
     77 static void boxBlurY(const SkBitmap& src, SkBitmap* dst, int kernelSize,
     78                      int topOffset, int bottomOffset)
     79 {
     80     int width = src.width(), height = src.height();
     81     int bottomBorder = SkMin32(bottomOffset + 1, height);
     82     int srcStride = src.rowBytesAsPixels();
     83     int dstStride = dst->rowBytesAsPixels();
     84     for (int x = 0; x < width; ++x) {
     85         int sumA = 0, sumR = 0, sumG = 0, sumB = 0;
     86         SkColor* p = src.getAddr32(x, 0);
     87         for (int i = 0; i < bottomBorder; ++i) {
     88             sumA += SkGetPackedA32(*p);
     89             sumR += SkGetPackedR32(*p);
     90             sumG += SkGetPackedG32(*p);
     91             sumB += SkGetPackedB32(*p);
     92             p += srcStride;
     93         }
     94 
     95         const SkColor* sptr = src.getAddr32(x, 0);
     96         SkColor* dptr = dst->getAddr32(x, 0);
     97         for (int y = 0; y < height; ++y) {
     98             *dptr = SkPackARGB32(sumA / kernelSize,
     99                                  sumR / kernelSize,
    100                                  sumG / kernelSize,
    101                                  sumB / kernelSize);
    102             if (y >= topOffset) {
    103                 SkColor l = *(sptr - topOffset * srcStride);
    104                 sumA -= SkGetPackedA32(l);
    105                 sumR -= SkGetPackedR32(l);
    106                 sumG -= SkGetPackedG32(l);
    107                 sumB -= SkGetPackedB32(l);
    108             }
    109             if (y + bottomOffset + 1 < height) {
    110                 SkColor r = *(sptr + (bottomOffset + 1) * srcStride);
    111                 sumA += SkGetPackedA32(r);
    112                 sumR += SkGetPackedR32(r);
    113                 sumG += SkGetPackedG32(r);
    114                 sumB += SkGetPackedB32(r);
    115             }
    116             sptr += srcStride;
    117             dptr += dstStride;
    118         }
    119     }
    120 }
    121 
    122 static void getBox3Params(SkScalar s, int *kernelSize, int* kernelSize3, int *lowOffset, int *highOffset)
    123 {
    124     float pi = SkScalarToFloat(SK_ScalarPI);
    125     int d = static_cast<int>(floorf(SkScalarToFloat(s) * 3.0f * sqrtf(2.0f * pi) / 4.0f + 0.5f));
    126     *kernelSize = d;
    127     if (d % 2 == 1) {
    128         *lowOffset = *highOffset = (d - 1) / 2;
    129         *kernelSize3 = d;
    130     } else {
    131         *highOffset = d / 2;
    132         *lowOffset = *highOffset - 1;
    133         *kernelSize3 = d + 1;
    134     }
    135 }
    136 
    137 bool SkBlurImageFilter::onFilterImage(Proxy* proxy,
    138                                       const SkBitmap& source, const SkMatrix& ctm,
    139                                       SkBitmap* dst, SkIPoint* offset) {
    140     SkBitmap src = this->getInputResult(proxy, source, ctm, offset);
    141     if (src.config() != SkBitmap::kARGB_8888_Config) {
    142         return false;
    143     }
    144 
    145     SkAutoLockPixels alp(src);
    146     if (!src.getPixels()) {
    147         return false;
    148     }
    149 
    150     dst->setConfig(src.config(), src.width(), src.height());
    151     dst->allocPixels();
    152     int kernelSizeX, kernelSizeX3, lowOffsetX, highOffsetX;
    153     int kernelSizeY, kernelSizeY3, lowOffsetY, highOffsetY;
    154     getBox3Params(fSigma.width(), &kernelSizeX, &kernelSizeX3, &lowOffsetX, &highOffsetX);
    155     getBox3Params(fSigma.height(), &kernelSizeY, &kernelSizeY3, &lowOffsetY, &highOffsetY);
    156 
    157     if (kernelSizeX < 0 || kernelSizeY < 0) {
    158         return false;
    159     }
    160 
    161     if (kernelSizeX == 0 && kernelSizeY == 0) {
    162         src.copyTo(dst, dst->config());
    163         return true;
    164     }
    165 
    166     SkBitmap temp;
    167     temp.setConfig(dst->config(), dst->width(), dst->height());
    168     if (!temp.allocPixels()) {
    169         return false;
    170     }
    171 
    172     if (kernelSizeX > 0 && kernelSizeY > 0) {
    173         boxBlurX(src,  &temp, kernelSizeX,  lowOffsetX, highOffsetX);
    174         boxBlurY(temp, dst,   kernelSizeY,  lowOffsetY, highOffsetY);
    175         boxBlurX(*dst, &temp, kernelSizeX,  highOffsetX,  lowOffsetX);
    176         boxBlurY(temp, dst,   kernelSizeY,  highOffsetY,  lowOffsetY);
    177         boxBlurX(*dst, &temp, kernelSizeX3, highOffsetX, highOffsetX);
    178         boxBlurY(temp, dst,   kernelSizeY3, highOffsetY, highOffsetY);
    179     } else if (kernelSizeX > 0) {
    180         boxBlurX(src,  dst,   kernelSizeX,  lowOffsetX, highOffsetX);
    181         boxBlurX(*dst, &temp, kernelSizeX,  highOffsetX,  lowOffsetX);
    182         boxBlurX(temp, dst,   kernelSizeX3, highOffsetX, highOffsetX);
    183     } else if (kernelSizeY > 0) {
    184         boxBlurY(src,  dst,   kernelSizeY,  lowOffsetY, highOffsetY);
    185         boxBlurY(*dst, &temp, kernelSizeY,  highOffsetY, lowOffsetY);
    186         boxBlurY(temp, dst,   kernelSizeY3, highOffsetY, highOffsetY);
    187     }
    188     return true;
    189 }
    190 
    191 bool SkBlurImageFilter::filterImageGPU(Proxy* proxy, const SkBitmap& src, SkBitmap* result) {
    192 #if SK_SUPPORT_GPU
    193     SkBitmap input;
    194     if (!SkImageFilterUtils::GetInputResultGPU(getInput(0), proxy, src, &input)) {
    195         return false;
    196     }
    197     GrTexture* source = (GrTexture*) input.getTexture();
    198     SkRect rect;
    199     src.getBounds(&rect);
    200     SkAutoTUnref<GrTexture> tex(source->getContext()->gaussianBlur(source, false, rect,
    201         fSigma.width(), fSigma.height()));
    202     return SkImageFilterUtils::WrapTexture(tex, src.width(), src.height(), result);
    203 #else
    204     SkDEBUGFAIL("Should not call in GPU-less build");
    205     return false;
    206 #endif
    207 }
    208