1 /* 2 * Copyright (C) 2004, 2005, 2006, 2007 Nikolas Zimmermann <zimmermann (at) kde.org> 3 * Copyright (C) 2004, 2005 Rob Buis <buis (at) kde.org> 4 * Copyright (C) 2005 Eric Seidel <eric (at) webkit.org> 5 * Copyright (C) 2009 Dirk Schulze <krit (at) 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 45 FEGaussianBlur::FEGaussianBlur(Filter* filter, float x, float y) 46 : FilterEffect(filter) 47 , m_stdX(x) 48 , m_stdY(y) 49 { 50 } 51 52 PassRefPtr<FEGaussianBlur> FEGaussianBlur::create(Filter* filter, float x, float y) 53 { 54 return adoptRef(new FEGaussianBlur(filter, x, y)); 55 } 56 57 float FEGaussianBlur::stdDeviationX() const 58 { 59 return m_stdX; 60 } 61 62 void FEGaussianBlur::setStdDeviationX(float x) 63 { 64 m_stdX = x; 65 } 66 67 float FEGaussianBlur::stdDeviationY() const 68 { 69 return m_stdY; 70 } 71 72 void FEGaussianBlur::setStdDeviationY(float y) 73 { 74 m_stdY = y; 75 } 76 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 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 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 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 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 222 void FEGaussianBlur::dump() 223 { 224 } 225 226 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 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