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