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