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