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 Zoltan Herczeg <zherczeg (at) webkit.org>
      7  *
      8  * This library is free software; you can redistribute it and/or
      9  * modify it under the terms of the GNU Library General Public
     10  * License as published by the Free Software Foundation; either
     11  * version 2 of the License, or (at your option) any later version.
     12  *
     13  * This library is distributed in the hope that it will be useful,
     14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
     15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     16  * Library General Public License for more details.
     17  *
     18  * You should have received a copy of the GNU Library General Public License
     19  * along with this library; see the file COPYING.LIB.  If not, write to
     20  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
     21  * Boston, MA 02110-1301, USA.
     22  */
     23 
     24 #include "config.h"
     25 
     26 #if ENABLE(FILTERS)
     27 #include "FEConvolveMatrix.h"
     28 
     29 #include "Filter.h"
     30 #include "RenderTreeAsText.h"
     31 #include "TextStream.h"
     32 
     33 #include <wtf/ByteArray.h>
     34 
     35 namespace WebCore {
     36 
     37 FEConvolveMatrix::FEConvolveMatrix(Filter* filter, const IntSize& kernelSize,
     38     float divisor, float bias, const IntPoint& targetOffset, EdgeModeType edgeMode,
     39     const FloatPoint& kernelUnitLength, bool preserveAlpha, const Vector<float>& kernelMatrix)
     40     : FilterEffect(filter)
     41     , m_kernelSize(kernelSize)
     42     , m_divisor(divisor)
     43     , m_bias(bias)
     44     , m_targetOffset(targetOffset)
     45     , m_edgeMode(edgeMode)
     46     , m_kernelUnitLength(kernelUnitLength)
     47     , m_preserveAlpha(preserveAlpha)
     48     , m_kernelMatrix(kernelMatrix)
     49 {
     50 }
     51 
     52 PassRefPtr<FEConvolveMatrix> FEConvolveMatrix::create(Filter* filter, const IntSize& kernelSize,
     53     float divisor, float bias, const IntPoint& targetOffset, EdgeModeType edgeMode,
     54     const FloatPoint& kernelUnitLength, bool preserveAlpha, const Vector<float>& kernelMatrix)
     55 {
     56     return adoptRef(new FEConvolveMatrix(filter, kernelSize, divisor, bias, targetOffset, edgeMode, kernelUnitLength,
     57         preserveAlpha, kernelMatrix));
     58 }
     59 
     60 
     61 IntSize FEConvolveMatrix::kernelSize() const
     62 {
     63     return m_kernelSize;
     64 }
     65 
     66 void FEConvolveMatrix::setKernelSize(const IntSize& kernelSize)
     67 {
     68     m_kernelSize = kernelSize;
     69 }
     70 
     71 const Vector<float>& FEConvolveMatrix::kernel() const
     72 {
     73     return m_kernelMatrix;
     74 }
     75 
     76 void FEConvolveMatrix::setKernel(const Vector<float>& kernel)
     77 {
     78     m_kernelMatrix = kernel;
     79 }
     80 
     81 float FEConvolveMatrix::divisor() const
     82 {
     83     return m_divisor;
     84 }
     85 
     86 bool FEConvolveMatrix::setDivisor(float divisor)
     87 {
     88     if (m_divisor == divisor)
     89         return false;
     90     m_divisor = divisor;
     91     return true;
     92 }
     93 
     94 float FEConvolveMatrix::bias() const
     95 {
     96     return m_bias;
     97 }
     98 
     99 bool FEConvolveMatrix::setBias(float bias)
    100 {
    101     if (m_bias == bias)
    102         return false;
    103     m_bias = bias;
    104     return true;
    105 }
    106 
    107 IntPoint FEConvolveMatrix::targetOffset() const
    108 {
    109     return m_targetOffset;
    110 }
    111 
    112 bool FEConvolveMatrix::setTargetOffset(const IntPoint& targetOffset)
    113 {
    114     if (m_targetOffset == targetOffset)
    115         return false;
    116     m_targetOffset = targetOffset;
    117     return true;
    118 }
    119 
    120 EdgeModeType FEConvolveMatrix::edgeMode() const
    121 {
    122     return m_edgeMode;
    123 }
    124 
    125 bool FEConvolveMatrix::setEdgeMode(EdgeModeType edgeMode)
    126 {
    127     if (m_edgeMode == edgeMode)
    128         return false;
    129     m_edgeMode = edgeMode;
    130     return true;
    131 }
    132 
    133 FloatPoint FEConvolveMatrix::kernelUnitLength() const
    134 {
    135     return m_kernelUnitLength;
    136 }
    137 
    138 bool FEConvolveMatrix::setKernelUnitLength(const FloatPoint& kernelUnitLength)
    139 {
    140     if (m_kernelUnitLength == kernelUnitLength)
    141         return false;
    142     m_kernelUnitLength = kernelUnitLength;
    143     return true;
    144 }
    145 
    146 bool FEConvolveMatrix::preserveAlpha() const
    147 {
    148     return m_preserveAlpha;
    149 }
    150 
    151 bool FEConvolveMatrix::setPreserveAlpha(bool preserveAlpha)
    152 {
    153     if (m_preserveAlpha == preserveAlpha)
    154         return false;
    155     m_preserveAlpha = preserveAlpha;
    156     return true;
    157 }
    158 
    159 /*
    160    -----------------------------------
    161       ConvolveMatrix implementation
    162    -----------------------------------
    163 
    164    The image rectangle is split in the following way:
    165 
    166       +---------------------+
    167       |          A          |
    168       +---------------------+
    169       |   |             |   |
    170       | B |      C      | D |
    171       |   |             |   |
    172       +---------------------+
    173       |          E          |
    174       +---------------------+
    175 
    176    Where region C contains those pixels, whose values
    177    can be calculated without crossing the edge of the rectangle.
    178 
    179    Example:
    180       Image size: width: 10, height: 10
    181 
    182       Order (kernel matrix size): width: 3, height 4
    183       Target: x:1, y:3
    184 
    185       The following figure shows the target inside the kernel matrix:
    186 
    187         ...
    188         ...
    189         ...
    190         .X.
    191 
    192    The regions in this case are the following:
    193       Note: (x1, y1) top-left and (x2, y2) is the bottom-right corner
    194       Note: row x2 and column y2 is not part of the region
    195             only those (x, y) pixels, where x1 <= x < x2 and y1 <= y < y2
    196 
    197       Region A: x1: 0, y1: 0, x2: 10, y2: 3
    198       Region B: x1: 0, y1: 3, x2: 1, y2: 10
    199       Region C: x1: 1, y1: 3, x2: 9, y2: 10
    200       Region D: x1: 9, y1: 3, x2: 10, y2: 10
    201       Region E: x1: 0, y1: 10, x2: 10, y2: 10 (empty region)
    202 
    203    Since region C (often) contains most of the pixels, we implemented
    204    a fast algoritm to calculate these values, called fastSetInteriorPixels.
    205    For other regions, fastSetOuterPixels is used, which calls getPixelValue,
    206    to handle pixels outside of the image. In a rare situations, when
    207    kernel matrix is bigger than the image, all pixels are calculated by this
    208    function.
    209 
    210    Although these two functions have lot in common, I decided not to make
    211    common a template for them, since there are key differences as well,
    212    and would make it really hard to understand.
    213 */
    214 
    215 static ALWAYS_INLINE unsigned char clampRGBAValue(float channel, unsigned char max = 255)
    216 {
    217     if (channel <= 0)
    218         return 0;
    219     if (channel >= max)
    220         return max;
    221     return channel;
    222 }
    223 
    224 template<bool preserveAlphaValues>
    225 ALWAYS_INLINE void setDestinationPixels(ByteArray* image, int& pixel, float* totals, float divisor, float bias, ByteArray* src)
    226 {
    227     unsigned char maxAlpha = preserveAlphaValues ? 255 : clampRGBAValue(totals[3] / divisor + bias);
    228     for (int i = 0; i < 3; ++i)
    229         image->set(pixel++, clampRGBAValue(totals[i] / divisor + bias, maxAlpha));
    230 
    231     if (preserveAlphaValues) {
    232         image->set(pixel, src->get(pixel));
    233         ++pixel;
    234     } else
    235         image->set(pixel++, maxAlpha);
    236 }
    237 
    238 // Only for region C
    239 template<bool preserveAlphaValues>
    240 ALWAYS_INLINE void FEConvolveMatrix::fastSetInteriorPixels(PaintingData& paintingData, int clipRight, int clipBottom)
    241 {
    242     // edge mode does not affect these pixels
    243     int pixel = (m_targetOffset.y() * paintingData.width + m_targetOffset.x()) * 4;
    244     int startKernelPixel = 0;
    245     int kernelIncrease = clipRight * 4;
    246     int xIncrease = (m_kernelSize.width() - 1) * 4;
    247     // Contains the sum of rgb(a) components
    248     float totals[3 + (preserveAlphaValues ? 0 : 1)];
    249 
    250     // m_divisor cannot be 0, SVGFEConvolveMatrixElement ensures this
    251     ASSERT(m_divisor);
    252 
    253     for (int y = clipBottom + 1; y > 0; --y) {
    254         for (int x = clipRight + 1; x > 0; --x) {
    255             int kernelValue = m_kernelMatrix.size() - 1;
    256             int kernelPixel = startKernelPixel;
    257             int width = m_kernelSize.width();
    258 
    259             totals[0] = 0;
    260             totals[1] = 0;
    261             totals[2] = 0;
    262             if (!preserveAlphaValues)
    263                 totals[3] = 0;
    264 
    265             while (kernelValue >= 0) {
    266                 totals[0] += m_kernelMatrix[kernelValue] * static_cast<float>(paintingData.srcPixelArray->get(kernelPixel++));
    267                 totals[1] += m_kernelMatrix[kernelValue] * static_cast<float>(paintingData.srcPixelArray->get(kernelPixel++));
    268                 totals[2] += m_kernelMatrix[kernelValue] * static_cast<float>(paintingData.srcPixelArray->get(kernelPixel++));
    269                 if (!preserveAlphaValues)
    270                     totals[3] += m_kernelMatrix[kernelValue] * static_cast<float>(paintingData.srcPixelArray->get(kernelPixel));
    271                 ++kernelPixel;
    272                 --kernelValue;
    273                 if (!--width) {
    274                     kernelPixel += kernelIncrease;
    275                     width = m_kernelSize.width();
    276                 }
    277             }
    278 
    279             setDestinationPixels<preserveAlphaValues>(paintingData.dstPixelArray, pixel, totals, m_divisor, paintingData.bias, paintingData.srcPixelArray);
    280             startKernelPixel += 4;
    281         }
    282         pixel += xIncrease;
    283         startKernelPixel += xIncrease;
    284     }
    285 }
    286 
    287 ALWAYS_INLINE int FEConvolveMatrix::getPixelValue(PaintingData& paintingData, int x, int y)
    288 {
    289     if (x >= 0 && x < paintingData.width && x >= 0 && y < paintingData.height)
    290         return (y * paintingData.width + x) << 2;
    291 
    292     switch (m_edgeMode) {
    293     default: // EDGEMODE_NONE
    294         return -1;
    295     case EDGEMODE_DUPLICATE:
    296         if (x < 0)
    297             x = 0;
    298         else if (x >= paintingData.width)
    299             x = paintingData.width - 1;
    300         if (y < 0)
    301             y = 0;
    302         else if (y >= paintingData.height)
    303             y = paintingData.height - 1;
    304         return (y * paintingData.width + x) << 2;
    305     case EDGEMODE_WRAP:
    306         while (x < 0)
    307             x += paintingData.width;
    308         x %= paintingData.width;
    309         while (y < 0)
    310             y += paintingData.height;
    311         y %= paintingData.height;
    312         return (y * paintingData.width + x) << 2;
    313     }
    314 }
    315 
    316 // For other regions than C
    317 template<bool preserveAlphaValues>
    318 void FEConvolveMatrix::fastSetOuterPixels(PaintingData& paintingData, int x1, int y1, int x2, int y2)
    319 {
    320     int pixel = (y1 * paintingData.width + x1) * 4;
    321     int height = y2 - y1;
    322     int width = x2 - x1;
    323     int beginKernelPixelX = x1 - m_targetOffset.x();
    324     int startKernelPixelX = beginKernelPixelX;
    325     int startKernelPixelY = y1 - m_targetOffset.y();
    326     int xIncrease = (paintingData.width - width) * 4;
    327     // Contains the sum of rgb(a) components
    328     float totals[3 + (preserveAlphaValues ? 0 : 1)];
    329 
    330     // m_divisor cannot be 0, SVGFEConvolveMatrixElement ensures this
    331     ASSERT(m_divisor);
    332 
    333     for (int y = height; y > 0; --y) {
    334         for (int x = width; x > 0; --x) {
    335             int kernelValue = m_kernelMatrix.size() - 1;
    336             int kernelPixelX = startKernelPixelX;
    337             int kernelPixelY = startKernelPixelY;
    338             int width = m_kernelSize.width();
    339 
    340             totals[0] = 0;
    341             totals[1] = 0;
    342             totals[2] = 0;
    343             if (!preserveAlphaValues)
    344                 totals[3] = 0;
    345 
    346             while (kernelValue >= 0) {
    347                 int pixelIndex = getPixelValue(paintingData, kernelPixelX, kernelPixelY);
    348                 if (pixelIndex >= 0) {
    349                     totals[0] += m_kernelMatrix[kernelValue] * static_cast<float>(paintingData.srcPixelArray->get(pixelIndex));
    350                     totals[1] += m_kernelMatrix[kernelValue] * static_cast<float>(paintingData.srcPixelArray->get(pixelIndex + 1));
    351                     totals[2] += m_kernelMatrix[kernelValue] * static_cast<float>(paintingData.srcPixelArray->get(pixelIndex + 2));
    352                 }
    353                 if (!preserveAlphaValues && pixelIndex >= 0)
    354                     totals[3] += m_kernelMatrix[kernelValue] * static_cast<float>(paintingData.srcPixelArray->get(pixelIndex + 3));
    355                 ++kernelPixelX;
    356                 --kernelValue;
    357                 if (!--width) {
    358                     kernelPixelX = startKernelPixelX;
    359                     ++kernelPixelY;
    360                     width = m_kernelSize.width();
    361                 }
    362             }
    363 
    364             setDestinationPixels<preserveAlphaValues>(paintingData.dstPixelArray, pixel, totals, m_divisor, paintingData.bias, paintingData.srcPixelArray);
    365             ++startKernelPixelX;
    366         }
    367         pixel += xIncrease;
    368         startKernelPixelX = beginKernelPixelX;
    369         ++startKernelPixelY;
    370     }
    371 }
    372 
    373 ALWAYS_INLINE void FEConvolveMatrix::setInteriorPixels(PaintingData& paintingData, int clipRight, int clipBottom)
    374 {
    375     // Must be implemented here, since it refers another ALWAYS_INLINE
    376     // function, which defined in this C++ source file as well
    377     if (m_preserveAlpha)
    378         fastSetInteriorPixels<true>(paintingData, clipRight, clipBottom);
    379     else
    380         fastSetInteriorPixels<false>(paintingData, clipRight, clipBottom);
    381 }
    382 
    383 ALWAYS_INLINE void FEConvolveMatrix::setOuterPixels(PaintingData& paintingData, int x1, int y1, int x2, int y2)
    384 {
    385     // Although this function can be moved to the header, it is implemented here
    386     // because setInteriorPixels is also implemented here
    387     if (m_preserveAlpha)
    388         fastSetOuterPixels<true>(paintingData, x1, y1, x2, y2);
    389     else
    390         fastSetOuterPixels<false>(paintingData, x1, y1, x2, y2);
    391 }
    392 
    393 void FEConvolveMatrix::apply()
    394 {
    395     if (hasResult())
    396         return;
    397     FilterEffect* in = inputEffect(0);
    398     in->apply();
    399     if (!in->hasResult())
    400         return;
    401 
    402     ByteArray* resultImage;
    403     if (m_preserveAlpha)
    404         resultImage = createUnmultipliedImageResult();
    405     else
    406         resultImage = createPremultipliedImageResult();
    407     if (!resultImage)
    408         return;
    409 
    410     IntRect effectDrawingRect = requestedRegionOfInputImageData(in->absolutePaintRect());
    411 
    412     RefPtr<ByteArray> srcPixelArray;
    413     if (m_preserveAlpha)
    414         srcPixelArray = in->asUnmultipliedImage(effectDrawingRect);
    415     else
    416         srcPixelArray = in->asPremultipliedImage(effectDrawingRect);
    417 
    418     IntSize paintSize = absolutePaintRect().size();
    419     PaintingData paintingData;
    420     paintingData.srcPixelArray = srcPixelArray.get();
    421     paintingData.dstPixelArray = resultImage;
    422     paintingData.width = paintSize.width();
    423     paintingData.height = paintSize.height();
    424     paintingData.bias = m_bias * 255;
    425 
    426     // Drawing fully covered pixels
    427     int clipRight = paintSize.width() - m_kernelSize.width();
    428     int clipBottom = paintSize.height() - m_kernelSize.height();
    429 
    430     if (clipRight >= 0 && clipBottom >= 0) {
    431         setInteriorPixels(paintingData, clipRight, clipBottom);
    432 
    433         clipRight += m_targetOffset.x() + 1;
    434         clipBottom += m_targetOffset.y() + 1;
    435         if (m_targetOffset.y() > 0)
    436             setOuterPixels(paintingData, 0, 0, paintSize.width(), m_targetOffset.y());
    437         if (clipBottom < paintSize.height())
    438             setOuterPixels(paintingData, 0, clipBottom, paintSize.width(), paintSize.height());
    439         if (m_targetOffset.x() > 0)
    440             setOuterPixels(paintingData, 0, m_targetOffset.y(), m_targetOffset.x(), clipBottom);
    441         if (clipRight < paintSize.width())
    442             setOuterPixels(paintingData, clipRight, m_targetOffset.y(), paintSize.width(), clipBottom);
    443     } else {
    444         // Rare situation, not optimizied for speed
    445         setOuterPixels(paintingData, 0, 0, paintSize.width(), paintSize.height());
    446     }
    447 }
    448 
    449 void FEConvolveMatrix::dump()
    450 {
    451 }
    452 
    453 static TextStream& operator<<(TextStream& ts, const EdgeModeType& type)
    454 {
    455     switch (type) {
    456     case EDGEMODE_UNKNOWN:
    457         ts << "UNKNOWN";
    458         break;
    459     case EDGEMODE_DUPLICATE:
    460         ts << "DUPLICATE";
    461         break;
    462     case EDGEMODE_WRAP:
    463         ts << "WRAP";
    464         break;
    465     case EDGEMODE_NONE:
    466         ts << "NONE";
    467         break;
    468     }
    469     return ts;
    470 }
    471 
    472 TextStream& FEConvolveMatrix::externalRepresentation(TextStream& ts, int indent) const
    473 {
    474     writeIndent(ts, indent);
    475     ts << "[feConvolveMatrix";
    476     FilterEffect::externalRepresentation(ts);
    477     ts << " order=\"" << m_kernelSize << "\" "
    478        << "kernelMatrix=\"" << m_kernelMatrix  << "\" "
    479        << "divisor=\"" << m_divisor << "\" "
    480        << "bias=\"" << m_bias << "\" "
    481        << "target=\"" << m_targetOffset << "\" "
    482        << "edgeMode=\"" << m_edgeMode << "\" "
    483        << "kernelUnitLength=\"" << m_kernelUnitLength << "\" "
    484        << "preserveAlpha=\"" << m_preserveAlpha << "\"]\n";
    485     inputEffect(0)->externalRepresentation(ts, indent + 1);
    486     return ts;
    487 }
    488 
    489 }; // namespace WebCore
    490 
    491 #endif // ENABLE(FILTERS)
    492