• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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