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
37namespace blink {
38
39FELighting::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
55FloatRect 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
63const static int cPixelSize = 4;
64const static int cAlphaChannelOffset = 3;
65const static unsigned char cOpaqueAlpha = static_cast<unsigned char>(0xff);
66const static float cFactor1div2 = -1 / 2.f;
67const static float cFactor1div3 = -1 / 3.f;
68const static float cFactor1div4 = -1 / 4.f;
69const static float cFactor2div3 = -2 / 3.f;
70
71// << 1 is signed multiply by 2
72inline 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
83inline 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
96inline 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
107inline 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
121inline 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
137inline 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
151inline 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
162inline 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
175inline 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
186inline 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
235void 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
241inline 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
255void FELighting::platformApplyGenericWorker(PlatformApplyGenericParameters* parameters)
256{
257    parameters->filter->platformApplyGenericPaint(parameters->data, parameters->paintingData, parameters->yStart, parameters->yEnd);
258}
259
260inline 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
294inline void FELighting::platformApply(LightingData& data, LightSource::PaintingData& paintingData)
295{
296    platformApplyGeneric(data, paintingData);
297}
298
299void 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
313bool 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
413void 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
435PassRefPtr<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