Home | History | Annotate | Download | only in filters
      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