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.
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.
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.
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.
26 #include "platform/graphics/filters/FilterEffect.h"
28 #include "platform/graphics/ImageBuffer.h"
29 #include "platform/graphics/UnacceleratedImageBufferSurface.h"
30 #include "platform/graphics/filters/Filter.h"
32 #if HAVE(ARM_NEON_INTRINSICS)
38 static const float kMaxFilterArea = 4096 * 4096;
40 FilterEffect::FilterEffect(Filter* filter)
47 , m_clipsToBounds(true)
48 , m_operatingColorSpace(ColorSpaceLinearRGB)
49 , m_resultColorSpace(ColorSpaceDeviceRGB)
54 FilterEffect::~FilterEffect()
58 float FilterEffect::maxFilterArea()
60 return kMaxFilterArea;
63 bool FilterEffect::isFilterSizeValid(const FloatRect& rect)
65 if (rect.width() < 0 || rect.height() < 0
66 || (rect.height() * rect.width() > kMaxFilterArea))
72 FloatRect FilterEffect::determineAbsolutePaintRect(const FloatRect& originalRequestedRect)
74 FloatRect requestedRect = originalRequestedRect;
75 // Filters in SVG clip to primitive subregion, while CSS doesn't.
77 requestedRect.intersect(maxEffectRect());
79 // We may be called multiple times if result is used more than once. Return
80 // quickly if if nothing new is required.
81 if (absolutePaintRect().contains(enclosingIntRect(requestedRect)))
84 FloatRect inputRect = mapPaintRect(requestedRect, false);
86 unsigned size = m_inputEffects.size();
88 for (unsigned i = 0; i < size; ++i)
89 inputUnion.unite(m_inputEffects.at(i)->determineAbsolutePaintRect(inputRect));
90 inputUnion = mapPaintRect(inputUnion, true);
92 if (affectsTransparentPixels() || !size) {
93 inputUnion = requestedRect;
95 // Rect may have inflated. Re-intersect with request.
96 inputUnion.intersect(requestedRect);
99 addAbsolutePaintRect(inputUnion);
103 FloatRect FilterEffect::mapRectRecursive(const FloatRect& rect)
106 if (m_inputEffects.size() > 0) {
107 result = m_inputEffects.at(0)->mapRectRecursive(rect);
108 for (unsigned i = 1; i < m_inputEffects.size(); ++i)
109 result.unite(m_inputEffects.at(i)->mapRectRecursive(rect));
112 return mapRect(result);
115 FloatRect FilterEffect::getSourceRect(const FloatRect& destRect, const FloatRect& destClipRect)
117 FloatRect sourceRect = mapRect(destRect, false);
118 FloatRect sourceClipRect = mapRect(destClipRect, false);
120 FloatRect boundaries = filter()->mapLocalRectToAbsoluteRect(effectBoundaries());
122 sourceClipRect.setX(boundaries.x());
124 sourceClipRect.setY(boundaries.y());
126 sourceClipRect.setWidth(boundaries.width());
128 sourceClipRect.setHeight(boundaries.height());
131 if (m_inputEffects.size() > 0) {
132 result = m_inputEffects.at(0)->getSourceRect(sourceRect, sourceClipRect);
133 for (unsigned i = 1; i < m_inputEffects.size(); ++i)
134 result.unite(m_inputEffects.at(i)->getSourceRect(sourceRect, sourceClipRect));
137 result.intersect(sourceClipRect);
142 IntRect FilterEffect::requestedRegionOfInputImageData(const IntRect& effectRect) const
145 IntPoint location = m_absolutePaintRect.location();
146 location.moveBy(-effectRect.location());
147 return IntRect(location, m_absolutePaintRect.size());
150 IntRect FilterEffect::drawingRegionOfInputImage(const IntRect& srcRect) const
152 return IntRect(IntPoint(srcRect.x() - m_absolutePaintRect.x(),
153 srcRect.y() - m_absolutePaintRect.y()), srcRect.size());
156 FilterEffect* FilterEffect::inputEffect(unsigned number) const
158 ASSERT_WITH_SECURITY_IMPLICATION(number < m_inputEffects.size());
159 return m_inputEffects.at(number).get();
162 void FilterEffect::addAbsolutePaintRect(const FloatRect& paintRect)
164 IntRect intPaintRect(enclosingIntRect(paintRect));
165 if (m_absolutePaintRect.contains(intPaintRect))
167 intPaintRect.unite(m_absolutePaintRect);
168 // Make sure we are not holding on to a smaller rendering.
170 m_absolutePaintRect = intPaintRect;
173 void FilterEffect::apply()
175 // Recursively determine paint rects first, so that we don't redraw images
176 // if a smaller section is requested first.
177 determineAbsolutePaintRect(maxEffectRect());
181 void FilterEffect::applyRecursive()
185 unsigned size = m_inputEffects.size();
186 for (unsigned i = 0; i < size; ++i) {
187 FilterEffect* in = m_inputEffects.at(i).get();
188 in->applyRecursive();
189 if (!in->hasResult())
192 // Convert input results to the current effect's color space.
193 transformResultColorSpace(in, i);
196 setResultColorSpace(m_operatingColorSpace);
198 if (!isFilterSizeValid(m_absolutePaintRect))
201 if (requiresValidPreMultipliedPixels()) {
202 for (unsigned i = 0; i < size; ++i)
203 inputEffect(i)->correctFilterResultIfNeeded();
212 void FilterEffect::forceValidPreMultipliedPixels()
214 // Must operate on pre-multiplied results; other formats cannot have invalid pixels.
215 if (!m_premultipliedImageResult)
218 Uint8ClampedArray* imageArray = m_premultipliedImageResult.get();
219 unsigned char* pixelData = imageArray->data();
220 int pixelArrayLength = imageArray->length();
222 // We must have four bytes per pixel, and complete pixels
223 ASSERT(!(pixelArrayLength % 4));
225 #if HAVE(ARM_NEON_INTRINSICS)
226 if (pixelArrayLength >= 64) {
227 unsigned char* lastPixel = pixelData + (pixelArrayLength & ~0x3f);
229 // Increments pixelData by 64.
230 uint8x16x4_t sixteenPixels = vld4q_u8(pixelData);
231 sixteenPixels.val[0] = vminq_u8(sixteenPixels.val[0], sixteenPixels.val[3]);
232 sixteenPixels.val[1] = vminq_u8(sixteenPixels.val[1], sixteenPixels.val[3]);
233 sixteenPixels.val[2] = vminq_u8(sixteenPixels.val[2], sixteenPixels.val[3]);
234 vst4q_u8(pixelData, sixteenPixels);
236 } while (pixelData < lastPixel);
238 pixelArrayLength &= 0x3f;
239 if (!pixelArrayLength)
244 int numPixels = pixelArrayLength / 4;
246 // Iterate over each pixel, checking alpha and adjusting color components if necessary
247 while (--numPixels >= 0) {
248 // Alpha is the 4th byte in a pixel
249 unsigned char a = *(pixelData + 3);
250 // Clamp each component to alpha, and increment the pixel location
251 for (int i = 0; i < 3; ++i) {
256 // Increment for alpha
261 void FilterEffect::clearResult()
263 if (m_imageBufferResult)
264 m_imageBufferResult.clear();
265 if (m_unmultipliedImageResult)
266 m_unmultipliedImageResult.clear();
267 if (m_premultipliedImageResult)
268 m_premultipliedImageResult.clear();
270 m_absolutePaintRect = IntRect();
273 void FilterEffect::clearResultsRecursive()
275 // Clear all results, regardless that the current effect has
276 // a result. Can be used if an effect is in an erroneous state.
280 unsigned size = m_inputEffects.size();
281 for (unsigned i = 0; i < size; ++i)
282 m_inputEffects.at(i).get()->clearResultsRecursive();
285 ImageBuffer* FilterEffect::asImageBuffer()
289 if (m_imageBufferResult)
290 return m_imageBufferResult.get();
291 OwnPtr<ImageBufferSurface> surface;
292 surface = adoptPtr(new UnacceleratedImageBufferSurface(m_absolutePaintRect.size()));
293 m_imageBufferResult = ImageBuffer::create(surface.release());
294 if (!m_imageBufferResult)
297 IntRect destinationRect(IntPoint(), m_absolutePaintRect.size());
298 if (m_premultipliedImageResult)
299 m_imageBufferResult->putByteArray(Premultiplied, m_premultipliedImageResult.get(), destinationRect.size(), destinationRect, IntPoint());
301 m_imageBufferResult->putByteArray(Unmultiplied, m_unmultipliedImageResult.get(), destinationRect.size(), destinationRect, IntPoint());
302 return m_imageBufferResult.get();
305 PassRefPtr<Uint8ClampedArray> FilterEffect::asUnmultipliedImage(const IntRect& rect)
307 ASSERT(isFilterSizeValid(rect));
308 RefPtr<Uint8ClampedArray> imageData = Uint8ClampedArray::createUninitialized(rect.width() * rect.height() * 4);
309 copyUnmultipliedImage(imageData.get(), rect);
310 return imageData.release();
313 PassRefPtr<Uint8ClampedArray> FilterEffect::asPremultipliedImage(const IntRect& rect)
315 ASSERT(isFilterSizeValid(rect));
316 RefPtr<Uint8ClampedArray> imageData = Uint8ClampedArray::createUninitialized(rect.width() * rect.height() * 4);
317 copyPremultipliedImage(imageData.get(), rect);
318 return imageData.release();
321 inline void FilterEffect::copyImageBytes(Uint8ClampedArray* source, Uint8ClampedArray* destination, const IntRect& rect)
323 // Initialize the destination to transparent black, if not entirely covered by the source.
324 if (rect.x() < 0 || rect.y() < 0 || rect.maxX() > m_absolutePaintRect.width() || rect.maxY() > m_absolutePaintRect.height())
325 memset(destination->data(), 0, destination->length());
327 // Early return if the rect does not intersect with the source.
328 if (rect.maxX() <= 0 || rect.maxY() <= 0 || rect.x() >= m_absolutePaintRect.width() || rect.y() >= m_absolutePaintRect.height())
331 int xOrigin = rect.x();
337 int xEnd = rect.maxX();
338 if (xEnd > m_absolutePaintRect.width())
339 xEnd = m_absolutePaintRect.width();
341 int yOrigin = rect.y();
347 int yEnd = rect.maxY();
348 if (yEnd > m_absolutePaintRect.height())
349 yEnd = m_absolutePaintRect.height();
351 int size = (xEnd - xOrigin) * 4;
352 int destinationScanline = rect.width() * 4;
353 int sourceScanline = m_absolutePaintRect.width() * 4;
354 unsigned char *destinationPixel = destination->data() + ((yDest * rect.width()) + xDest) * 4;
355 unsigned char *sourcePixel = source->data() + ((yOrigin * m_absolutePaintRect.width()) + xOrigin) * 4;
357 while (yOrigin < yEnd) {
358 memcpy(destinationPixel, sourcePixel, size);
359 destinationPixel += destinationScanline;
360 sourcePixel += sourceScanline;
365 void FilterEffect::copyUnmultipliedImage(Uint8ClampedArray* destination, const IntRect& rect)
369 if (!m_unmultipliedImageResult) {
370 // We prefer a conversion from the image buffer.
371 if (m_imageBufferResult)
372 m_unmultipliedImageResult = m_imageBufferResult->getUnmultipliedImageData(IntRect(IntPoint(), m_absolutePaintRect.size()));
374 ASSERT(isFilterSizeValid(m_absolutePaintRect));
375 m_unmultipliedImageResult = Uint8ClampedArray::createUninitialized(m_absolutePaintRect.width() * m_absolutePaintRect.height() * 4);
376 unsigned char* sourceComponent = m_premultipliedImageResult->data();
377 unsigned char* destinationComponent = m_unmultipliedImageResult->data();
378 unsigned char* end = sourceComponent + (m_absolutePaintRect.width() * m_absolutePaintRect.height() * 4);
379 while (sourceComponent < end) {
380 int alpha = sourceComponent[3];
382 destinationComponent[0] = static_cast<int>(sourceComponent[0]) * 255 / alpha;
383 destinationComponent[1] = static_cast<int>(sourceComponent[1]) * 255 / alpha;
384 destinationComponent[2] = static_cast<int>(sourceComponent[2]) * 255 / alpha;
386 destinationComponent[0] = 0;
387 destinationComponent[1] = 0;
388 destinationComponent[2] = 0;
390 destinationComponent[3] = alpha;
391 sourceComponent += 4;
392 destinationComponent += 4;
396 copyImageBytes(m_unmultipliedImageResult.get(), destination, rect);
399 void FilterEffect::copyPremultipliedImage(Uint8ClampedArray* destination, const IntRect& rect)
403 if (!m_premultipliedImageResult) {
404 // We prefer a conversion from the image buffer.
405 if (m_imageBufferResult)
406 m_premultipliedImageResult = m_imageBufferResult->getPremultipliedImageData(IntRect(IntPoint(), m_absolutePaintRect.size()));
408 ASSERT(isFilterSizeValid(m_absolutePaintRect));
409 m_premultipliedImageResult = Uint8ClampedArray::createUninitialized(m_absolutePaintRect.width() * m_absolutePaintRect.height() * 4);
410 unsigned char* sourceComponent = m_unmultipliedImageResult->data();
411 unsigned char* destinationComponent = m_premultipliedImageResult->data();
412 unsigned char* end = sourceComponent + (m_absolutePaintRect.width() * m_absolutePaintRect.height() * 4);
413 while (sourceComponent < end) {
414 int alpha = sourceComponent[3];
415 destinationComponent[0] = static_cast<int>(sourceComponent[0]) * alpha / 255;
416 destinationComponent[1] = static_cast<int>(sourceComponent[1]) * alpha / 255;
417 destinationComponent[2] = static_cast<int>(sourceComponent[2]) * alpha / 255;
418 destinationComponent[3] = alpha;
419 sourceComponent += 4;
420 destinationComponent += 4;
424 copyImageBytes(m_premultipliedImageResult.get(), destination, rect);
427 ImageBuffer* FilterEffect::createImageBufferResult()
429 // Only one result type is allowed.
430 if (m_absolutePaintRect.isEmpty())
432 OwnPtr<ImageBufferSurface> surface;
433 surface = adoptPtr(new UnacceleratedImageBufferSurface(m_absolutePaintRect.size()));
434 m_imageBufferResult = ImageBuffer::create(surface.release());
435 return m_imageBufferResult.get();
438 Uint8ClampedArray* FilterEffect::createUnmultipliedImageResult()
440 // Only one result type is allowed.
441 ASSERT(!hasResult());
442 ASSERT(isFilterSizeValid(m_absolutePaintRect));
444 if (m_absolutePaintRect.isEmpty())
446 m_unmultipliedImageResult = Uint8ClampedArray::createUninitialized(m_absolutePaintRect.width() * m_absolutePaintRect.height() * 4);
447 return m_unmultipliedImageResult.get();
450 Uint8ClampedArray* FilterEffect::createPremultipliedImageResult()
452 // Only one result type is allowed.
453 ASSERT(!hasResult());
454 ASSERT(isFilterSizeValid(m_absolutePaintRect));
456 if (m_absolutePaintRect.isEmpty())
458 m_premultipliedImageResult = Uint8ClampedArray::createUninitialized(m_absolutePaintRect.width() * m_absolutePaintRect.height() * 4);
459 return m_premultipliedImageResult.get();
462 Color FilterEffect::adaptColorToOperatingColorSpace(const Color& deviceColor)
464 // |deviceColor| is assumed to be DeviceRGB.
465 return ColorSpaceUtilities::convertColor(deviceColor, operatingColorSpace());
468 void FilterEffect::transformResultColorSpace(ColorSpace dstColorSpace)
470 if (!hasResult() || dstColorSpace == m_resultColorSpace)
473 // FIXME: We can avoid this potentially unnecessary ImageBuffer conversion by adding
474 // color space transform support for the {pre,un}multiplied arrays.
475 asImageBuffer()->transformColorSpace(m_resultColorSpace, dstColorSpace);
477 m_resultColorSpace = dstColorSpace;
479 if (m_unmultipliedImageResult)
480 m_unmultipliedImageResult.clear();
481 if (m_premultipliedImageResult)
482 m_premultipliedImageResult.clear();
485 TextStream& FilterEffect::externalRepresentation(TextStream& ts, int) const
487 // FIXME: We should dump the subRegions of the filter primitives here later. This isn't
488 // possible at the moment, because we need more detailed informations from the target object.
492 FloatRect FilterEffect::determineFilterPrimitiveSubregion(DetermineSubregionFlags flags)
494 Filter* filter = this->filter();
497 // FETile, FETurbulence, FEFlood don't have input effects, take the filter region as unite rect.
499 if (unsigned numberOfInputEffects = inputEffects().size()) {
500 subregion = inputEffect(0)->determineFilterPrimitiveSubregion(flags);
501 for (unsigned i = 1; i < numberOfInputEffects; ++i)
502 subregion.unite(inputEffect(i)->determineFilterPrimitiveSubregion(flags));
504 subregion = filter->filterRegion();
507 // After calling determineFilterPrimitiveSubregion on the target effect, reset the subregion again for <feTile>.
508 if (filterEffectType() == FilterEffectTypeTile)
509 subregion = filter->filterRegion();
511 if (flags & MapRectForward) {
512 // mapRect works on absolute rectangles.
513 subregion = filter->mapAbsoluteRectToLocalRect(mapRect(
514 filter->mapLocalRectToAbsoluteRect(subregion)));
517 FloatRect boundaries = effectBoundaries();
519 subregion.setX(boundaries.x());
521 subregion.setY(boundaries.y());
523 subregion.setWidth(boundaries.width());
525 subregion.setHeight(boundaries.height());
527 setFilterPrimitiveSubregion(subregion);
529 FloatRect absoluteSubregion = filter->mapLocalRectToAbsoluteRect(subregion);
531 // Clip every filter effect to the filter region.
532 if (flags & ClipToFilterRegion) {
533 absoluteSubregion.intersect(filter->absoluteFilterRegion());
536 setMaxEffectRect(absoluteSubregion);
540 PassRefPtr<SkImageFilter> FilterEffect::createImageFilter(SkiaImageFilterBuilder* builder)
545 SkImageFilter::CropRect FilterEffect::getCropRect(const FloatSize& cropOffset) const
547 FloatRect rect = filter()->filterRegion();
549 FloatRect boundaries = effectBoundaries();
550 boundaries.move(cropOffset);
552 rect.setX(boundaries.x());
553 flags |= SkImageFilter::CropRect::kHasLeft_CropEdge;
554 flags |= SkImageFilter::CropRect::kHasRight_CropEdge;
557 rect.setY(boundaries.y());
558 flags |= SkImageFilter::CropRect::kHasTop_CropEdge;
559 flags |= SkImageFilter::CropRect::kHasBottom_CropEdge;
562 rect.setWidth(boundaries.width());
563 flags |= SkImageFilter::CropRect::kHasRight_CropEdge;
566 rect.setHeight(boundaries.height());
567 flags |= SkImageFilter::CropRect::kHasBottom_CropEdge;
569 rect = filter()->mapLocalRectToAbsoluteRect(rect);
570 return SkImageFilter::CropRect(rect, flags);
573 } // namespace WebCore