blob: 4d23658fb6eda5d70f0bb080c560e5a424b62b88 [file] [log] [blame]
senorblanco@chromium.org60014ca2011-11-09 16:05:58 +00001/*
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
djsollen@google.com64a0ec32012-06-12 15:17:27 +00008#include "SkBitmap.h"
senorblanco@chromium.org60014ca2011-11-09 16:05:58 +00009#include "SkBlurImageFilter.h"
senorblanco@chromium.orgae814c72011-12-20 20:02:19 +000010#include "SkColorPriv.h"
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +000011#include "SkReadBuffer.h"
12#include "SkWriteBuffer.h"
robertphillips@google.com736dd032013-07-15 15:06:54 +000013#include "SkGpuBlurUtils.h"
senorblanco@chromium.org27eec462013-11-08 20:49:04 +000014#include "SkBlurImage_opts.h"
bsalomon@google.comcf8fb1f2012-08-02 14:03:32 +000015#if SK_SUPPORT_GPU
senorblanco@chromium.org302cffb2012-08-01 20:16:34 +000016#include "GrContext.h"
senorblanco@chromium.orgc2594f42013-01-30 19:08:47 +000017#include "SkImageFilterUtils.h"
bsalomon@google.comcf8fb1f2012-08-02 14:03:32 +000018#endif
senorblanco@chromium.org60014ca2011-11-09 16:05:58 +000019
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +000020SkBlurImageFilter::SkBlurImageFilter(SkReadBuffer& buffer)
commit-bot@chromium.orgce33d602013-11-25 21:46:31 +000021 : INHERITED(1, buffer) {
senorblanco@chromium.org54e01b22011-11-16 18:20:47 +000022 fSigma.fWidth = buffer.readScalar();
23 fSigma.fHeight = buffer.readScalar();
commit-bot@chromium.orgc0b7e102013-10-23 17:06:21 +000024 buffer.validate(SkScalarIsFinite(fSigma.fWidth) &&
25 SkScalarIsFinite(fSigma.fHeight) &&
26 (fSigma.fWidth >= 0) &&
27 (fSigma.fHeight >= 0));
senorblanco@chromium.org54e01b22011-11-16 18:20:47 +000028}
29
senorblanco@chromium.org194d7752013-07-24 22:19:24 +000030SkBlurImageFilter::SkBlurImageFilter(SkScalar sigmaX,
31 SkScalar sigmaY,
32 SkImageFilter* input,
senorblanco@chromium.orgb295fb62013-10-10 13:51:19 +000033 const CropRect* cropRect)
senorblanco@chromium.org194d7752013-07-24 22:19:24 +000034 : INHERITED(input, cropRect), fSigma(SkSize::Make(sigmaX, sigmaY)) {
senorblanco@chromium.org60014ca2011-11-09 16:05:58 +000035 SkASSERT(sigmaX >= 0 && sigmaY >= 0);
36}
37
commit-bot@chromium.org8b0e8ac2014-01-30 18:58:24 +000038void SkBlurImageFilter::flatten(SkWriteBuffer& buffer) const {
senorblanco@chromium.org54e01b22011-11-16 18:20:47 +000039 this->INHERITED::flatten(buffer);
40 buffer.writeScalar(fSigma.fWidth);
41 buffer.writeScalar(fSigma.fHeight);
42}
43
senorblanco@chromium.org0cc00c22013-11-07 18:35:12 +000044enum BlurDirection {
45 kX, kY
46};
47
48/**
49 *
50 * In order to make memory accesses cache-friendly, we reorder the passes to
51 * use contiguous memory reads wherever possible.
52 *
53 * For example, the 6 passes of the X-and-Y blur case are rewritten as
54 * follows. Instead of 3 passes in X and 3 passes in Y, we perform
55 * 2 passes in X, 1 pass in X transposed to Y on write, 2 passes in X,
56 * then 1 pass in X transposed to Y on write.
57 *
58 * +----+ +----+ +----+ +---+ +---+ +---+ +----+
59 * + AB + ----> | AB | ----> | AB | -----> | A | ----> | A | ----> | A | -----> | AB |
60 * +----+ blurX +----+ blurX +----+ blurXY | B | blurX | B | blurX | B | blurXY +----+
61 * +---+ +---+ +---+
62 *
63 * In this way, two of the y-blurs become x-blurs applied to transposed
64 * images, and all memory reads are contiguous.
65 */
66
67template<BlurDirection srcDirection, BlurDirection dstDirection>
68static void boxBlur(const SkPMColor* src, int srcStride, SkPMColor* dst, int kernelSize,
69 int leftOffset, int rightOffset, int width, int height)
senorblanco@chromium.orgae814c72011-12-20 20:02:19 +000070{
senorblanco@chromium.orgae814c72011-12-20 20:02:19 +000071 int rightBorder = SkMin32(rightOffset + 1, width);
senorblanco@chromium.org0cc00c22013-11-07 18:35:12 +000072 int srcStrideX = srcDirection == kX ? 1 : srcStride;
73 int dstStrideX = dstDirection == kX ? 1 : height;
74 int srcStrideY = srcDirection == kX ? srcStride : 1;
75 int dstStrideY = dstDirection == kX ? width : 1;
senorblanco@chromium.orgfe2faa82013-11-04 16:07:33 +000076 uint32_t scale = (1 << 24) / kernelSize;
77 uint32_t half = 1 << 23;
senorblanco@chromium.orgae814c72011-12-20 20:02:19 +000078 for (int y = 0; y < height; ++y) {
79 int sumA = 0, sumR = 0, sumG = 0, sumB = 0;
senorblanco@chromium.org0cc00c22013-11-07 18:35:12 +000080 const SkPMColor* p = src;
senorblanco@chromium.orgae814c72011-12-20 20:02:19 +000081 for (int i = 0; i < rightBorder; ++i) {
82 sumA += SkGetPackedA32(*p);
83 sumR += SkGetPackedR32(*p);
84 sumG += SkGetPackedG32(*p);
85 sumB += SkGetPackedB32(*p);
senorblanco@chromium.org0cc00c22013-11-07 18:35:12 +000086 p += srcStrideX;
senorblanco@chromium.orgae814c72011-12-20 20:02:19 +000087 }
88
senorblanco@chromium.org0cc00c22013-11-07 18:35:12 +000089 const SkPMColor* sptr = src;
90 SkColor* dptr = dst;
senorblanco@chromium.orgae814c72011-12-20 20:02:19 +000091 for (int x = 0; x < width; ++x) {
senorblanco@chromium.orgfe2faa82013-11-04 16:07:33 +000092 *dptr = SkPackARGB32((sumA * scale + half) >> 24,
93 (sumR * scale + half) >> 24,
94 (sumG * scale + half) >> 24,
95 (sumB * scale + half) >> 24);
senorblanco@chromium.orgae814c72011-12-20 20:02:19 +000096 if (x >= leftOffset) {
senorblanco@chromium.org0cc00c22013-11-07 18:35:12 +000097 SkColor l = *(sptr - leftOffset * srcStrideX);
senorblanco@chromium.orgae814c72011-12-20 20:02:19 +000098 sumA -= SkGetPackedA32(l);
99 sumR -= SkGetPackedR32(l);
100 sumG -= SkGetPackedG32(l);
101 sumB -= SkGetPackedB32(l);
102 }
103 if (x + rightOffset + 1 < width) {
senorblanco@chromium.org0cc00c22013-11-07 18:35:12 +0000104 SkColor r = *(sptr + (rightOffset + 1) * srcStrideX);
senorblanco@chromium.orgae814c72011-12-20 20:02:19 +0000105 sumA += SkGetPackedA32(r);
106 sumR += SkGetPackedR32(r);
107 sumG += SkGetPackedG32(r);
108 sumB += SkGetPackedB32(r);
109 }
senorblanco@chromium.org0cc00c22013-11-07 18:35:12 +0000110 sptr += srcStrideX;
111 if (srcDirection == kY) {
112 SK_PREFETCH(sptr + (rightOffset + 1) * srcStrideX);
113 }
114 dptr += dstStrideX;
senorblanco@chromium.orgae814c72011-12-20 20:02:19 +0000115 }
senorblanco@chromium.org0cc00c22013-11-07 18:35:12 +0000116 src += srcStrideY;
117 dst += dstStrideY;
senorblanco@chromium.orgae814c72011-12-20 20:02:19 +0000118 }
119}
120
commit-bot@chromium.org7b320702013-07-10 21:22:18 +0000121static void getBox3Params(SkScalar s, int *kernelSize, int* kernelSize3, int *lowOffset,
122 int *highOffset)
senorblanco@chromium.orgae814c72011-12-20 20:02:19 +0000123{
schenney@chromium.org73f3ded2011-12-20 22:31:40 +0000124 float pi = SkScalarToFloat(SK_ScalarPI);
125 int d = static_cast<int>(floorf(SkScalarToFloat(s) * 3.0f * sqrtf(2.0f * pi) / 4.0f + 0.5f));
senorblanco@chromium.orgae814c72011-12-20 20:02:19 +0000126 *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
senorblanco@chromium.orgf1369ce2012-08-20 14:53:21 +0000137bool SkBlurImageFilter::onFilterImage(Proxy* proxy,
138 const SkBitmap& source, const SkMatrix& ctm,
commit-bot@chromium.orgae761f72014-02-05 22:32:02 +0000139 SkBitmap* dst, SkIPoint* offset) const {
senorblanco@chromium.org68400762013-05-24 15:04:07 +0000140 SkBitmap src = source;
senorblanco@chromium.org6776b822014-01-03 21:48:22 +0000141 SkIPoint srcOffset = SkIPoint::Make(0, 0);
142 if (getInput(0) && !getInput(0)->filterImage(proxy, source, ctm, &src, &srcOffset)) {
senorblanco@chromium.org68400762013-05-24 15:04:07 +0000143 return false;
144 }
145
commit-bot@chromium.orge24ad232014-02-16 22:03:38 +0000146 if (src.colorType() != kPMColor_SkColorType) {
senorblanco@chromium.orgae814c72011-12-20 20:02:19 +0000147 return false;
148 }
149
reed@google.com76dd2772012-01-05 21:15:07 +0000150 SkAutoLockPixels alp(src);
151 if (!src.getPixels()) {
152 return false;
153 }
154
senorblanco@chromium.org194d7752013-07-24 22:19:24 +0000155 SkIRect srcBounds, dstBounds;
156 src.getBounds(&srcBounds);
senorblanco@chromium.org6776b822014-01-03 21:48:22 +0000157 srcBounds.offset(srcOffset);
senorblanco@chromium.orgfbaea532013-08-27 21:37:01 +0000158 if (!this->applyCropRect(&srcBounds, ctm)) {
senorblanco@chromium.org194d7752013-07-24 22:19:24 +0000159 return false;
160 }
161
162 dst->setConfig(src.config(), srcBounds.width(), srcBounds.height());
163 dst->getBounds(&dstBounds);
reed@google.com9ebcac52014-01-24 18:53:42 +0000164 if (!dst->allocPixels()) {
commit-bot@chromium.orgcd3b15c2013-12-04 17:06:49 +0000165 return false;
166 }
167
senorblanco@chromium.org2bfe36b2014-01-20 19:58:28 +0000168 SkVector sigma, localSigma = SkVector::Make(fSigma.width(), fSigma.height());
169 ctm.mapVectors(&sigma, &localSigma, 1);
170
senorblanco@chromium.orgae814c72011-12-20 20:02:19 +0000171 int kernelSizeX, kernelSizeX3, lowOffsetX, highOffsetX;
172 int kernelSizeY, kernelSizeY3, lowOffsetY, highOffsetY;
senorblanco@chromium.org2bfe36b2014-01-20 19:58:28 +0000173 getBox3Params(sigma.x(), &kernelSizeX, &kernelSizeX3, &lowOffsetX, &highOffsetX);
174 getBox3Params(sigma.y(), &kernelSizeY, &kernelSizeY3, &lowOffsetY, &highOffsetY);
senorblanco@chromium.orgae814c72011-12-20 20:02:19 +0000175
176 if (kernelSizeX < 0 || kernelSizeY < 0) {
177 return false;
178 }
179
180 if (kernelSizeX == 0 && kernelSizeY == 0) {
commit-bot@chromium.org8a2ad3c2014-02-23 03:59:35 +0000181 src.copyTo(dst, dst->colorType());
senorblanco@chromium.org6776b822014-01-03 21:48:22 +0000182 offset->fX = srcBounds.fLeft;
183 offset->fY = srcBounds.fTop;
senorblanco@chromium.orgae814c72011-12-20 20:02:19 +0000184 return true;
185 }
186
187 SkBitmap temp;
188 temp.setConfig(dst->config(), dst->width(), dst->height());
189 if (!temp.allocPixels()) {
190 return false;
191 }
192
senorblanco@chromium.org6776b822014-01-03 21:48:22 +0000193 offset->fX = srcBounds.fLeft;
194 offset->fY = srcBounds.fTop;
195 srcBounds.offset(-srcOffset);
senorblanco@chromium.org0cc00c22013-11-07 18:35:12 +0000196 const SkPMColor* s = src.getAddr32(srcBounds.left(), srcBounds.top());
197 SkPMColor* t = temp.getAddr32(0, 0);
198 SkPMColor* d = dst->getAddr32(0, 0);
199 int w = dstBounds.width(), h = dstBounds.height();
200 int sw = src.rowBytesAsPixels();
senorblanco@chromium.org05edd022013-11-11 20:12:34 +0000201 SkBoxBlurProc boxBlurX, boxBlurY, boxBlurXY, boxBlurYX;
202 if (!SkBoxBlurGetPlatformProcs(&boxBlurX, &boxBlurY, &boxBlurXY, &boxBlurYX)) {
senorblanco@chromium.org27eec462013-11-08 20:49:04 +0000203 boxBlurX = boxBlur<kX, kX>;
204 boxBlurY = boxBlur<kY, kY>;
205 boxBlurXY = boxBlur<kX, kY>;
senorblanco@chromium.org05edd022013-11-11 20:12:34 +0000206 boxBlurYX = boxBlur<kY, kX>;
senorblanco@chromium.org27eec462013-11-08 20:49:04 +0000207 }
208
senorblanco@chromium.orgae814c72011-12-20 20:02:19 +0000209 if (kernelSizeX > 0 && kernelSizeY > 0) {
senorblanco@chromium.org0cc00c22013-11-07 18:35:12 +0000210 boxBlurX(s, sw, t, kernelSizeX, lowOffsetX, highOffsetX, w, h);
211 boxBlurX(t, w, d, kernelSizeX, highOffsetX, lowOffsetX, w, h);
212 boxBlurXY(d, w, t, kernelSizeX3, highOffsetX, highOffsetX, w, h);
213 boxBlurX(t, h, d, kernelSizeY, lowOffsetY, highOffsetY, h, w);
214 boxBlurX(d, h, t, kernelSizeY, highOffsetY, lowOffsetY, h, w);
215 boxBlurXY(t, h, d, kernelSizeY3, highOffsetY, highOffsetY, h, w);
senorblanco@chromium.orgae814c72011-12-20 20:02:19 +0000216 } else if (kernelSizeX > 0) {
senorblanco@chromium.org0cc00c22013-11-07 18:35:12 +0000217 boxBlurX(s, sw, d, kernelSizeX, lowOffsetX, highOffsetX, w, h);
218 boxBlurX(d, w, t, kernelSizeX, highOffsetX, lowOffsetX, w, h);
219 boxBlurX(t, w, d, kernelSizeX3, highOffsetX, highOffsetX, w, h);
senorblanco@chromium.orgae814c72011-12-20 20:02:19 +0000220 } else if (kernelSizeY > 0) {
senorblanco@chromium.org05edd022013-11-11 20:12:34 +0000221 boxBlurYX(s, sw, d, kernelSizeY, lowOffsetY, highOffsetY, h, w);
222 boxBlurX(d, h, t, kernelSizeY, highOffsetY, lowOffsetY, h, w);
223 boxBlurXY(t, h, d, kernelSizeY3, highOffsetY, highOffsetY, h, w);
senorblanco@chromium.orgae814c72011-12-20 20:02:19 +0000224 }
225 return true;
226}
227
senorblanco@chromium.org336d1d72014-01-27 21:03:17 +0000228
229void SkBlurImageFilter::computeFastBounds(const SkRect& src, SkRect* dst) const {
230 if (getInput(0)) {
231 getInput(0)->computeFastBounds(src, dst);
232 } else {
233 *dst = src;
234 }
235
236 dst->outset(SkScalarMul(fSigma.width(), SkIntToScalar(3)),
237 SkScalarMul(fSigma.height(), SkIntToScalar(3)));
238}
senorblanco@chromium.orgc4b12f12014-02-05 17:51:22 +0000239
240bool SkBlurImageFilter::onFilterBounds(const SkIRect& src, const SkMatrix& ctm,
241 SkIRect* dst) const {
242 SkIRect bounds = src;
243 if (getInput(0) && !getInput(0)->filterBounds(src, ctm, &bounds)) {
244 return false;
245 }
246 SkVector sigma, localSigma = SkVector::Make(fSigma.width(), fSigma.height());
247 ctm.mapVectors(&sigma, &localSigma, 1);
248 bounds.outset(SkScalarCeilToInt(SkScalarMul(sigma.x(), SkIntToScalar(3))),
249 SkScalarCeilToInt(SkScalarMul(sigma.y(), SkIntToScalar(3))));
250 *dst = bounds;
251 return true;
252}
253
commit-bot@chromium.org1aa54bf2013-08-05 16:53:50 +0000254bool SkBlurImageFilter::filterImageGPU(Proxy* proxy, const SkBitmap& src, const SkMatrix& ctm,
commit-bot@chromium.orgae761f72014-02-05 22:32:02 +0000255 SkBitmap* result, SkIPoint* offset) const {
bsalomon@google.comcf8fb1f2012-08-02 14:03:32 +0000256#if SK_SUPPORT_GPU
senorblanco@chromium.orgc2594f42013-01-30 19:08:47 +0000257 SkBitmap input;
senorblanco@chromium.orgaba651c2014-02-03 22:22:16 +0000258 SkIPoint srcOffset = SkIPoint::Make(0, 0);
259 if (!SkImageFilterUtils::GetInputResultGPU(getInput(0), proxy, src, ctm, &input, &srcOffset)) {
senorblanco@chromium.orgc2594f42013-01-30 19:08:47 +0000260 return false;
261 }
commit-bot@chromium.orgb8d00db2013-06-26 19:18:23 +0000262 GrTexture* source = input.getTexture();
senorblanco@chromium.org194d7752013-07-24 22:19:24 +0000263 SkIRect rect;
senorblanco@chromium.orgc2594f42013-01-30 19:08:47 +0000264 src.getBounds(&rect);
senorblanco@chromium.orgaba651c2014-02-03 22:22:16 +0000265 rect.offset(srcOffset);
senorblanco@chromium.orgfbaea532013-08-27 21:37:01 +0000266 if (!this->applyCropRect(&rect, ctm)) {
senorblanco@chromium.org194d7752013-07-24 22:19:24 +0000267 return false;
268 }
senorblanco@chromium.org2bfe36b2014-01-20 19:58:28 +0000269 SkVector sigma, localSigma = SkVector::Make(fSigma.width(), fSigma.height());
270 ctm.mapVectors(&sigma, &localSigma, 1);
senorblanco@chromium.orgaba651c2014-02-03 22:22:16 +0000271 offset->fX = rect.fLeft;
272 offset->fY = rect.fTop;
273 rect.offset(-srcOffset);
skia.committer@gmail.com6ae63832013-07-23 07:01:05 +0000274 SkAutoTUnref<GrTexture> tex(SkGpuBlurUtils::GaussianBlur(source->getContext(),
senorblanco@chromium.org194d7752013-07-24 22:19:24 +0000275 source,
276 false,
277 SkRect::Make(rect),
278 true,
senorblanco@chromium.org2bfe36b2014-01-20 19:58:28 +0000279 sigma.x(),
280 sigma.y()));
senorblanco@chromium.org194d7752013-07-24 22:19:24 +0000281 return SkImageFilterUtils::WrapTexture(tex, rect.width(), rect.height(), result);
bsalomon@google.comcf8fb1f2012-08-02 14:03:32 +0000282#else
283 SkDEBUGFAIL("Should not call in GPU-less build");
senorblanco@chromium.orgc2594f42013-01-30 19:08:47 +0000284 return false;
bsalomon@google.comcf8fb1f2012-08-02 14:03:32 +0000285#endif
senorblanco@chromium.org302cffb2012-08-01 20:16:34 +0000286}