Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(318)

Side by Side Diff: Source/core/platform/graphics/filters/FEGaussianBlur.cpp

Issue 99103006: Moving GraphicsContext and dependencies from core to platform. (Closed) Base URL: svn://svn.chromium.org/blink/trunk
Patch Set: Final patch - fixes Android Created 7 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(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
OLDNEW
« no previous file with comments | « Source/core/platform/graphics/filters/FEGaussianBlur.h ('k') | Source/core/platform/graphics/filters/FELighting.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698