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