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