Home | History | Annotate | Download | only in filters
      1 /*
      2  * Copyright (C) 2008 Alex Mathews <possessedpenguinbob (at) gmail.com>
      3  * Copyright (C) 2009 Dirk Schulze <krit (at) 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 
     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 
     53 FilterEffect::~FilterEffect()
     54 {
     55 }
     56 
     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 
     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 
     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 
     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 
    119 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 
    127 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 
    133 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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    442 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 
    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 
    496 PassRefPtr<SkImageFilter> FilterEffect::createImageFilter(SkiaImageFilterBuilder* builder)
    497 {
    498     return 0;
    499 }
    500 
    501 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