SkBlurImageFilter.cpp revision 736dd031f177681bfa284e19291ef031ad0822d5
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, SkScalar sigmaY, SkImageFilter* input)
25    : INHERITED(input), fSigma(SkSize::Make(sigmaX, sigmaY)) {
26    SkASSERT(sigmaX >= 0 && sigmaY >= 0);
27}
28
29void SkBlurImageFilter::flatten(SkFlattenableWriteBuffer& buffer) const {
30    this->INHERITED::flatten(buffer);
31    buffer.writeScalar(fSigma.fWidth);
32    buffer.writeScalar(fSigma.fHeight);
33}
34
35static void boxBlurX(const SkBitmap& src, SkBitmap* dst, int kernelSize,
36                     int leftOffset, int rightOffset)
37{
38    int width = src.width(), height = src.height();
39    int rightBorder = SkMin32(rightOffset + 1, width);
40    for (int y = 0; y < height; ++y) {
41        int sumA = 0, sumR = 0, sumG = 0, sumB = 0;
42        SkPMColor* p = src.getAddr32(0, y);
43        for (int i = 0; i < rightBorder; ++i) {
44            sumA += SkGetPackedA32(*p);
45            sumR += SkGetPackedR32(*p);
46            sumG += SkGetPackedG32(*p);
47            sumB += SkGetPackedB32(*p);
48            p++;
49        }
50
51        const SkColor* sptr = src.getAddr32(0, y);
52        SkColor* dptr = dst->getAddr32(0, y);
53        for (int x = 0; x < width; ++x) {
54            *dptr = SkPackARGB32(sumA / kernelSize,
55                                 sumR / kernelSize,
56                                 sumG / kernelSize,
57                                 sumB / kernelSize);
58            if (x >= leftOffset) {
59                SkColor l = *(sptr - leftOffset);
60                sumA -= SkGetPackedA32(l);
61                sumR -= SkGetPackedR32(l);
62                sumG -= SkGetPackedG32(l);
63                sumB -= SkGetPackedB32(l);
64            }
65            if (x + rightOffset + 1 < width) {
66                SkColor r = *(sptr + rightOffset + 1);
67                sumA += SkGetPackedA32(r);
68                sumR += SkGetPackedR32(r);
69                sumG += SkGetPackedG32(r);
70                sumB += SkGetPackedB32(r);
71            }
72            sptr++;
73            dptr++;
74        }
75    }
76}
77
78static void boxBlurY(const SkBitmap& src, SkBitmap* dst, int kernelSize,
79                     int topOffset, int bottomOffset)
80{
81    int width = src.width(), height = src.height();
82    int bottomBorder = SkMin32(bottomOffset + 1, height);
83    int srcStride = src.rowBytesAsPixels();
84    int dstStride = dst->rowBytesAsPixels();
85    for (int x = 0; x < width; ++x) {
86        int sumA = 0, sumR = 0, sumG = 0, sumB = 0;
87        SkColor* p = src.getAddr32(x, 0);
88        for (int i = 0; i < bottomBorder; ++i) {
89            sumA += SkGetPackedA32(*p);
90            sumR += SkGetPackedR32(*p);
91            sumG += SkGetPackedG32(*p);
92            sumB += SkGetPackedB32(*p);
93            p += srcStride;
94        }
95
96        const SkColor* sptr = src.getAddr32(x, 0);
97        SkColor* dptr = dst->getAddr32(x, 0);
98        for (int y = 0; y < height; ++y) {
99            *dptr = SkPackARGB32(sumA / kernelSize,
100                                 sumR / kernelSize,
101                                 sumG / kernelSize,
102                                 sumB / kernelSize);
103            if (y >= topOffset) {
104                SkColor l = *(sptr - topOffset * srcStride);
105                sumA -= SkGetPackedA32(l);
106                sumR -= SkGetPackedR32(l);
107                sumG -= SkGetPackedG32(l);
108                sumB -= SkGetPackedB32(l);
109            }
110            if (y + bottomOffset + 1 < height) {
111                SkColor r = *(sptr + (bottomOffset + 1) * srcStride);
112                sumA += SkGetPackedA32(r);
113                sumR += SkGetPackedR32(r);
114                sumG += SkGetPackedG32(r);
115                sumB += SkGetPackedB32(r);
116            }
117            sptr += srcStride;
118            dptr += dstStride;
119        }
120    }
121}
122
123static void getBox3Params(SkScalar s, int *kernelSize, int* kernelSize3, int *lowOffset,
124                          int *highOffset)
125{
126    float pi = SkScalarToFloat(SK_ScalarPI);
127    int d = static_cast<int>(floorf(SkScalarToFloat(s) * 3.0f * sqrtf(2.0f * pi) / 4.0f + 0.5f));
128    *kernelSize = d;
129    if (d % 2 == 1) {
130        *lowOffset = *highOffset = (d - 1) / 2;
131        *kernelSize3 = d;
132    } else {
133        *highOffset = d / 2;
134        *lowOffset = *highOffset - 1;
135        *kernelSize3 = d + 1;
136    }
137}
138
139bool SkBlurImageFilter::onFilterImage(Proxy* proxy,
140                                      const SkBitmap& source, const SkMatrix& ctm,
141                                      SkBitmap* dst, SkIPoint* offset) {
142    SkBitmap src = source;
143    if (getInput(0) && !getInput(0)->filterImage(proxy, source, ctm, &src, offset)) {
144        return false;
145    }
146
147    if (src.config() != SkBitmap::kARGB_8888_Config) {
148        return false;
149    }
150
151    SkAutoLockPixels alp(src);
152    if (!src.getPixels()) {
153        return false;
154    }
155
156    dst->setConfig(src.config(), src.width(), src.height());
157    dst->allocPixels();
158    int kernelSizeX, kernelSizeX3, lowOffsetX, highOffsetX;
159    int kernelSizeY, kernelSizeY3, lowOffsetY, highOffsetY;
160    getBox3Params(fSigma.width(), &kernelSizeX, &kernelSizeX3, &lowOffsetX, &highOffsetX);
161    getBox3Params(fSigma.height(), &kernelSizeY, &kernelSizeY3, &lowOffsetY, &highOffsetY);
162
163    if (kernelSizeX < 0 || kernelSizeY < 0) {
164        return false;
165    }
166
167    if (kernelSizeX == 0 && kernelSizeY == 0) {
168        src.copyTo(dst, dst->config());
169        return true;
170    }
171
172    SkBitmap temp;
173    temp.setConfig(dst->config(), dst->width(), dst->height());
174    if (!temp.allocPixels()) {
175        return false;
176    }
177
178    if (kernelSizeX > 0 && kernelSizeY > 0) {
179        boxBlurX(src,  &temp, kernelSizeX,  lowOffsetX, highOffsetX);
180        boxBlurY(temp, dst,   kernelSizeY,  lowOffsetY, highOffsetY);
181        boxBlurX(*dst, &temp, kernelSizeX,  highOffsetX,  lowOffsetX);
182        boxBlurY(temp, dst,   kernelSizeY,  highOffsetY,  lowOffsetY);
183        boxBlurX(*dst, &temp, kernelSizeX3, highOffsetX, highOffsetX);
184        boxBlurY(temp, dst,   kernelSizeY3, highOffsetY, highOffsetY);
185    } else if (kernelSizeX > 0) {
186        boxBlurX(src,  dst,   kernelSizeX,  lowOffsetX, highOffsetX);
187        boxBlurX(*dst, &temp, kernelSizeX,  highOffsetX,  lowOffsetX);
188        boxBlurX(temp, dst,   kernelSizeX3, highOffsetX, highOffsetX);
189    } else if (kernelSizeY > 0) {
190        boxBlurY(src,  dst,   kernelSizeY,  lowOffsetY, highOffsetY);
191        boxBlurY(*dst, &temp, kernelSizeY,  highOffsetY, lowOffsetY);
192        boxBlurY(temp, dst,   kernelSizeY3, highOffsetY, highOffsetY);
193    }
194    return true;
195}
196
197bool SkBlurImageFilter::filterImageGPU(Proxy* proxy, const SkBitmap& src, SkBitmap* result,
198                                       SkIPoint* offset) {
199#if SK_SUPPORT_GPU
200    SkBitmap input;
201    if (!SkImageFilterUtils::GetInputResultGPU(getInput(0), proxy, src, &input, offset)) {
202        return false;
203    }
204    GrTexture* source = input.getTexture();
205    SkRect rect;
206    src.getBounds(&rect);
207    SkAutoTUnref<GrTexture> tex(SkGpuBlurUtils::GaussianBlur(source->getContext(),
208                                                             source, false, rect,
209                                                             fSigma.width(), fSigma.height()));
210    return SkImageFilterUtils::WrapTexture(tex, src.width(), src.height(), result);
211#else
212    SkDEBUGFAIL("Should not call in GPU-less build");
213    return false;
214#endif
215}
216