• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Library General Public
11  * License as published by the Free Software Foundation; either
12  * version 2 of the License, or (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Library General Public License for more details.
18  *
19  * You should have received a copy of the GNU Library General Public License
20  * along with this library; see the file COPYING.LIB.  If not, write to
21  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
22  * Boston, MA 02110-1301, USA.
23  */
24 
25 #include "config.h"
26 
27 #if ENABLE(FILTERS)
28 #include "FEGaussianBlur.h"
29 
30 #include "Filter.h"
31 #include "GraphicsContext.h"
32 #include "RenderTreeAsText.h"
33 #include "TextStream.h"
34 
35 #include <wtf/ByteArray.h>
36 #include <wtf/MathExtras.h>
37 
38 using std::max;
39 
40 static const float gGaussianKernelFactor = 3 / 4.f * sqrtf(2 * piFloat);
41 static const unsigned gMaxKernelSize = 1000;
42 
43 namespace WebCore {
44 
FEGaussianBlur(Filter * filter,float x,float y)45 FEGaussianBlur::FEGaussianBlur(Filter* filter, float x, float y)
46     : FilterEffect(filter)
47     , m_stdX(x)
48     , m_stdY(y)
49 {
50 }
51 
create(Filter * filter,float x,float y)52 PassRefPtr<FEGaussianBlur> FEGaussianBlur::create(Filter* filter, float x, float y)
53 {
54     return adoptRef(new FEGaussianBlur(filter, x, y));
55 }
56 
stdDeviationX() const57 float FEGaussianBlur::stdDeviationX() const
58 {
59     return m_stdX;
60 }
61 
setStdDeviationX(float x)62 void FEGaussianBlur::setStdDeviationX(float x)
63 {
64     m_stdX = x;
65 }
66 
stdDeviationY() const67 float FEGaussianBlur::stdDeviationY() const
68 {
69     return m_stdY;
70 }
71 
setStdDeviationY(float y)72 void FEGaussianBlur::setStdDeviationY(float y)
73 {
74     m_stdY = y;
75 }
76 
boxBlur(ByteArray * srcPixelArray,ByteArray * dstPixelArray,unsigned dx,int dxLeft,int dxRight,int stride,int strideLine,int effectWidth,int effectHeight,bool alphaImage)77 inline void boxBlur(ByteArray* srcPixelArray, ByteArray* dstPixelArray,
78                     unsigned dx, int dxLeft, int dxRight, int stride, int strideLine, int effectWidth, int effectHeight, bool alphaImage)
79 {
80     for (int y = 0; y < effectHeight; ++y) {
81         int line = y * strideLine;
82         for (int channel = 3; channel >= 0; --channel) {
83             int sum = 0;
84             // Fill the kernel
85             int maxKernelSize = std::min(dxRight, effectWidth);
86             for (int i = 0; i < maxKernelSize; ++i)
87                 sum += srcPixelArray->get(line + i * stride + channel);
88 
89             // Blurring
90             for (int x = 0; x < effectWidth; ++x) {
91                 int pixelByteOffset = line + x * stride + channel;
92                 dstPixelArray->set(pixelByteOffset, static_cast<unsigned char>(sum / dx));
93                 if (x >= dxLeft)
94                     sum -= srcPixelArray->get(pixelByteOffset - dxLeft * stride);
95                 if (x + dxRight < effectWidth)
96                     sum += srcPixelArray->get(pixelByteOffset + dxRight * stride);
97             }
98             if (alphaImage) // Source image is black, it just has different alpha values
99                 break;
100         }
101     }
102 }
103 
kernelPosition(int boxBlur,unsigned & std,int & dLeft,int & dRight)104 inline void kernelPosition(int boxBlur, unsigned& std, int& dLeft, int& dRight)
105 {
106     // check http://www.w3.org/TR/SVG/filters.html#feGaussianBlurElement for details
107     switch (boxBlur) {
108     case 0:
109         if (!(std % 2)) {
110             dLeft = std / 2 - 1;
111             dRight = std - dLeft;
112         } else {
113             dLeft = std / 2;
114             dRight = std - dLeft;
115         }
116         break;
117     case 1:
118         if (!(std % 2)) {
119             dLeft++;
120             dRight--;
121         }
122         break;
123     case 2:
124         if (!(std % 2)) {
125             dRight++;
126             std++;
127         }
128         break;
129     }
130 }
131 
calculateKernelSize(Filter * filter,unsigned & kernelSizeX,unsigned & kernelSizeY,float stdX,float stdY)132 inline void calculateKernelSize(Filter* filter, unsigned& kernelSizeX, unsigned& kernelSizeY, float stdX, float stdY)
133 {
134     stdX = filter->applyHorizontalScale(stdX);
135     stdY = filter->applyVerticalScale(stdY);
136 
137     kernelSizeX = 0;
138     if (stdX)
139         kernelSizeX = max<unsigned>(2, static_cast<unsigned>(floorf(stdX * gGaussianKernelFactor + 0.5f)));
140     kernelSizeY = 0;
141     if (stdY)
142         kernelSizeY = max<unsigned>(2, static_cast<unsigned>(floorf(stdY * gGaussianKernelFactor + 0.5f)));
143 
144     // Limit the kernel size to 1000. A bigger radius won't make a big difference for the result image but
145     // inflates the absolute paint rect to much. This is compatible with Firefox' behavior.
146     if (kernelSizeX > gMaxKernelSize)
147         kernelSizeX = gMaxKernelSize;
148     if (kernelSizeY > gMaxKernelSize)
149         kernelSizeY = gMaxKernelSize;
150 }
151 
determineAbsolutePaintRect()152 void FEGaussianBlur::determineAbsolutePaintRect()
153 {
154     FloatRect absolutePaintRect = inputEffect(0)->absolutePaintRect();
155     absolutePaintRect.intersect(maxEffectRect());
156 
157     unsigned kernelSizeX = 0;
158     unsigned kernelSizeY = 0;
159     calculateKernelSize(filter(), kernelSizeX, kernelSizeY, m_stdX, m_stdY);
160 
161     // We take the half kernel size and multiply it with three, because we run box blur three times.
162     absolutePaintRect.inflateX(3 * kernelSizeX * 0.5f);
163     absolutePaintRect.inflateY(3 * kernelSizeY * 0.5f);
164     setAbsolutePaintRect(enclosingIntRect(absolutePaintRect));
165 }
166 
apply()167 void FEGaussianBlur::apply()
168 {
169     if (hasResult())
170         return;
171     FilterEffect* in = inputEffect(0);
172     in->apply();
173     if (!in->hasResult())
174         return;
175 
176     ByteArray* srcPixelArray = createPremultipliedImageResult();
177     if (!srcPixelArray)
178         return;
179 
180     setIsAlphaImage(in->isAlphaImage());
181 
182     IntRect effectDrawingRect = requestedRegionOfInputImageData(in->absolutePaintRect());
183     in->copyPremultipliedImage(srcPixelArray, effectDrawingRect);
184 
185     if (!m_stdX && !m_stdY)
186         return;
187 
188     unsigned kernelSizeX = 0;
189     unsigned kernelSizeY = 0;
190     calculateKernelSize(filter(), kernelSizeX, kernelSizeY, m_stdX, m_stdY);
191 
192     IntSize paintSize = absolutePaintRect().size();
193     RefPtr<ByteArray> tmpImageData = ByteArray::create(paintSize.width() * paintSize.height() * 4);
194     ByteArray* tmpPixelArray = tmpImageData.get();
195 
196     int stride = 4 * paintSize.width();
197     int dxLeft = 0;
198     int dxRight = 0;
199     int dyLeft = 0;
200     int dyRight = 0;
201     for (int i = 0; i < 3; ++i) {
202         if (kernelSizeX) {
203             kernelPosition(i, kernelSizeX, dxLeft, dxRight);
204             boxBlur(srcPixelArray, tmpPixelArray, kernelSizeX, dxLeft, dxRight, 4, stride, paintSize.width(), paintSize.height(), isAlphaImage());
205         } else {
206             ByteArray* auxPixelArray = tmpPixelArray;
207             tmpPixelArray = srcPixelArray;
208             srcPixelArray = auxPixelArray;
209         }
210 
211         if (kernelSizeY) {
212             kernelPosition(i, kernelSizeY, dyLeft, dyRight);
213             boxBlur(tmpPixelArray, srcPixelArray, kernelSizeY, dyLeft, dyRight, stride, 4, paintSize.height(), paintSize.width(), isAlphaImage());
214         } else {
215             ByteArray* auxPixelArray = tmpPixelArray;
216             tmpPixelArray = srcPixelArray;
217             srcPixelArray = auxPixelArray;
218         }
219     }
220 }
221 
dump()222 void FEGaussianBlur::dump()
223 {
224 }
225 
externalRepresentation(TextStream & ts,int indent) const226 TextStream& FEGaussianBlur::externalRepresentation(TextStream& ts, int indent) const
227 {
228     writeIndent(ts, indent);
229     ts << "[feGaussianBlur";
230     FilterEffect::externalRepresentation(ts);
231     ts << " stdDeviation=\"" << m_stdX << ", " << m_stdY << "\"]\n";
232     inputEffect(0)->externalRepresentation(ts, indent + 1);
233     return ts;
234 }
235 
calculateStdDeviation(float radius)236 float FEGaussianBlur::calculateStdDeviation(float radius)
237 {
238     // Blur radius represents 2/3 times the kernel size, the dest pixel is half of the radius applied 3 times
239     return max((radius * 2 / 3.f - 0.5f) / gGaussianKernelFactor, 0.f);
240 }
241 
242 } // namespace WebCore
243 
244 #endif // ENABLE(FILTERS)
245