2 * Copyright (C) 2010 University of Szeged
3 * Copyright (C) 2010 Zoltan Herczeg
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
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.
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.
30 #include "FELighting.h"
32 #include "CanvasPixelArray.h"
33 #include "ImageData.h"
34 #include "LightSource.h"
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)
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)
54 const static int cPixelSize = 4;
55 const static int cAlphaChannelOffset = 3;
56 const static unsigned char cOpaqueAlpha = static_cast<unsigned char>(0xff);
58 ALWAYS_INLINE int FELighting::LightingData::upLeftPixelValue()
60 return static_cast<int>(pixels->get(offset - widthMultipliedByPixelSize - cPixelSize + cAlphaChannelOffset));
63 ALWAYS_INLINE int FELighting::LightingData::upPixelValue()
65 return static_cast<int>(pixels->get(offset - widthMultipliedByPixelSize + cAlphaChannelOffset));
68 ALWAYS_INLINE int FELighting::LightingData::upRightPixelValue()
70 return static_cast<int>(pixels->get(offset - widthMultipliedByPixelSize + cPixelSize + cAlphaChannelOffset));
73 ALWAYS_INLINE int FELighting::LightingData::leftPixelValue()
75 return static_cast<int>(pixels->get(offset - cPixelSize + cAlphaChannelOffset));
78 ALWAYS_INLINE int FELighting::LightingData::centerPixelValue()
80 return static_cast<int>(pixels->get(offset + cAlphaChannelOffset));
83 ALWAYS_INLINE int FELighting::LightingData::rightPixelValue()
85 return static_cast<int>(pixels->get(offset + cPixelSize + cAlphaChannelOffset));
88 ALWAYS_INLINE int FELighting::LightingData::downLeftPixelValue()
90 return static_cast<int>(pixels->get(offset + widthMultipliedByPixelSize - cPixelSize + cAlphaChannelOffset));
93 ALWAYS_INLINE int FELighting::LightingData::downPixelValue()
95 return static_cast<int>(pixels->get(offset + widthMultipliedByPixelSize + cAlphaChannelOffset));
98 ALWAYS_INLINE int FELighting::LightingData::downRightPixelValue()
100 return static_cast<int>(pixels->get(offset + widthMultipliedByPixelSize + cPixelSize + cAlphaChannelOffset));
103 ALWAYS_INLINE void FELighting::setPixel(LightingData& data, LightSource::PaintingData& paintingData,
104 int lightX, int lightY, float factorX, int normalX, float factorY, int normalY)
106 m_lightSource->updatePaintingData(paintingData, lightX, lightY, static_cast<float>(data.pixels->get(data.offset + 3)) * data.surfaceScale);
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();
113 if (m_lightingType == FELighting::DiffuseLighting)
114 data.lightStrength = m_diffuseConstant * (data.normalVector * paintingData.lightVector);
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);
122 data.lightStrength = m_specularConstant * powf(data.normalVector * halfwayVector, m_specularExponent);
125 if (data.lightStrength > 1.0f)
126 data.lightStrength = 1.0f;
127 if (data.lightStrength < 0.0f)
128 data.lightStrength = 0.0f;
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()));
135 bool FELighting::drawLighting(CanvasPixelArray* pixels, int width, int height)
137 LightSource::PaintingData paintingData;
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)
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);
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());
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());
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());
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());
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());
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());
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());
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());
214 if (width >= 3 && height >= 3) {
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());
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);
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));
243 void FELighting::apply(Filter* filter)
245 FilterEffect* in = inputEffect(0);
247 if (!in->resultImage())
250 if (!effectContext())
253 setIsAlphaImage(false);
255 IntRect effectDrawingRect = requestedRegionOfInputImageData(in->repaintRectInLocalCoordinates());
256 RefPtr<ImageData> srcImageData(in->resultImage()->getUnmultipliedImageData(effectDrawingRect));
257 CanvasPixelArray* srcPixelArray(srcImageData->data());
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
264 if (drawLighting(srcPixelArray, effectDrawingRect.width(), effectDrawingRect.height()))
265 resultImage()->putUnmultipliedImageData(srcImageData.get(), IntRect(IntPoint(), resultImage()->size()), IntPoint());
268 } // namespace WebCore
270 #endif // ENABLE(FILTERS)