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 35namespace WebCore { 36 37FELighting::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 53const static int cPixelSize = 4; 54const static int cAlphaChannelOffset = 3; 55const static unsigned char cOpaqueAlpha = static_cast<unsigned char>(0xff); 56const static float cFactor1div2 = -1 / 2.f; 57const static float cFactor1div3 = -1 / 3.f; 58const static float cFactor1div4 = -1 / 4.f; 59const static float cFactor2div3 = -2 / 3.f; 60 61// << 1 is signed multiply by 2 62inline 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 73inline 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 86inline 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 97inline 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 111inline 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 127inline 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 141inline 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 152inline 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 165inline 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 176inline 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 225void 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 231inline 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 245void FELighting::platformApplyGenericWorker(PlatformApplyGenericParameters* parameters) 246{ 247 parameters->filter->platformApplyGenericPaint(parameters->data, parameters->paintingData, parameters->yStart, parameters->yEnd); 248} 249 250inline 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 // Split the job into "yStep"-sized jobs but there a few jobs that need to be slightly larger since 261 // yStep * jobs < total size. These extras are handled by the remainder "jobsWithExtra". 262 const int yStep = (data.heightDecreasedByOne - 1) / job; 263 const int jobsWithExtra = (data.heightDecreasedByOne - 1) % job; 264 265 int yStart = 1; 266 for (--job; job >= 0; --job) { 267 PlatformApplyGenericParameters& params = parallelJobs.parameter(job); 268 params.filter = this; 269 params.data = data; 270 params.paintingData = paintingData; 271 params.yStart = yStart; 272 yStart += job < jobsWithExtra ? yStep + 1 : yStep; 273 params.yEnd = yStart; 274 } 275 parallelJobs.execute(); 276 return; 277 } 278 // Fallback to single threaded mode. 279 } 280 281 platformApplyGenericPaint(data, paintingData, 1, data.heightDecreasedByOne); 282} 283 284inline void FELighting::platformApply(LightingData& data, LightSource::PaintingData& paintingData) 285{ 286 // The selection here eventually should happen dynamically on some platforms. 287#if CPU(ARM_NEON) && CPU(ARM_TRADITIONAL) && COMPILER(GCC) 288 platformApplyNeon(data, paintingData); 289#else 290 platformApplyGeneric(data, paintingData); 291#endif 292} 293 294bool FELighting::drawLighting(Uint8ClampedArray* pixels, int width, int height) 295{ 296 LightSource::PaintingData paintingData; 297 LightingData data; 298 299 if (!m_lightSource) 300 return false; 301 302 // FIXME: do something if width or height (or both) is 1 pixel. 303 // The W3 spec does not define this case. Now the filter just returns. 304 if (width <= 2 || height <= 2) 305 return false; 306 307 data.pixels = pixels; 308 data.surfaceScale = m_surfaceScale / 255.0f; 309 data.widthMultipliedByPixelSize = width * cPixelSize; 310 data.widthDecreasedByOne = width - 1; 311 data.heightDecreasedByOne = height - 1; 312 paintingData.colorVector = FloatPoint3D(m_lightingColor.red(), m_lightingColor.green(), m_lightingColor.blue()); 313 m_lightSource->initPaintingData(paintingData); 314 315 // Top/Left corner. 316 IntPoint normalVector; 317 int offset = 0; 318 data.topLeft(offset, normalVector); 319 setPixel(offset, data, paintingData, 0, 0, cFactor2div3, cFactor2div3, normalVector); 320 321 // Top/Right pixel. 322 offset = data.widthMultipliedByPixelSize - cPixelSize; 323 data.topRight(offset, normalVector); 324 setPixel(offset, data, paintingData, data.widthDecreasedByOne, 0, cFactor2div3, cFactor2div3, normalVector); 325 326 // Bottom/Left pixel. 327 offset = data.heightDecreasedByOne * data.widthMultipliedByPixelSize; 328 data.bottomLeft(offset, normalVector); 329 setPixel(offset, data, paintingData, 0, data.heightDecreasedByOne, cFactor2div3, cFactor2div3, normalVector); 330 331 // Bottom/Right pixel. 332 offset = height * data.widthMultipliedByPixelSize - cPixelSize; 333 data.bottomRight(offset, normalVector); 334 setPixel(offset, data, paintingData, data.widthDecreasedByOne, data.heightDecreasedByOne, cFactor2div3, cFactor2div3, normalVector); 335 336 if (width >= 3) { 337 // Top row. 338 offset = cPixelSize; 339 for (int x = 1; x < data.widthDecreasedByOne; ++x, offset += cPixelSize) { 340 data.topRow(offset, normalVector); 341 inlineSetPixel(offset, data, paintingData, x, 0, cFactor1div3, cFactor1div2, normalVector); 342 } 343 // Bottom row. 344 offset = data.heightDecreasedByOne * data.widthMultipliedByPixelSize + cPixelSize; 345 for (int x = 1; x < data.widthDecreasedByOne; ++x, offset += cPixelSize) { 346 data.bottomRow(offset, normalVector); 347 inlineSetPixel(offset, data, paintingData, x, data.heightDecreasedByOne, cFactor1div3, cFactor1div2, normalVector); 348 } 349 } 350 351 if (height >= 3) { 352 // Left column. 353 offset = data.widthMultipliedByPixelSize; 354 for (int y = 1; y < data.heightDecreasedByOne; ++y, offset += data.widthMultipliedByPixelSize) { 355 data.leftColumn(offset, normalVector); 356 inlineSetPixel(offset, data, paintingData, 0, y, cFactor1div2, cFactor1div3, normalVector); 357 } 358 // Right column. 359 offset = (data.widthMultipliedByPixelSize << 1) - cPixelSize; 360 for (int y = 1; y < data.heightDecreasedByOne; ++y, offset += data.widthMultipliedByPixelSize) { 361 data.rightColumn(offset, normalVector); 362 inlineSetPixel(offset, data, paintingData, data.widthDecreasedByOne, y, cFactor1div2, cFactor1div3, normalVector); 363 } 364 } 365 366 if (width >= 3 && height >= 3) { 367 // Interior pixels. 368 platformApply(data, paintingData); 369 } 370 371 int lastPixel = data.widthMultipliedByPixelSize * height; 372 if (m_lightingType == DiffuseLighting) { 373 for (int i = cAlphaChannelOffset; i < lastPixel; i += cPixelSize) 374 data.pixels->set(i, cOpaqueAlpha); 375 } else { 376 for (int i = 0; i < lastPixel; i += cPixelSize) { 377 unsigned char a1 = data.pixels->item(i); 378 unsigned char a2 = data.pixels->item(i + 1); 379 unsigned char a3 = data.pixels->item(i + 2); 380 // alpha set to set to max(a1, a2, a3) 381 data.pixels->set(i + 3, a1 >= a2 ? (a1 >= a3 ? a1 : a3) : (a2 >= a3 ? a2 : a3)); 382 } 383 } 384 385 return true; 386} 387 388void FELighting::platformApplySoftware() 389{ 390 FilterEffect* in = inputEffect(0); 391 392 Uint8ClampedArray* srcPixelArray = createPremultipliedImageResult(); 393 if (!srcPixelArray) 394 return; 395 396 setIsAlphaImage(false); 397 398 IntRect effectDrawingRect = requestedRegionOfInputImageData(in->absolutePaintRect()); 399 in->copyPremultipliedImage(srcPixelArray, effectDrawingRect); 400 401 // FIXME: support kernelUnitLengths other than (1,1). The issue here is that the W3 402 // standard has no test case for them, and other browsers (like Firefox) has strange 403 // output for various kernelUnitLengths, and I am not sure they are reliable. 404 // Anyway, feConvolveMatrix should also use the implementation 405 406 IntSize absolutePaintSize = absolutePaintRect().size(); 407 drawLighting(srcPixelArray, absolutePaintSize.width(), absolutePaintSize.height()); 408} 409 410} // namespace WebCore 411 412#endif // ENABLE(FILTERS) 413