• 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 Zoltan Herczeg <zherczeg@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 
FEConvolveMatrix(Filter * filter,const IntSize & kernelSize,float divisor,float bias,const IntPoint & targetOffset,EdgeModeType edgeMode,const FloatPoint & kernelUnitLength,bool preserveAlpha,const Vector<float> & kernelMatrix)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 
create(Filter * filter,const IntSize & kernelSize,float divisor,float bias,const IntPoint & targetOffset,EdgeModeType edgeMode,const FloatPoint & kernelUnitLength,bool preserveAlpha,const Vector<float> & kernelMatrix)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 
kernelSize() const61 IntSize FEConvolveMatrix::kernelSize() const
62 {
63     return m_kernelSize;
64 }
65 
setKernelSize(const IntSize & kernelSize)66 void FEConvolveMatrix::setKernelSize(const IntSize& kernelSize)
67 {
68     m_kernelSize = kernelSize;
69 }
70 
kernel() const71 const Vector<float>& FEConvolveMatrix::kernel() const
72 {
73     return m_kernelMatrix;
74 }
75 
setKernel(const Vector<float> & kernel)76 void FEConvolveMatrix::setKernel(const Vector<float>& kernel)
77 {
78     m_kernelMatrix = kernel;
79 }
80 
divisor() const81 float FEConvolveMatrix::divisor() const
82 {
83     return m_divisor;
84 }
85 
setDivisor(float divisor)86 bool FEConvolveMatrix::setDivisor(float divisor)
87 {
88     if (m_divisor == divisor)
89         return false;
90     m_divisor = divisor;
91     return true;
92 }
93 
bias() const94 float FEConvolveMatrix::bias() const
95 {
96     return m_bias;
97 }
98 
setBias(float bias)99 bool FEConvolveMatrix::setBias(float bias)
100 {
101     if (m_bias == bias)
102         return false;
103     m_bias = bias;
104     return true;
105 }
106 
targetOffset() const107 IntPoint FEConvolveMatrix::targetOffset() const
108 {
109     return m_targetOffset;
110 }
111 
setTargetOffset(const IntPoint & targetOffset)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 
edgeMode() const120 EdgeModeType FEConvolveMatrix::edgeMode() const
121 {
122     return m_edgeMode;
123 }
124 
setEdgeMode(EdgeModeType edgeMode)125 bool FEConvolveMatrix::setEdgeMode(EdgeModeType edgeMode)
126 {
127     if (m_edgeMode == edgeMode)
128         return false;
129     m_edgeMode = edgeMode;
130     return true;
131 }
132 
kernelUnitLength() const133 FloatPoint FEConvolveMatrix::kernelUnitLength() const
134 {
135     return m_kernelUnitLength;
136 }
137 
setKernelUnitLength(const FloatPoint & kernelUnitLength)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 
preserveAlpha() const146 bool FEConvolveMatrix::preserveAlpha() const
147 {
148     return m_preserveAlpha;
149 }
150 
setPreserveAlpha(bool preserveAlpha)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 
clampRGBAValue(float channel,unsigned char max=255)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>
setDestinationPixels(ByteArray * image,int & pixel,float * totals,float divisor,float bias,ByteArray * src)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>
fastSetInteriorPixels(PaintingData & paintingData,int clipRight,int clipBottom)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 
getPixelValue(PaintingData & paintingData,int x,int y)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>
fastSetOuterPixels(PaintingData & paintingData,int x1,int y1,int x2,int y2)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 
setInteriorPixels(PaintingData & paintingData,int clipRight,int clipBottom)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 
setOuterPixels(PaintingData & paintingData,int x1,int y1,int x2,int y2)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 
apply()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 
dump()449 void FEConvolveMatrix::dump()
450 {
451 }
452 
operator <<(TextStream & ts,const EdgeModeType & type)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 
externalRepresentation(TextStream & ts,int indent) const472 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