• Home
  • History
  • Annotate
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
FELighting(Filter * filter,LightingType lightingType,const Color & lightingColor,float surfaceScale,float diffuseConstant,float specularConstant,float specularExponent,float kernelUnitLengthX,float kernelUnitLengthY,PassRefPtr<LightSource> lightSource)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 
mapPaintRect(const FloatRect & rect,bool)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
topLeft(int offset,IntPoint & normalVector)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 
topRow(int offset,IntPoint & normalVector)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 
topRight(int offset,IntPoint & normalVector)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 
leftColumn(int offset,IntPoint & normalVector)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 
interior(int offset,IntPoint & normalVector)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 
rightColumn(int offset,IntPoint & normalVector)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 
bottomLeft(int offset,IntPoint & normalVector)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 
bottomRow(int offset,IntPoint & normalVector)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 
bottomRight(int offset,IntPoint & normalVector)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 
inlineSetPixel(int offset,LightingData & data,LightSource::PaintingData & paintingData,int lightX,int lightY,float factorX,float factorY,IntPoint & normal2DVector)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 
setPixel(int offset,LightingData & data,LightSource::PaintingData & paintingData,int lightX,int lightY,float factorX,float factorY,IntPoint & normalVector)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 
platformApplyGenericPaint(LightingData & data,LightSource::PaintingData & paintingData,int startY,int endY)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 
platformApplyGenericWorker(PlatformApplyGenericParameters * parameters)255 void FELighting::platformApplyGenericWorker(PlatformApplyGenericParameters* parameters)
256 {
257     parameters->filter->platformApplyGenericPaint(parameters->data, parameters->paintingData, parameters->yStart, parameters->yEnd);
258 }
259 
platformApplyGeneric(LightingData & data,LightSource::PaintingData & paintingData)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 
platformApply(LightingData & data,LightSource::PaintingData & paintingData)294 inline void FELighting::platformApply(LightingData& data, LightSource::PaintingData& paintingData)
295 {
296     platformApplyGeneric(data, paintingData);
297 }
298 
getTransform(FloatPoint3D * scale,FloatSize * offset) const299 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 
drawLighting(Uint8ClampedArray * pixels,int width,int height)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 
applySoftware()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 
createImageFilter(SkiaImageFilterBuilder * builder)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