Home | History | Annotate | Download | only in filters
      1 /*
      2  * Copyright (C) 2010 University of Szeged
      3  * Copyright (C) 2010 Zoltan Herczeg
      4  * Copyright (C) 2013 Google Inc. All rights reserved.
      5  *
      6  * Redistribution and use in source and binary forms, with or without
      7  * modification, are permitted provided that the following conditions
      8  * are met:
      9  * 1. Redistributions of source code must retain the above copyright
     10  *    notice, this list of conditions and the following disclaimer.
     11  * 2. Redistributions in binary form must reproduce the above copyright
     12  *    notice, this list of conditions and the following disclaimer in the
     13  *    documentation and/or other materials provided with the distribution.
     14  *
     15  * THIS SOFTWARE IS PROVIDED BY UNIVERSITY OF SZEGED ``AS IS'' AND ANY
     16  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
     18  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL UNIVERSITY OF SZEGED OR
     19  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
     20  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
     21  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
     22  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
     23  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
     24  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
     25  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
     26  */
     27 
     28 #include "config.h"
     29 #include "platform/graphics/filters/FELighting.h"
     30 
     31 #include "SkLightingImageFilter.h"
     32 #include "platform/graphics/filters/DistantLightSource.h"
     33 #include "platform/graphics/filters/ParallelJobs.h"
     34 #include "platform/graphics/filters/SkiaImageFilterBuilder.h"
     35 #include "platform/graphics/skia/NativeImageSkia.h"
     36 
     37 namespace blink {
     38 
     39 FELighting::FELighting(Filter* filter, LightingType lightingType, const Color& lightingColor, float surfaceScale,
     40     float diffuseConstant, float specularConstant, float specularExponent,
     41     float kernelUnitLengthX, float kernelUnitLengthY, PassRefPtr<LightSource> lightSource)
     42     : FilterEffect(filter)
     43     , m_lightingType(lightingType)
     44     , m_lightSource(lightSource)
     45     , m_lightingColor(lightingColor)
     46     , m_surfaceScale(surfaceScale)
     47     , m_diffuseConstant(std::max(diffuseConstant, 0.0f))
     48     , m_specularConstant(std::max(specularConstant, 0.0f))
     49     , m_specularExponent(std::min(std::max(specularExponent, 1.0f), 128.0f))
     50     , m_kernelUnitLengthX(kernelUnitLengthX)
     51     , m_kernelUnitLengthY(kernelUnitLengthY)
     52 {
     53 }
     54 
     55 FloatRect FELighting::mapPaintRect(const FloatRect& rect, bool)
     56 {
     57     FloatRect result = rect;
     58     // The areas affected need to be a pixel bigger to accommodate the Sobel kernel.
     59     result.inflate(1);
     60     return result;
     61 }
     62 
     63 const static int cPixelSize = 4;
     64 const static int cAlphaChannelOffset = 3;
     65 const static unsigned char cOpaqueAlpha = static_cast<unsigned char>(0xff);
     66 const static float cFactor1div2 = -1 / 2.f;
     67 const static float cFactor1div3 = -1 / 3.f;
     68 const static float cFactor1div4 = -1 / 4.f;
     69 const static float cFactor2div3 = -2 / 3.f;
     70 
     71 // << 1 is signed multiply by 2
     72 inline void FELighting::LightingData::topLeft(int offset, IntPoint& normalVector)
     73 {
     74     int center = static_cast<int>(pixels->item(offset + cAlphaChannelOffset));
     75     int right = static_cast<int>(pixels->item(offset + cPixelSize + cAlphaChannelOffset));
     76     offset += widthMultipliedByPixelSize;
     77     int bottom = static_cast<int>(pixels->item(offset + cAlphaChannelOffset));
     78     int bottomRight = static_cast<int>(pixels->item(offset + cPixelSize + cAlphaChannelOffset));
     79     normalVector.setX(-(center << 1) + (right << 1) - bottom + bottomRight);
     80     normalVector.setY(-(center << 1) - right + (bottom << 1) + bottomRight);
     81 }
     82 
     83 inline void FELighting::LightingData::topRow(int offset, IntPoint& normalVector)
     84 {
     85     int left = static_cast<int>(pixels->item(offset - cPixelSize + cAlphaChannelOffset));
     86     int center = static_cast<int>(pixels->item(offset + cAlphaChannelOffset));
     87     int right = static_cast<int>(pixels->item(offset + cPixelSize + cAlphaChannelOffset));
     88     offset += widthMultipliedByPixelSize;
     89     int bottomLeft = static_cast<int>(pixels->item(offset - cPixelSize + cAlphaChannelOffset));
     90     int bottom = static_cast<int>(pixels->item(offset + cAlphaChannelOffset));
     91     int bottomRight = static_cast<int>(pixels->item(offset + cPixelSize + cAlphaChannelOffset));
     92     normalVector.setX(-(left << 1) + (right << 1) - bottomLeft + bottomRight);
     93     normalVector.setY(-left - (center << 1) - right + bottomLeft + (bottom << 1) + bottomRight);
     94 }
     95 
     96 inline void FELighting::LightingData::topRight(int offset, IntPoint& normalVector)
     97 {
     98     int left = static_cast<int>(pixels->item(offset - cPixelSize + cAlphaChannelOffset));
     99     int center = static_cast<int>(pixels->item(offset + cAlphaChannelOffset));
    100     offset += widthMultipliedByPixelSize;
    101     int bottomLeft = static_cast<int>(pixels->item(offset - cPixelSize + cAlphaChannelOffset));
    102     int bottom = static_cast<int>(pixels->item(offset + cAlphaChannelOffset));
    103     normalVector.setX(-(left << 1) + (center << 1) - bottomLeft + bottom);
    104     normalVector.setY(-left - (center << 1) + bottomLeft + (bottom << 1));
    105 }
    106 
    107 inline void FELighting::LightingData::leftColumn(int offset, IntPoint& normalVector)
    108 {
    109     int center = static_cast<int>(pixels->item(offset + cAlphaChannelOffset));
    110     int right = static_cast<int>(pixels->item(offset + cPixelSize + cAlphaChannelOffset));
    111     offset -= widthMultipliedByPixelSize;
    112     int top = static_cast<int>(pixels->item(offset + cAlphaChannelOffset));
    113     int topRight = static_cast<int>(pixels->item(offset + cPixelSize + cAlphaChannelOffset));
    114     offset += widthMultipliedByPixelSize << 1;
    115     int bottom = static_cast<int>(pixels->item(offset + cAlphaChannelOffset));
    116     int bottomRight = static_cast<int>(pixels->item(offset + cPixelSize + cAlphaChannelOffset));
    117     normalVector.setX(-top + topRight - (center << 1) + (right << 1) - bottom + bottomRight);
    118     normalVector.setY(-(top << 1) - topRight + (bottom << 1) + bottomRight);
    119 }
    120 
    121 inline void FELighting::LightingData::interior(int offset, IntPoint& normalVector)
    122 {
    123     int left = static_cast<int>(pixels->item(offset - cPixelSize + cAlphaChannelOffset));
    124     int right = static_cast<int>(pixels->item(offset + cPixelSize + cAlphaChannelOffset));
    125     offset -= widthMultipliedByPixelSize;
    126     int topLeft = static_cast<int>(pixels->item(offset - cPixelSize + cAlphaChannelOffset));
    127     int top = static_cast<int>(pixels->item(offset + cAlphaChannelOffset));
    128     int topRight = static_cast<int>(pixels->item(offset + cPixelSize + cAlphaChannelOffset));
    129     offset += widthMultipliedByPixelSize << 1;
    130     int bottomLeft = static_cast<int>(pixels->item(offset - cPixelSize + cAlphaChannelOffset));
    131     int bottom = static_cast<int>(pixels->item(offset + cAlphaChannelOffset));
    132     int bottomRight = static_cast<int>(pixels->item(offset + cPixelSize + cAlphaChannelOffset));
    133     normalVector.setX(-topLeft + topRight - (left << 1) + (right << 1) - bottomLeft + bottomRight);
    134     normalVector.setY(-topLeft - (top << 1) - topRight + bottomLeft + (bottom << 1) + bottomRight);
    135 }
    136 
    137 inline void FELighting::LightingData::rightColumn(int offset, IntPoint& normalVector)
    138 {
    139     int left = static_cast<int>(pixels->item(offset - cPixelSize + cAlphaChannelOffset));
    140     int center = static_cast<int>(pixels->item(offset + cAlphaChannelOffset));
    141     offset -= widthMultipliedByPixelSize;
    142     int topLeft = static_cast<int>(pixels->item(offset - cPixelSize + cAlphaChannelOffset));
    143     int top = static_cast<int>(pixels->item(offset + cAlphaChannelOffset));
    144     offset += widthMultipliedByPixelSize << 1;
    145     int bottomLeft = static_cast<int>(pixels->item(offset - cPixelSize + cAlphaChannelOffset));
    146     int bottom = static_cast<int>(pixels->item(offset + cAlphaChannelOffset));
    147     normalVector.setX(-topLeft + top - (left << 1) + (center << 1) - bottomLeft + bottom);
    148     normalVector.setY(-topLeft - (top << 1) + bottomLeft + (bottom << 1));
    149 }
    150 
    151 inline void FELighting::LightingData::bottomLeft(int offset, IntPoint& normalVector)
    152 {
    153     int center = static_cast<int>(pixels->item(offset + cAlphaChannelOffset));
    154     int right = static_cast<int>(pixels->item(offset + cPixelSize + cAlphaChannelOffset));
    155     offset -= widthMultipliedByPixelSize;
    156     int top = static_cast<int>(pixels->item(offset + cAlphaChannelOffset));
    157     int topRight = static_cast<int>(pixels->item(offset + cPixelSize + cAlphaChannelOffset));
    158     normalVector.setX(-top + topRight - (center << 1) + (right << 1));
    159     normalVector.setY(-(top << 1) - topRight + (center << 1) + right);
    160 }
    161 
    162 inline void FELighting::LightingData::bottomRow(int offset, IntPoint& normalVector)
    163 {
    164     int left = static_cast<int>(pixels->item(offset - cPixelSize + cAlphaChannelOffset));
    165     int center = static_cast<int>(pixels->item(offset + cAlphaChannelOffset));
    166     int right = static_cast<int>(pixels->item(offset + cPixelSize + cAlphaChannelOffset));
    167     offset -= widthMultipliedByPixelSize;
    168     int topLeft = static_cast<int>(pixels->item(offset - cPixelSize + cAlphaChannelOffset));
    169     int top = static_cast<int>(pixels->item(offset + cAlphaChannelOffset));
    170     int topRight = static_cast<int>(pixels->item(offset + cPixelSize + cAlphaChannelOffset));
    171     normalVector.setX(-topLeft + topRight - (left << 1) + (right << 1));
    172     normalVector.setY(-topLeft - (top << 1) - topRight + left + (center << 1) + right);
    173 }
    174 
    175 inline void FELighting::LightingData::bottomRight(int offset, IntPoint& normalVector)
    176 {
    177     int left = static_cast<int>(pixels->item(offset - cPixelSize + cAlphaChannelOffset));
    178     int center = static_cast<int>(pixels->item(offset + cAlphaChannelOffset));
    179     offset -= widthMultipliedByPixelSize;
    180     int topLeft = static_cast<int>(pixels->item(offset - cPixelSize + cAlphaChannelOffset));
    181     int top = static_cast<int>(pixels->item(offset + cAlphaChannelOffset));
    182     normalVector.setX(-topLeft + top - (left << 1) + (center << 1));
    183     normalVector.setY(-topLeft - (top << 1) + left + (center << 1));
    184 }
    185 
    186 inline void FELighting::inlineSetPixel(int offset, LightingData& data, LightSource::PaintingData& paintingData,
    187                                        int lightX, int lightY, float factorX, float factorY, IntPoint& normal2DVector)
    188 {
    189     data.lightSource->updatePaintingData(paintingData, lightX, lightY, static_cast<float>(data.pixels->item(offset + cAlphaChannelOffset)) * data.surfaceScale);
    190 
    191     float lightStrength;
    192     if (!normal2DVector.x() && !normal2DVector.y()) {
    193         // Normal vector is (0, 0, 1). This is a quite frequent case.
    194         if (m_lightingType == FELighting::DiffuseLighting) {
    195             lightStrength = m_diffuseConstant * paintingData.lightVector.z() / paintingData.lightVectorLength;
    196         } else {
    197             FloatPoint3D halfwayVector = paintingData.lightVector;
    198             halfwayVector.setZ(halfwayVector.z() + paintingData.lightVectorLength);
    199             float halfwayVectorLength = halfwayVector.length();
    200             if (m_specularExponent == 1)
    201                 lightStrength = m_specularConstant * halfwayVector.z() / halfwayVectorLength;
    202             else
    203                 lightStrength = m_specularConstant * powf(halfwayVector.z() / halfwayVectorLength, m_specularExponent);
    204         }
    205     } else {
    206         FloatPoint3D normalVector;
    207         normalVector.setX(factorX * static_cast<float>(normal2DVector.x()) * data.surfaceScale);
    208         normalVector.setY(factorY * static_cast<float>(normal2DVector.y()) * data.surfaceScale);
    209         normalVector.setZ(1);
    210         float normalVectorLength = normalVector.length();
    211 
    212         if (m_lightingType == FELighting::DiffuseLighting) {
    213             lightStrength = m_diffuseConstant * (normalVector * paintingData.lightVector) / (normalVectorLength * paintingData.lightVectorLength);
    214         } else {
    215             FloatPoint3D halfwayVector = paintingData.lightVector;
    216             halfwayVector.setZ(halfwayVector.z() + paintingData.lightVectorLength);
    217             float halfwayVectorLength = halfwayVector.length();
    218             if (m_specularExponent == 1)
    219                 lightStrength = m_specularConstant * (normalVector * halfwayVector) / (normalVectorLength * halfwayVectorLength);
    220             else
    221                 lightStrength = m_specularConstant * powf((normalVector * halfwayVector) / (normalVectorLength * halfwayVectorLength), m_specularExponent);
    222         }
    223     }
    224 
    225     if (lightStrength > 1)
    226         lightStrength = 1;
    227     if (lightStrength < 0)
    228         lightStrength = 0;
    229 
    230     data.pixels->set(offset, static_cast<unsigned char>(lightStrength * paintingData.colorVector.x()));
    231     data.pixels->set(offset + 1, static_cast<unsigned char>(lightStrength * paintingData.colorVector.y()));
    232     data.pixels->set(offset + 2, static_cast<unsigned char>(lightStrength * paintingData.colorVector.z()));
    233 }
    234 
    235 void FELighting::setPixel(int offset, LightingData& data, LightSource::PaintingData& paintingData,
    236                           int lightX, int lightY, float factorX, float factorY, IntPoint& normalVector)
    237 {
    238     inlineSetPixel(offset, data, paintingData, lightX, lightY, factorX, factorY, normalVector);
    239 }
    240 
    241 inline void FELighting::platformApplyGenericPaint(LightingData& data, LightSource::PaintingData& paintingData, int startY, int endY)
    242 {
    243     IntPoint normalVector;
    244     int offset = 0;
    245 
    246     for (int y = startY; y < endY; ++y) {
    247         offset = y * data.widthMultipliedByPixelSize + cPixelSize;
    248         for (int x = 1; x < data.widthDecreasedByOne; ++x, offset += cPixelSize) {
    249             data.interior(offset, normalVector);
    250             inlineSetPixel(offset, data, paintingData, x, y, cFactor1div4, cFactor1div4, normalVector);
    251         }
    252     }
    253 }
    254 
    255 void FELighting::platformApplyGenericWorker(PlatformApplyGenericParameters* parameters)
    256 {
    257     parameters->filter->platformApplyGenericPaint(parameters->data, parameters->paintingData, parameters->yStart, parameters->yEnd);
    258 }
    259 
    260 inline void FELighting::platformApplyGeneric(LightingData& data, LightSource::PaintingData& paintingData)
    261 {
    262     int optimalThreadNumber = ((data.widthDecreasedByOne - 1) * (data.heightDecreasedByOne - 1)) / s_minimalRectDimension;
    263     if (optimalThreadNumber > 1) {
    264         // Initialize parallel jobs
    265         ParallelJobs<PlatformApplyGenericParameters> parallelJobs(&platformApplyGenericWorker, optimalThreadNumber);
    266 
    267         // Fill the parameter array
    268         int job = parallelJobs.numberOfJobs();
    269         if (job > 1) {
    270             // Split the job into "yStep"-sized jobs but there a few jobs that need to be slightly larger since
    271             // yStep * jobs < total size. These extras are handled by the remainder "jobsWithExtra".
    272             const int yStep = (data.heightDecreasedByOne - 1) / job;
    273             const int jobsWithExtra = (data.heightDecreasedByOne - 1) % job;
    274 
    275             int yStart = 1;
    276             for (--job; job >= 0; --job) {
    277                 PlatformApplyGenericParameters& params = parallelJobs.parameter(job);
    278                 params.filter = this;
    279                 params.data = data;
    280                 params.paintingData = paintingData;
    281                 params.yStart = yStart;
    282                 yStart += job < jobsWithExtra ? yStep + 1 : yStep;
    283                 params.yEnd = yStart;
    284             }
    285             parallelJobs.execute();
    286             return;
    287         }
    288         // Fallback to single threaded mode.
    289     }
    290 
    291     platformApplyGenericPaint(data, paintingData, 1, data.heightDecreasedByOne);
    292 }
    293 
    294 inline void FELighting::platformApply(LightingData& data, LightSource::PaintingData& paintingData)
    295 {
    296     platformApplyGeneric(data, paintingData);
    297 }
    298 
    299 void FELighting::getTransform(FloatPoint3D* scale, FloatSize* offset) const
    300 {
    301     FloatRect initialEffectRect = effectBoundaries();
    302     FloatRect absoluteEffectRect = filter()->mapLocalRectToAbsoluteRect(initialEffectRect);
    303     FloatPoint absoluteLocation(absolutePaintRect().location());
    304     FloatSize positionOffset(absoluteLocation - absoluteEffectRect.location());
    305     offset->setWidth(positionOffset.width());
    306     offset->setHeight(positionOffset.height());
    307     scale->setX(initialEffectRect.width() > 0.0f && initialEffectRect.width() > 0.0f ? absoluteEffectRect.width() / initialEffectRect.width() : 1.0f);
    308     scale->setY(initialEffectRect.height() > 0.0f && initialEffectRect.height() > 0.0f ? absoluteEffectRect.height() / initialEffectRect.height() : 1.0f);
    309     // X and Y scale should be the same, but, if not, do a best effort by averaging the 2 for Z scale
    310     scale->setZ(0.5f * (scale->x() + scale->y()));
    311 }
    312 
    313 bool FELighting::drawLighting(Uint8ClampedArray* pixels, int width, int height)
    314 {
    315     LightSource::PaintingData paintingData;
    316     LightingData data;
    317 
    318     if (!m_lightSource)
    319         return false;
    320 
    321     // FIXME: do something if width or height (or both) is 1 pixel.
    322     // The W3 spec does not define this case. Now the filter just returns.
    323     if (width <= 2 || height <= 2)
    324         return false;
    325 
    326     data.pixels = pixels;
    327     data.surfaceScale = m_surfaceScale / 255.0f;
    328     data.widthMultipliedByPixelSize = width * cPixelSize;
    329     data.widthDecreasedByOne = width - 1;
    330     data.heightDecreasedByOne = height - 1;
    331     FloatPoint3D worldScale;
    332     FloatSize originOffset;
    333     getTransform(&worldScale, &originOffset);
    334     RefPtr<LightSource> lightSource = m_lightSource->create(worldScale, originOffset);
    335     data.lightSource = lightSource.get();
    336     Color lightColor = adaptColorToOperatingColorSpace(m_lightingColor);
    337     paintingData.colorVector = FloatPoint3D(lightColor.red(), lightColor.green(), lightColor.blue());
    338     data.lightSource->initPaintingData(paintingData);
    339 
    340     // Top/Left corner.
    341     IntPoint normalVector;
    342     int offset = 0;
    343     data.topLeft(offset, normalVector);
    344     setPixel(offset, data, paintingData, 0, 0, cFactor2div3, cFactor2div3, normalVector);
    345 
    346     // Top/Right pixel.
    347     offset = data.widthMultipliedByPixelSize - cPixelSize;
    348     data.topRight(offset, normalVector);
    349     setPixel(offset, data, paintingData, data.widthDecreasedByOne, 0, cFactor2div3, cFactor2div3, normalVector);
    350 
    351     // Bottom/Left pixel.
    352     offset = data.heightDecreasedByOne * data.widthMultipliedByPixelSize;
    353     data.bottomLeft(offset, normalVector);
    354     setPixel(offset, data, paintingData, 0, data.heightDecreasedByOne, cFactor2div3, cFactor2div3, normalVector);
    355 
    356     // Bottom/Right pixel.
    357     offset = height * data.widthMultipliedByPixelSize - cPixelSize;
    358     data.bottomRight(offset, normalVector);
    359     setPixel(offset, data, paintingData, data.widthDecreasedByOne, data.heightDecreasedByOne, cFactor2div3, cFactor2div3, normalVector);
    360 
    361     if (width >= 3) {
    362         // Top row.
    363         offset = cPixelSize;
    364         for (int x = 1; x < data.widthDecreasedByOne; ++x, offset += cPixelSize) {
    365             data.topRow(offset, normalVector);
    366             inlineSetPixel(offset, data, paintingData, x, 0, cFactor1div3, cFactor1div2, normalVector);
    367         }
    368         // Bottom row.
    369         offset = data.heightDecreasedByOne * data.widthMultipliedByPixelSize + cPixelSize;
    370         for (int x = 1; x < data.widthDecreasedByOne; ++x, offset += cPixelSize) {
    371             data.bottomRow(offset, normalVector);
    372             inlineSetPixel(offset, data, paintingData, x, data.heightDecreasedByOne, cFactor1div3, cFactor1div2, normalVector);
    373         }
    374     }
    375 
    376     if (height >= 3) {
    377         // Left column.
    378         offset = data.widthMultipliedByPixelSize;
    379         for (int y = 1; y < data.heightDecreasedByOne; ++y, offset += data.widthMultipliedByPixelSize) {
    380             data.leftColumn(offset, normalVector);
    381             inlineSetPixel(offset, data, paintingData, 0, y, cFactor1div2, cFactor1div3, normalVector);
    382         }
    383         // Right column.
    384         offset = (data.widthMultipliedByPixelSize << 1) - cPixelSize;
    385         for (int y = 1; y < data.heightDecreasedByOne; ++y, offset += data.widthMultipliedByPixelSize) {
    386             data.rightColumn(offset, normalVector);
    387             inlineSetPixel(offset, data, paintingData, data.widthDecreasedByOne, y, cFactor1div2, cFactor1div3, normalVector);
    388         }
    389     }
    390 
    391     if (width >= 3 && height >= 3) {
    392         // Interior pixels.
    393         platformApply(data, paintingData);
    394     }
    395 
    396     int lastPixel = data.widthMultipliedByPixelSize * height;
    397     if (m_lightingType == DiffuseLighting) {
    398         for (int i = cAlphaChannelOffset; i < lastPixel; i += cPixelSize)
    399             data.pixels->set(i, cOpaqueAlpha);
    400     } else {
    401         for (int i = 0; i < lastPixel; i += cPixelSize) {
    402             unsigned char a1 = data.pixels->item(i);
    403             unsigned char a2 = data.pixels->item(i + 1);
    404             unsigned char a3 = data.pixels->item(i + 2);
    405             // alpha set to set to max(a1, a2, a3)
    406             data.pixels->set(i + 3, a1 >= a2 ? (a1 >= a3 ? a1 : a3) : (a2 >= a3 ? a2 : a3));
    407         }
    408     }
    409 
    410     return true;
    411 }
    412 
    413 void FELighting::applySoftware()
    414 {
    415     FilterEffect* in = inputEffect(0);
    416 
    417     Uint8ClampedArray* srcPixelArray = createPremultipliedImageResult();
    418     if (!srcPixelArray)
    419         return;
    420 
    421     setIsAlphaImage(false);
    422 
    423     IntRect effectDrawingRect = requestedRegionOfInputImageData(in->absolutePaintRect());
    424     in->copyPremultipliedImage(srcPixelArray, effectDrawingRect);
    425 
    426     // FIXME: support kernelUnitLengths other than (1,1). The issue here is that the W3
    427     // standard has no test case for them, and other browsers (like Firefox) has strange
    428     // output for various kernelUnitLengths, and I am not sure they are reliable.
    429     // Anyway, feConvolveMatrix should also use the implementation
    430 
    431     IntSize absolutePaintSize = absolutePaintRect().size();
    432     drawLighting(srcPixelArray, absolutePaintSize.width(), absolutePaintSize.height());
    433 }
    434 
    435 PassRefPtr<SkImageFilter> FELighting::createImageFilter(SkiaImageFilterBuilder* builder)
    436 {
    437     SkImageFilter::CropRect rect = getCropRect(builder ? builder->cropOffset() : FloatSize());
    438     Color lightColor = adaptColorToOperatingColorSpace(m_lightingColor);
    439     RefPtr<SkImageFilter> input(builder ? builder->build(inputEffect(0), operatingColorSpace()) : nullptr);
    440     switch (m_lightSource->type()) {
    441     case LS_DISTANT: {
    442         DistantLightSource* distantLightSource = static_cast<DistantLightSource*>(m_lightSource.get());
    443         float azimuthRad = deg2rad(distantLightSource->azimuth());
    444         float elevationRad = deg2rad(distantLightSource->elevation());
    445         SkPoint3 direction(cosf(azimuthRad) * cosf(elevationRad),
    446                            sinf(azimuthRad) * cosf(elevationRad),
    447                            sinf(elevationRad));
    448         if (m_specularConstant > 0)
    449             return adoptRef(SkLightingImageFilter::CreateDistantLitSpecular(direction, lightColor.rgb(), m_surfaceScale, m_specularConstant, m_specularExponent, input.get(), &rect));
    450         return adoptRef(SkLightingImageFilter::CreateDistantLitDiffuse(direction, lightColor.rgb(), m_surfaceScale, m_diffuseConstant, input.get(), &rect));
    451     }
    452     case LS_POINT: {
    453         PointLightSource* pointLightSource = static_cast<PointLightSource*>(m_lightSource.get());
    454         FloatPoint3D position = pointLightSource->position();
    455         SkPoint3 skPosition(position.x(), position.y(), position.z());
    456         if (m_specularConstant > 0)
    457             return adoptRef(SkLightingImageFilter::CreatePointLitSpecular(skPosition, lightColor.rgb(), m_surfaceScale, m_specularConstant, m_specularExponent, input.get(), &rect));
    458         return adoptRef(SkLightingImageFilter::CreatePointLitDiffuse(skPosition, lightColor.rgb(), m_surfaceScale, m_diffuseConstant, input.get(), &rect));
    459     }
    460     case LS_SPOT: {
    461         SpotLightSource* spotLightSource = static_cast<SpotLightSource*>(m_lightSource.get());
    462         SkPoint3 location(spotLightSource->position().x(), spotLightSource->position().y(), spotLightSource->position().z());
    463         SkPoint3 target(spotLightSource->direction().x(), spotLightSource->direction().y(), spotLightSource->direction().z());
    464         float specularExponent = spotLightSource->specularExponent();
    465         float limitingConeAngle = spotLightSource->limitingConeAngle();
    466         if (!limitingConeAngle || limitingConeAngle > 90 || limitingConeAngle < -90)
    467             limitingConeAngle = 90;
    468         if (m_specularConstant > 0)
    469             return adoptRef(SkLightingImageFilter::CreateSpotLitSpecular(location, target, specularExponent, limitingConeAngle, lightColor.rgb(), m_surfaceScale, m_specularConstant, m_specularExponent, input.get(), &rect));
    470         return adoptRef(SkLightingImageFilter::CreateSpotLitDiffuse(location, target, specularExponent, limitingConeAngle, lightColor.rgb(), m_surfaceScale, m_diffuseConstant, input.get(), &rect));
    471     }
    472     default:
    473         ASSERT_NOT_REACHED();
    474         return nullptr;
    475     }
    476 }
    477 
    478 } // namespace blink
    479