REGRESSION (r226981): ASSERTION FAILED: startY >= 0 && endY <= height && startY ...
[WebKit-https.git] / Source / WebCore / platform / graphics / filters / FELighting.cpp
1 /*
2  * Copyright (C) 2010 University of Szeged
3  * Copyright (C) 2010 Zoltan Herczeg
4  * Copyright (C) 2018 Apple Inc.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY UNIVERSITY OF SZEGED ``AS IS'' AND ANY
16  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
18  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL UNIVERSITY OF SZEGED OR
19  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
20  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
22  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
23  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
25  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
26  */
27
28 #include "config.h"
29 #include "FELighting.h"
30
31 #include "ColorUtilities.h"
32 #include "FELightingNEON.h"
33 #include <wtf/ParallelJobs.h>
34
35 namespace WebCore {
36
37 FELighting::FELighting(Filter& filter, LightingType lightingType, const Color& lightingColor, float surfaceScale, float diffuseConstant, float specularConstant, float specularExponent, float kernelUnitLengthX, float kernelUnitLengthY, Ref<LightSource>&& lightSource)
38     : FilterEffect(filter)
39     , m_lightingType(lightingType)
40     , m_lightSource(WTFMove(lightSource))
41     , m_lightingColor(lightingColor)
42     , m_surfaceScale(surfaceScale)
43     , m_diffuseConstant(diffuseConstant)
44     , m_specularConstant(specularConstant)
45     , m_specularExponent(specularExponent)
46     , m_kernelUnitLengthX(kernelUnitLengthX)
47     , m_kernelUnitLengthY(kernelUnitLengthY)
48 {
49 }
50
51 bool FELighting::setSurfaceScale(float surfaceScale)
52 {
53     if (m_surfaceScale == surfaceScale)
54         return false;
55
56     m_surfaceScale = surfaceScale;
57     return true;
58 }
59
60 bool FELighting::setLightingColor(const Color& lightingColor)
61 {
62     if (m_lightingColor == lightingColor)
63         return false;
64
65     m_lightingColor = lightingColor;
66     return true;
67 }
68
69 bool FELighting::setKernelUnitLengthX(float kernelUnitLengthX)
70 {
71     if (m_kernelUnitLengthX == kernelUnitLengthX)
72         return false;
73
74     m_kernelUnitLengthX = kernelUnitLengthX;
75     return true;
76 }
77
78 bool FELighting::setKernelUnitLengthY(float kernelUnitLengthY)
79 {
80     if (m_kernelUnitLengthY == kernelUnitLengthY)
81         return false;
82
83     m_kernelUnitLengthY = kernelUnitLengthY;
84     return true;
85 }
86
87 const static int cPixelSize = 4;
88 const static int cAlphaChannelOffset = 3;
89 const static uint8_t cOpaqueAlpha = static_cast<uint8_t>(0xFF);
90
91 // These factors and the normal coefficients come from the table under https://www.w3.org/TR/SVG/filters.html#feDiffuseLightingElement.
92 const static float cFactor1div2 = -1 / 2.f;
93 const static float cFactor1div3 = -1 / 3.f;
94 const static float cFactor1div4 = -1 / 4.f;
95 const static float cFactor2div3 = -2 / 3.f;
96
97 inline IntSize FELighting::LightingData::topLeftNormal(int offset) const
98 {
99     int center = static_cast<int>(pixels->item(offset + cAlphaChannelOffset));
100     int right = static_cast<int>(pixels->item(offset + cPixelSize + cAlphaChannelOffset));
101     offset += widthMultipliedByPixelSize;
102     int bottom = static_cast<int>(pixels->item(offset + cAlphaChannelOffset));
103     int bottomRight = static_cast<int>(pixels->item(offset + cPixelSize + cAlphaChannelOffset));
104     return {
105         -2 * center + 2 * right - bottom + bottomRight,
106         -2 * center - right + 2 * bottom + bottomRight
107     };
108 }
109
110 inline IntSize FELighting::LightingData::topRowNormal(int offset) const
111 {
112     int left = static_cast<int>(pixels->item(offset - cPixelSize + cAlphaChannelOffset));
113     int center = static_cast<int>(pixels->item(offset + cAlphaChannelOffset));
114     int right = static_cast<int>(pixels->item(offset + cPixelSize + cAlphaChannelOffset));
115     offset += widthMultipliedByPixelSize;
116     int bottomLeft = static_cast<int>(pixels->item(offset - cPixelSize + cAlphaChannelOffset));
117     int bottom = static_cast<int>(pixels->item(offset + cAlphaChannelOffset));
118     int bottomRight = static_cast<int>(pixels->item(offset + cPixelSize + cAlphaChannelOffset));
119     return {
120         -2 * left + 2 * right - bottomLeft + bottomRight,
121         -left - 2 * center - right + bottomLeft + 2 * bottom + bottomRight
122     };
123 }
124
125 inline IntSize FELighting::LightingData::topRightNormal(int offset) const
126 {
127     int left = static_cast<int>(pixels->item(offset - cPixelSize + cAlphaChannelOffset));
128     int center = static_cast<int>(pixels->item(offset + cAlphaChannelOffset));
129     offset += widthMultipliedByPixelSize;
130     int bottomLeft = static_cast<int>(pixels->item(offset - cPixelSize + cAlphaChannelOffset));
131     int bottom = static_cast<int>(pixels->item(offset + cAlphaChannelOffset));
132     return {
133         -2 * left + 2 * center - bottomLeft + bottom,
134         -left - 2 * center + bottomLeft + 2 * bottom
135     };
136 }
137
138 inline IntSize FELighting::LightingData::leftColumnNormal(int offset) const
139 {
140     int center = static_cast<int>(pixels->item(offset + cAlphaChannelOffset));
141     int right = static_cast<int>(pixels->item(offset + cPixelSize + cAlphaChannelOffset));
142     offset -= widthMultipliedByPixelSize;
143     int top = static_cast<int>(pixels->item(offset + cAlphaChannelOffset));
144     int topRight = static_cast<int>(pixels->item(offset + cPixelSize + cAlphaChannelOffset));
145     offset += 2 * widthMultipliedByPixelSize;
146     int bottom = static_cast<int>(pixels->item(offset + cAlphaChannelOffset));
147     int bottomRight = static_cast<int>(pixels->item(offset + cPixelSize + cAlphaChannelOffset));
148     return {
149         -top + topRight - 2 * center + 2 * right - bottom + bottomRight,
150         -2 * top - topRight + 2 * bottom + bottomRight
151     };
152 }
153
154 inline IntSize FELighting::LightingData::interiorNormal(int offset, AlphaWindow& alphaWindow) const
155 {
156     int rightAlphaOffset = offset + cPixelSize + cAlphaChannelOffset;
157     
158     int right = static_cast<int>(pixels->item(rightAlphaOffset));
159     int topRight = static_cast<int>(pixels->item(rightAlphaOffset - widthMultipliedByPixelSize));
160     int bottomRight = static_cast<int>(pixels->item(rightAlphaOffset + widthMultipliedByPixelSize));
161
162     int left = alphaWindow.left();
163     int topLeft = alphaWindow.topLeft();
164     int top = alphaWindow.top();
165
166     int bottomLeft = alphaWindow.bottomLeft();
167     int bottom = alphaWindow.bottom();
168
169     // The alphaWindow has been shifted, and here we fill in the right column.
170     alphaWindow.alpha[0][2] = topRight;
171     alphaWindow.alpha[1][2] = right;
172     alphaWindow.alpha[2][2] = bottomRight;
173     
174     // Check that the alphaWindow is working with some spot-checks.
175     ASSERT(alphaWindow.topLeft() == pixels->item(offset - cPixelSize - widthMultipliedByPixelSize + cAlphaChannelOffset)); // topLeft
176     ASSERT(alphaWindow.top() == pixels->item(offset - widthMultipliedByPixelSize + cAlphaChannelOffset)); // top
177
178     return {
179         -topLeft + topRight - 2 * left + 2 * right - bottomLeft + bottomRight,
180         -topLeft - 2 * top - topRight + bottomLeft + 2 * bottom + bottomRight
181     };
182 }
183
184 inline IntSize FELighting::LightingData::rightColumnNormal(int offset) const
185 {
186     int left = static_cast<int>(pixels->item(offset - cPixelSize + cAlphaChannelOffset));
187     int center = static_cast<int>(pixels->item(offset + cAlphaChannelOffset));
188     offset -= widthMultipliedByPixelSize;
189     int topLeft = static_cast<int>(pixels->item(offset - cPixelSize + cAlphaChannelOffset));
190     int top = static_cast<int>(pixels->item(offset + cAlphaChannelOffset));
191     offset += 2 * widthMultipliedByPixelSize;
192     int bottomLeft = static_cast<int>(pixels->item(offset - cPixelSize + cAlphaChannelOffset));
193     int bottom = static_cast<int>(pixels->item(offset + cAlphaChannelOffset));
194     return {
195         -topLeft + top - 2 * left + 2 * center - bottomLeft + bottom,
196         -topLeft - 2 * top + bottomLeft + 2 * bottom
197     };
198 }
199
200 inline IntSize FELighting::LightingData::bottomLeftNormal(int offset) const
201 {
202     int center = static_cast<int>(pixels->item(offset + cAlphaChannelOffset));
203     int right = static_cast<int>(pixels->item(offset + cPixelSize + cAlphaChannelOffset));
204     offset -= widthMultipliedByPixelSize;
205     int top = static_cast<int>(pixels->item(offset + cAlphaChannelOffset));
206     int topRight = static_cast<int>(pixels->item(offset + cPixelSize + cAlphaChannelOffset));
207     return {
208         -top + topRight - 2 * center + 2 * right,
209         -2 * top - topRight + 2 * center + right
210     };
211 }
212
213 inline IntSize FELighting::LightingData::bottomRowNormal(int offset) const
214 {
215     int left = static_cast<int>(pixels->item(offset - cPixelSize + cAlphaChannelOffset));
216     int center = static_cast<int>(pixels->item(offset + cAlphaChannelOffset));
217     int right = static_cast<int>(pixels->item(offset + cPixelSize + cAlphaChannelOffset));
218     offset -= widthMultipliedByPixelSize;
219     int topLeft = static_cast<int>(pixels->item(offset - cPixelSize + cAlphaChannelOffset));
220     int top = static_cast<int>(pixels->item(offset + cAlphaChannelOffset));
221     int topRight = static_cast<int>(pixels->item(offset + cPixelSize + cAlphaChannelOffset));
222     return {
223         -topLeft + topRight - 2 * left + 2 * right,
224         -topLeft - 2 * top - topRight + left + 2 * center + right
225     };
226 }
227
228 inline IntSize FELighting::LightingData::bottomRightNormal(int offset) const
229 {
230     int left = static_cast<int>(pixels->item(offset - cPixelSize + cAlphaChannelOffset));
231     int center = static_cast<int>(pixels->item(offset + cAlphaChannelOffset));
232     offset -= widthMultipliedByPixelSize;
233     int topLeft = static_cast<int>(pixels->item(offset - cPixelSize + cAlphaChannelOffset));
234     int top = static_cast<int>(pixels->item(offset + cAlphaChannelOffset));
235     return {
236         -topLeft + top - 2 * left + 2 * center,
237         -topLeft - 2 * top + left + 2 * center
238     };
239 }
240
241 void FELighting::setPixel(int offset, const LightingData& data, const LightSource::PaintingData& paintingData, int x, int y, float factorX, float factorY, IntSize normal2DVector)
242 {
243     setPixelInternal(offset, data, paintingData, x, y, factorX, factorY, normal2DVector, data.pixels->item(offset + cAlphaChannelOffset));
244 }
245
246 void FELighting::setPixelInternal(int offset, const LightingData& data, const LightSource::PaintingData& paintingData, int x, int y, float factorX, float factorY, IntSize normal2DVector, float alpha)
247 {
248     float z = alpha * data.surfaceScale;
249     LightSource::ComputedLightingData lightingData = m_lightSource->computePixelLightingData(paintingData, x, y, z);
250
251     float lightStrength;
252     if (normal2DVector.isZero()) {
253         // Normal vector is (0, 0, 1). This is a quite frequent case.
254         if (m_lightingType == FELighting::DiffuseLighting)
255             lightStrength = m_diffuseConstant * lightingData.lightVector.z() / lightingData.lightVectorLength;
256         else {
257             FloatPoint3D halfwayVector = {
258                 lightingData.lightVector.x(),
259                 lightingData.lightVector.y(),
260                 lightingData.lightVector.z() + lightingData.lightVectorLength
261             };
262             float halfwayVectorLength = halfwayVector.length();
263             if (m_specularExponent == 1)
264                 lightStrength = m_specularConstant * halfwayVector.z() / halfwayVectorLength;
265             else
266                 lightStrength = m_specularConstant * powf(halfwayVector.z() / halfwayVectorLength, m_specularExponent);
267         }
268     } else {
269         FloatPoint3D normalVector = {
270             factorX * normal2DVector.width() * data.surfaceScale,
271             factorY * normal2DVector.height() * data.surfaceScale,
272             1.0f
273         };
274         float normalVectorLength = normalVector.length();
275
276         if (m_lightingType == FELighting::DiffuseLighting)
277             lightStrength = m_diffuseConstant * (normalVector * lightingData.lightVector) / (normalVectorLength * lightingData.lightVectorLength);
278         else {
279             FloatPoint3D halfwayVector = {
280                 lightingData.lightVector.x(),
281                 lightingData.lightVector.y(),
282                 lightingData.lightVector.z() + lightingData.lightVectorLength
283             };
284             float halfwayVectorLength = halfwayVector.length();
285             if (m_specularExponent == 1)
286                 lightStrength = m_specularConstant * (normalVector * halfwayVector) / (normalVectorLength * halfwayVectorLength);
287             else
288                 lightStrength = m_specularConstant * powf((normalVector * halfwayVector) / (normalVectorLength * halfwayVectorLength), m_specularExponent);
289         }
290     }
291
292     if (lightStrength > 1)
293         lightStrength = 1;
294     if (lightStrength < 0)
295         lightStrength = 0;
296
297     uint8_t pixelValue[3] = {
298         static_cast<uint8_t>(lightStrength * lightingData.colorVector.x()),
299         static_cast<uint8_t>(lightStrength * lightingData.colorVector.y()),
300         static_cast<uint8_t>(lightStrength * lightingData.colorVector.z())
301     };
302     
303     data.pixels->setRange(pixelValue, 3, offset);
304 }
305
306 // This appears to read from and write to the same pixel buffer, but it only reads the alpha channel, and writes the non-alpha channels.
307 void FELighting::platformApplyGenericPaint(const LightingData& data, const LightSource::PaintingData& paintingData, int startY, int endY)
308 {
309     // Make sure startY is > 0 since we read from the previous row in the loop.
310     ASSERT(startY);
311     ASSERT(endY > startY);
312
313     for (int y = startY; y < endY; ++y) {
314         int rowStartOffset = y * data.widthMultipliedByPixelSize;
315         int previousRowStart = rowStartOffset - data.widthMultipliedByPixelSize;
316         int nextRowStart = rowStartOffset + data.widthMultipliedByPixelSize;
317
318         // alphaWindow is a local cache of alpha values.
319         // Fill the two right columns putting the left edge value in the center column.
320         // For each pixel, we shift each row left then fill the right column.
321         AlphaWindow alphaWindow;
322         alphaWindow.setTop(data.pixels->item(previousRowStart + cAlphaChannelOffset));
323         alphaWindow.setTopRight(data.pixels->item(previousRowStart + cPixelSize + cAlphaChannelOffset));
324
325         alphaWindow.setCenter(data.pixels->item(rowStartOffset + cAlphaChannelOffset));
326         alphaWindow.setRight(data.pixels->item(rowStartOffset + cPixelSize + cAlphaChannelOffset));
327
328         alphaWindow.setBottom(data.pixels->item(nextRowStart + cAlphaChannelOffset));
329         alphaWindow.setBottomRight(data.pixels->item(nextRowStart + cPixelSize + cAlphaChannelOffset));
330
331         int offset = rowStartOffset + cPixelSize;
332         for (int x = 1; x < data.widthDecreasedByOne; ++x, offset += cPixelSize) {
333             alphaWindow.shift();
334             setPixelInternal(offset, data, paintingData, x, y, cFactor1div4, cFactor1div4, data.interiorNormal(offset, alphaWindow), alphaWindow.center());
335         }
336     }
337 }
338
339 void FELighting::platformApplyGenericWorker(PlatformApplyGenericParameters* parameters)
340 {
341     parameters->filter->platformApplyGenericPaint(parameters->data, parameters->paintingData, parameters->yStart, parameters->yEnd);
342 }
343
344 void FELighting::platformApplyGeneric(const LightingData& data, const LightSource::PaintingData& paintingData)
345 {
346     unsigned rowsToProcess = data.heightDecreasedByOne - 1;
347     unsigned maxNumThreads = rowsToProcess / 8;
348     unsigned optimalThreadNumber = std::min<unsigned>(((data.widthDecreasedByOne - 1) * rowsToProcess) / s_minimalRectDimension, maxNumThreads);
349     if (optimalThreadNumber > 1) {
350         // Initialize parallel jobs
351         WTF::ParallelJobs<PlatformApplyGenericParameters> parallelJobs(&platformApplyGenericWorker, optimalThreadNumber);
352
353         // Fill the parameter array
354         int job = parallelJobs.numberOfJobs();
355         if (job > 1) {
356             // Split the job into "yStep"-sized jobs but there a few jobs that need to be slightly larger since
357             // yStep * jobs < total size. These extras are handled by the remainder "jobsWithExtra".
358             const int yStep = rowsToProcess / job;
359             const int jobsWithExtra = rowsToProcess % job;
360
361             int yStart = 1;
362             for (--job; job >= 0; --job) {
363                 PlatformApplyGenericParameters& params = parallelJobs.parameter(job);
364                 params.filter = this;
365                 params.data = data;
366                 params.paintingData = paintingData;
367                 params.yStart = yStart;
368                 yStart += job < jobsWithExtra ? yStep + 1 : yStep;
369                 params.yEnd = yStart;
370             }
371             parallelJobs.execute();
372             return;
373         }
374         // Fallback to single threaded mode.
375     }
376
377     platformApplyGenericPaint(data, paintingData, 1, data.heightDecreasedByOne);
378 }
379
380 inline void FELighting::platformApply(const LightingData& data, const LightSource::PaintingData& paintingData)
381 {
382     // The selection here eventually should happen dynamically on some platforms.
383 #if CPU(ARM_NEON) && CPU(ARM_TRADITIONAL) && COMPILER(GCC_OR_CLANG)
384     platformApplyNeon(data, paintingData);
385 #else
386     platformApplyGeneric(data, paintingData);
387 #endif
388 }
389
390 bool FELighting::drawLighting(Uint8ClampedArray& pixels, int width, int height)
391 {
392     LightSource::PaintingData paintingData;
393     LightingData data;
394
395     // FIXME: do something if width or height (or both) is 1 pixel.
396     // The W3 spec does not define this case. Now the filter just returns.
397     if (width <= 2 || height <= 2)
398         return false;
399
400     data.pixels = &pixels;
401     data.surfaceScale = m_surfaceScale / 255.0f;
402     data.widthMultipliedByPixelSize = width * cPixelSize;
403     data.widthDecreasedByOne = width - 1;
404     data.heightDecreasedByOne = height - 1;
405     
406     Color lightColor = (operatingColorSpace() == ColorSpaceLinearRGB) ? sRGBToLinearColor(m_lightingColor) : m_lightingColor;
407     paintingData.initialLightingData.colorVector = FloatPoint3D(lightColor.red(), lightColor.green(), lightColor.blue());
408     m_lightSource->initPaintingData(*this, paintingData);
409
410     // Top left.
411     int offset = 0;
412     setPixel(offset, data, paintingData, 0, 0, cFactor2div3, cFactor2div3, data.topLeftNormal(offset));
413
414     // Top right.
415     offset = data.widthMultipliedByPixelSize - cPixelSize;
416     setPixel(offset, data, paintingData, data.widthDecreasedByOne, 0, cFactor2div3, cFactor2div3, data.topRightNormal(offset));
417
418     // Bottom left.
419     offset = data.heightDecreasedByOne * data.widthMultipliedByPixelSize;
420     setPixel(offset, data, paintingData, 0, data.heightDecreasedByOne, cFactor2div3, cFactor2div3, data.bottomLeftNormal(offset));
421
422     // Bottom right.
423     offset = height * data.widthMultipliedByPixelSize - cPixelSize;
424     setPixel(offset, data, paintingData, data.widthDecreasedByOne, data.heightDecreasedByOne, cFactor2div3, cFactor2div3, data.bottomRightNormal(offset));
425
426     if (width >= 3) {
427         // Top row.
428         offset = cPixelSize;
429         for (int x = 1; x < data.widthDecreasedByOne; ++x, offset += cPixelSize)
430             setPixel(offset, data, paintingData, x, 0, cFactor1div3, cFactor1div2, data.topRowNormal(offset));
431
432         // Bottom row.
433         offset = data.heightDecreasedByOne * data.widthMultipliedByPixelSize + cPixelSize;
434         for (int x = 1; x < data.widthDecreasedByOne; ++x, offset += cPixelSize)
435             setPixel(offset, data, paintingData, x, data.heightDecreasedByOne, cFactor1div3, cFactor1div2, data.bottomRowNormal(offset));
436     }
437
438     if (height >= 3) {
439         // Left column.
440         offset = data.widthMultipliedByPixelSize;
441         for (int y = 1; y < data.heightDecreasedByOne; ++y, offset += data.widthMultipliedByPixelSize)
442             setPixel(offset, data, paintingData, 0, y, cFactor1div2, cFactor1div3, data.leftColumnNormal(offset));
443
444         // Right column.
445         offset = 2 * data.widthMultipliedByPixelSize - cPixelSize;
446         for (int y = 1; y < data.heightDecreasedByOne; ++y, offset += data.widthMultipliedByPixelSize)
447             setPixel(offset, data, paintingData, data.widthDecreasedByOne, y, cFactor1div2, cFactor1div3, data.rightColumnNormal(offset));
448     }
449
450     if (width >= 3 && height >= 3) {
451         // Interior pixels.
452         platformApply(data, paintingData);
453     }
454
455     int lastPixel = data.widthMultipliedByPixelSize * height;
456     if (m_lightingType == DiffuseLighting) {
457         for (int i = cAlphaChannelOffset; i < lastPixel; i += cPixelSize)
458             data.pixels->set(i, cOpaqueAlpha);
459     } else {
460         for (int i = 0; i < lastPixel; i += cPixelSize) {
461             uint8_t a1 = data.pixels->item(i);
462             uint8_t a2 = data.pixels->item(i + 1);
463             uint8_t a3 = data.pixels->item(i + 2);
464             // alpha set to set to max(a1, a2, a3)
465             data.pixels->set(i + 3, a1 >= a2 ? (a1 >= a3 ? a1 : a3) : (a2 >= a3 ? a2 : a3));
466         }
467     }
468
469     return true;
470 }
471
472 void FELighting::platformApplySoftware()
473 {
474     FilterEffect* in = inputEffect(0);
475
476     Uint8ClampedArray* resutPixelArray = createPremultipliedImageResult();
477     if (!resutPixelArray)
478         return;
479
480     setIsAlphaImage(false);
481
482     IntRect effectDrawingRect = requestedRegionOfInputImageData(in->absolutePaintRect());
483     in->copyPremultipliedResult(*resutPixelArray, effectDrawingRect);
484
485     // FIXME: support kernelUnitLengths other than (1,1). The issue here is that the W3
486     // standard has no test case for them, and other browsers (like Firefox) has strange
487     // output for various kernelUnitLengths, and I am not sure they are reliable.
488     // Anyway, feConvolveMatrix should also use the implementation
489
490     IntSize absolutePaintSize = absolutePaintRect().size();
491     drawLighting(*resutPixelArray, absolutePaintSize.width(), absolutePaintSize.height());
492 }
493
494 } // namespace WebCore