Stop using PassRefPtr in platform/graphics
[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  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY UNIVERSITY OF SZEGED ``AS IS'' AND ANY
15  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL UNIVERSITY OF SZEGED OR
18  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
21  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
22  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
25  */
26
27 #include "config.h"
28 #include "FELighting.h"
29
30 #include "FELightingNEON.h"
31 #include <wtf/ParallelJobs.h>
32
33 namespace WebCore {
34
35 FELighting::FELighting(Filter& filter, LightingType lightingType, const Color& lightingColor, float surfaceScale, float diffuseConstant, float specularConstant, float specularExponent, float kernelUnitLengthX, float kernelUnitLengthY, Ref<LightSource>&& lightSource)
36     : FilterEffect(filter)
37     , m_lightingType(lightingType)
38     , m_lightSource(WTFMove(lightSource))
39     , m_lightingColor(lightingColor)
40     , m_surfaceScale(surfaceScale)
41     , m_diffuseConstant(diffuseConstant)
42     , m_specularConstant(specularConstant)
43     , m_specularExponent(specularExponent)
44     , m_kernelUnitLengthX(kernelUnitLengthX)
45     , m_kernelUnitLengthY(kernelUnitLengthY)
46 {
47 }
48
49 const static int cPixelSize = 4;
50 const static int cAlphaChannelOffset = 3;
51 const static unsigned char cOpaqueAlpha = static_cast<unsigned char>(0xff);
52 const static float cFactor1div2 = -1 / 2.f;
53 const static float cFactor1div3 = -1 / 3.f;
54 const static float cFactor1div4 = -1 / 4.f;
55 const static float cFactor2div3 = -2 / 3.f;
56
57 // << 1 is signed multiply by 2
58 inline void FELighting::LightingData::topLeft(int offset, IntPoint& normalVector)
59 {
60     int center = static_cast<int>(pixels->item(offset + cAlphaChannelOffset));
61     int right = static_cast<int>(pixels->item(offset + cPixelSize + cAlphaChannelOffset));
62     offset += widthMultipliedByPixelSize;
63     int bottom = static_cast<int>(pixels->item(offset + cAlphaChannelOffset));
64     int bottomRight = static_cast<int>(pixels->item(offset + cPixelSize + cAlphaChannelOffset));
65     normalVector.setX(-(center << 1) + (right << 1) - bottom + bottomRight);
66     normalVector.setY(-(center << 1) - right + (bottom << 1) + bottomRight);
67 }
68
69 inline void FELighting::LightingData::topRow(int offset, IntPoint& normalVector)
70 {
71     int left = static_cast<int>(pixels->item(offset - cPixelSize + cAlphaChannelOffset));
72     int center = static_cast<int>(pixels->item(offset + cAlphaChannelOffset));
73     int right = static_cast<int>(pixels->item(offset + cPixelSize + cAlphaChannelOffset));
74     offset += widthMultipliedByPixelSize;
75     int bottomLeft = static_cast<int>(pixels->item(offset - cPixelSize + cAlphaChannelOffset));
76     int bottom = static_cast<int>(pixels->item(offset + cAlphaChannelOffset));
77     int bottomRight = static_cast<int>(pixels->item(offset + cPixelSize + cAlphaChannelOffset));
78     normalVector.setX(-(left << 1) + (right << 1) - bottomLeft + bottomRight);
79     normalVector.setY(-left - (center << 1) - right + bottomLeft + (bottom << 1) + bottomRight);
80 }
81
82 inline void FELighting::LightingData::topRight(int offset, IntPoint& normalVector)
83 {
84     int left = static_cast<int>(pixels->item(offset - cPixelSize + cAlphaChannelOffset));
85     int center = static_cast<int>(pixels->item(offset + cAlphaChannelOffset));
86     offset += widthMultipliedByPixelSize;
87     int bottomLeft = static_cast<int>(pixels->item(offset - cPixelSize + cAlphaChannelOffset));
88     int bottom = static_cast<int>(pixels->item(offset + cAlphaChannelOffset));
89     normalVector.setX(-(left << 1) + (center << 1) - bottomLeft + bottom);
90     normalVector.setY(-left - (center << 1) + bottomLeft + (bottom << 1));
91 }
92
93 inline void FELighting::LightingData::leftColumn(int offset, IntPoint& normalVector)
94 {
95     int center = static_cast<int>(pixels->item(offset + cAlphaChannelOffset));
96     int right = static_cast<int>(pixels->item(offset + cPixelSize + cAlphaChannelOffset));
97     offset -= widthMultipliedByPixelSize;
98     int top = static_cast<int>(pixels->item(offset + cAlphaChannelOffset));
99     int topRight = static_cast<int>(pixels->item(offset + cPixelSize + cAlphaChannelOffset));
100     offset += widthMultipliedByPixelSize << 1;
101     int bottom = static_cast<int>(pixels->item(offset + cAlphaChannelOffset));
102     int bottomRight = static_cast<int>(pixels->item(offset + cPixelSize + cAlphaChannelOffset));
103     normalVector.setX(-top + topRight - (center << 1) + (right << 1) - bottom + bottomRight);
104     normalVector.setY(-(top << 1) - topRight + (bottom << 1) + bottomRight);
105 }
106
107 inline void FELighting::LightingData::interior(int offset, IntPoint& normalVector)
108 {
109     int left = static_cast<int>(pixels->item(offset - cPixelSize + cAlphaChannelOffset));
110     int right = static_cast<int>(pixels->item(offset + cPixelSize + cAlphaChannelOffset));
111     offset -= widthMultipliedByPixelSize;
112     int topLeft = static_cast<int>(pixels->item(offset - cPixelSize + cAlphaChannelOffset));
113     int top = static_cast<int>(pixels->item(offset + cAlphaChannelOffset));
114     int topRight = static_cast<int>(pixels->item(offset + cPixelSize + cAlphaChannelOffset));
115     offset += widthMultipliedByPixelSize << 1;
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     normalVector.setX(-topLeft + topRight - (left << 1) + (right << 1) - bottomLeft + bottomRight);
120     normalVector.setY(-topLeft - (top << 1) - topRight + bottomLeft + (bottom << 1) + bottomRight);
121 }
122
123 inline void FELighting::LightingData::rightColumn(int offset, IntPoint& normalVector)
124 {
125     int left = static_cast<int>(pixels->item(offset - cPixelSize + cAlphaChannelOffset));
126     int center = static_cast<int>(pixels->item(offset + cAlphaChannelOffset));
127     offset -= widthMultipliedByPixelSize;
128     int topLeft = static_cast<int>(pixels->item(offset - cPixelSize + cAlphaChannelOffset));
129     int top = static_cast<int>(pixels->item(offset + cAlphaChannelOffset));
130     offset += widthMultipliedByPixelSize << 1;
131     int bottomLeft = static_cast<int>(pixels->item(offset - cPixelSize + cAlphaChannelOffset));
132     int bottom = static_cast<int>(pixels->item(offset + cAlphaChannelOffset));
133     normalVector.setX(-topLeft + top - (left << 1) + (center << 1) - bottomLeft + bottom);
134     normalVector.setY(-topLeft - (top << 1) + bottomLeft + (bottom << 1));
135 }
136
137 inline void FELighting::LightingData::bottomLeft(int offset, IntPoint& normalVector)
138 {
139     int center = static_cast<int>(pixels->item(offset + cAlphaChannelOffset));
140     int right = static_cast<int>(pixels->item(offset + cPixelSize + cAlphaChannelOffset));
141     offset -= widthMultipliedByPixelSize;
142     int top = static_cast<int>(pixels->item(offset + cAlphaChannelOffset));
143     int topRight = static_cast<int>(pixels->item(offset + cPixelSize + cAlphaChannelOffset));
144     normalVector.setX(-top + topRight - (center << 1) + (right << 1));
145     normalVector.setY(-(top << 1) - topRight + (center << 1) + right);
146 }
147
148 inline void FELighting::LightingData::bottomRow(int offset, IntPoint& normalVector)
149 {
150     int left = static_cast<int>(pixels->item(offset - cPixelSize + cAlphaChannelOffset));
151     int center = static_cast<int>(pixels->item(offset + cAlphaChannelOffset));
152     int right = static_cast<int>(pixels->item(offset + cPixelSize + cAlphaChannelOffset));
153     offset -= widthMultipliedByPixelSize;
154     int topLeft = static_cast<int>(pixels->item(offset - cPixelSize + cAlphaChannelOffset));
155     int top = static_cast<int>(pixels->item(offset + cAlphaChannelOffset));
156     int topRight = static_cast<int>(pixels->item(offset + cPixelSize + cAlphaChannelOffset));
157     normalVector.setX(-topLeft + topRight - (left << 1) + (right << 1));
158     normalVector.setY(-topLeft - (top << 1) - topRight + left + (center << 1) + right);
159 }
160
161 inline void FELighting::LightingData::bottomRight(int offset, IntPoint& normalVector)
162 {
163     int left = static_cast<int>(pixels->item(offset - cPixelSize + cAlphaChannelOffset));
164     int center = static_cast<int>(pixels->item(offset + cAlphaChannelOffset));
165     offset -= widthMultipliedByPixelSize;
166     int topLeft = static_cast<int>(pixels->item(offset - cPixelSize + cAlphaChannelOffset));
167     int top = static_cast<int>(pixels->item(offset + cAlphaChannelOffset));
168     normalVector.setX(-topLeft + top - (left << 1) + (center << 1));
169     normalVector.setY(-topLeft - (top << 1) + left + (center << 1));
170 }
171
172 inline void FELighting::inlineSetPixel(int offset, LightingData& data, LightSource::PaintingData& paintingData,
173                                        int lightX, int lightY, float factorX, float factorY, IntPoint& normal2DVector)
174 {
175     m_lightSource->updatePaintingData(paintingData, lightX, lightY, static_cast<float>(data.pixels->item(offset + cAlphaChannelOffset)) * data.surfaceScale);
176
177     float lightStrength;
178     if (!normal2DVector.x() && !normal2DVector.y()) {
179         // Normal vector is (0, 0, 1). This is a quite frequent case.
180         if (m_lightingType == FELighting::DiffuseLighting)
181             lightStrength = m_diffuseConstant * paintingData.lightVector.z() / paintingData.lightVectorLength;
182         else {
183             FloatPoint3D halfwayVector = paintingData.lightVector;
184             halfwayVector.setZ(halfwayVector.z() + paintingData.lightVectorLength);
185             float halfwayVectorLength = halfwayVector.length();
186             if (m_specularExponent == 1)
187                 lightStrength = m_specularConstant * halfwayVector.z() / halfwayVectorLength;
188             else
189                 lightStrength = m_specularConstant * powf(halfwayVector.z() / halfwayVectorLength, m_specularExponent);
190         }
191     } else {
192         FloatPoint3D normalVector;
193         normalVector.setX(factorX * static_cast<float>(normal2DVector.x()) * data.surfaceScale);
194         normalVector.setY(factorY * static_cast<float>(normal2DVector.y()) * data.surfaceScale);
195         normalVector.setZ(1);
196         float normalVectorLength = normalVector.length();
197
198         if (m_lightingType == FELighting::DiffuseLighting)
199             lightStrength = m_diffuseConstant * (normalVector * paintingData.lightVector) / (normalVectorLength * paintingData.lightVectorLength);
200         else {
201             FloatPoint3D halfwayVector = paintingData.lightVector;
202             halfwayVector.setZ(halfwayVector.z() + paintingData.lightVectorLength);
203             float halfwayVectorLength = halfwayVector.length();
204             if (m_specularExponent == 1)
205                 lightStrength = m_specularConstant * (normalVector * halfwayVector) / (normalVectorLength * halfwayVectorLength);
206             else
207                 lightStrength = m_specularConstant * powf((normalVector * halfwayVector) / (normalVectorLength * halfwayVectorLength), m_specularExponent);
208         }
209     }
210
211     if (lightStrength > 1)
212         lightStrength = 1;
213     if (lightStrength < 0)
214         lightStrength = 0;
215
216     data.pixels->set(offset, static_cast<unsigned char>(lightStrength * paintingData.colorVector.x()));
217     data.pixels->set(offset + 1, static_cast<unsigned char>(lightStrength * paintingData.colorVector.y()));
218     data.pixels->set(offset + 2, static_cast<unsigned char>(lightStrength * paintingData.colorVector.z()));
219 }
220
221 void FELighting::setPixel(int offset, LightingData& data, LightSource::PaintingData& paintingData,
222                           int lightX, int lightY, float factorX, float factorY, IntPoint& normalVector)
223 {
224     inlineSetPixel(offset, data, paintingData, lightX, lightY, factorX, factorY, normalVector);
225 }
226
227 inline void FELighting::platformApplyGenericPaint(LightingData& data, LightSource::PaintingData& paintingData, int startY, int endY)
228 {
229     IntPoint normalVector;
230     int offset = 0;
231
232     for (int y = startY; y < endY; ++y) {
233         offset = y * data.widthMultipliedByPixelSize + cPixelSize;
234         for (int x = 1; x < data.widthDecreasedByOne; ++x, offset += cPixelSize) {
235             data.interior(offset, normalVector);
236             inlineSetPixel(offset, data, paintingData, x, y, cFactor1div4, cFactor1div4, normalVector);
237         }
238     }
239 }
240
241 void FELighting::platformApplyGenericWorker(PlatformApplyGenericParameters* parameters)
242 {
243     parameters->filter->platformApplyGenericPaint(parameters->data, parameters->paintingData, parameters->yStart, parameters->yEnd);
244 }
245
246 inline void FELighting::platformApplyGeneric(LightingData& data, LightSource::PaintingData& paintingData)
247 {
248     int optimalThreadNumber = ((data.widthDecreasedByOne - 1) * (data.heightDecreasedByOne - 1)) / s_minimalRectDimension;
249     if (optimalThreadNumber > 1) {
250         // Initialize parallel jobs
251         WTF::ParallelJobs<PlatformApplyGenericParameters> parallelJobs(&platformApplyGenericWorker, optimalThreadNumber);
252
253         // Fill the parameter array
254         int job = parallelJobs.numberOfJobs();
255         if (job > 1) {
256             // Split the job into "yStep"-sized jobs but there a few jobs that need to be slightly larger since
257             // yStep * jobs < total size. These extras are handled by the remainder "jobsWithExtra".
258             const int yStep = (data.heightDecreasedByOne - 1) / job;
259             const int jobsWithExtra = (data.heightDecreasedByOne - 1) % job;
260
261             int yStart = 1;
262             for (--job; job >= 0; --job) {
263                 PlatformApplyGenericParameters& params = parallelJobs.parameter(job);
264                 params.filter = this;
265                 params.data = data;
266                 params.paintingData = paintingData;
267                 params.yStart = yStart;
268                 yStart += job < jobsWithExtra ? yStep + 1 : yStep;
269                 params.yEnd = yStart;
270             }
271             parallelJobs.execute();
272             return;
273         }
274         // Fallback to single threaded mode.
275     }
276
277     platformApplyGenericPaint(data, paintingData, 1, data.heightDecreasedByOne);
278 }
279
280 inline void FELighting::platformApply(LightingData& data, LightSource::PaintingData& paintingData)
281 {
282     // The selection here eventually should happen dynamically on some platforms.
283 #if CPU(ARM_NEON) && CPU(ARM_TRADITIONAL) && COMPILER(GCC_OR_CLANG)
284     platformApplyNeon(data, paintingData);
285 #else
286     platformApplyGeneric(data, paintingData);
287 #endif
288 }
289
290 bool FELighting::drawLighting(Uint8ClampedArray* pixels, int width, int height)
291 {
292     LightSource::PaintingData paintingData;
293     LightingData data;
294
295     // FIXME: do something if width or height (or both) is 1 pixel.
296     // The W3 spec does not define this case. Now the filter just returns.
297     if (width <= 2 || height <= 2)
298         return false;
299
300     data.pixels = pixels;
301     data.surfaceScale = m_surfaceScale / 255.0f;
302     data.widthMultipliedByPixelSize = width * cPixelSize;
303     data.widthDecreasedByOne = width - 1;
304     data.heightDecreasedByOne = height - 1;
305     paintingData.colorVector = FloatPoint3D(m_lightingColor.red(), m_lightingColor.green(), m_lightingColor.blue());
306     m_lightSource->initPaintingData(paintingData);
307
308     // Top/Left corner.
309     IntPoint normalVector;
310     int offset = 0;
311     data.topLeft(offset, normalVector);
312     setPixel(offset, data, paintingData, 0, 0, cFactor2div3, cFactor2div3, normalVector);
313
314     // Top/Right pixel.
315     offset = data.widthMultipliedByPixelSize - cPixelSize;
316     data.topRight(offset, normalVector);
317     setPixel(offset, data, paintingData, data.widthDecreasedByOne, 0, cFactor2div3, cFactor2div3, normalVector);
318
319     // Bottom/Left pixel.
320     offset = data.heightDecreasedByOne * data.widthMultipliedByPixelSize;
321     data.bottomLeft(offset, normalVector);
322     setPixel(offset, data, paintingData, 0, data.heightDecreasedByOne, cFactor2div3, cFactor2div3, normalVector);
323
324     // Bottom/Right pixel.
325     offset = height * data.widthMultipliedByPixelSize - cPixelSize;
326     data.bottomRight(offset, normalVector);
327     setPixel(offset, data, paintingData, data.widthDecreasedByOne, data.heightDecreasedByOne, cFactor2div3, cFactor2div3, normalVector);
328
329     if (width >= 3) {
330         // Top row.
331         offset = cPixelSize;
332         for (int x = 1; x < data.widthDecreasedByOne; ++x, offset += cPixelSize) {
333             data.topRow(offset, normalVector);
334             inlineSetPixel(offset, data, paintingData, x, 0, cFactor1div3, cFactor1div2, normalVector);
335         }
336         // Bottom row.
337         offset = data.heightDecreasedByOne * data.widthMultipliedByPixelSize + cPixelSize;
338         for (int x = 1; x < data.widthDecreasedByOne; ++x, offset += cPixelSize) {
339             data.bottomRow(offset, normalVector);
340             inlineSetPixel(offset, data, paintingData, x, data.heightDecreasedByOne, cFactor1div3, cFactor1div2, normalVector);
341         }
342     }
343
344     if (height >= 3) {
345         // Left column.
346         offset = data.widthMultipliedByPixelSize;
347         for (int y = 1; y < data.heightDecreasedByOne; ++y, offset += data.widthMultipliedByPixelSize) {
348             data.leftColumn(offset, normalVector);
349             inlineSetPixel(offset, data, paintingData, 0, y, cFactor1div2, cFactor1div3, normalVector);
350         }
351         // Right column.
352         offset = (data.widthMultipliedByPixelSize << 1) - cPixelSize;
353         for (int y = 1; y < data.heightDecreasedByOne; ++y, offset += data.widthMultipliedByPixelSize) {
354             data.rightColumn(offset, normalVector);
355             inlineSetPixel(offset, data, paintingData, data.widthDecreasedByOne, y, cFactor1div2, cFactor1div3, normalVector);
356         }
357     }
358
359     if (width >= 3 && height >= 3) {
360         // Interior pixels.
361         platformApply(data, paintingData);
362     }
363
364     int lastPixel = data.widthMultipliedByPixelSize * height;
365     if (m_lightingType == DiffuseLighting) {
366         for (int i = cAlphaChannelOffset; i < lastPixel; i += cPixelSize)
367             data.pixels->set(i, cOpaqueAlpha);
368     } else {
369         for (int i = 0; i < lastPixel; i += cPixelSize) {
370             unsigned char a1 = data.pixels->item(i);
371             unsigned char a2 = data.pixels->item(i + 1);
372             unsigned char a3 = data.pixels->item(i + 2);
373             // alpha set to set to max(a1, a2, a3)
374             data.pixels->set(i + 3, a1 >= a2 ? (a1 >= a3 ? a1 : a3) : (a2 >= a3 ? a2 : a3));
375         }
376     }
377
378     return true;
379 }
380
381 void FELighting::platformApplySoftware()
382 {
383     FilterEffect* in = inputEffect(0);
384
385     Uint8ClampedArray* srcPixelArray = createPremultipliedImageResult();
386     if (!srcPixelArray)
387         return;
388
389     setIsAlphaImage(false);
390
391     IntRect effectDrawingRect = requestedRegionOfInputImageData(in->absolutePaintRect());
392     in->copyPremultipliedImage(srcPixelArray, effectDrawingRect);
393
394     // FIXME: support kernelUnitLengths other than (1,1). The issue here is that the W3
395     // standard has no test case for them, and other browsers (like Firefox) has strange
396     // output for various kernelUnitLengths, and I am not sure they are reliable.
397     // Anyway, feConvolveMatrix should also use the implementation
398
399     IntSize absolutePaintSize = absolutePaintRect().size();
400     drawLighting(srcPixelArray, absolutePaintSize.width(), absolutePaintSize.height());
401 }
402
403 } // namespace WebCore