OSDN Git Service

am 7fc27f09: (-s ours) am 3d57c253: Implement the audio tag in webkit -- the correspo...
[android-x86/external-webkit.git] / 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 "CanvasPixelArray.h"
33 #include "ImageData.h"
34 #include "LightSource.h"
35
36 namespace WebCore {
37
38 FELighting::FELighting(LightingType lightingType, const Color& lightingColor, float surfaceScale,
39     float diffuseConstant, float specularConstant, float specularExponent,
40     float kernelUnitLengthX, float kernelUnitLengthY, PassRefPtr<LightSource> lightSource)
41     : FilterEffect()
42     , m_lightingType(lightingType)
43     , m_lightSource(lightSource)
44     , m_lightingColor(lightingColor)
45     , m_surfaceScale(surfaceScale)
46     , m_diffuseConstant(diffuseConstant)
47     , m_specularConstant(specularConstant)
48     , m_specularExponent(specularExponent)
49     , m_kernelUnitLengthX(kernelUnitLengthX)
50     , m_kernelUnitLengthY(kernelUnitLengthY)
51 {
52 }
53
54 const static int cPixelSize = 4;
55 const static int cAlphaChannelOffset = 3;
56 const static unsigned char cOpaqueAlpha = static_cast<unsigned char>(0xff);
57
58 ALWAYS_INLINE int FELighting::LightingData::upLeftPixelValue()
59 {
60     return static_cast<int>(pixels->get(offset - widthMultipliedByPixelSize - cPixelSize + cAlphaChannelOffset));
61 }
62
63 ALWAYS_INLINE int FELighting::LightingData::upPixelValue()
64 {
65     return static_cast<int>(pixels->get(offset - widthMultipliedByPixelSize + cAlphaChannelOffset));
66 }
67
68 ALWAYS_INLINE int FELighting::LightingData::upRightPixelValue()
69 {
70     return static_cast<int>(pixels->get(offset - widthMultipliedByPixelSize + cPixelSize + cAlphaChannelOffset));
71 }
72
73 ALWAYS_INLINE int FELighting::LightingData::leftPixelValue()
74 {
75     return static_cast<int>(pixels->get(offset - cPixelSize + cAlphaChannelOffset));
76 }
77
78 ALWAYS_INLINE int FELighting::LightingData::centerPixelValue()
79 {
80     return static_cast<int>(pixels->get(offset + cAlphaChannelOffset));
81 }
82
83 ALWAYS_INLINE int FELighting::LightingData::rightPixelValue()
84 {
85     return static_cast<int>(pixels->get(offset + cPixelSize + cAlphaChannelOffset));
86 }
87
88 ALWAYS_INLINE int FELighting::LightingData::downLeftPixelValue()
89 {
90     return static_cast<int>(pixels->get(offset + widthMultipliedByPixelSize - cPixelSize + cAlphaChannelOffset));
91 }
92
93 ALWAYS_INLINE int FELighting::LightingData::downPixelValue()
94 {
95     return static_cast<int>(pixels->get(offset + widthMultipliedByPixelSize + cAlphaChannelOffset));
96 }
97
98 ALWAYS_INLINE int FELighting::LightingData::downRightPixelValue()
99 {
100     return static_cast<int>(pixels->get(offset + widthMultipliedByPixelSize + cPixelSize + cAlphaChannelOffset));
101 }
102
103 ALWAYS_INLINE void FELighting::setPixel(LightingData& data, LightSource::PaintingData& paintingData,
104     int lightX, int lightY, float factorX, int normalX, float factorY, int normalY)
105 {
106     m_lightSource->updatePaintingData(paintingData, lightX, lightY, static_cast<float>(data.pixels->get(data.offset + 3)) * data.surfaceScale);
107
108     data.normalVector.setX(factorX * static_cast<float>(normalX) * data.surfaceScale);
109     data.normalVector.setY(factorY * static_cast<float>(normalY) * data.surfaceScale);
110     data.normalVector.setZ(1.0f);
111     data.normalVector.normalize();
112
113     if (m_lightingType == FELighting::DiffuseLighting)
114         data.lightStrength = m_diffuseConstant * (data.normalVector * paintingData.lightVector);
115     else {
116         FloatPoint3D halfwayVector = paintingData.lightVector;
117         halfwayVector.setZ(halfwayVector.z() + 1.0f);
118         halfwayVector.normalize();
119         if (m_specularExponent == 1.0f)
120             data.lightStrength = m_specularConstant * (data.normalVector * halfwayVector);
121         else
122             data.lightStrength = m_specularConstant * powf(data.normalVector * halfwayVector, m_specularExponent);
123     }
124
125     if (data.lightStrength > 1.0f)
126         data.lightStrength = 1.0f;
127     if (data.lightStrength < 0.0f)
128         data.lightStrength = 0.0f;
129
130     data.pixels->set(data.offset, static_cast<unsigned char>(data.lightStrength * paintingData.colorVector.x()));
131     data.pixels->set(data.offset + 1, static_cast<unsigned char>(data.lightStrength * paintingData.colorVector.y()));
132     data.pixels->set(data.offset + 2, static_cast<unsigned char>(data.lightStrength * paintingData.colorVector.z()));
133 }
134
135 bool FELighting::drawLighting(CanvasPixelArray* pixels, int width, int height)
136 {
137     LightSource::PaintingData paintingData;
138     LightingData data;
139
140     if (!m_lightSource)
141         return false;
142
143     // FIXME: do something if width or height (or both) is 1 pixel.
144     // The W3 spec does not define this case. Now the filter just returns.
145     if (width <= 2 || height <= 2)
146         return false;
147
148     data.pixels = pixels;
149     data.surfaceScale = m_surfaceScale / 255.0f;
150     data.widthMultipliedByPixelSize = width * cPixelSize;
151     data.widthDecreasedByOne = width - 1;
152     data.heightDecreasedByOne = height - 1;
153     paintingData.colorVector = FloatPoint3D(m_lightingColor.red(), m_lightingColor.green(), m_lightingColor.blue());
154     m_lightSource->initPaintingData(paintingData);
155
156     // Top/Left corner
157     data.offset = 0;
158     setPixel(data, paintingData, 0, 0,
159         -2.0f / 3.0f, -2 * data.centerPixelValue() + 2 * data.rightPixelValue() - data.downPixelValue() + data.downRightPixelValue(),
160         -2.0f / 3.0f, -2 * data.centerPixelValue() - data.rightPixelValue() + 2 * data.downPixelValue() + data.downRightPixelValue());
161
162     // Top/Right pixel
163     data.offset = data.widthMultipliedByPixelSize - cPixelSize;
164     setPixel(data, paintingData, data.widthDecreasedByOne, 0,
165         -2.0f / 3.0f, -2 * data.leftPixelValue() + 2 * data.centerPixelValue() - data.downLeftPixelValue() + data.downPixelValue(),
166         -2.0f / 3.0f, -data.leftPixelValue() - 2 * data.centerPixelValue() + data.downLeftPixelValue() + 2 * data.downPixelValue());
167
168     // Bottom/Left pixel
169     data.offset = data.heightDecreasedByOne * data.widthMultipliedByPixelSize;
170     setPixel(data, paintingData, 0, data.heightDecreasedByOne,
171         -2.0f / 3.0f, -data.upPixelValue() + data.upRightPixelValue() - 2 * data.centerPixelValue() + 2 * data.rightPixelValue(),
172         -2.0f / 3.0f, -2 * data.upPixelValue() - data.upRightPixelValue() + 2 * data.centerPixelValue() + data.rightPixelValue());
173
174     // Bottom/Right pixel
175     data.offset = height * data.widthMultipliedByPixelSize - cPixelSize;
176     setPixel(data, paintingData, data.widthDecreasedByOne, data.heightDecreasedByOne,
177         -2.0f / 3.0f, -data.upLeftPixelValue() + data.upPixelValue() - 2 * data.leftPixelValue() + 2 * data.centerPixelValue(),
178         -2.0f / 3.0f, -data.upLeftPixelValue() - 2 * data.upPixelValue() + data.leftPixelValue() + 2 * data.centerPixelValue());
179
180     if (width >= 3) {
181         // Top line
182         data.offset = cPixelSize;
183         for (int x = 1; x < data.widthDecreasedByOne; ++x, data.offset += cPixelSize) {
184             setPixel(data, paintingData, x, 0,
185                 -1.0f / 3.0f, -2 * data.leftPixelValue() + 2 * data.rightPixelValue() - data.downLeftPixelValue() + data.downRightPixelValue(),
186                 -1.0f / 2.0f, -data.leftPixelValue() - 2 * data.centerPixelValue() - data.rightPixelValue() + data.downLeftPixelValue() + 2 * data.downPixelValue() + data.downRightPixelValue());
187         }
188         // Bottom line
189         data.offset = data.heightDecreasedByOne * data.widthMultipliedByPixelSize + cPixelSize;
190         for (int x = 1; x < data.widthDecreasedByOne; ++x, data.offset += cPixelSize) {
191             setPixel(data, paintingData, x, data.heightDecreasedByOne,
192                 -1.0f / 3.0f, -data.upLeftPixelValue() + data.upRightPixelValue() - 2 * data.leftPixelValue() + 2 * data.rightPixelValue(),
193                 -1.0f / 2.0f, -data.upLeftPixelValue() - 2 * data.upPixelValue() - data.upRightPixelValue() + data.leftPixelValue() + 2 * data.centerPixelValue() + data.rightPixelValue());
194         }
195     }
196
197     if (height >= 3) {
198         // Left line
199         data.offset = data.widthMultipliedByPixelSize;
200         for (int y = 1; y < data.heightDecreasedByOne; ++y, data.offset += data.widthMultipliedByPixelSize) {
201             setPixel(data, paintingData, 0, y,
202                 -1.0f / 2.0f, -data.upPixelValue() + data.upRightPixelValue() - 2 * data.centerPixelValue() + 2 * data.rightPixelValue() - data.downPixelValue() + data.downRightPixelValue(),
203                 -1.0f / 3.0f, -2 * data.upPixelValue() - data.upRightPixelValue() + 2 * data.downPixelValue() + data.downRightPixelValue());
204         }
205         // Right line
206         data.offset = data.widthMultipliedByPixelSize + data.widthMultipliedByPixelSize - cPixelSize;
207         for (int y = 1; y < data.heightDecreasedByOne; ++y, data.offset += data.widthMultipliedByPixelSize) {
208             setPixel(data, paintingData, data.widthDecreasedByOne, y,
209                 -1.0f / 2.0f, -data.upLeftPixelValue() + data.upPixelValue() -2 * data.leftPixelValue() + 2 * data.centerPixelValue() - data.downLeftPixelValue() + data.downPixelValue(),
210                 -1.0f / 3.0f, -data.upLeftPixelValue() - 2 * data.upPixelValue() + data.downLeftPixelValue() + 2 * data.downPixelValue());
211         }
212     }
213
214     if (width >= 3 && height >= 3) {
215         // Interior pixels
216         for (int y = 1; y < data.heightDecreasedByOne; ++y) {
217             data.offset = y * data.widthMultipliedByPixelSize + cPixelSize;
218             for (int x = 1; x < data.widthDecreasedByOne; ++x, data.offset += cPixelSize) {
219                 setPixel(data, paintingData, x, y,
220                     -1.0f / 4.0f, -data.upLeftPixelValue() + data.upRightPixelValue() - 2 * data.leftPixelValue() + 2 * data.rightPixelValue() - data.downLeftPixelValue() + data.downRightPixelValue(),
221                     -1.0f / 4.0f, -data.upLeftPixelValue() - 2 * data.upPixelValue() - data.upRightPixelValue() + data.downLeftPixelValue() + 2 * data.downPixelValue() + data.downRightPixelValue());
222             }
223         }
224     }
225
226     int totalSize = data.widthMultipliedByPixelSize * height;
227     if (m_lightingType == DiffuseLighting) {
228         for (int i = 3; i < totalSize; i += 4)
229             data.pixels->set(i, cOpaqueAlpha);
230     } else {
231         for (int i = 0; i < totalSize; i += 4) {
232             unsigned char a1 = data.pixels->get(i);
233             unsigned char a2 = data.pixels->get(i + 1);
234             unsigned char a3 = data.pixels->get(i + 2);
235             // alpha set to set to max(a1, a2, a3)
236             data.pixels->set(i + 3, a1 >= a2 ? (a1 >= a3 ? a1 : a3) : (a2 >= a3 ? a2 : a3));
237         }
238     }
239
240     return true;
241 }
242
243 void FELighting::apply(Filter* filter)
244 {
245     FilterEffect* in = inputEffect(0);
246     in->apply(filter);
247     if (!in->resultImage())
248         return;
249
250     if (!effectContext())
251         return;
252
253     setIsAlphaImage(false);
254
255     IntRect effectDrawingRect = requestedRegionOfInputImageData(in->repaintRectInLocalCoordinates());
256     RefPtr<ImageData> srcImageData(in->resultImage()->getUnmultipliedImageData(effectDrawingRect));
257     CanvasPixelArray* srcPixelArray(srcImageData->data());
258
259     // FIXME: support kernelUnitLengths other than (1,1). The issue here is that the W3
260     // standard has no test case for them, and other browsers (like Firefox) has strange
261     // output for various kernelUnitLengths, and I am not sure they are reliable.
262     // Anyway, feConvolveMatrix should also use the implementation
263
264     if (drawLighting(srcPixelArray, effectDrawingRect.width(), effectDrawingRect.height()))
265         resultImage()->putUnmultipliedImageData(srcImageData.get(), IntRect(IntPoint(), resultImage()->size()), IntPoint());
266 }
267
268 } // namespace WebCore
269
270 #endif // ENABLE(FILTERS)