SkBlurImageFilter.cpp revision b295fb6ff3222453912dfcb7a1ea5184d40014b5
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
18SkBlurImageFilter::SkBlurImageFilter(SkFlattenableReadBuffer& buffer)
19  : INHERITED(buffer) {
20    fSigma.fWidth = buffer.readScalar();
21    fSigma.fHeight = buffer.readScalar();
22}
23
24SkBlurImageFilter::SkBlurImageFilter(SkScalar sigmaX,
25                                     SkScalar sigmaY,
26                                     SkImageFilter* input,
27                                     const CropRect* cropRect)
28    : INHERITED(input, cropRect), fSigma(SkSize::Make(sigmaX, sigmaY)) {
29    SkASSERT(sigmaX >= 0 && sigmaY >= 0);
30}
31
32void SkBlurImageFilter::flatten(SkFlattenableWriteBuffer& buffer) const {
33    this->INHERITED::flatten(buffer);
34    buffer.writeScalar(fSigma.fWidth);
35    buffer.writeScalar(fSigma.fHeight);
36}
37
38static 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
81static 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
126static 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
142bool 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, ctm)) {
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
209bool 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, ctm)) {
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