blob: abbf9719dbe287b1e611198cc4747de20301590b [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"
djsollen@google.comc73dd5c2012-08-07 15:54:32 +000011#include "SkFlattenableBuffers.h"
robertphillips@google.com736dd032013-07-15 15:06:54 +000012#include "SkGpuBlurUtils.h"
bsalomon@google.comcf8fb1f2012-08-02 14:03:32 +000013#if SK_SUPPORT_GPU
senorblanco@chromium.org302cffb2012-08-01 20:16:34 +000014#include "GrContext.h"
senorblanco@chromium.orgc2594f42013-01-30 19:08:47 +000015#include "SkImageFilterUtils.h"
bsalomon@google.comcf8fb1f2012-08-02 14:03:32 +000016#endif
senorblanco@chromium.org60014ca2011-11-09 16:05:58 +000017
senorblanco@chromium.org54e01b22011-11-16 18:20:47 +000018SkBlurImageFilter::SkBlurImageFilter(SkFlattenableReadBuffer& buffer)
19 : INHERITED(buffer) {
20 fSigma.fWidth = buffer.readScalar();
21 fSigma.fHeight = buffer.readScalar();
22}
23
senorblanco@chromium.org194d7752013-07-24 22:19:24 +000024SkBlurImageFilter::SkBlurImageFilter(SkScalar sigmaX,
25 SkScalar sigmaY,
26 SkImageFilter* input,
senorblanco@chromium.orgb295fb62013-10-10 13:51:19 +000027 const CropRect* cropRect)
senorblanco@chromium.org194d7752013-07-24 22:19:24 +000028 : INHERITED(input, cropRect), fSigma(SkSize::Make(sigmaX, sigmaY)) {
senorblanco@chromium.org60014ca2011-11-09 16:05:58 +000029 SkASSERT(sigmaX >= 0 && sigmaY >= 0);
30}
31
djsollen@google.com54924242012-03-29 15:18:04 +000032void SkBlurImageFilter::flatten(SkFlattenableWriteBuffer& buffer) const {
senorblanco@chromium.org54e01b22011-11-16 18:20:47 +000033 this->INHERITED::flatten(buffer);
34 buffer.writeScalar(fSigma.fWidth);
35 buffer.writeScalar(fSigma.fHeight);
36}
37
senorblanco@chromium.orgae814c72011-12-20 20:02:19 +000038static void boxBlurX(const SkBitmap& src, SkBitmap* dst, int kernelSize,
senorblanco@chromium.org194d7752013-07-24 22:19:24 +000039 int leftOffset, int rightOffset, const SkIRect& bounds)
senorblanco@chromium.orgae814c72011-12-20 20:02:19 +000040{
senorblanco@chromium.org194d7752013-07-24 22:19:24 +000041 int width = bounds.width(), height = bounds.height();
senorblanco@chromium.orgae814c72011-12-20 20:02:19 +000042 int rightBorder = SkMin32(rightOffset + 1, width);
43 for (int y = 0; y < height; ++y) {
44 int sumA = 0, sumR = 0, sumG = 0, sumB = 0;
senorblanco@chromium.org194d7752013-07-24 22:19:24 +000045 SkPMColor* p = src.getAddr32(bounds.fLeft, y + bounds.fTop);
senorblanco@chromium.orgae814c72011-12-20 20:02:19 +000046 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
senorblanco@chromium.org194d7752013-07-24 22:19:24 +000054 const SkColor* sptr = src.getAddr32(bounds.fLeft, bounds.fTop + y);
senorblanco@chromium.orgae814c72011-12-20 20:02:19 +000055 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,
senorblanco@chromium.org194d7752013-07-24 22:19:24 +000082 int topOffset, int bottomOffset, const SkIRect& bounds)
senorblanco@chromium.orgae814c72011-12-20 20:02:19 +000083{
senorblanco@chromium.org194d7752013-07-24 22:19:24 +000084 int width = bounds.width(), height = bounds.height();
senorblanco@chromium.orgae814c72011-12-20 20:02:19 +000085 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;
senorblanco@chromium.org194d7752013-07-24 22:19:24 +000090 SkColor* p = src.getAddr32(bounds.fLeft + x, bounds.fTop);
senorblanco@chromium.orgae814c72011-12-20 20:02:19 +000091 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
senorblanco@chromium.org194d7752013-07-24 22:19:24 +000099 const SkColor* sptr = src.getAddr32(bounds.fLeft + x, bounds.fTop);
senorblanco@chromium.orgae814c72011-12-20 20:02:19 +0000100 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
commit-bot@chromium.org7b320702013-07-10 21:22:18 +0000126static void getBox3Params(SkScalar s, int *kernelSize, int* kernelSize3, int *lowOffset,
127 int *highOffset)
senorblanco@chromium.orgae814c72011-12-20 20:02:19 +0000128{
schenney@chromium.org73f3ded2011-12-20 22:31:40 +0000129 float pi = SkScalarToFloat(SK_ScalarPI);
130 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 +0000131 *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
senorblanco@chromium.orgf1369ce2012-08-20 14:53:21 +0000142bool SkBlurImageFilter::onFilterImage(Proxy* proxy,
143 const SkBitmap& source, const SkMatrix& ctm,
144 SkBitmap* dst, SkIPoint* offset) {
senorblanco@chromium.org68400762013-05-24 15:04:07 +0000145 SkBitmap src = source;
146 if (getInput(0) && !getInput(0)->filterImage(proxy, source, ctm, &src, offset)) {
147 return false;
148 }
149
senorblanco@chromium.orgae814c72011-12-20 20:02:19 +0000150 if (src.config() != SkBitmap::kARGB_8888_Config) {
151 return false;
152 }
153
reed@google.com76dd2772012-01-05 21:15:07 +0000154 SkAutoLockPixels alp(src);
155 if (!src.getPixels()) {
156 return false;
157 }
158
senorblanco@chromium.org194d7752013-07-24 22:19:24 +0000159 SkIRect srcBounds, dstBounds;
160 src.getBounds(&srcBounds);
senorblanco@chromium.orgfbaea532013-08-27 21:37:01 +0000161 if (!this->applyCropRect(&srcBounds, ctm)) {
senorblanco@chromium.org194d7752013-07-24 22:19:24 +0000162 return false;
163 }
164
165 dst->setConfig(src.config(), srcBounds.width(), srcBounds.height());
166 dst->getBounds(&dstBounds);
senorblanco@chromium.orgae814c72011-12-20 20:02:19 +0000167 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) {
senorblanco@chromium.org194d7752013-07-24 22:19:24 +0000189 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);
senorblanco@chromium.orgae814c72011-12-20 20:02:19 +0000195 } else if (kernelSizeX > 0) {
senorblanco@chromium.org194d7752013-07-24 22:19:24 +0000196 boxBlurX(src, dst, kernelSizeX, lowOffsetX, highOffsetX, srcBounds);
197 boxBlurX(*dst, &temp, kernelSizeX, highOffsetX, lowOffsetX, dstBounds);
198 boxBlurX(temp, dst, kernelSizeX3, highOffsetX, highOffsetX, dstBounds);
senorblanco@chromium.orgae814c72011-12-20 20:02:19 +0000199 } else if (kernelSizeY > 0) {
senorblanco@chromium.org194d7752013-07-24 22:19:24 +0000200 boxBlurY(src, dst, kernelSizeY, lowOffsetY, highOffsetY, srcBounds);
201 boxBlurY(*dst, &temp, kernelSizeY, highOffsetY, lowOffsetY, dstBounds);
202 boxBlurY(temp, dst, kernelSizeY3, highOffsetY, highOffsetY, dstBounds);
senorblanco@chromium.orgae814c72011-12-20 20:02:19 +0000203 }
senorblanco@chromium.org194d7752013-07-24 22:19:24 +0000204 offset->fX += srcBounds.fLeft;
205 offset->fY += srcBounds.fTop;
senorblanco@chromium.orgae814c72011-12-20 20:02:19 +0000206 return true;
207}
208
commit-bot@chromium.org1aa54bf2013-08-05 16:53:50 +0000209bool SkBlurImageFilter::filterImageGPU(Proxy* proxy, const SkBitmap& src, const SkMatrix& ctm,
210 SkBitmap* result, SkIPoint* offset) {
bsalomon@google.comcf8fb1f2012-08-02 14:03:32 +0000211#if SK_SUPPORT_GPU
senorblanco@chromium.orgc2594f42013-01-30 19:08:47 +0000212 SkBitmap input;
commit-bot@chromium.org1aa54bf2013-08-05 16:53:50 +0000213 if (!SkImageFilterUtils::GetInputResultGPU(getInput(0), proxy, src, ctm, &input, offset)) {
senorblanco@chromium.orgc2594f42013-01-30 19:08:47 +0000214 return false;
215 }
commit-bot@chromium.orgb8d00db2013-06-26 19:18:23 +0000216 GrTexture* source = input.getTexture();
senorblanco@chromium.org194d7752013-07-24 22:19:24 +0000217 SkIRect rect;
senorblanco@chromium.orgc2594f42013-01-30 19:08:47 +0000218 src.getBounds(&rect);
senorblanco@chromium.orgfbaea532013-08-27 21:37:01 +0000219 if (!this->applyCropRect(&rect, ctm)) {
senorblanco@chromium.org194d7752013-07-24 22:19:24 +0000220 return false;
221 }
skia.committer@gmail.com6ae63832013-07-23 07:01:05 +0000222 SkAutoTUnref<GrTexture> tex(SkGpuBlurUtils::GaussianBlur(source->getContext(),
senorblanco@chromium.org194d7752013-07-24 22:19:24 +0000223 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);
bsalomon@google.comcf8fb1f2012-08-02 14:03:32 +0000232#else
233 SkDEBUGFAIL("Should not call in GPU-less build");
senorblanco@chromium.orgc2594f42013-01-30 19:08:47 +0000234 return false;
bsalomon@google.comcf8fb1f2012-08-02 14:03:32 +0000235#endif
senorblanco@chromium.org302cffb2012-08-01 20:16:34 +0000236}