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