The ARM NEON optimized filters does not compile on THUMB2
[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
29 #if ENABLE(FILTERS)
30 #include "FELighting.h"
31
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,
38     float diffuseConstant, float specularConstant, float specularExponent,
39     float kernelUnitLengthX, float kernelUnitLengthY, PassRefPtr<LightSource> lightSource)
40     : FilterEffect(filter)
41     , m_lightingType(lightingType)
42     , m_lightSource(lightSource)
43     , m_lightingColor(lightingColor)
44     , m_surfaceScale(surfaceScale)
45     , m_diffuseConstant(diffuseConstant)
46     , m_specularConstant(specularConstant)
47     , m_specularExponent(specularExponent)
48     , m_kernelUnitLengthX(kernelUnitLengthX)
49     , m_kernelUnitLengthY(kernelUnitLengthY)
50 {
51 }
52
53 const static int cPixelSize = 4;
54 const static int cAlphaChannelOffset = 3;
55 const static unsigned char cOpaqueAlpha = static_cast<unsigned char>(0xff);
56 const static float cFactor1div2 = -1 / 2.f;
57 const static float cFactor1div3 = -1 / 3.f;
58 const static float cFactor1div4 = -1 / 4.f;
59 const static float cFactor2div3 = -2 / 3.f;
60
61 // << 1 is signed multiply by 2
62 inline void FELighting::LightingData::topLeft(int offset, IntPoint& normalVector)
63 {
64     int center = static_cast<int>(pixels->item(offset + cAlphaChannelOffset));
65     int right = static_cast<int>(pixels->item(offset + cPixelSize + cAlphaChannelOffset));
66     offset += widthMultipliedByPixelSize;
67     int bottom = static_cast<int>(pixels->item(offset + cAlphaChannelOffset));
68     int bottomRight = static_cast<int>(pixels->item(offset + cPixelSize + cAlphaChannelOffset));
69     normalVector.setX(-(center << 1) + (right << 1) - bottom + bottomRight);
70     normalVector.setY(-(center << 1) - right + (bottom << 1) + bottomRight);
71 }
72
73 inline void FELighting::LightingData::topRow(int offset, IntPoint& normalVector)
74 {
75     int left = static_cast<int>(pixels->item(offset - cPixelSize + cAlphaChannelOffset));
76     int center = static_cast<int>(pixels->item(offset + cAlphaChannelOffset));
77     int right = static_cast<int>(pixels->item(offset + cPixelSize + cAlphaChannelOffset));
78     offset += widthMultipliedByPixelSize;
79     int bottomLeft = static_cast<int>(pixels->item(offset - cPixelSize + cAlphaChannelOffset));
80     int bottom = static_cast<int>(pixels->item(offset + cAlphaChannelOffset));
81     int bottomRight = static_cast<int>(pixels->item(offset + cPixelSize + cAlphaChannelOffset));
82     normalVector.setX(-(left << 1) + (right << 1) - bottomLeft + bottomRight);
83     normalVector.setY(-left - (center << 1) - right + bottomLeft + (bottom << 1) + bottomRight);
84 }
85
86 inline void FELighting::LightingData::topRight(int offset, IntPoint& normalVector)
87 {
88     int left = static_cast<int>(pixels->item(offset - cPixelSize + cAlphaChannelOffset));
89     int center = static_cast<int>(pixels->item(offset + cAlphaChannelOffset));
90     offset += widthMultipliedByPixelSize;
91     int bottomLeft = static_cast<int>(pixels->item(offset - cPixelSize + cAlphaChannelOffset));
92     int bottom = static_cast<int>(pixels->item(offset + cAlphaChannelOffset));
93     normalVector.setX(-(left << 1) + (center << 1) - bottomLeft + bottom);
94     normalVector.setY(-left - (center << 1) + bottomLeft + (bottom << 1));
95 }
96
97 inline void FELighting::LightingData::leftColumn(int offset, IntPoint& normalVector)
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 top = static_cast<int>(pixels->item(offset + cAlphaChannelOffset));
103     int topRight = static_cast<int>(pixels->item(offset + cPixelSize + cAlphaChannelOffset));
104     offset += widthMultipliedByPixelSize << 1;
105     int bottom = static_cast<int>(pixels->item(offset + cAlphaChannelOffset));
106     int bottomRight = static_cast<int>(pixels->item(offset + cPixelSize + cAlphaChannelOffset));
107     normalVector.setX(-top + topRight - (center << 1) + (right << 1) - bottom + bottomRight);
108     normalVector.setY(-(top << 1) - topRight + (bottom << 1) + bottomRight);
109 }
110
111 inline void FELighting::LightingData::interior(int offset, IntPoint& normalVector)
112 {
113     int left = static_cast<int>(pixels->item(offset - cPixelSize + cAlphaChannelOffset));
114     int right = static_cast<int>(pixels->item(offset + cPixelSize + cAlphaChannelOffset));
115     offset -= widthMultipliedByPixelSize;
116     int topLeft = static_cast<int>(pixels->item(offset - cPixelSize + cAlphaChannelOffset));
117     int top = static_cast<int>(pixels->item(offset + cAlphaChannelOffset));
118     int topRight = static_cast<int>(pixels->item(offset + cPixelSize + cAlphaChannelOffset));
119     offset += widthMultipliedByPixelSize << 1;
120     int bottomLeft = static_cast<int>(pixels->item(offset - cPixelSize + cAlphaChannelOffset));
121     int bottom = static_cast<int>(pixels->item(offset + cAlphaChannelOffset));
122     int bottomRight = static_cast<int>(pixels->item(offset + cPixelSize + cAlphaChannelOffset));
123     normalVector.setX(-topLeft + topRight - (left << 1) + (right << 1) - bottomLeft + bottomRight);
124     normalVector.setY(-topLeft - (top << 1) - topRight + bottomLeft + (bottom << 1) + bottomRight);
125 }
126
127 inline void FELighting::LightingData::rightColumn(int offset, IntPoint& normalVector)
128 {
129     int left = static_cast<int>(pixels->item(offset - cPixelSize + cAlphaChannelOffset));
130     int center = static_cast<int>(pixels->item(offset + cAlphaChannelOffset));
131     offset -= widthMultipliedByPixelSize;
132     int topLeft = static_cast<int>(pixels->item(offset - cPixelSize + cAlphaChannelOffset));
133     int top = static_cast<int>(pixels->item(offset + cAlphaChannelOffset));
134     offset += widthMultipliedByPixelSize << 1;
135     int bottomLeft = static_cast<int>(pixels->item(offset - cPixelSize + cAlphaChannelOffset));
136     int bottom = static_cast<int>(pixels->item(offset + cAlphaChannelOffset));
137     normalVector.setX(-topLeft + top - (left << 1) + (center << 1) - bottomLeft + bottom);
138     normalVector.setY(-topLeft - (top << 1) + bottomLeft + (bottom << 1));
139 }
140
141 inline void FELighting::LightingData::bottomLeft(int offset, IntPoint& normalVector)
142 {
143     int center = static_cast<int>(pixels->item(offset + cAlphaChannelOffset));
144     int right = static_cast<int>(pixels->item(offset + cPixelSize + cAlphaChannelOffset));
145     offset -= widthMultipliedByPixelSize;
146     int top = static_cast<int>(pixels->item(offset + cAlphaChannelOffset));
147     int topRight = static_cast<int>(pixels->item(offset + cPixelSize + cAlphaChannelOffset));
148     normalVector.setX(-top + topRight - (center << 1) + (right << 1));
149     normalVector.setY(-(top << 1) - topRight + (center << 1) + right);
150 }
151
152 inline void FELighting::LightingData::bottomRow(int offset, IntPoint& normalVector)
153 {
154     int left = static_cast<int>(pixels->item(offset - cPixelSize + cAlphaChannelOffset));
155     int center = static_cast<int>(pixels->item(offset + cAlphaChannelOffset));
156     int right = static_cast<int>(pixels->item(offset + cPixelSize + cAlphaChannelOffset));
157     offset -= widthMultipliedByPixelSize;
158     int topLeft = static_cast<int>(pixels->item(offset - cPixelSize + cAlphaChannelOffset));
159     int top = static_cast<int>(pixels->item(offset + cAlphaChannelOffset));
160     int topRight = static_cast<int>(pixels->item(offset + cPixelSize + cAlphaChannelOffset));
161     normalVector.setX(-topLeft + topRight - (left << 1) + (right << 1));
162     normalVector.setY(-topLeft - (top << 1) - topRight + left + (center << 1) + right);
163 }
164
165 inline void FELighting::LightingData::bottomRight(int offset, IntPoint& normalVector)
166 {
167     int left = static_cast<int>(pixels->item(offset - cPixelSize + cAlphaChannelOffset));
168     int center = static_cast<int>(pixels->item(offset + cAlphaChannelOffset));
169     offset -= widthMultipliedByPixelSize;
170     int topLeft = static_cast<int>(pixels->item(offset - cPixelSize + cAlphaChannelOffset));
171     int top = static_cast<int>(pixels->item(offset + cAlphaChannelOffset));
172     normalVector.setX(-topLeft + top - (left << 1) + (center << 1));
173     normalVector.setY(-topLeft - (top << 1) + left + (center << 1));
174 }
175
176 inline void FELighting::inlineSetPixel(int offset, LightingData& data, LightSource::PaintingData& paintingData,
177                                        int lightX, int lightY, float factorX, float factorY, IntPoint& normal2DVector)
178 {
179     m_lightSource->updatePaintingData(paintingData, lightX, lightY, static_cast<float>(data.pixels->item(offset + cAlphaChannelOffset)) * data.surfaceScale);
180
181     float lightStrength;
182     if (!normal2DVector.x() && !normal2DVector.y()) {
183         // Normal vector is (0, 0, 1). This is a quite frequent case.
184         if (m_lightingType == FELighting::DiffuseLighting)
185             lightStrength = m_diffuseConstant * paintingData.lightVector.z() / paintingData.lightVectorLength;
186         else {
187             FloatPoint3D halfwayVector = paintingData.lightVector;
188             halfwayVector.setZ(halfwayVector.z() + paintingData.lightVectorLength);
189             float halfwayVectorLength = halfwayVector.length();
190             if (m_specularExponent == 1)
191                 lightStrength = m_specularConstant * halfwayVector.z() / halfwayVectorLength;
192             else
193                 lightStrength = m_specularConstant * powf(halfwayVector.z() / halfwayVectorLength, m_specularExponent);
194         }
195     } else {
196         FloatPoint3D normalVector;
197         normalVector.setX(factorX * static_cast<float>(normal2DVector.x()) * data.surfaceScale);
198         normalVector.setY(factorY * static_cast<float>(normal2DVector.y()) * data.surfaceScale);
199         normalVector.setZ(1);
200         float normalVectorLength = normalVector.length();
201
202         if (m_lightingType == FELighting::DiffuseLighting)
203             lightStrength = m_diffuseConstant * (normalVector * paintingData.lightVector) / (normalVectorLength * paintingData.lightVectorLength);
204         else {
205             FloatPoint3D halfwayVector = paintingData.lightVector;
206             halfwayVector.setZ(halfwayVector.z() + paintingData.lightVectorLength);
207             float halfwayVectorLength = halfwayVector.length();
208             if (m_specularExponent == 1)
209                 lightStrength = m_specularConstant * (normalVector * halfwayVector) / (normalVectorLength * halfwayVectorLength);
210             else
211                 lightStrength = m_specularConstant * powf((normalVector * halfwayVector) / (normalVectorLength * halfwayVectorLength), m_specularExponent);
212         }
213     }
214
215     if (lightStrength > 1)
216         lightStrength = 1;
217     if (lightStrength < 0)
218         lightStrength = 0;
219
220     data.pixels->set(offset, static_cast<unsigned char>(lightStrength * paintingData.colorVector.x()));
221     data.pixels->set(offset + 1, static_cast<unsigned char>(lightStrength * paintingData.colorVector.y()));
222     data.pixels->set(offset + 2, static_cast<unsigned char>(lightStrength * paintingData.colorVector.z()));
223 }
224
225 void FELighting::setPixel(int offset, LightingData& data, LightSource::PaintingData& paintingData,
226                           int lightX, int lightY, float factorX, float factorY, IntPoint& normalVector)
227 {
228     inlineSetPixel(offset, data, paintingData, lightX, lightY, factorX, factorY, normalVector);
229 }
230
231 inline void FELighting::platformApplyGenericPaint(LightingData& data, LightSource::PaintingData& paintingData, int startY, int endY)
232 {
233     IntPoint normalVector;
234     int offset = 0;
235
236     for (int y = startY; y < endY; ++y) {
237         offset = y * data.widthMultipliedByPixelSize + cPixelSize;
238         for (int x = 1; x < data.widthDecreasedByOne; ++x, offset += cPixelSize) {
239             data.interior(offset, normalVector);
240             inlineSetPixel(offset, data, paintingData, x, y, cFactor1div4, cFactor1div4, normalVector);
241         }
242     }
243 }
244
245 void FELighting::platformApplyGenericWorker(PlatformApplyGenericParameters* parameters)
246 {
247     parameters->filter->platformApplyGenericPaint(parameters->data, parameters->paintingData, parameters->yStart, parameters->yEnd);
248 }
249
250 inline void FELighting::platformApplyGeneric(LightingData& data, LightSource::PaintingData& paintingData)
251 {
252     int optimalThreadNumber = ((data.widthDecreasedByOne - 1) * (data.heightDecreasedByOne - 1)) / s_minimalRectDimension;
253     if (optimalThreadNumber > 1) {
254         // Initialize parallel jobs
255         WTF::ParallelJobs<PlatformApplyGenericParameters> parallelJobs(&platformApplyGenericWorker, optimalThreadNumber);
256
257         // Fill the parameter array
258         int job = parallelJobs.numberOfJobs();
259         if (job > 1) {
260             int yStart = 1;
261             int yStep = (data.heightDecreasedByOne - 1) / job;
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                 if (job > 0) {
269                     params.yEnd = yStart + yStep;
270                     yStart += yStep;
271                 } else
272                     params.yEnd = data.heightDecreasedByOne;
273             }
274             parallelJobs.execute();
275             return;
276         }
277         // Fallback to single threaded mode.
278     }
279
280     platformApplyGenericPaint(data, paintingData, 1, data.heightDecreasedByOne);
281 }
282
283 inline void FELighting::platformApply(LightingData& data, LightSource::PaintingData& paintingData)
284 {
285     // The selection here eventually should happen dynamically on some platforms.
286 #if CPU(ARM_NEON) && CPU(ARM_TRADITIONAL) && COMPILER(GCC)
287     platformApplyNeon(data, paintingData);
288 #else
289     platformApplyGeneric(data, paintingData);
290 #endif
291 }
292
293 bool FELighting::drawLighting(Uint8ClampedArray* pixels, int width, int height)
294 {
295     LightSource::PaintingData paintingData;
296     LightingData data;
297
298     if (!m_lightSource)
299         return false;
300
301     // FIXME: do something if width or height (or both) is 1 pixel.
302     // The W3 spec does not define this case. Now the filter just returns.
303     if (width <= 2 || height <= 2)
304         return false;
305
306     data.pixels = pixels;
307     data.surfaceScale = m_surfaceScale / 255.0f;
308     data.widthMultipliedByPixelSize = width * cPixelSize;
309     data.widthDecreasedByOne = width - 1;
310     data.heightDecreasedByOne = height - 1;
311     paintingData.colorVector = FloatPoint3D(m_lightingColor.red(), m_lightingColor.green(), m_lightingColor.blue());
312     m_lightSource->initPaintingData(paintingData);
313
314     // Top/Left corner.
315     IntPoint normalVector;
316     int offset = 0;
317     data.topLeft(offset, normalVector);
318     setPixel(offset, data, paintingData, 0, 0, cFactor2div3, cFactor2div3, normalVector);
319
320     // Top/Right pixel.
321     offset = data.widthMultipliedByPixelSize - cPixelSize;
322     data.topRight(offset, normalVector);
323     setPixel(offset, data, paintingData, data.widthDecreasedByOne, 0, cFactor2div3, cFactor2div3, normalVector);
324
325     // Bottom/Left pixel.
326     offset = data.heightDecreasedByOne * data.widthMultipliedByPixelSize;
327     data.bottomLeft(offset, normalVector);
328     setPixel(offset, data, paintingData, 0, data.heightDecreasedByOne, cFactor2div3, cFactor2div3, normalVector);
329
330     // Bottom/Right pixel.
331     offset = height * data.widthMultipliedByPixelSize - cPixelSize;
332     data.bottomRight(offset, normalVector);
333     setPixel(offset, data, paintingData, data.widthDecreasedByOne, data.heightDecreasedByOne, cFactor2div3, cFactor2div3, normalVector);
334
335     if (width >= 3) {
336         // Top row.
337         offset = cPixelSize;
338         for (int x = 1; x < data.widthDecreasedByOne; ++x, offset += cPixelSize) {
339             data.topRow(offset, normalVector);
340             inlineSetPixel(offset, data, paintingData, x, 0, cFactor1div3, cFactor1div2, normalVector);
341         }
342         // Bottom row.
343         offset = data.heightDecreasedByOne * data.widthMultipliedByPixelSize + cPixelSize;
344         for (int x = 1; x < data.widthDecreasedByOne; ++x, offset += cPixelSize) {
345             data.bottomRow(offset, normalVector);
346             inlineSetPixel(offset, data, paintingData, x, data.heightDecreasedByOne, cFactor1div3, cFactor1div2, normalVector);
347         }
348     }
349
350     if (height >= 3) {
351         // Left column.
352         offset = data.widthMultipliedByPixelSize;
353         for (int y = 1; y < data.heightDecreasedByOne; ++y, offset += data.widthMultipliedByPixelSize) {
354             data.leftColumn(offset, normalVector);
355             inlineSetPixel(offset, data, paintingData, 0, y, cFactor1div2, cFactor1div3, normalVector);
356         }
357         // Right column.
358         offset = (data.widthMultipliedByPixelSize << 1) - cPixelSize;
359         for (int y = 1; y < data.heightDecreasedByOne; ++y, offset += data.widthMultipliedByPixelSize) {
360             data.rightColumn(offset, normalVector);
361             inlineSetPixel(offset, data, paintingData, data.widthDecreasedByOne, y, cFactor1div2, cFactor1div3, normalVector);
362         }
363     }
364
365     if (width >= 3 && height >= 3) {
366         // Interior pixels.
367         platformApply(data, paintingData);
368     }
369
370     int lastPixel = data.widthMultipliedByPixelSize * height;
371     if (m_lightingType == DiffuseLighting) {
372         for (int i = cAlphaChannelOffset; i < lastPixel; i += cPixelSize)
373             data.pixels->set(i, cOpaqueAlpha);
374     } else {
375         for (int i = 0; i < lastPixel; i += cPixelSize) {
376             unsigned char a1 = data.pixels->item(i);
377             unsigned char a2 = data.pixels->item(i + 1);
378             unsigned char a3 = data.pixels->item(i + 2);
379             // alpha set to set to max(a1, a2, a3)
380             data.pixels->set(i + 3, a1 >= a2 ? (a1 >= a3 ? a1 : a3) : (a2 >= a3 ? a2 : a3));
381         }
382     }
383
384     return true;
385 }
386
387 void FELighting::platformApplySoftware()
388 {
389     FilterEffect* in = inputEffect(0);
390
391     Uint8ClampedArray* srcPixelArray = createUnmultipliedImageResult();
392     if (!srcPixelArray)
393         return;
394
395     setIsAlphaImage(false);
396
397     IntRect effectDrawingRect = requestedRegionOfInputImageData(in->absolutePaintRect());
398     in->copyUnmultipliedImage(srcPixelArray, effectDrawingRect);
399
400     // FIXME: support kernelUnitLengths other than (1,1). The issue here is that the W3
401     // standard has no test case for them, and other browsers (like Firefox) has strange
402     // output for various kernelUnitLengths, and I am not sure they are reliable.
403     // Anyway, feConvolveMatrix should also use the implementation
404
405     IntSize absolutePaintSize = absolutePaintRect().size();
406     drawLighting(srcPixelArray, absolutePaintSize.width(), absolutePaintSize.height());
407 }
408
409 } // namespace WebCore
410
411 #endif // ENABLE(FILTERS)