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