| Index: Source/core/platform/graphics/filters/FilterEffect.cpp
|
| diff --git a/Source/core/platform/graphics/filters/FilterEffect.cpp b/Source/core/platform/graphics/filters/FilterEffect.cpp
|
| deleted file mode 100644
|
| index 849796d3adc6c16d218150a6e5463016caaafc17..0000000000000000000000000000000000000000
|
| --- a/Source/core/platform/graphics/filters/FilterEffect.cpp
|
| +++ /dev/null
|
| @@ -1,517 +0,0 @@
|
| -/*
|
| - * Copyright (C) 2008 Alex Mathews <possessedpenguinbob@gmail.com>
|
| - * Copyright (C) 2009 Dirk Schulze <krit@webkit.org>
|
| - * Copyright (C) Research In Motion Limited 2010. All rights reserved.
|
| - * Copyright (C) 2012 University of Szeged
|
| - * Copyright (C) 2013 Google Inc. All rights reserved.
|
| - *
|
| - * This library is free software; you can redistribute it and/or
|
| - * modify it under the terms of the GNU Library General Public
|
| - * License as published by the Free Software Foundation; either
|
| - * version 2 of the License, or (at your option) any later version.
|
| - *
|
| - * This library is distributed in the hope that it will be useful,
|
| - * but WITHOUT ANY WARRANTY; without even the implied warranty of
|
| - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
| - * Library General Public License for more details.
|
| - *
|
| - * You should have received a copy of the GNU Library General Public License
|
| - * along with this library; see the file COPYING.LIB. If not, write to
|
| - * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
| - * Boston, MA 02110-1301, USA.
|
| - */
|
| -
|
| -#include "config.h"
|
| -
|
| -#include "core/platform/graphics/filters/FilterEffect.h"
|
| -
|
| -#include "core/platform/graphics/ImageBuffer.h"
|
| -#include "core/platform/graphics/filters/Filter.h"
|
| -
|
| -#if HAVE(ARM_NEON_INTRINSICS)
|
| -#include <arm_neon.h>
|
| -#endif
|
| -
|
| -namespace WebCore {
|
| -
|
| -FilterEffect::FilterEffect(Filter* filter)
|
| - : m_alphaImage(false)
|
| - , m_filter(filter)
|
| - , m_hasX(false)
|
| - , m_hasY(false)
|
| - , m_hasWidth(false)
|
| - , m_hasHeight(false)
|
| - , m_clipsToBounds(true)
|
| - , m_operatingColorSpace(ColorSpaceLinearRGB)
|
| - , m_resultColorSpace(ColorSpaceDeviceRGB)
|
| -{
|
| - ASSERT(m_filter);
|
| -}
|
| -
|
| -FilterEffect::~FilterEffect()
|
| -{
|
| -}
|
| -
|
| -inline bool isFilterSizeValid(IntRect rect)
|
| -{
|
| - if (rect.width() < 0 || rect.width() > kMaxFilterSize
|
| - || rect.height() < 0 || rect.height() > kMaxFilterSize)
|
| - return false;
|
| - return true;
|
| -}
|
| -
|
| -void FilterEffect::determineAbsolutePaintRect()
|
| -{
|
| - m_absolutePaintRect = IntRect();
|
| - unsigned size = m_inputEffects.size();
|
| - for (unsigned i = 0; i < size; ++i)
|
| - m_absolutePaintRect.unite(m_inputEffects.at(i)->absolutePaintRect());
|
| -
|
| - // Filters in SVG clip to primitive subregion, while CSS doesn't.
|
| - if (m_clipsToBounds)
|
| - m_absolutePaintRect.intersect(enclosingIntRect(m_maxEffectRect));
|
| - else
|
| - m_absolutePaintRect.unite(enclosingIntRect(m_maxEffectRect));
|
| -
|
| -}
|
| -
|
| -FloatRect FilterEffect::mapRectRecursive(const FloatRect& rect)
|
| -{
|
| - FloatRect result;
|
| - if (m_inputEffects.size() > 0) {
|
| - result = m_inputEffects.at(0)->mapRectRecursive(rect);
|
| - for (unsigned i = 1; i < m_inputEffects.size(); ++i)
|
| - result.unite(m_inputEffects.at(i)->mapRectRecursive(rect));
|
| - } else
|
| - result = rect;
|
| - return mapRect(result);
|
| -}
|
| -
|
| -FloatRect FilterEffect::getSourceRect(const FloatRect& destRect, const FloatRect& destClipRect)
|
| -{
|
| - FloatRect sourceRect = mapRect(destRect, false);
|
| - FloatRect sourceClipRect = mapRect(destClipRect, false);
|
| -
|
| - FloatRect boundaries = effectBoundaries();
|
| - if (hasX())
|
| - sourceClipRect.setX(boundaries.x());
|
| - if (hasY())
|
| - sourceClipRect.setY(boundaries.y());
|
| - if (hasWidth())
|
| - sourceClipRect.setWidth(boundaries.width());
|
| - if (hasHeight())
|
| - sourceClipRect.setHeight(boundaries.height());
|
| -
|
| - FloatRect result;
|
| - if (m_inputEffects.size() > 0) {
|
| - result = m_inputEffects.at(0)->getSourceRect(sourceRect, sourceClipRect);
|
| - for (unsigned i = 1; i < m_inputEffects.size(); ++i)
|
| - result.unite(m_inputEffects.at(i)->getSourceRect(sourceRect, sourceClipRect));
|
| - } else {
|
| - result = sourceRect;
|
| - result.intersect(sourceClipRect);
|
| - }
|
| - return result;
|
| -}
|
| -
|
| -IntRect FilterEffect::requestedRegionOfInputImageData(const IntRect& effectRect) const
|
| -{
|
| - ASSERT(hasResult());
|
| - IntPoint location = m_absolutePaintRect.location();
|
| - location.moveBy(-effectRect.location());
|
| - return IntRect(location, m_absolutePaintRect.size());
|
| -}
|
| -
|
| -IntRect FilterEffect::drawingRegionOfInputImage(const IntRect& srcRect) const
|
| -{
|
| - return IntRect(IntPoint(srcRect.x() - m_absolutePaintRect.x(),
|
| - srcRect.y() - m_absolutePaintRect.y()), srcRect.size());
|
| -}
|
| -
|
| -FilterEffect* FilterEffect::inputEffect(unsigned number) const
|
| -{
|
| - ASSERT_WITH_SECURITY_IMPLICATION(number < m_inputEffects.size());
|
| - return m_inputEffects.at(number).get();
|
| -}
|
| -
|
| -void FilterEffect::apply()
|
| -{
|
| - if (hasResult())
|
| - return;
|
| - unsigned size = m_inputEffects.size();
|
| - for (unsigned i = 0; i < size; ++i) {
|
| - FilterEffect* in = m_inputEffects.at(i).get();
|
| - in->apply();
|
| - if (!in->hasResult())
|
| - return;
|
| -
|
| - // Convert input results to the current effect's color space.
|
| - transformResultColorSpace(in, i);
|
| - }
|
| -
|
| - determineAbsolutePaintRect();
|
| - setResultColorSpace(m_operatingColorSpace);
|
| -
|
| - if (!isFilterSizeValid(m_absolutePaintRect))
|
| - return;
|
| -
|
| - if (requiresValidPreMultipliedPixels()) {
|
| - for (unsigned i = 0; i < size; ++i)
|
| - inputEffect(i)->correctFilterResultIfNeeded();
|
| - }
|
| -
|
| - if (applySkia())
|
| - return;
|
| -
|
| - applySoftware();
|
| -}
|
| -
|
| -void FilterEffect::forceValidPreMultipliedPixels()
|
| -{
|
| - // Must operate on pre-multiplied results; other formats cannot have invalid pixels.
|
| - if (!m_premultipliedImageResult)
|
| - return;
|
| -
|
| - Uint8ClampedArray* imageArray = m_premultipliedImageResult.get();
|
| - unsigned char* pixelData = imageArray->data();
|
| - int pixelArrayLength = imageArray->length();
|
| -
|
| - // We must have four bytes per pixel, and complete pixels
|
| - ASSERT(!(pixelArrayLength % 4));
|
| -
|
| -#if HAVE(ARM_NEON_INTRINSICS)
|
| - if (pixelArrayLength >= 64) {
|
| - unsigned char* lastPixel = pixelData + (pixelArrayLength & ~0x3f);
|
| - do {
|
| - // Increments pixelData by 64.
|
| - uint8x16x4_t sixteenPixels = vld4q_u8(pixelData);
|
| - sixteenPixels.val[0] = vminq_u8(sixteenPixels.val[0], sixteenPixels.val[3]);
|
| - sixteenPixels.val[1] = vminq_u8(sixteenPixels.val[1], sixteenPixels.val[3]);
|
| - sixteenPixels.val[2] = vminq_u8(sixteenPixels.val[2], sixteenPixels.val[3]);
|
| - vst4q_u8(pixelData, sixteenPixels);
|
| - pixelData += 64;
|
| - } while (pixelData < lastPixel);
|
| -
|
| - pixelArrayLength &= 0x3f;
|
| - if (!pixelArrayLength)
|
| - return;
|
| - }
|
| -#endif
|
| -
|
| - int numPixels = pixelArrayLength / 4;
|
| -
|
| - // Iterate over each pixel, checking alpha and adjusting color components if necessary
|
| - while (--numPixels >= 0) {
|
| - // Alpha is the 4th byte in a pixel
|
| - unsigned char a = *(pixelData + 3);
|
| - // Clamp each component to alpha, and increment the pixel location
|
| - for (int i = 0; i < 3; ++i) {
|
| - if (*pixelData > a)
|
| - *pixelData = a;
|
| - ++pixelData;
|
| - }
|
| - // Increment for alpha
|
| - ++pixelData;
|
| - }
|
| -}
|
| -
|
| -void FilterEffect::clearResult()
|
| -{
|
| - if (m_imageBufferResult)
|
| - m_imageBufferResult.clear();
|
| - if (m_unmultipliedImageResult)
|
| - m_unmultipliedImageResult.clear();
|
| - if (m_premultipliedImageResult)
|
| - m_premultipliedImageResult.clear();
|
| -}
|
| -
|
| -void FilterEffect::clearResultsRecursive()
|
| -{
|
| - // Clear all results, regardless that the current effect has
|
| - // a result. Can be used if an effect is in an erroneous state.
|
| - if (hasResult())
|
| - clearResult();
|
| -
|
| - unsigned size = m_inputEffects.size();
|
| - for (unsigned i = 0; i < size; ++i)
|
| - m_inputEffects.at(i).get()->clearResultsRecursive();
|
| -}
|
| -
|
| -ImageBuffer* FilterEffect::asImageBuffer()
|
| -{
|
| - if (!hasResult())
|
| - return 0;
|
| - if (m_imageBufferResult)
|
| - return m_imageBufferResult.get();
|
| - m_imageBufferResult = ImageBuffer::create(m_absolutePaintRect.size(), 1, m_filter->renderingMode());
|
| - IntRect destinationRect(IntPoint(), m_absolutePaintRect.size());
|
| - if (m_premultipliedImageResult)
|
| - m_imageBufferResult->putByteArray(Premultiplied, m_premultipliedImageResult.get(), destinationRect.size(), destinationRect, IntPoint());
|
| - else
|
| - m_imageBufferResult->putByteArray(Unmultiplied, m_unmultipliedImageResult.get(), destinationRect.size(), destinationRect, IntPoint());
|
| - return m_imageBufferResult.get();
|
| -}
|
| -
|
| -PassRefPtr<Uint8ClampedArray> FilterEffect::asUnmultipliedImage(const IntRect& rect)
|
| -{
|
| - ASSERT(isFilterSizeValid(rect));
|
| - RefPtr<Uint8ClampedArray> imageData = Uint8ClampedArray::createUninitialized(rect.width() * rect.height() * 4);
|
| - copyUnmultipliedImage(imageData.get(), rect);
|
| - return imageData.release();
|
| -}
|
| -
|
| -PassRefPtr<Uint8ClampedArray> FilterEffect::asPremultipliedImage(const IntRect& rect)
|
| -{
|
| - ASSERT(isFilterSizeValid(rect));
|
| - RefPtr<Uint8ClampedArray> imageData = Uint8ClampedArray::createUninitialized(rect.width() * rect.height() * 4);
|
| - copyPremultipliedImage(imageData.get(), rect);
|
| - return imageData.release();
|
| -}
|
| -
|
| -inline void FilterEffect::copyImageBytes(Uint8ClampedArray* source, Uint8ClampedArray* destination, const IntRect& rect)
|
| -{
|
| - // Initialize the destination to transparent black, if not entirely covered by the source.
|
| - if (rect.x() < 0 || rect.y() < 0 || rect.maxX() > m_absolutePaintRect.width() || rect.maxY() > m_absolutePaintRect.height())
|
| - memset(destination->data(), 0, destination->length());
|
| -
|
| - // Early return if the rect does not intersect with the source.
|
| - if (rect.maxX() <= 0 || rect.maxY() <= 0 || rect.x() >= m_absolutePaintRect.width() || rect.y() >= m_absolutePaintRect.height())
|
| - return;
|
| -
|
| - int xOrigin = rect.x();
|
| - int xDest = 0;
|
| - if (xOrigin < 0) {
|
| - xDest = -xOrigin;
|
| - xOrigin = 0;
|
| - }
|
| - int xEnd = rect.maxX();
|
| - if (xEnd > m_absolutePaintRect.width())
|
| - xEnd = m_absolutePaintRect.width();
|
| -
|
| - int yOrigin = rect.y();
|
| - int yDest = 0;
|
| - if (yOrigin < 0) {
|
| - yDest = -yOrigin;
|
| - yOrigin = 0;
|
| - }
|
| - int yEnd = rect.maxY();
|
| - if (yEnd > m_absolutePaintRect.height())
|
| - yEnd = m_absolutePaintRect.height();
|
| -
|
| - int size = (xEnd - xOrigin) * 4;
|
| - int destinationScanline = rect.width() * 4;
|
| - int sourceScanline = m_absolutePaintRect.width() * 4;
|
| - unsigned char *destinationPixel = destination->data() + ((yDest * rect.width()) + xDest) * 4;
|
| - unsigned char *sourcePixel = source->data() + ((yOrigin * m_absolutePaintRect.width()) + xOrigin) * 4;
|
| -
|
| - while (yOrigin < yEnd) {
|
| - memcpy(destinationPixel, sourcePixel, size);
|
| - destinationPixel += destinationScanline;
|
| - sourcePixel += sourceScanline;
|
| - ++yOrigin;
|
| - }
|
| -}
|
| -
|
| -void FilterEffect::copyUnmultipliedImage(Uint8ClampedArray* destination, const IntRect& rect)
|
| -{
|
| - ASSERT(hasResult());
|
| -
|
| - if (!m_unmultipliedImageResult) {
|
| - // We prefer a conversion from the image buffer.
|
| - if (m_imageBufferResult)
|
| - m_unmultipliedImageResult = m_imageBufferResult->getUnmultipliedImageData(IntRect(IntPoint(), m_absolutePaintRect.size()));
|
| - else {
|
| - ASSERT(isFilterSizeValid(m_absolutePaintRect));
|
| - m_unmultipliedImageResult = Uint8ClampedArray::createUninitialized(m_absolutePaintRect.width() * m_absolutePaintRect.height() * 4);
|
| - unsigned char* sourceComponent = m_premultipliedImageResult->data();
|
| - unsigned char* destinationComponent = m_unmultipliedImageResult->data();
|
| - unsigned char* end = sourceComponent + (m_absolutePaintRect.width() * m_absolutePaintRect.height() * 4);
|
| - while (sourceComponent < end) {
|
| - int alpha = sourceComponent[3];
|
| - if (alpha) {
|
| - destinationComponent[0] = static_cast<int>(sourceComponent[0]) * 255 / alpha;
|
| - destinationComponent[1] = static_cast<int>(sourceComponent[1]) * 255 / alpha;
|
| - destinationComponent[2] = static_cast<int>(sourceComponent[2]) * 255 / alpha;
|
| - } else {
|
| - destinationComponent[0] = 0;
|
| - destinationComponent[1] = 0;
|
| - destinationComponent[2] = 0;
|
| - }
|
| - destinationComponent[3] = alpha;
|
| - sourceComponent += 4;
|
| - destinationComponent += 4;
|
| - }
|
| - }
|
| - }
|
| - copyImageBytes(m_unmultipliedImageResult.get(), destination, rect);
|
| -}
|
| -
|
| -void FilterEffect::copyPremultipliedImage(Uint8ClampedArray* destination, const IntRect& rect)
|
| -{
|
| - ASSERT(hasResult());
|
| -
|
| - if (!m_premultipliedImageResult) {
|
| - // We prefer a conversion from the image buffer.
|
| - if (m_imageBufferResult)
|
| - m_premultipliedImageResult = m_imageBufferResult->getPremultipliedImageData(IntRect(IntPoint(), m_absolutePaintRect.size()));
|
| - else {
|
| - ASSERT(isFilterSizeValid(m_absolutePaintRect));
|
| - m_premultipliedImageResult = Uint8ClampedArray::createUninitialized(m_absolutePaintRect.width() * m_absolutePaintRect.height() * 4);
|
| - unsigned char* sourceComponent = m_unmultipliedImageResult->data();
|
| - unsigned char* destinationComponent = m_premultipliedImageResult->data();
|
| - unsigned char* end = sourceComponent + (m_absolutePaintRect.width() * m_absolutePaintRect.height() * 4);
|
| - while (sourceComponent < end) {
|
| - int alpha = sourceComponent[3];
|
| - destinationComponent[0] = static_cast<int>(sourceComponent[0]) * alpha / 255;
|
| - destinationComponent[1] = static_cast<int>(sourceComponent[1]) * alpha / 255;
|
| - destinationComponent[2] = static_cast<int>(sourceComponent[2]) * alpha / 255;
|
| - destinationComponent[3] = alpha;
|
| - sourceComponent += 4;
|
| - destinationComponent += 4;
|
| - }
|
| - }
|
| - }
|
| - copyImageBytes(m_premultipliedImageResult.get(), destination, rect);
|
| -}
|
| -
|
| -ImageBuffer* FilterEffect::createImageBufferResult()
|
| -{
|
| - // Only one result type is allowed.
|
| - ASSERT(!hasResult());
|
| - if (m_absolutePaintRect.isEmpty())
|
| - return 0;
|
| - m_imageBufferResult = ImageBuffer::create(m_absolutePaintRect.size(), 1, m_filter->renderingMode());
|
| - if (!m_imageBufferResult)
|
| - return 0;
|
| - ASSERT(m_imageBufferResult->context());
|
| - return m_imageBufferResult.get();
|
| -}
|
| -
|
| -Uint8ClampedArray* FilterEffect::createUnmultipliedImageResult()
|
| -{
|
| - // Only one result type is allowed.
|
| - ASSERT(!hasResult());
|
| - ASSERT(isFilterSizeValid(m_absolutePaintRect));
|
| -
|
| - if (m_absolutePaintRect.isEmpty())
|
| - return 0;
|
| - m_unmultipliedImageResult = Uint8ClampedArray::createUninitialized(m_absolutePaintRect.width() * m_absolutePaintRect.height() * 4);
|
| - return m_unmultipliedImageResult.get();
|
| -}
|
| -
|
| -Uint8ClampedArray* FilterEffect::createPremultipliedImageResult()
|
| -{
|
| - // Only one result type is allowed.
|
| - ASSERT(!hasResult());
|
| - ASSERT(isFilterSizeValid(m_absolutePaintRect));
|
| -
|
| - if (m_absolutePaintRect.isEmpty())
|
| - return 0;
|
| - m_premultipliedImageResult = Uint8ClampedArray::createUninitialized(m_absolutePaintRect.width() * m_absolutePaintRect.height() * 4);
|
| - return m_premultipliedImageResult.get();
|
| -}
|
| -
|
| -void FilterEffect::transformResultColorSpace(ColorSpace dstColorSpace)
|
| -{
|
| - if (!hasResult() || dstColorSpace == m_resultColorSpace)
|
| - return;
|
| -
|
| - // FIXME: We can avoid this potentially unnecessary ImageBuffer conversion by adding
|
| - // color space transform support for the {pre,un}multiplied arrays.
|
| - asImageBuffer()->transformColorSpace(m_resultColorSpace, dstColorSpace);
|
| -
|
| - m_resultColorSpace = dstColorSpace;
|
| -
|
| - if (m_unmultipliedImageResult)
|
| - m_unmultipliedImageResult.clear();
|
| - if (m_premultipliedImageResult)
|
| - m_premultipliedImageResult.clear();
|
| -}
|
| -
|
| -TextStream& FilterEffect::externalRepresentation(TextStream& ts, int) const
|
| -{
|
| - // FIXME: We should dump the subRegions of the filter primitives here later. This isn't
|
| - // possible at the moment, because we need more detailed informations from the target object.
|
| - return ts;
|
| -}
|
| -
|
| -FloatRect FilterEffect::determineFilterPrimitiveSubregion(DetermineSubregionFlags flags)
|
| -{
|
| - ASSERT(filter());
|
| -
|
| - // FETile, FETurbulence, FEFlood don't have input effects, take the filter region as unite rect.
|
| - FloatRect subregion;
|
| - if (unsigned numberOfInputEffects = inputEffects().size()) {
|
| - subregion = inputEffect(0)->determineFilterPrimitiveSubregion(flags);
|
| - for (unsigned i = 1; i < numberOfInputEffects; ++i)
|
| - subregion.unite(inputEffect(i)->determineFilterPrimitiveSubregion(flags));
|
| - } else
|
| - subregion = filter()->filterRegion();
|
| -
|
| - // After calling determineFilterPrimitiveSubregion on the target effect, reset the subregion again for <feTile>.
|
| - if (filterEffectType() == FilterEffectTypeTile)
|
| - subregion = filter()->filterRegion();
|
| -
|
| - if (flags & MapRectForward)
|
| - subregion = mapRect(subregion);
|
| -
|
| - FloatRect boundaries = effectBoundaries();
|
| - if (hasX())
|
| - subregion.setX(boundaries.x());
|
| - if (hasY())
|
| - subregion.setY(boundaries.y());
|
| - if (hasWidth())
|
| - subregion.setWidth(boundaries.width());
|
| - if (hasHeight())
|
| - subregion.setHeight(boundaries.height());
|
| -
|
| - setFilterPrimitiveSubregion(subregion);
|
| -
|
| - FloatRect absoluteSubregion = filter()->absoluteTransform().mapRect(subregion);
|
| - FloatSize filterResolution = filter()->filterResolution();
|
| - absoluteSubregion.scale(filterResolution.width(), filterResolution.height());
|
| -
|
| - // Clip every filter effect to the filter region.
|
| - if (flags & ClipToFilterRegion) {
|
| - FloatRect absoluteScaledFilterRegion = filter()->absoluteFilterRegion();
|
| - absoluteScaledFilterRegion.scale(filterResolution.width(), filterResolution.height());
|
| - absoluteSubregion.intersect(absoluteScaledFilterRegion);
|
| - }
|
| -
|
| - setMaxEffectRect(absoluteSubregion);
|
| - return subregion;
|
| -}
|
| -
|
| -PassRefPtr<SkImageFilter> FilterEffect::createImageFilter(SkiaImageFilterBuilder* builder)
|
| -{
|
| - return 0;
|
| -}
|
| -
|
| -SkImageFilter::CropRect FilterEffect::getCropRect(const FloatSize& cropOffset) const
|
| -{
|
| - SkRect rect = SkRect::MakeEmpty();
|
| - uint32_t flags = 0;
|
| - FloatRect boundaries = effectBoundaries();
|
| - FloatSize resolution = filter()->filterResolution();
|
| - boundaries.scale(resolution.width(), resolution.height());
|
| - boundaries.move(cropOffset);
|
| - if (hasX()) {
|
| - rect.fLeft = boundaries.x();
|
| - flags |= SkImageFilter::CropRect::kHasLeft_CropEdge;
|
| - }
|
| - if (hasY()) {
|
| - rect.fTop = boundaries.y();
|
| - flags |= SkImageFilter::CropRect::kHasTop_CropEdge;
|
| - }
|
| - if (hasWidth()) {
|
| - rect.fRight = rect.fLeft + boundaries.width();
|
| - flags |= SkImageFilter::CropRect::kHasRight_CropEdge;
|
| - }
|
| - if (hasHeight()) {
|
| - rect.fBottom = rect.fTop + boundaries.height();
|
| - flags |= SkImageFilter::CropRect::kHasBottom_CropEdge;
|
| - }
|
| - return SkImageFilter::CropRect(rect, flags);
|
| -}
|
| -
|
| -} // namespace WebCore
|
|
|