OLD | NEW |
| (Empty) |
1 /* | |
2 * Copyright (C) 2004, 2005, 2006, 2007 Nikolas Zimmermann <zimmermann@kde.org> | |
3 * Copyright (C) 2004, 2005 Rob Buis <buis@kde.org> | |
4 * Copyright (C) 2005 Eric Seidel <eric@webkit.org> | |
5 * Copyright (C) 2009 Dirk Schulze <krit@webkit.org> | |
6 * Copyright (C) 2010 Igalia, S.L. | |
7 * Copyright (C) Research In Motion Limited 2010. All rights reserved. | |
8 * Copyright (C) 2013 Google Inc. All rights reserved. | |
9 * | |
10 * This library is free software; you can redistribute it and/or | |
11 * modify it under the terms of the GNU Library General Public | |
12 * License as published by the Free Software Foundation; either | |
13 * version 2 of the License, or (at your option) any later version. | |
14 * | |
15 * This library is distributed in the hope that it will be useful, | |
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
18 * Library General Public License for more details. | |
19 * | |
20 * You should have received a copy of the GNU Library General Public License | |
21 * along with this library; see the file COPYING.LIB. If not, write to | |
22 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, | |
23 * Boston, MA 02110-1301, USA. | |
24 */ | |
25 | |
26 #include "config.h" | |
27 | |
28 #include "core/platform/graphics/filters/FEGaussianBlur.h" | |
29 | |
30 #include "core/platform/graphics/GraphicsContext.h" | |
31 #include "core/platform/graphics/cpu/arm/filters/FEGaussianBlurNEON.h" | |
32 #include "core/platform/graphics/filters/ParallelJobs.h" | |
33 #include "core/platform/graphics/filters/SkiaImageFilterBuilder.h" | |
34 #include "platform/text/TextStream.h" | |
35 #include "wtf/MathExtras.h" | |
36 #include "wtf/Uint8ClampedArray.h" | |
37 | |
38 #include "SkBlurImageFilter.h" | |
39 | |
40 using namespace std; | |
41 | |
42 static inline float gaussianKernelFactor() | |
43 { | |
44 return 3 / 4.f * sqrtf(2 * piFloat); | |
45 } | |
46 | |
47 static const unsigned gMaxKernelSize = 1000; | |
48 | |
49 namespace WebCore { | |
50 | |
51 FEGaussianBlur::FEGaussianBlur(Filter* filter, float x, float y) | |
52 : FilterEffect(filter) | |
53 , m_stdX(x) | |
54 , m_stdY(y) | |
55 { | |
56 } | |
57 | |
58 PassRefPtr<FEGaussianBlur> FEGaussianBlur::create(Filter* filter, float x, float
y) | |
59 { | |
60 return adoptRef(new FEGaussianBlur(filter, x, y)); | |
61 } | |
62 | |
63 float FEGaussianBlur::stdDeviationX() const | |
64 { | |
65 return m_stdX; | |
66 } | |
67 | |
68 void FEGaussianBlur::setStdDeviationX(float x) | |
69 { | |
70 m_stdX = x; | |
71 } | |
72 | |
73 float FEGaussianBlur::stdDeviationY() const | |
74 { | |
75 return m_stdY; | |
76 } | |
77 | |
78 void FEGaussianBlur::setStdDeviationY(float y) | |
79 { | |
80 m_stdY = y; | |
81 } | |
82 | |
83 inline void boxBlur(Uint8ClampedArray* srcPixelArray, Uint8ClampedArray* dstPixe
lArray, | |
84 unsigned dx, int dxLeft, int dxRight, int stride, int stride
Line, int effectWidth, int effectHeight, bool alphaImage) | |
85 { | |
86 for (int y = 0; y < effectHeight; ++y) { | |
87 int line = y * strideLine; | |
88 for (int channel = 3; channel >= 0; --channel) { | |
89 int sum = 0; | |
90 // Fill the kernel | |
91 int maxKernelSize = min(dxRight, effectWidth); | |
92 for (int i = 0; i < maxKernelSize; ++i) | |
93 sum += srcPixelArray->item(line + i * stride + channel); | |
94 | |
95 // Blurring | |
96 for (int x = 0; x < effectWidth; ++x) { | |
97 int pixelByteOffset = line + x * stride + channel; | |
98 dstPixelArray->set(pixelByteOffset, static_cast<unsigned char>(s
um / dx)); | |
99 if (x >= dxLeft) | |
100 sum -= srcPixelArray->item(pixelByteOffset - dxLeft * stride
); | |
101 if (x + dxRight < effectWidth) | |
102 sum += srcPixelArray->item(pixelByteOffset + dxRight * strid
e); | |
103 } | |
104 if (alphaImage) // Source image is black, it just has different alph
a values | |
105 break; | |
106 } | |
107 } | |
108 } | |
109 | |
110 inline void FEGaussianBlur::platformApplyGeneric(Uint8ClampedArray* srcPixelArra
y, Uint8ClampedArray* tmpPixelArray, unsigned kernelSizeX, unsigned kernelSizeY,
IntSize& paintSize) | |
111 { | |
112 int stride = 4 * paintSize.width(); | |
113 int dxLeft = 0; | |
114 int dxRight = 0; | |
115 int dyLeft = 0; | |
116 int dyRight = 0; | |
117 Uint8ClampedArray* src = srcPixelArray; | |
118 Uint8ClampedArray* dst = tmpPixelArray; | |
119 | |
120 for (int i = 0; i < 3; ++i) { | |
121 if (kernelSizeX) { | |
122 kernelPosition(i, kernelSizeX, dxLeft, dxRight); | |
123 #if HAVE(ARM_NEON_INTRINSICS) | |
124 if (!isAlphaImage()) | |
125 boxBlurNEON(src, dst, kernelSizeX, dxLeft, dxRight, 4, stride, p
aintSize.width(), paintSize.height()); | |
126 else | |
127 boxBlur(src, dst, kernelSizeX, dxLeft, dxRight, 4, stride, paint
Size.width(), paintSize.height(), true); | |
128 #else | |
129 boxBlur(src, dst, kernelSizeX, dxLeft, dxRight, 4, stride, paintSize
.width(), paintSize.height(), isAlphaImage()); | |
130 #endif | |
131 swap(src, dst); | |
132 } | |
133 | |
134 if (kernelSizeY) { | |
135 kernelPosition(i, kernelSizeY, dyLeft, dyRight); | |
136 #if HAVE(ARM_NEON_INTRINSICS) | |
137 if (!isAlphaImage()) | |
138 boxBlurNEON(src, dst, kernelSizeY, dyLeft, dyRight, stride, 4, p
aintSize.height(), paintSize.width()); | |
139 else | |
140 boxBlur(src, dst, kernelSizeY, dyLeft, dyRight, stride, 4, paint
Size.height(), paintSize.width(), true); | |
141 #else | |
142 boxBlur(src, dst, kernelSizeY, dyLeft, dyRight, stride, 4, paintSize
.height(), paintSize.width(), isAlphaImage()); | |
143 #endif | |
144 swap(src, dst); | |
145 } | |
146 } | |
147 | |
148 // The final result should be stored in srcPixelArray. | |
149 if (dst == srcPixelArray) { | |
150 ASSERT(src->length() == dst->length()); | |
151 memcpy(dst->data(), src->data(), src->length()); | |
152 } | |
153 | |
154 } | |
155 | |
156 void FEGaussianBlur::platformApplyWorker(PlatformApplyParameters* parameters) | |
157 { | |
158 IntSize paintSize(parameters->width, parameters->height); | |
159 parameters->filter->platformApplyGeneric(parameters->srcPixelArray.get(), pa
rameters->dstPixelArray.get(), | |
160 parameters->kernelSizeX, parameters->kernelSizeY, paintSize); | |
161 } | |
162 | |
163 inline void FEGaussianBlur::platformApply(Uint8ClampedArray* srcPixelArray, Uint
8ClampedArray* tmpPixelArray, unsigned kernelSizeX, unsigned kernelSizeY, IntSiz
e& paintSize) | |
164 { | |
165 int scanline = 4 * paintSize.width(); | |
166 int extraHeight = 3 * kernelSizeY * 0.5f; | |
167 int optimalThreadNumber = (paintSize.width() * paintSize.height()) / (s_mini
malRectDimension + extraHeight * paintSize.width()); | |
168 | |
169 if (optimalThreadNumber > 1) { | |
170 ParallelJobs<PlatformApplyParameters> parallelJobs(&platformApplyWorker,
optimalThreadNumber); | |
171 | |
172 int jobs = parallelJobs.numberOfJobs(); | |
173 if (jobs > 1) { | |
174 // Split the job into "blockHeight"-sized jobs but there a few jobs
that need to be slightly larger since | |
175 // blockHeight * jobs < total size. These extras are handled by the
remainder "jobsWithExtra". | |
176 const int blockHeight = paintSize.height() / jobs; | |
177 const int jobsWithExtra = paintSize.height() % jobs; | |
178 | |
179 int currentY = 0; | |
180 for (int job = 0; job < jobs; job++) { | |
181 PlatformApplyParameters& params = parallelJobs.parameter(job); | |
182 params.filter = this; | |
183 | |
184 int startY = !job ? 0 : currentY - extraHeight; | |
185 currentY += job < jobsWithExtra ? blockHeight + 1 : blockHeight; | |
186 int endY = job == jobs - 1 ? currentY : currentY + extraHeight; | |
187 | |
188 int blockSize = (endY - startY) * scanline; | |
189 if (!job) { | |
190 params.srcPixelArray = srcPixelArray; | |
191 params.dstPixelArray = tmpPixelArray; | |
192 } else { | |
193 params.srcPixelArray = Uint8ClampedArray::createUninitialize
d(blockSize); | |
194 params.dstPixelArray = Uint8ClampedArray::createUninitialize
d(blockSize); | |
195 memcpy(params.srcPixelArray->data(), srcPixelArray->data() +
startY * scanline, blockSize); | |
196 } | |
197 | |
198 params.width = paintSize.width(); | |
199 params.height = endY - startY; | |
200 params.kernelSizeX = kernelSizeX; | |
201 params.kernelSizeY = kernelSizeY; | |
202 } | |
203 | |
204 parallelJobs.execute(); | |
205 | |
206 // Copy together the parts of the image. | |
207 currentY = 0; | |
208 for (int job = 1; job < jobs; job++) { | |
209 PlatformApplyParameters& params = parallelJobs.parameter(job); | |
210 int sourceOffset; | |
211 int destinationOffset; | |
212 int size; | |
213 int adjustedBlockHeight = job < jobsWithExtra ? blockHeight + 1
: blockHeight; | |
214 | |
215 currentY += adjustedBlockHeight; | |
216 sourceOffset = extraHeight * scanline; | |
217 destinationOffset = currentY * scanline; | |
218 size = adjustedBlockHeight * scanline; | |
219 | |
220 memcpy(srcPixelArray->data() + destinationOffset, params.srcPixe
lArray->data() + sourceOffset, size); | |
221 } | |
222 return; | |
223 } | |
224 // Fallback to single threaded mode. | |
225 } | |
226 | |
227 // The selection here eventually should happen dynamically on some platforms
. | |
228 platformApplyGeneric(srcPixelArray, tmpPixelArray, kernelSizeX, kernelSizeY,
paintSize); | |
229 } | |
230 | |
231 void FEGaussianBlur::calculateUnscaledKernelSize(unsigned& kernelSizeX, unsigned
& kernelSizeY, float stdX, float stdY) | |
232 { | |
233 ASSERT(stdX >= 0 && stdY >= 0); | |
234 | |
235 kernelSizeX = 0; | |
236 if (stdX) | |
237 kernelSizeX = max<unsigned>(2, static_cast<unsigned>(floorf(stdX * gauss
ianKernelFactor() + 0.5f))); | |
238 kernelSizeY = 0; | |
239 if (stdY) | |
240 kernelSizeY = max<unsigned>(2, static_cast<unsigned>(floorf(stdY * gauss
ianKernelFactor() + 0.5f))); | |
241 | |
242 // Limit the kernel size to 1000. A bigger radius won't make a big differenc
e for the result image but | |
243 // inflates the absolute paint rect to much. This is compatible with Firefox
' behavior. | |
244 if (kernelSizeX > gMaxKernelSize) | |
245 kernelSizeX = gMaxKernelSize; | |
246 if (kernelSizeY > gMaxKernelSize) | |
247 kernelSizeY = gMaxKernelSize; | |
248 } | |
249 | |
250 void FEGaussianBlur::calculateKernelSize(Filter* filter, unsigned& kernelSizeX,
unsigned& kernelSizeY, float stdX, float stdY) | |
251 { | |
252 stdX = filter->applyHorizontalScale(stdX); | |
253 stdY = filter->applyVerticalScale(stdY); | |
254 | |
255 calculateUnscaledKernelSize(kernelSizeX, kernelSizeY, stdX, stdY); | |
256 } | |
257 | |
258 void FEGaussianBlur::determineAbsolutePaintRect() | |
259 { | |
260 FloatRect absolutePaintRect = mapRect(inputEffect(0)->absolutePaintRect()); | |
261 | |
262 if (clipsToBounds()) | |
263 absolutePaintRect.intersect(maxEffectRect()); | |
264 else | |
265 absolutePaintRect.unite(maxEffectRect()); | |
266 | |
267 setAbsolutePaintRect(enclosingIntRect(absolutePaintRect)); | |
268 } | |
269 | |
270 FloatRect FEGaussianBlur::mapRect(const FloatRect& rect, bool) | |
271 { | |
272 FloatRect result = rect; | |
273 unsigned kernelSizeX = 0; | |
274 unsigned kernelSizeY = 0; | |
275 calculateKernelSize(filter(), kernelSizeX, kernelSizeY, m_stdX, m_stdY); | |
276 | |
277 // We take the half kernel size and multiply it with three, because we run b
ox blur three times. | |
278 result.inflateX(3 * kernelSizeX * 0.5f); | |
279 result.inflateY(3 * kernelSizeY * 0.5f); | |
280 return result; | |
281 } | |
282 | |
283 void FEGaussianBlur::applySoftware() | |
284 { | |
285 FilterEffect* in = inputEffect(0); | |
286 | |
287 Uint8ClampedArray* srcPixelArray = createPremultipliedImageResult(); | |
288 if (!srcPixelArray) | |
289 return; | |
290 | |
291 setIsAlphaImage(in->isAlphaImage()); | |
292 | |
293 IntRect effectDrawingRect = requestedRegionOfInputImageData(in->absolutePain
tRect()); | |
294 in->copyPremultipliedImage(srcPixelArray, effectDrawingRect); | |
295 | |
296 if (!m_stdX && !m_stdY) | |
297 return; | |
298 | |
299 unsigned kernelSizeX = 0; | |
300 unsigned kernelSizeY = 0; | |
301 calculateKernelSize(filter(), kernelSizeX, kernelSizeY, m_stdX, m_stdY); | |
302 | |
303 IntSize paintSize = absolutePaintRect().size(); | |
304 RefPtr<Uint8ClampedArray> tmpImageData = Uint8ClampedArray::createUninitiali
zed(paintSize.width() * paintSize.height() * 4); | |
305 Uint8ClampedArray* tmpPixelArray = tmpImageData.get(); | |
306 | |
307 platformApply(srcPixelArray, tmpPixelArray, kernelSizeX, kernelSizeY, paintS
ize); | |
308 } | |
309 | |
310 bool FEGaussianBlur::applySkia() | |
311 { | |
312 ImageBuffer* resultImage = createImageBufferResult(); | |
313 if (!resultImage) | |
314 return false; | |
315 | |
316 FilterEffect* in = inputEffect(0); | |
317 | |
318 IntRect drawingRegion = drawingRegionOfInputImage(in->absolutePaintRect()); | |
319 | |
320 setIsAlphaImage(in->isAlphaImage()); | |
321 | |
322 float stdX = filter()->applyHorizontalScale(m_stdX); | |
323 float stdY = filter()->applyVerticalScale(m_stdY); | |
324 | |
325 RefPtr<Image> image = in->asImageBuffer()->copyImage(DontCopyBackingStore); | |
326 | |
327 SkPaint paint; | |
328 GraphicsContext* dstContext = resultImage->context(); | |
329 paint.setImageFilter(new SkBlurImageFilter(stdX, stdY))->unref(); | |
330 | |
331 dstContext->saveLayer(0, &paint); | |
332 paint.setColor(0xFFFFFFFF); | |
333 dstContext->drawImage(image.get(), drawingRegion.location(), CompositeCopy); | |
334 dstContext->restoreLayer(); | |
335 return true; | |
336 } | |
337 | |
338 PassRefPtr<SkImageFilter> FEGaussianBlur::createImageFilter(SkiaImageFilterBuild
er* builder) | |
339 { | |
340 RefPtr<SkImageFilter> input(builder->build(inputEffect(0), operatingColorSpa
ce())); | |
341 float stdX = filter()->applyHorizontalScale(m_stdX); | |
342 float stdY = filter()->applyVerticalScale(m_stdY); | |
343 SkImageFilter::CropRect rect = getCropRect(builder->cropOffset()); | |
344 return adoptRef(new SkBlurImageFilter(SkFloatToScalar(stdX), SkFloatToScalar
(stdY), input.get(), &rect)); | |
345 } | |
346 | |
347 TextStream& FEGaussianBlur::externalRepresentation(TextStream& ts, int indent) c
onst | |
348 { | |
349 writeIndent(ts, indent); | |
350 ts << "[feGaussianBlur"; | |
351 FilterEffect::externalRepresentation(ts); | |
352 ts << " stdDeviation=\"" << m_stdX << ", " << m_stdY << "\"]\n"; | |
353 inputEffect(0)->externalRepresentation(ts, indent + 1); | |
354 return ts; | |
355 } | |
356 | |
357 float FEGaussianBlur::calculateStdDeviation(float radius) | |
358 { | |
359 // Blur radius represents 2/3 times the kernel size, the dest pixel is half
of the radius applied 3 times | |
360 return max((radius * 2 / 3.f - 0.5f) / gaussianKernelFactor(), 0.f); | |
361 } | |
362 | |
363 } // namespace WebCore | |
OLD | NEW |