1 /*
2 * Copyright (C) 2008 Alex Mathews <possessedpenguinbob@gmail.com>
3 * Copyright (C) 2009 Dirk Schulze <krit@webkit.org>
4 * Copyright (C) Research In Motion Limited 2010. All rights reserved.
5 * Copyright (C) 2012 University of Szeged
6 * Copyright (C) 2013 Google Inc. All rights reserved.
7 *
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Library General Public
10 * License as published by the Free Software Foundation; either
11 * version 2 of the License, or (at your option) any later version.
12 *
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Library General Public License for more details.
17 *
18 * You should have received a copy of the GNU Library General Public License
19 * along with this library; see the file COPYING.LIB. If not, write to
20 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
21 * Boston, MA 02110-1301, USA.
22 */
23
24 #include "config.h"
25
26 #include "platform/graphics/filters/FilterEffect.h"
27
28 #include "platform/graphics/ImageBuffer.h"
29 #include "platform/graphics/UnacceleratedImageBufferSurface.h"
30 #include "platform/graphics/filters/Filter.h"
31 #include "platform/graphics/gpu/AcceleratedImageBufferSurface.h"
32
33 #if HAVE(ARM_NEON_INTRINSICS)
34 #include <arm_neon.h>
35 #endif
36
37 namespace WebCore {
38
FilterEffect(Filter * filter)39 FilterEffect::FilterEffect(Filter* filter)
40 : m_alphaImage(false)
41 , m_filter(filter)
42 , m_hasX(false)
43 , m_hasY(false)
44 , m_hasWidth(false)
45 , m_hasHeight(false)
46 , m_clipsToBounds(true)
47 , m_operatingColorSpace(ColorSpaceLinearRGB)
48 , m_resultColorSpace(ColorSpaceDeviceRGB)
49 {
50 ASSERT(m_filter);
51 }
52
~FilterEffect()53 FilterEffect::~FilterEffect()
54 {
55 }
56
isFilterSizeValid(IntRect rect)57 inline bool isFilterSizeValid(IntRect rect)
58 {
59 if (rect.width() < 0 || rect.width() > kMaxFilterSize
60 || rect.height() < 0 || rect.height() > kMaxFilterSize)
61 return false;
62 return true;
63 }
64
determineAbsolutePaintRect()65 void FilterEffect::determineAbsolutePaintRect()
66 {
67 m_absolutePaintRect = IntRect();
68 unsigned size = m_inputEffects.size();
69 for (unsigned i = 0; i < size; ++i)
70 m_absolutePaintRect.unite(m_inputEffects.at(i)->absolutePaintRect());
71
72 // Filters in SVG clip to primitive subregion, while CSS doesn't.
73 if (m_clipsToBounds)
74 m_absolutePaintRect.intersect(enclosingIntRect(m_maxEffectRect));
75 else
76 m_absolutePaintRect.unite(enclosingIntRect(m_maxEffectRect));
77
78 }
79
mapRectRecursive(const FloatRect & rect)80 FloatRect FilterEffect::mapRectRecursive(const FloatRect& rect)
81 {
82 FloatRect result;
83 if (m_inputEffects.size() > 0) {
84 result = m_inputEffects.at(0)->mapRectRecursive(rect);
85 for (unsigned i = 1; i < m_inputEffects.size(); ++i)
86 result.unite(m_inputEffects.at(i)->mapRectRecursive(rect));
87 } else
88 result = rect;
89 return mapRect(result);
90 }
91
getSourceRect(const FloatRect & destRect,const FloatRect & destClipRect)92 FloatRect FilterEffect::getSourceRect(const FloatRect& destRect, const FloatRect& destClipRect)
93 {
94 FloatRect sourceRect = mapRect(destRect, false);
95 FloatRect sourceClipRect = mapRect(destClipRect, false);
96
97 FloatRect boundaries = effectBoundaries();
98 if (hasX())
99 sourceClipRect.setX(boundaries.x());
100 if (hasY())
101 sourceClipRect.setY(boundaries.y());
102 if (hasWidth())
103 sourceClipRect.setWidth(boundaries.width());
104 if (hasHeight())
105 sourceClipRect.setHeight(boundaries.height());
106
107 FloatRect result;
108 if (m_inputEffects.size() > 0) {
109 result = m_inputEffects.at(0)->getSourceRect(sourceRect, sourceClipRect);
110 for (unsigned i = 1; i < m_inputEffects.size(); ++i)
111 result.unite(m_inputEffects.at(i)->getSourceRect(sourceRect, sourceClipRect));
112 } else {
113 result = sourceRect;
114 result.intersect(sourceClipRect);
115 }
116 return result;
117 }
118
requestedRegionOfInputImageData(const IntRect & effectRect) const119 IntRect FilterEffect::requestedRegionOfInputImageData(const IntRect& effectRect) const
120 {
121 ASSERT(hasResult());
122 IntPoint location = m_absolutePaintRect.location();
123 location.moveBy(-effectRect.location());
124 return IntRect(location, m_absolutePaintRect.size());
125 }
126
drawingRegionOfInputImage(const IntRect & srcRect) const127 IntRect FilterEffect::drawingRegionOfInputImage(const IntRect& srcRect) const
128 {
129 return IntRect(IntPoint(srcRect.x() - m_absolutePaintRect.x(),
130 srcRect.y() - m_absolutePaintRect.y()), srcRect.size());
131 }
132
inputEffect(unsigned number) const133 FilterEffect* FilterEffect::inputEffect(unsigned number) const
134 {
135 ASSERT_WITH_SECURITY_IMPLICATION(number < m_inputEffects.size());
136 return m_inputEffects.at(number).get();
137 }
138
apply()139 void FilterEffect::apply()
140 {
141 if (hasResult())
142 return;
143 unsigned size = m_inputEffects.size();
144 for (unsigned i = 0; i < size; ++i) {
145 FilterEffect* in = m_inputEffects.at(i).get();
146 in->apply();
147 if (!in->hasResult())
148 return;
149
150 // Convert input results to the current effect's color space.
151 transformResultColorSpace(in, i);
152 }
153
154 determineAbsolutePaintRect();
155 setResultColorSpace(m_operatingColorSpace);
156
157 if (!isFilterSizeValid(m_absolutePaintRect))
158 return;
159
160 if (requiresValidPreMultipliedPixels()) {
161 for (unsigned i = 0; i < size; ++i)
162 inputEffect(i)->correctFilterResultIfNeeded();
163 }
164
165 if (applySkia())
166 return;
167
168 applySoftware();
169 }
170
forceValidPreMultipliedPixels()171 void FilterEffect::forceValidPreMultipliedPixels()
172 {
173 // Must operate on pre-multiplied results; other formats cannot have invalid pixels.
174 if (!m_premultipliedImageResult)
175 return;
176
177 Uint8ClampedArray* imageArray = m_premultipliedImageResult.get();
178 unsigned char* pixelData = imageArray->data();
179 int pixelArrayLength = imageArray->length();
180
181 // We must have four bytes per pixel, and complete pixels
182 ASSERT(!(pixelArrayLength % 4));
183
184 #if HAVE(ARM_NEON_INTRINSICS)
185 if (pixelArrayLength >= 64) {
186 unsigned char* lastPixel = pixelData + (pixelArrayLength & ~0x3f);
187 do {
188 // Increments pixelData by 64.
189 uint8x16x4_t sixteenPixels = vld4q_u8(pixelData);
190 sixteenPixels.val[0] = vminq_u8(sixteenPixels.val[0], sixteenPixels.val[3]);
191 sixteenPixels.val[1] = vminq_u8(sixteenPixels.val[1], sixteenPixels.val[3]);
192 sixteenPixels.val[2] = vminq_u8(sixteenPixels.val[2], sixteenPixels.val[3]);
193 vst4q_u8(pixelData, sixteenPixels);
194 pixelData += 64;
195 } while (pixelData < lastPixel);
196
197 pixelArrayLength &= 0x3f;
198 if (!pixelArrayLength)
199 return;
200 }
201 #endif
202
203 int numPixels = pixelArrayLength / 4;
204
205 // Iterate over each pixel, checking alpha and adjusting color components if necessary
206 while (--numPixels >= 0) {
207 // Alpha is the 4th byte in a pixel
208 unsigned char a = *(pixelData + 3);
209 // Clamp each component to alpha, and increment the pixel location
210 for (int i = 0; i < 3; ++i) {
211 if (*pixelData > a)
212 *pixelData = a;
213 ++pixelData;
214 }
215 // Increment for alpha
216 ++pixelData;
217 }
218 }
219
clearResult()220 void FilterEffect::clearResult()
221 {
222 if (m_imageBufferResult)
223 m_imageBufferResult.clear();
224 if (m_unmultipliedImageResult)
225 m_unmultipliedImageResult.clear();
226 if (m_premultipliedImageResult)
227 m_premultipliedImageResult.clear();
228 }
229
clearResultsRecursive()230 void FilterEffect::clearResultsRecursive()
231 {
232 // Clear all results, regardless that the current effect has
233 // a result. Can be used if an effect is in an erroneous state.
234 if (hasResult())
235 clearResult();
236
237 unsigned size = m_inputEffects.size();
238 for (unsigned i = 0; i < size; ++i)
239 m_inputEffects.at(i).get()->clearResultsRecursive();
240 }
241
asImageBuffer()242 ImageBuffer* FilterEffect::asImageBuffer()
243 {
244 if (!hasResult())
245 return 0;
246 if (m_imageBufferResult)
247 return m_imageBufferResult.get();
248 OwnPtr<ImageBufferSurface> surface;
249 if (m_filter->isAccelerated())
250 surface = adoptPtr(new AcceleratedImageBufferSurface(m_absolutePaintRect.size()));
251 if (!m_filter->isAccelerated() || !surface->isValid())
252 surface = adoptPtr(new UnacceleratedImageBufferSurface(m_absolutePaintRect.size()));
253 m_imageBufferResult = ImageBuffer::create(surface.release());
254 if (!m_imageBufferResult)
255 return 0;
256
257 IntRect destinationRect(IntPoint(), m_absolutePaintRect.size());
258 if (m_premultipliedImageResult)
259 m_imageBufferResult->putByteArray(Premultiplied, m_premultipliedImageResult.get(), destinationRect.size(), destinationRect, IntPoint());
260 else
261 m_imageBufferResult->putByteArray(Unmultiplied, m_unmultipliedImageResult.get(), destinationRect.size(), destinationRect, IntPoint());
262 return m_imageBufferResult.get();
263 }
264
asUnmultipliedImage(const IntRect & rect)265 PassRefPtr<Uint8ClampedArray> FilterEffect::asUnmultipliedImage(const IntRect& rect)
266 {
267 ASSERT(isFilterSizeValid(rect));
268 RefPtr<Uint8ClampedArray> imageData = Uint8ClampedArray::createUninitialized(rect.width() * rect.height() * 4);
269 copyUnmultipliedImage(imageData.get(), rect);
270 return imageData.release();
271 }
272
asPremultipliedImage(const IntRect & rect)273 PassRefPtr<Uint8ClampedArray> FilterEffect::asPremultipliedImage(const IntRect& rect)
274 {
275 ASSERT(isFilterSizeValid(rect));
276 RefPtr<Uint8ClampedArray> imageData = Uint8ClampedArray::createUninitialized(rect.width() * rect.height() * 4);
277 copyPremultipliedImage(imageData.get(), rect);
278 return imageData.release();
279 }
280
copyImageBytes(Uint8ClampedArray * source,Uint8ClampedArray * destination,const IntRect & rect)281 inline void FilterEffect::copyImageBytes(Uint8ClampedArray* source, Uint8ClampedArray* destination, const IntRect& rect)
282 {
283 // Initialize the destination to transparent black, if not entirely covered by the source.
284 if (rect.x() < 0 || rect.y() < 0 || rect.maxX() > m_absolutePaintRect.width() || rect.maxY() > m_absolutePaintRect.height())
285 memset(destination->data(), 0, destination->length());
286
287 // Early return if the rect does not intersect with the source.
288 if (rect.maxX() <= 0 || rect.maxY() <= 0 || rect.x() >= m_absolutePaintRect.width() || rect.y() >= m_absolutePaintRect.height())
289 return;
290
291 int xOrigin = rect.x();
292 int xDest = 0;
293 if (xOrigin < 0) {
294 xDest = -xOrigin;
295 xOrigin = 0;
296 }
297 int xEnd = rect.maxX();
298 if (xEnd > m_absolutePaintRect.width())
299 xEnd = m_absolutePaintRect.width();
300
301 int yOrigin = rect.y();
302 int yDest = 0;
303 if (yOrigin < 0) {
304 yDest = -yOrigin;
305 yOrigin = 0;
306 }
307 int yEnd = rect.maxY();
308 if (yEnd > m_absolutePaintRect.height())
309 yEnd = m_absolutePaintRect.height();
310
311 int size = (xEnd - xOrigin) * 4;
312 int destinationScanline = rect.width() * 4;
313 int sourceScanline = m_absolutePaintRect.width() * 4;
314 unsigned char *destinationPixel = destination->data() + ((yDest * rect.width()) + xDest) * 4;
315 unsigned char *sourcePixel = source->data() + ((yOrigin * m_absolutePaintRect.width()) + xOrigin) * 4;
316
317 while (yOrigin < yEnd) {
318 memcpy(destinationPixel, sourcePixel, size);
319 destinationPixel += destinationScanline;
320 sourcePixel += sourceScanline;
321 ++yOrigin;
322 }
323 }
324
copyUnmultipliedImage(Uint8ClampedArray * destination,const IntRect & rect)325 void FilterEffect::copyUnmultipliedImage(Uint8ClampedArray* destination, const IntRect& rect)
326 {
327 ASSERT(hasResult());
328
329 if (!m_unmultipliedImageResult) {
330 // We prefer a conversion from the image buffer.
331 if (m_imageBufferResult)
332 m_unmultipliedImageResult = m_imageBufferResult->getUnmultipliedImageData(IntRect(IntPoint(), m_absolutePaintRect.size()));
333 else {
334 ASSERT(isFilterSizeValid(m_absolutePaintRect));
335 m_unmultipliedImageResult = Uint8ClampedArray::createUninitialized(m_absolutePaintRect.width() * m_absolutePaintRect.height() * 4);
336 unsigned char* sourceComponent = m_premultipliedImageResult->data();
337 unsigned char* destinationComponent = m_unmultipliedImageResult->data();
338 unsigned char* end = sourceComponent + (m_absolutePaintRect.width() * m_absolutePaintRect.height() * 4);
339 while (sourceComponent < end) {
340 int alpha = sourceComponent[3];
341 if (alpha) {
342 destinationComponent[0] = static_cast<int>(sourceComponent[0]) * 255 / alpha;
343 destinationComponent[1] = static_cast<int>(sourceComponent[1]) * 255 / alpha;
344 destinationComponent[2] = static_cast<int>(sourceComponent[2]) * 255 / alpha;
345 } else {
346 destinationComponent[0] = 0;
347 destinationComponent[1] = 0;
348 destinationComponent[2] = 0;
349 }
350 destinationComponent[3] = alpha;
351 sourceComponent += 4;
352 destinationComponent += 4;
353 }
354 }
355 }
356 copyImageBytes(m_unmultipliedImageResult.get(), destination, rect);
357 }
358
copyPremultipliedImage(Uint8ClampedArray * destination,const IntRect & rect)359 void FilterEffect::copyPremultipliedImage(Uint8ClampedArray* destination, const IntRect& rect)
360 {
361 ASSERT(hasResult());
362
363 if (!m_premultipliedImageResult) {
364 // We prefer a conversion from the image buffer.
365 if (m_imageBufferResult)
366 m_premultipliedImageResult = m_imageBufferResult->getPremultipliedImageData(IntRect(IntPoint(), m_absolutePaintRect.size()));
367 else {
368 ASSERT(isFilterSizeValid(m_absolutePaintRect));
369 m_premultipliedImageResult = Uint8ClampedArray::createUninitialized(m_absolutePaintRect.width() * m_absolutePaintRect.height() * 4);
370 unsigned char* sourceComponent = m_unmultipliedImageResult->data();
371 unsigned char* destinationComponent = m_premultipliedImageResult->data();
372 unsigned char* end = sourceComponent + (m_absolutePaintRect.width() * m_absolutePaintRect.height() * 4);
373 while (sourceComponent < end) {
374 int alpha = sourceComponent[3];
375 destinationComponent[0] = static_cast<int>(sourceComponent[0]) * alpha / 255;
376 destinationComponent[1] = static_cast<int>(sourceComponent[1]) * alpha / 255;
377 destinationComponent[2] = static_cast<int>(sourceComponent[2]) * alpha / 255;
378 destinationComponent[3] = alpha;
379 sourceComponent += 4;
380 destinationComponent += 4;
381 }
382 }
383 }
384 copyImageBytes(m_premultipliedImageResult.get(), destination, rect);
385 }
386
createImageBufferResult()387 ImageBuffer* FilterEffect::createImageBufferResult()
388 {
389 // Only one result type is allowed.
390 if (m_absolutePaintRect.isEmpty())
391 return 0;
392 OwnPtr<ImageBufferSurface> surface;
393 if (m_filter->isAccelerated())
394 surface = adoptPtr(new AcceleratedImageBufferSurface(m_absolutePaintRect.size()));
395 if (!m_filter->isAccelerated() || !surface->isValid())
396 surface = adoptPtr(new UnacceleratedImageBufferSurface(m_absolutePaintRect.size()));
397 m_imageBufferResult = ImageBuffer::create(surface.release());
398 return m_imageBufferResult.get();
399 }
400
createUnmultipliedImageResult()401 Uint8ClampedArray* FilterEffect::createUnmultipliedImageResult()
402 {
403 // Only one result type is allowed.
404 ASSERT(!hasResult());
405 ASSERT(isFilterSizeValid(m_absolutePaintRect));
406
407 if (m_absolutePaintRect.isEmpty())
408 return 0;
409 m_unmultipliedImageResult = Uint8ClampedArray::createUninitialized(m_absolutePaintRect.width() * m_absolutePaintRect.height() * 4);
410 return m_unmultipliedImageResult.get();
411 }
412
createPremultipliedImageResult()413 Uint8ClampedArray* FilterEffect::createPremultipliedImageResult()
414 {
415 // Only one result type is allowed.
416 ASSERT(!hasResult());
417 ASSERT(isFilterSizeValid(m_absolutePaintRect));
418
419 if (m_absolutePaintRect.isEmpty())
420 return 0;
421 m_premultipliedImageResult = Uint8ClampedArray::createUninitialized(m_absolutePaintRect.width() * m_absolutePaintRect.height() * 4);
422 return m_premultipliedImageResult.get();
423 }
424
transformResultColorSpace(ColorSpace dstColorSpace)425 void FilterEffect::transformResultColorSpace(ColorSpace dstColorSpace)
426 {
427 if (!hasResult() || dstColorSpace == m_resultColorSpace)
428 return;
429
430 // FIXME: We can avoid this potentially unnecessary ImageBuffer conversion by adding
431 // color space transform support for the {pre,un}multiplied arrays.
432 asImageBuffer()->transformColorSpace(m_resultColorSpace, dstColorSpace);
433
434 m_resultColorSpace = dstColorSpace;
435
436 if (m_unmultipliedImageResult)
437 m_unmultipliedImageResult.clear();
438 if (m_premultipliedImageResult)
439 m_premultipliedImageResult.clear();
440 }
441
externalRepresentation(TextStream & ts,int) const442 TextStream& FilterEffect::externalRepresentation(TextStream& ts, int) const
443 {
444 // FIXME: We should dump the subRegions of the filter primitives here later. This isn't
445 // possible at the moment, because we need more detailed informations from the target object.
446 return ts;
447 }
448
determineFilterPrimitiveSubregion(DetermineSubregionFlags flags)449 FloatRect FilterEffect::determineFilterPrimitiveSubregion(DetermineSubregionFlags flags)
450 {
451 ASSERT(filter());
452
453 // FETile, FETurbulence, FEFlood don't have input effects, take the filter region as unite rect.
454 FloatRect subregion;
455 if (unsigned numberOfInputEffects = inputEffects().size()) {
456 subregion = inputEffect(0)->determineFilterPrimitiveSubregion(flags);
457 for (unsigned i = 1; i < numberOfInputEffects; ++i)
458 subregion.unite(inputEffect(i)->determineFilterPrimitiveSubregion(flags));
459 } else
460 subregion = filter()->filterRegion();
461
462 // After calling determineFilterPrimitiveSubregion on the target effect, reset the subregion again for <feTile>.
463 if (filterEffectType() == FilterEffectTypeTile)
464 subregion = filter()->filterRegion();
465
466 if (flags & MapRectForward)
467 subregion = mapRect(subregion);
468
469 FloatRect boundaries = effectBoundaries();
470 if (hasX())
471 subregion.setX(boundaries.x());
472 if (hasY())
473 subregion.setY(boundaries.y());
474 if (hasWidth())
475 subregion.setWidth(boundaries.width());
476 if (hasHeight())
477 subregion.setHeight(boundaries.height());
478
479 setFilterPrimitiveSubregion(subregion);
480
481 FloatRect absoluteSubregion = filter()->absoluteTransform().mapRect(subregion);
482 FloatSize filterResolution = filter()->filterResolution();
483 absoluteSubregion.scale(filterResolution.width(), filterResolution.height());
484
485 // Clip every filter effect to the filter region.
486 if (flags & ClipToFilterRegion) {
487 FloatRect absoluteScaledFilterRegion = filter()->absoluteFilterRegion();
488 absoluteScaledFilterRegion.scale(filterResolution.width(), filterResolution.height());
489 absoluteSubregion.intersect(absoluteScaledFilterRegion);
490 }
491
492 setMaxEffectRect(absoluteSubregion);
493 return subregion;
494 }
495
createImageFilter(SkiaImageFilterBuilder * builder)496 PassRefPtr<SkImageFilter> FilterEffect::createImageFilter(SkiaImageFilterBuilder* builder)
497 {
498 return 0;
499 }
500
getCropRect(const FloatSize & cropOffset) const501 SkImageFilter::CropRect FilterEffect::getCropRect(const FloatSize& cropOffset) const
502 {
503 SkRect rect = SkRect::MakeEmpty();
504 uint32_t flags = 0;
505 FloatRect boundaries = effectBoundaries();
506 FloatSize resolution = filter()->filterResolution();
507 boundaries.scale(resolution.width(), resolution.height());
508 boundaries.move(cropOffset);
509 if (hasX()) {
510 rect.fLeft = boundaries.x();
511 flags |= SkImageFilter::CropRect::kHasLeft_CropEdge;
512 }
513 if (hasY()) {
514 rect.fTop = boundaries.y();
515 flags |= SkImageFilter::CropRect::kHasTop_CropEdge;
516 }
517 if (hasWidth()) {
518 rect.fRight = rect.fLeft + boundaries.width();
519 flags |= SkImageFilter::CropRect::kHasRight_CropEdge;
520 }
521 if (hasHeight()) {
522 rect.fBottom = rect.fTop + boundaries.height();
523 flags |= SkImageFilter::CropRect::kHasBottom_CropEdge;
524 }
525 return SkImageFilter::CropRect(rect, flags);
526 }
527
528 } // namespace WebCore
529