FELighting.cpp revision cad810f21b803229eb11403f9209855525a25d57
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 "LightSource.h" 33 34namespace WebCore { 35 36FELighting::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 52const static int cPixelSize = 4; 53const static int cAlphaChannelOffset = 3; 54const static unsigned char cOpaqueAlpha = static_cast<unsigned char>(0xff); 55const static float cFactor1div2 = -1 / 2.f; 56const static float cFactor1div3 = -1 / 3.f; 57const static float cFactor1div4 = -1 / 4.f; 58const static float cFactor2div3 = -2 / 3.f; 59 60// << 1 is signed multiply by 2 61inline 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 72inline 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 85inline 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 96inline 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 110inline 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 126inline 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 140inline 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 151inline 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 164inline 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 175inline 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 224void 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 230bool FELighting::drawLighting(ByteArray* pixels, int width, int height) 231{ 232 LightSource::PaintingData paintingData; 233 LightingData data; 234 235 if (!m_lightSource) 236 return false; 237 238 // FIXME: do something if width or height (or both) is 1 pixel. 239 // The W3 spec does not define this case. Now the filter just returns. 240 if (width <= 2 || height <= 2) 241 return false; 242 243 data.pixels = pixels; 244 data.surfaceScale = m_surfaceScale / 255.0f; 245 data.widthMultipliedByPixelSize = width * cPixelSize; 246 data.widthDecreasedByOne = width - 1; 247 data.heightDecreasedByOne = height - 1; 248 paintingData.colorVector = FloatPoint3D(m_lightingColor.red(), m_lightingColor.green(), m_lightingColor.blue()); 249 m_lightSource->initPaintingData(paintingData); 250 251 // Top/Left corner 252 IntPoint normalVector; 253 int offset = 0; 254 data.topLeft(offset, normalVector); 255 setPixel(offset, data, paintingData, 0, 0, cFactor2div3, cFactor2div3, normalVector); 256 257 // Top/Right pixel 258 offset = data.widthMultipliedByPixelSize - cPixelSize; 259 data.topRight(offset, normalVector); 260 setPixel(offset, data, paintingData, data.widthDecreasedByOne, 0, cFactor2div3, cFactor2div3, normalVector); 261 262 // Bottom/Left pixel 263 offset = data.heightDecreasedByOne * data.widthMultipliedByPixelSize; 264 data.bottomLeft(offset, normalVector); 265 setPixel(offset, data, paintingData, 0, data.heightDecreasedByOne, cFactor2div3, cFactor2div3, normalVector); 266 267 // Bottom/Right pixel 268 offset = height * data.widthMultipliedByPixelSize - cPixelSize; 269 data.bottomRight(offset, normalVector); 270 setPixel(offset, data, paintingData, data.widthDecreasedByOne, data.heightDecreasedByOne, cFactor2div3, cFactor2div3, normalVector); 271 272 if (width >= 3) { 273 // Top row 274 offset = cPixelSize; 275 for (int x = 1; x < data.widthDecreasedByOne; ++x, offset += cPixelSize) { 276 data.topRow(offset, normalVector); 277 inlineSetPixel(offset, data, paintingData, x, 0, cFactor1div3, cFactor1div2, normalVector); 278 } 279 // Bottom row 280 offset = data.heightDecreasedByOne * data.widthMultipliedByPixelSize + cPixelSize; 281 for (int x = 1; x < data.widthDecreasedByOne; ++x, offset += cPixelSize) { 282 data.bottomRow(offset, normalVector); 283 inlineSetPixel(offset, data, paintingData, x, data.heightDecreasedByOne, cFactor1div3, cFactor1div2, normalVector); 284 } 285 } 286 287 if (height >= 3) { 288 // Left column 289 offset = data.widthMultipliedByPixelSize; 290 for (int y = 1; y < data.heightDecreasedByOne; ++y, offset += data.widthMultipliedByPixelSize) { 291 data.leftColumn(offset, normalVector); 292 inlineSetPixel(offset, data, paintingData, 0, y, cFactor1div2, cFactor1div3, normalVector); 293 } 294 // Right column 295 offset = (data.widthMultipliedByPixelSize << 1) - cPixelSize; 296 for (int y = 1; y < data.heightDecreasedByOne; ++y, offset += data.widthMultipliedByPixelSize) { 297 data.rightColumn(offset, normalVector); 298 inlineSetPixel(offset, data, paintingData, data.widthDecreasedByOne, y, cFactor1div2, cFactor1div3, normalVector); 299 } 300 } 301 302 if (width >= 3 && height >= 3) { 303 // Interior pixels 304 for (int y = 1; y < data.heightDecreasedByOne; ++y) { 305 offset = y * data.widthMultipliedByPixelSize + cPixelSize; 306 for (int x = 1; x < data.widthDecreasedByOne; ++x, offset += cPixelSize) { 307 data.interior(offset, normalVector); 308 inlineSetPixel(offset, data, paintingData, x, y, cFactor1div4, cFactor1div4, normalVector); 309 } 310 } 311 } 312 313 int lastPixel = data.widthMultipliedByPixelSize * height; 314 if (m_lightingType == DiffuseLighting) { 315 for (int i = cAlphaChannelOffset; i < lastPixel; i += cPixelSize) 316 data.pixels->set(i, cOpaqueAlpha); 317 } else { 318 for (int i = 0; i < lastPixel; i += cPixelSize) { 319 unsigned char a1 = data.pixels->get(i); 320 unsigned char a2 = data.pixels->get(i + 1); 321 unsigned char a3 = data.pixels->get(i + 2); 322 // alpha set to set to max(a1, a2, a3) 323 data.pixels->set(i + 3, a1 >= a2 ? (a1 >= a3 ? a1 : a3) : (a2 >= a3 ? a2 : a3)); 324 } 325 } 326 327 return true; 328} 329 330void FELighting::apply() 331{ 332 if (hasResult()) 333 return; 334 FilterEffect* in = inputEffect(0); 335 in->apply(); 336 if (!in->hasResult()) 337 return; 338 339 ByteArray* srcPixelArray = createUnmultipliedImageResult(); 340 if (!srcPixelArray) 341 return; 342 343 setIsAlphaImage(false); 344 345 IntRect effectDrawingRect = requestedRegionOfInputImageData(in->absolutePaintRect()); 346 in->copyUnmultipliedImage(srcPixelArray, effectDrawingRect); 347 348 // FIXME: support kernelUnitLengths other than (1,1). The issue here is that the W3 349 // standard has no test case for them, and other browsers (like Firefox) has strange 350 // output for various kernelUnitLengths, and I am not sure they are reliable. 351 // Anyway, feConvolveMatrix should also use the implementation 352 353 IntSize absolutePaintSize = absolutePaintRect().size(); 354 drawLighting(srcPixelArray, absolutePaintSize.width(), absolutePaintSize.height()); 355} 356 357} // namespace WebCore 358 359#endif // ENABLE(FILTERS) 360