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 "SkReadBuffer.h"
     12 #include "SkWriteBuffer.h"
     13 #include "SkGpuBlurUtils.h"
     14 #include "SkBlurImage_opts.h"
     15 #if SK_SUPPORT_GPU
     16 #include "GrContext.h"
     17 #endif
     18 
     19 // This rather arbitrary-looking value results in a maximum box blur kernel size
     20 // of 1000 pixels on the raster path, which matches the WebKit and Firefox
     21 // implementations. Since the GPU path does not compute a box blur, putting
     22 // the limit on sigma ensures consistent behaviour between the GPU and
     23 // raster paths.
     24 #define MAX_SIGMA SkIntToScalar(532)
     25 
     26 static SkVector mapSigma(const SkSize& localSigma, const SkMatrix& ctm) {
     27     SkVector sigma = SkVector::Make(localSigma.width(), localSigma.height());
     28     ctm.mapVectors(&sigma, 1);
     29     sigma.fX = SkMinScalar(SkScalarAbs(sigma.fX), MAX_SIGMA);
     30     sigma.fY = SkMinScalar(SkScalarAbs(sigma.fY), MAX_SIGMA);
     31     return sigma;
     32 }
     33 
     34 SkBlurImageFilter::SkBlurImageFilter(SkScalar sigmaX,
     35                                      SkScalar sigmaY,
     36                                      SkImageFilter* input,
     37                                      const CropRect* cropRect)
     38     : INHERITED(1, &input, cropRect), fSigma(SkSize::Make(sigmaX, sigmaY)) {
     39 }
     40 
     41 SkFlattenable* SkBlurImageFilter::CreateProc(SkReadBuffer& buffer) {
     42     SK_IMAGEFILTER_UNFLATTEN_COMMON(common, 1);
     43     SkScalar sigmaX = buffer.readScalar();
     44     SkScalar sigmaY = buffer.readScalar();
     45     return Create(sigmaX, sigmaY, common.getInput(0), &common.cropRect());
     46 }
     47 
     48 void SkBlurImageFilter::flatten(SkWriteBuffer& buffer) const {
     49     this->INHERITED::flatten(buffer);
     50     buffer.writeScalar(fSigma.fWidth);
     51     buffer.writeScalar(fSigma.fHeight);
     52 }
     53 
     54 enum BlurDirection {
     55     kX, kY
     56 };
     57 
     58 /**
     59  *
     60  * In order to make memory accesses cache-friendly, we reorder the passes to
     61  * use contiguous memory reads wherever possible.
     62  *
     63  * For example, the 6 passes of the X-and-Y blur case are rewritten as
     64  * follows. Instead of 3 passes in X and 3 passes in Y, we perform
     65  * 2 passes in X, 1 pass in X transposed to Y on write, 2 passes in X,
     66  * then 1 pass in X transposed to Y on write.
     67  *
     68  * +----+       +----+       +----+        +---+       +---+       +---+        +----+
     69  * + AB + ----> | AB | ----> | AB | -----> | A | ----> | A | ----> | A | -----> | AB |
     70  * +----+ blurX +----+ blurX +----+ blurXY | B | blurX | B | blurX | B | blurXY +----+
     71  *                                         +---+       +---+       +---+
     72  *
     73  * In this way, two of the y-blurs become x-blurs applied to transposed
     74  * images, and all memory reads are contiguous.
     75  */
     76 
     77 template<BlurDirection srcDirection, BlurDirection dstDirection>
     78 static void boxBlur(const SkPMColor* src, int srcStride, SkPMColor* dst, int kernelSize,
     79                     int leftOffset, int rightOffset, int width, int height)
     80 {
     81     int rightBorder = SkMin32(rightOffset + 1, width);
     82     int srcStrideX = srcDirection == kX ? 1 : srcStride;
     83     int dstStrideX = dstDirection == kX ? 1 : height;
     84     int srcStrideY = srcDirection == kX ? srcStride : 1;
     85     int dstStrideY = dstDirection == kX ? width : 1;
     86     uint32_t scale = (1 << 24) / kernelSize;
     87     uint32_t half = 1 << 23;
     88     for (int y = 0; y < height; ++y) {
     89         int sumA = 0, sumR = 0, sumG = 0, sumB = 0;
     90         const SkPMColor* p = src;
     91         for (int i = 0; i < rightBorder; ++i) {
     92             sumA += SkGetPackedA32(*p);
     93             sumR += SkGetPackedR32(*p);
     94             sumG += SkGetPackedG32(*p);
     95             sumB += SkGetPackedB32(*p);
     96             p += srcStrideX;
     97         }
     98 
     99         const SkPMColor* sptr = src;
    100         SkColor* dptr = dst;
    101         for (int x = 0; x < width; ++x) {
    102             *dptr = SkPackARGB32((sumA * scale + half) >> 24,
    103                                  (sumR * scale + half) >> 24,
    104                                  (sumG * scale + half) >> 24,
    105                                  (sumB * scale + half) >> 24);
    106             if (x >= leftOffset) {
    107                 SkColor l = *(sptr - leftOffset * srcStrideX);
    108                 sumA -= SkGetPackedA32(l);
    109                 sumR -= SkGetPackedR32(l);
    110                 sumG -= SkGetPackedG32(l);
    111                 sumB -= SkGetPackedB32(l);
    112             }
    113             if (x + rightOffset + 1 < width) {
    114                 SkColor r = *(sptr + (rightOffset + 1) * srcStrideX);
    115                 sumA += SkGetPackedA32(r);
    116                 sumR += SkGetPackedR32(r);
    117                 sumG += SkGetPackedG32(r);
    118                 sumB += SkGetPackedB32(r);
    119             }
    120             sptr += srcStrideX;
    121             if (srcDirection == kY) {
    122                 SK_PREFETCH(sptr + (rightOffset + 1) * srcStrideX);
    123             }
    124             dptr += dstStrideX;
    125         }
    126         src += srcStrideY;
    127         dst += dstStrideY;
    128     }
    129 }
    130 
    131 static void getBox3Params(SkScalar s, int *kernelSize, int* kernelSize3, int *lowOffset,
    132                           int *highOffset)
    133 {
    134     float pi = SkScalarToFloat(SK_ScalarPI);
    135     int d = static_cast<int>(floorf(SkScalarToFloat(s) * 3.0f * sqrtf(2.0f * pi) / 4.0f + 0.5f));
    136     *kernelSize = d;
    137     if (d % 2 == 1) {
    138         *lowOffset = *highOffset = (d - 1) / 2;
    139         *kernelSize3 = d;
    140     } else {
    141         *highOffset = d / 2;
    142         *lowOffset = *highOffset - 1;
    143         *kernelSize3 = d + 1;
    144     }
    145 }
    146 
    147 bool SkBlurImageFilter::onFilterImage(Proxy* proxy,
    148                                       const SkBitmap& source, const Context& ctx,
    149                                       SkBitmap* dst, SkIPoint* offset) const {
    150     SkBitmap src = source;
    151     SkIPoint srcOffset = SkIPoint::Make(0, 0);
    152     if (getInput(0) && !getInput(0)->filterImage(proxy, source, ctx, &src, &srcOffset)) {
    153         return false;
    154     }
    155 
    156     if (src.colorType() != kN32_SkColorType) {
    157         return false;
    158     }
    159 
    160     SkIRect srcBounds, dstBounds;
    161     if (!this->applyCropRect(ctx, proxy, src, &srcOffset, &srcBounds, &src)) {
    162         return false;
    163     }
    164 
    165     SkAutoLockPixels alp(src);
    166     if (!src.getPixels()) {
    167         return false;
    168     }
    169 
    170     if (!dst->tryAllocPixels(src.info().makeWH(srcBounds.width(), srcBounds.height()))) {
    171         return false;
    172     }
    173     dst->getBounds(&dstBounds);
    174 
    175     SkVector sigma = mapSigma(fSigma, ctx.ctm());
    176 
    177     int kernelSizeX, kernelSizeX3, lowOffsetX, highOffsetX;
    178     int kernelSizeY, kernelSizeY3, lowOffsetY, highOffsetY;
    179     getBox3Params(sigma.x(), &kernelSizeX, &kernelSizeX3, &lowOffsetX, &highOffsetX);
    180     getBox3Params(sigma.y(), &kernelSizeY, &kernelSizeY3, &lowOffsetY, &highOffsetY);
    181 
    182     if (kernelSizeX < 0 || kernelSizeY < 0) {
    183         return false;
    184     }
    185 
    186     if (kernelSizeX == 0 && kernelSizeY == 0) {
    187         src.copyTo(dst, dst->colorType());
    188         offset->fX = srcBounds.fLeft;
    189         offset->fY = srcBounds.fTop;
    190         return true;
    191     }
    192 
    193     SkBitmap temp;
    194     if (!temp.tryAllocPixels(dst->info())) {
    195         return false;
    196     }
    197 
    198     offset->fX = srcBounds.fLeft;
    199     offset->fY = srcBounds.fTop;
    200     srcBounds.offset(-srcOffset);
    201     const SkPMColor* s = src.getAddr32(srcBounds.left(), srcBounds.top());
    202     SkPMColor* t = temp.getAddr32(0, 0);
    203     SkPMColor* d = dst->getAddr32(0, 0);
    204     int w = dstBounds.width(), h = dstBounds.height();
    205     int sw = src.rowBytesAsPixels();
    206     SkBoxBlurProc boxBlurX, boxBlurXY, boxBlurYX;
    207     if (!SkBoxBlurGetPlatformProcs(&boxBlurX, &boxBlurXY, &boxBlurYX)) {
    208         boxBlurX = boxBlur<kX, kX>;
    209         boxBlurXY = boxBlur<kX, kY>;
    210         boxBlurYX = boxBlur<kY, kX>;
    211     }
    212 
    213     if (kernelSizeX > 0 && kernelSizeY > 0) {
    214         boxBlurX(s,  sw, t, kernelSizeX,  lowOffsetX,  highOffsetX, w, h);
    215         boxBlurX(t,  w,  d, kernelSizeX,  highOffsetX, lowOffsetX,  w, h);
    216         boxBlurXY(d, w,  t, kernelSizeX3, highOffsetX, highOffsetX, w, h);
    217         boxBlurX(t,  h,  d, kernelSizeY,  lowOffsetY,  highOffsetY, h, w);
    218         boxBlurX(d,  h,  t, kernelSizeY,  highOffsetY, lowOffsetY,  h, w);
    219         boxBlurXY(t, h,  d, kernelSizeY3, highOffsetY, highOffsetY, h, w);
    220     } else if (kernelSizeX > 0) {
    221         boxBlurX(s,  sw, d, kernelSizeX,  lowOffsetX,  highOffsetX, w, h);
    222         boxBlurX(d,  w,  t, kernelSizeX,  highOffsetX, lowOffsetX,  w, h);
    223         boxBlurX(t,  w,  d, kernelSizeX3, highOffsetX, highOffsetX, w, h);
    224     } else if (kernelSizeY > 0) {
    225         boxBlurYX(s, sw, d, kernelSizeY,  lowOffsetY,  highOffsetY, h, w);
    226         boxBlurX(d,  h,  t, kernelSizeY,  highOffsetY, lowOffsetY,  h, w);
    227         boxBlurXY(t, h,  d, kernelSizeY3, highOffsetY, highOffsetY, h, w);
    228     }
    229     return true;
    230 }
    231 
    232 
    233 void SkBlurImageFilter::computeFastBounds(const SkRect& src, SkRect* dst) const {
    234     if (getInput(0)) {
    235         getInput(0)->computeFastBounds(src, dst);
    236     } else {
    237         *dst = src;
    238     }
    239 
    240     dst->outset(SkScalarMul(fSigma.width(), SkIntToScalar(3)),
    241                 SkScalarMul(fSigma.height(), SkIntToScalar(3)));
    242 }
    243 
    244 bool SkBlurImageFilter::onFilterBounds(const SkIRect& src, const SkMatrix& ctm,
    245                                        SkIRect* dst) const {
    246     SkIRect bounds = src;
    247     SkVector sigma = mapSigma(fSigma, ctm);
    248     bounds.outset(SkScalarCeilToInt(SkScalarMul(sigma.x(), SkIntToScalar(3))),
    249                   SkScalarCeilToInt(SkScalarMul(sigma.y(), SkIntToScalar(3))));
    250     if (getInput(0) && !getInput(0)->filterBounds(bounds, ctm, &bounds)) {
    251         return false;
    252     }
    253     *dst = bounds;
    254     return true;
    255 }
    256 
    257 bool SkBlurImageFilter::filterImageGPU(Proxy* proxy, const SkBitmap& src, const Context& ctx,
    258                                        SkBitmap* result, SkIPoint* offset) const {
    259 #if SK_SUPPORT_GPU
    260     SkBitmap input = src;
    261     SkIPoint srcOffset = SkIPoint::Make(0, 0);
    262     if (getInput(0) && !getInput(0)->getInputResultGPU(proxy, src, ctx, &input, &srcOffset)) {
    263         return false;
    264     }
    265     SkIRect rect;
    266     if (!this->applyCropRect(ctx, proxy, input, &srcOffset, &rect, &input)) {
    267         return false;
    268     }
    269     GrTexture* source = input.getTexture();
    270     SkVector sigma = mapSigma(fSigma, ctx.ctm());
    271     offset->fX = rect.fLeft;
    272     offset->fY = rect.fTop;
    273     rect.offset(-srcOffset);
    274     SkAutoTUnref<GrTexture> tex(SkGpuBlurUtils::GaussianBlur(source->getContext(),
    275                                                              source,
    276                                                              false,
    277                                                              SkRect::Make(rect),
    278                                                              true,
    279                                                              sigma.x(),
    280                                                              sigma.y()));
    281     if (!tex) {
    282         return false;
    283     }
    284     WrapTexture(tex, rect.width(), rect.height(), result);
    285     return true;
    286 #else
    287     SkDEBUGFAIL("Should not call in GPU-less build");
    288     return false;
    289 #endif
    290 }
    291 
    292 #ifndef SK_IGNORE_TO_STRING
    293 void SkBlurImageFilter::toString(SkString* str) const {
    294     str->appendf("SkBlurImageFilter: (");
    295     str->appendf("sigma: (%f, %f) input (", fSigma.fWidth, fSigma.fHeight);
    296 
    297     if (this->getInput(0)) {
    298         this->getInput(0)->toString(str);
    299     }
    300 
    301     str->append("))");
    302 }
    303 #endif
    304