Upstream version 5.34.92.0
[platform/framework/web/crosswalk.git] / src / third_party / skia / src / effects / SkBlurImageFilter.cpp
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 #include "SkBlurImage_opts.h"
14 #if SK_SUPPORT_GPU
15 #include "GrContext.h"
16 #include "SkImageFilterUtils.h"
17 #endif
18
19 SkBlurImageFilter::SkBlurImageFilter(SkFlattenableReadBuffer& buffer)
20   : INHERITED(1, buffer) {
21     fSigma.fWidth = buffer.readScalar();
22     fSigma.fHeight = buffer.readScalar();
23     buffer.validate(SkScalarIsFinite(fSigma.fWidth) &&
24                     SkScalarIsFinite(fSigma.fHeight) &&
25                     (fSigma.fWidth >= 0) &&
26                     (fSigma.fHeight >= 0));
27 }
28
29 SkBlurImageFilter::SkBlurImageFilter(SkScalar sigmaX,
30                                      SkScalar sigmaY,
31                                      SkImageFilter* input,
32                                      const CropRect* cropRect)
33     : INHERITED(input, cropRect), fSigma(SkSize::Make(sigmaX, sigmaY)) {
34     SkASSERT(sigmaX >= 0 && sigmaY >= 0);
35 }
36
37 void SkBlurImageFilter::flatten(SkFlattenableWriteBuffer& buffer) const {
38     this->INHERITED::flatten(buffer);
39     buffer.writeScalar(fSigma.fWidth);
40     buffer.writeScalar(fSigma.fHeight);
41 }
42
43 enum BlurDirection {
44     kX, kY
45 };
46
47 /**
48  *
49  * In order to make memory accesses cache-friendly, we reorder the passes to
50  * use contiguous memory reads wherever possible.
51  *
52  * For example, the 6 passes of the X-and-Y blur case are rewritten as
53  * follows. Instead of 3 passes in X and 3 passes in Y, we perform
54  * 2 passes in X, 1 pass in X transposed to Y on write, 2 passes in X,
55  * then 1 pass in X transposed to Y on write.
56  *
57  * +----+       +----+       +----+        +---+       +---+       +---+        +----+
58  * + AB + ----> | AB | ----> | AB | -----> | A | ----> | A | ----> | A | -----> | AB |
59  * +----+ blurX +----+ blurX +----+ blurXY | B | blurX | B | blurX | B | blurXY +----+
60  *                                         +---+       +---+       +---+
61  *
62  * In this way, two of the y-blurs become x-blurs applied to transposed
63  * images, and all memory reads are contiguous.
64  */
65
66 template<BlurDirection srcDirection, BlurDirection dstDirection>
67 static void boxBlur(const SkPMColor* src, int srcStride, SkPMColor* dst, int kernelSize,
68                     int leftOffset, int rightOffset, int width, int height)
69 {
70     int rightBorder = SkMin32(rightOffset + 1, width);
71     int srcStrideX = srcDirection == kX ? 1 : srcStride;
72     int dstStrideX = dstDirection == kX ? 1 : height;
73     int srcStrideY = srcDirection == kX ? srcStride : 1;
74     int dstStrideY = dstDirection == kX ? width : 1;
75     uint32_t scale = (1 << 24) / kernelSize;
76     uint32_t half = 1 << 23;
77     for (int y = 0; y < height; ++y) {
78         int sumA = 0, sumR = 0, sumG = 0, sumB = 0;
79         const SkPMColor* p = src;
80         for (int i = 0; i < rightBorder; ++i) {
81             sumA += SkGetPackedA32(*p);
82             sumR += SkGetPackedR32(*p);
83             sumG += SkGetPackedG32(*p);
84             sumB += SkGetPackedB32(*p);
85             p += srcStrideX;
86         }
87
88         const SkPMColor* sptr = src;
89         SkColor* dptr = dst;
90         for (int x = 0; x < width; ++x) {
91             *dptr = SkPackARGB32((sumA * scale + half) >> 24,
92                                  (sumR * scale + half) >> 24,
93                                  (sumG * scale + half) >> 24,
94                                  (sumB * scale + half) >> 24);
95             if (x >= leftOffset) {
96                 SkColor l = *(sptr - leftOffset * srcStrideX);
97                 sumA -= SkGetPackedA32(l);
98                 sumR -= SkGetPackedR32(l);
99                 sumG -= SkGetPackedG32(l);
100                 sumB -= SkGetPackedB32(l);
101             }
102             if (x + rightOffset + 1 < width) {
103                 SkColor r = *(sptr + (rightOffset + 1) * srcStrideX);
104                 sumA += SkGetPackedA32(r);
105                 sumR += SkGetPackedR32(r);
106                 sumG += SkGetPackedG32(r);
107                 sumB += SkGetPackedB32(r);
108             }
109             sptr += srcStrideX;
110             if (srcDirection == kY) {
111                 SK_PREFETCH(sptr + (rightOffset + 1) * srcStrideX);
112             }
113             dptr += dstStrideX;
114         }
115         src += srcStrideY;
116         dst += dstStrideY;
117     }
118 }
119
120 static void getBox3Params(SkScalar s, int *kernelSize, int* kernelSize3, int *lowOffset,
121                           int *highOffset)
122 {
123     float pi = SkScalarToFloat(SK_ScalarPI);
124     int d = static_cast<int>(floorf(SkScalarToFloat(s) * 3.0f * sqrtf(2.0f * pi) / 4.0f + 0.5f));
125     *kernelSize = d;
126     if (d % 2 == 1) {
127         *lowOffset = *highOffset = (d - 1) / 2;
128         *kernelSize3 = d;
129     } else {
130         *highOffset = d / 2;
131         *lowOffset = *highOffset - 1;
132         *kernelSize3 = d + 1;
133     }
134 }
135
136 bool SkBlurImageFilter::onFilterImage(Proxy* proxy,
137                                       const SkBitmap& source, const SkMatrix& ctm,
138                                       SkBitmap* dst, SkIPoint* offset) {
139     SkBitmap src = source;
140     SkIPoint srcOffset = SkIPoint::Make(0, 0);
141     if (getInput(0) && !getInput(0)->filterImage(proxy, source, ctm, &src, &srcOffset)) {
142         return false;
143     }
144
145     if (src.config() != SkBitmap::kARGB_8888_Config) {
146         return false;
147     }
148
149     SkAutoLockPixels alp(src);
150     if (!src.getPixels()) {
151         return false;
152     }
153
154     SkIRect srcBounds, dstBounds;
155     src.getBounds(&srcBounds);
156     srcBounds.offset(srcOffset);
157     if (!this->applyCropRect(&srcBounds, ctm)) {
158         return false;
159     }
160
161     dst->setConfig(src.config(), srcBounds.width(), srcBounds.height());
162     dst->getBounds(&dstBounds);
163     if (!dst->allocPixels()) {
164         return false;
165     }
166
167     SkVector sigma, localSigma = SkVector::Make(fSigma.width(), fSigma.height());
168     ctm.mapVectors(&sigma, &localSigma, 1);
169
170     int kernelSizeX, kernelSizeX3, lowOffsetX, highOffsetX;
171     int kernelSizeY, kernelSizeY3, lowOffsetY, highOffsetY;
172     getBox3Params(sigma.x(), &kernelSizeX, &kernelSizeX3, &lowOffsetX, &highOffsetX);
173     getBox3Params(sigma.y(), &kernelSizeY, &kernelSizeY3, &lowOffsetY, &highOffsetY);
174
175     if (kernelSizeX < 0 || kernelSizeY < 0) {
176         return false;
177     }
178
179     if (kernelSizeX == 0 && kernelSizeY == 0) {
180         src.copyTo(dst, dst->config());
181         offset->fX = srcBounds.fLeft;
182         offset->fY = srcBounds.fTop;
183         return true;
184     }
185
186     SkBitmap temp;
187     temp.setConfig(dst->config(), dst->width(), dst->height());
188     if (!temp.allocPixels()) {
189         return false;
190     }
191
192     offset->fX = srcBounds.fLeft;
193     offset->fY = srcBounds.fTop;
194     srcBounds.offset(-srcOffset);
195     const SkPMColor* s = src.getAddr32(srcBounds.left(), srcBounds.top());
196     SkPMColor* t = temp.getAddr32(0, 0);
197     SkPMColor* d = dst->getAddr32(0, 0);
198     int w = dstBounds.width(), h = dstBounds.height();
199     int sw = src.rowBytesAsPixels();
200     SkBoxBlurProc boxBlurX, boxBlurY, boxBlurXY, boxBlurYX;
201     if (!SkBoxBlurGetPlatformProcs(&boxBlurX, &boxBlurY, &boxBlurXY, &boxBlurYX)) {
202         boxBlurX = boxBlur<kX, kX>;
203         boxBlurY = boxBlur<kY, kY>;
204         boxBlurXY = boxBlur<kX, kY>;
205         boxBlurYX = boxBlur<kY, kX>;
206     }
207
208     if (kernelSizeX > 0 && kernelSizeY > 0) {
209         boxBlurX(s,  sw, t, kernelSizeX,  lowOffsetX,  highOffsetX, w, h);
210         boxBlurX(t,  w,  d, kernelSizeX,  highOffsetX, lowOffsetX,  w, h);
211         boxBlurXY(d, w,  t, kernelSizeX3, highOffsetX, highOffsetX, w, h);
212         boxBlurX(t,  h,  d, kernelSizeY,  lowOffsetY,  highOffsetY, h, w);
213         boxBlurX(d,  h,  t, kernelSizeY,  highOffsetY, lowOffsetY,  h, w);
214         boxBlurXY(t, h,  d, kernelSizeY3, highOffsetY, highOffsetY, h, w);
215     } else if (kernelSizeX > 0) {
216         boxBlurX(s,  sw, d, kernelSizeX,  lowOffsetX,  highOffsetX, w, h);
217         boxBlurX(d,  w,  t, kernelSizeX,  highOffsetX, lowOffsetX,  w, h);
218         boxBlurX(t,  w,  d, kernelSizeX3, highOffsetX, highOffsetX, w, h);
219     } else if (kernelSizeY > 0) {
220         boxBlurYX(s, sw, d, kernelSizeY,  lowOffsetY,  highOffsetY, h, w);
221         boxBlurX(d,  h,  t, kernelSizeY,  highOffsetY, lowOffsetY,  h, w);
222         boxBlurXY(t, h,  d, kernelSizeY3, highOffsetY, highOffsetY, h, w);
223     }
224     return true;
225 }
226
227 bool SkBlurImageFilter::filterImageGPU(Proxy* proxy, const SkBitmap& src, const SkMatrix& ctm,
228                                        SkBitmap* result, SkIPoint* offset) {
229 #if SK_SUPPORT_GPU
230     SkBitmap input;
231     if (!SkImageFilterUtils::GetInputResultGPU(getInput(0), proxy, src, ctm, &input, offset)) {
232         return false;
233     }
234     GrTexture* source = input.getTexture();
235     SkIRect rect;
236     src.getBounds(&rect);
237     if (!this->applyCropRect(&rect, ctm)) {
238         return false;
239     }
240     SkVector sigma, localSigma = SkVector::Make(fSigma.width(), fSigma.height());
241     ctm.mapVectors(&sigma, &localSigma, 1);
242     SkAutoTUnref<GrTexture> tex(SkGpuBlurUtils::GaussianBlur(source->getContext(),
243                                                              source,
244                                                              false,
245                                                              SkRect::Make(rect),
246                                                              true,
247                                                              sigma.x(),
248                                                              sigma.y()));
249     offset->fX = rect.fLeft;
250     offset->fY = rect.fTop;
251     return SkImageFilterUtils::WrapTexture(tex, rect.width(), rect.height(), result);
252 #else
253     SkDEBUGFAIL("Should not call in GPU-less build");
254     return false;
255 #endif
256 }