summaryrefslogtreecommitdiffstats
path: root/WebCore/platform/graphics/filters/FELighting.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'WebCore/platform/graphics/filters/FELighting.cpp')
-rw-r--r--WebCore/platform/graphics/filters/FELighting.cpp270
1 files changed, 270 insertions, 0 deletions
diff --git a/WebCore/platform/graphics/filters/FELighting.cpp b/WebCore/platform/graphics/filters/FELighting.cpp
new file mode 100644
index 0000000..f49b67d
--- /dev/null
+++ b/WebCore/platform/graphics/filters/FELighting.cpp
@@ -0,0 +1,270 @@
+/*
+ * Copyright (C) 2010 University of Szeged
+ * Copyright (C) 2010 Zoltan Herczeg
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY UNIVERSITY OF SZEGED ``AS IS'' AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL UNIVERSITY OF SZEGED OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+ * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "config.h"
+
+#if ENABLE(FILTERS)
+#include "FELighting.h"
+
+#include "CanvasPixelArray.h"
+#include "ImageData.h"
+#include "LightSource.h"
+
+namespace WebCore {
+
+FELighting::FELighting(LightingType lightingType, const Color& lightingColor, float surfaceScale,
+ float diffuseConstant, float specularConstant, float specularExponent,
+ float kernelUnitLengthX, float kernelUnitLengthY, PassRefPtr<LightSource> lightSource)
+ : FilterEffect()
+ , m_lightingType(lightingType)
+ , m_lightSource(lightSource)
+ , m_lightingColor(lightingColor)
+ , m_surfaceScale(surfaceScale)
+ , m_diffuseConstant(diffuseConstant)
+ , m_specularConstant(specularConstant)
+ , m_specularExponent(specularExponent)
+ , m_kernelUnitLengthX(kernelUnitLengthX)
+ , m_kernelUnitLengthY(kernelUnitLengthY)
+{
+}
+
+const static int cPixelSize = 4;
+const static int cAlphaChannelOffset = 3;
+const static unsigned char cOpaqueAlpha = static_cast<unsigned char>(0xff);
+
+ALWAYS_INLINE int FELighting::LightingData::upLeftPixelValue()
+{
+ return static_cast<int>(pixels->get(offset - widthMultipliedByPixelSize - cPixelSize + cAlphaChannelOffset));
+}
+
+ALWAYS_INLINE int FELighting::LightingData::upPixelValue()
+{
+ return static_cast<int>(pixels->get(offset - widthMultipliedByPixelSize + cAlphaChannelOffset));
+}
+
+ALWAYS_INLINE int FELighting::LightingData::upRightPixelValue()
+{
+ return static_cast<int>(pixels->get(offset - widthMultipliedByPixelSize + cPixelSize + cAlphaChannelOffset));
+}
+
+ALWAYS_INLINE int FELighting::LightingData::leftPixelValue()
+{
+ return static_cast<int>(pixels->get(offset - cPixelSize + cAlphaChannelOffset));
+}
+
+ALWAYS_INLINE int FELighting::LightingData::centerPixelValue()
+{
+ return static_cast<int>(pixels->get(offset + cAlphaChannelOffset));
+}
+
+ALWAYS_INLINE int FELighting::LightingData::rightPixelValue()
+{
+ return static_cast<int>(pixels->get(offset + cPixelSize + cAlphaChannelOffset));
+}
+
+ALWAYS_INLINE int FELighting::LightingData::downLeftPixelValue()
+{
+ return static_cast<int>(pixels->get(offset + widthMultipliedByPixelSize - cPixelSize + cAlphaChannelOffset));
+}
+
+ALWAYS_INLINE int FELighting::LightingData::downPixelValue()
+{
+ return static_cast<int>(pixels->get(offset + widthMultipliedByPixelSize + cAlphaChannelOffset));
+}
+
+ALWAYS_INLINE int FELighting::LightingData::downRightPixelValue()
+{
+ return static_cast<int>(pixels->get(offset + widthMultipliedByPixelSize + cPixelSize + cAlphaChannelOffset));
+}
+
+ALWAYS_INLINE void FELighting::setPixel(LightingData& data, LightSource::PaintingData& paintingData,
+ int lightX, int lightY, float factorX, int normalX, float factorY, int normalY)
+{
+ m_lightSource->updatePaintingData(paintingData, lightX, lightY, static_cast<float>(data.pixels->get(data.offset + 3)) * data.surfaceScale);
+
+ data.normalVector.setX(factorX * static_cast<float>(normalX) * data.surfaceScale);
+ data.normalVector.setY(factorY * static_cast<float>(normalY) * data.surfaceScale);
+ data.normalVector.setZ(1.0f);
+ data.normalVector.normalize();
+
+ if (m_lightingType == FELighting::DiffuseLighting)
+ data.lightStrength = m_diffuseConstant * (data.normalVector * paintingData.lightVector);
+ else {
+ FloatPoint3D halfwayVector = paintingData.lightVector;
+ halfwayVector.setZ(halfwayVector.z() + 1.0f);
+ halfwayVector.normalize();
+ if (m_specularExponent == 1.0f)
+ data.lightStrength = m_specularConstant * (data.normalVector * halfwayVector);
+ else
+ data.lightStrength = m_specularConstant * powf(data.normalVector * halfwayVector, m_specularExponent);
+ }
+
+ if (data.lightStrength > 1.0f)
+ data.lightStrength = 1.0f;
+ if (data.lightStrength < 0.0f)
+ data.lightStrength = 0.0f;
+
+ data.pixels->set(data.offset, static_cast<unsigned char>(data.lightStrength * paintingData.colorVector.x()));
+ data.pixels->set(data.offset + 1, static_cast<unsigned char>(data.lightStrength * paintingData.colorVector.y()));
+ data.pixels->set(data.offset + 2, static_cast<unsigned char>(data.lightStrength * paintingData.colorVector.z()));
+}
+
+bool FELighting::drawLighting(CanvasPixelArray* pixels, int width, int height)
+{
+ LightSource::PaintingData paintingData;
+ LightingData data;
+
+ if (!m_lightSource)
+ return false;
+
+ // FIXME: do something if width or height (or both) is 1 pixel.
+ // The W3 spec does not define this case. Now the filter just returns.
+ if (width <= 2 || height <= 2)
+ return false;
+
+ data.pixels = pixels;
+ data.surfaceScale = m_surfaceScale / 255.0f;
+ data.widthMultipliedByPixelSize = width * cPixelSize;
+ data.widthDecreasedByOne = width - 1;
+ data.heightDecreasedByOne = height - 1;
+ paintingData.colorVector = FloatPoint3D(m_lightingColor.red(), m_lightingColor.green(), m_lightingColor.blue());
+ m_lightSource->initPaintingData(paintingData);
+
+ // Top/Left corner
+ data.offset = 0;
+ setPixel(data, paintingData, 0, 0,
+ -2.0f / 3.0f, -2 * data.centerPixelValue() + 2 * data.rightPixelValue() - data.downPixelValue() + data.downRightPixelValue(),
+ -2.0f / 3.0f, -2 * data.centerPixelValue() - data.rightPixelValue() + 2 * data.downPixelValue() + data.downRightPixelValue());
+
+ // Top/Right pixel
+ data.offset = data.widthMultipliedByPixelSize - cPixelSize;
+ setPixel(data, paintingData, data.widthDecreasedByOne, 0,
+ -2.0f / 3.0f, -2 * data.leftPixelValue() + 2 * data.centerPixelValue() - data.downLeftPixelValue() + data.downPixelValue(),
+ -2.0f / 3.0f, -data.leftPixelValue() - 2 * data.centerPixelValue() + data.downLeftPixelValue() + 2 * data.downPixelValue());
+
+ // Bottom/Left pixel
+ data.offset = data.heightDecreasedByOne * data.widthMultipliedByPixelSize;
+ setPixel(data, paintingData, 0, data.heightDecreasedByOne,
+ -2.0f / 3.0f, -data.upPixelValue() + data.upRightPixelValue() - 2 * data.centerPixelValue() + 2 * data.rightPixelValue(),
+ -2.0f / 3.0f, -2 * data.upPixelValue() - data.upRightPixelValue() + 2 * data.centerPixelValue() + data.rightPixelValue());
+
+ // Bottom/Right pixel
+ data.offset = height * data.widthMultipliedByPixelSize - cPixelSize;
+ setPixel(data, paintingData, data.widthDecreasedByOne, data.heightDecreasedByOne,
+ -2.0f / 3.0f, -data.upLeftPixelValue() + data.upPixelValue() - 2 * data.leftPixelValue() + 2 * data.centerPixelValue(),
+ -2.0f / 3.0f, -data.upLeftPixelValue() - 2 * data.upPixelValue() + data.leftPixelValue() + 2 * data.centerPixelValue());
+
+ if (width >= 3) {
+ // Top line
+ data.offset = cPixelSize;
+ for (int x = 1; x < data.widthDecreasedByOne; ++x, data.offset += cPixelSize) {
+ setPixel(data, paintingData, x, 0,
+ -1.0f / 3.0f, -2 * data.leftPixelValue() + 2 * data.rightPixelValue() - data.downLeftPixelValue() + data.downRightPixelValue(),
+ -1.0f / 2.0f, -data.leftPixelValue() - 2 * data.centerPixelValue() - data.rightPixelValue() + data.downLeftPixelValue() + 2 * data.downPixelValue() + data.downRightPixelValue());
+ }
+ // Bottom line
+ data.offset = data.heightDecreasedByOne * data.widthMultipliedByPixelSize + cPixelSize;
+ for (int x = 1; x < data.widthDecreasedByOne; ++x, data.offset += cPixelSize) {
+ setPixel(data, paintingData, x, data.heightDecreasedByOne,
+ -1.0f / 3.0f, -data.upLeftPixelValue() + data.upRightPixelValue() - 2 * data.leftPixelValue() + 2 * data.rightPixelValue(),
+ -1.0f / 2.0f, -data.upLeftPixelValue() - 2 * data.upPixelValue() - data.upRightPixelValue() + data.leftPixelValue() + 2 * data.centerPixelValue() + data.rightPixelValue());
+ }
+ }
+
+ if (height >= 3) {
+ // Left line
+ data.offset = data.widthMultipliedByPixelSize;
+ for (int y = 1; y < data.heightDecreasedByOne; ++y, data.offset += data.widthMultipliedByPixelSize) {
+ setPixel(data, paintingData, 0, y,
+ -1.0f / 2.0f, -data.upPixelValue() + data.upRightPixelValue() - 2 * data.centerPixelValue() + 2 * data.rightPixelValue() - data.downPixelValue() + data.downRightPixelValue(),
+ -1.0f / 3.0f, -2 * data.upPixelValue() - data.upRightPixelValue() + 2 * data.downPixelValue() + data.downRightPixelValue());
+ }
+ // Right line
+ data.offset = data.widthMultipliedByPixelSize + data.widthMultipliedByPixelSize - cPixelSize;
+ for (int y = 1; y < data.heightDecreasedByOne; ++y, data.offset += data.widthMultipliedByPixelSize) {
+ setPixel(data, paintingData, data.widthDecreasedByOne, y,
+ -1.0f / 2.0f, -data.upLeftPixelValue() + data.upPixelValue() -2 * data.leftPixelValue() + 2 * data.centerPixelValue() - data.downLeftPixelValue() + data.downPixelValue(),
+ -1.0f / 3.0f, -data.upLeftPixelValue() - 2 * data.upPixelValue() + data.downLeftPixelValue() + 2 * data.downPixelValue());
+ }
+ }
+
+ if (width >= 3 && height >= 3) {
+ // Interior pixels
+ for (int y = 1; y < data.heightDecreasedByOne; ++y) {
+ data.offset = y * data.widthMultipliedByPixelSize + cPixelSize;
+ for (int x = 1; x < data.widthDecreasedByOne; ++x, data.offset += cPixelSize) {
+ setPixel(data, paintingData, x, y,
+ -1.0f / 4.0f, -data.upLeftPixelValue() + data.upRightPixelValue() - 2 * data.leftPixelValue() + 2 * data.rightPixelValue() - data.downLeftPixelValue() + data.downRightPixelValue(),
+ -1.0f / 4.0f, -data.upLeftPixelValue() - 2 * data.upPixelValue() - data.upRightPixelValue() + data.downLeftPixelValue() + 2 * data.downPixelValue() + data.downRightPixelValue());
+ }
+ }
+ }
+
+ int totalSize = data.widthMultipliedByPixelSize * height;
+ if (m_lightingType == DiffuseLighting) {
+ for (int i = 3; i < totalSize; i += 4)
+ data.pixels->set(i, cOpaqueAlpha);
+ } else {
+ for (int i = 0; i < totalSize; i += 4) {
+ unsigned char a1 = data.pixels->get(i);
+ unsigned char a2 = data.pixels->get(i + 1);
+ unsigned char a3 = data.pixels->get(i + 2);
+ // alpha set to set to max(a1, a2, a3)
+ data.pixels->set(i + 3, a1 >= a2 ? (a1 >= a3 ? a1 : a3) : (a2 >= a3 ? a2 : a3));
+ }
+ }
+
+ return true;
+}
+
+void FELighting::apply(Filter* filter)
+{
+ FilterEffect* in = inputEffect(0);
+ in->apply(filter);
+ if (!in->resultImage())
+ return;
+
+ if (!effectContext())
+ return;
+
+ setIsAlphaImage(false);
+
+ IntRect effectDrawingRect = requestedRegionOfInputImageData(in->repaintRectInLocalCoordinates());
+ RefPtr<ImageData> srcImageData(in->resultImage()->getUnmultipliedImageData(effectDrawingRect));
+ CanvasPixelArray* srcPixelArray(srcImageData->data());
+
+ // FIXME: support kernelUnitLengths other than (1,1). The issue here is that the W3
+ // standard has no test case for them, and other browsers (like Firefox) has strange
+ // output for various kernelUnitLengths, and I am not sure they are reliable.
+ // Anyway, feConvolveMatrix should also use the implementation
+
+ if (drawLighting(srcPixelArray, effectDrawingRect.width(), effectDrawingRect.height()))
+ resultImage()->putUnmultipliedImageData(srcImageData.get(), IntRect(IntPoint(), resultImage()->size()), IntPoint());
+}
+
+} // namespace WebCore
+
+#endif // ENABLE(FILTERS)