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 "core/platform/graphics/filters/FilterEffect.h"
     27 
     28 #include "core/platform/graphics/ImageBuffer.h"
     29 #include "core/platform/graphics/filters/Filter.h"
     30 #include "third_party/skia/include/core/SkImageFilter.h"
     31 #include "wtf/Uint8ClampedArray.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     m_imageBufferResult = ImageBuffer::create(m_absolutePaintRect.size(), 1, m_filter->renderingMode());
    249     IntRect destinationRect(IntPoint(), m_absolutePaintRect.size());
    250     if (m_premultipliedImageResult)
    251         m_imageBufferResult->putByteArray(Premultiplied, m_premultipliedImageResult.get(), destinationRect.size(), destinationRect, IntPoint());
    252     else
    253         m_imageBufferResult->putByteArray(Unmultiplied, m_unmultipliedImageResult.get(), destinationRect.size(), destinationRect, IntPoint());
    254     return m_imageBufferResult.get();
    255 }
    256 
    257 PassRefPtr<Uint8ClampedArray> FilterEffect::asUnmultipliedImage(const IntRect& rect)
    258 {
    259     ASSERT(isFilterSizeValid(rect));
    260     RefPtr<Uint8ClampedArray> imageData = Uint8ClampedArray::createUninitialized(rect.width() * rect.height() * 4);
    261     copyUnmultipliedImage(imageData.get(), rect);
    262     return imageData.release();
    263 }
    264 
    265 PassRefPtr<Uint8ClampedArray> FilterEffect::asPremultipliedImage(const IntRect& rect)
    266 {
    267     ASSERT(isFilterSizeValid(rect));
    268     RefPtr<Uint8ClampedArray> imageData = Uint8ClampedArray::createUninitialized(rect.width() * rect.height() * 4);
    269     copyPremultipliedImage(imageData.get(), rect);
    270     return imageData.release();
    271 }
    272 
    273 inline void FilterEffect::copyImageBytes(Uint8ClampedArray* source, Uint8ClampedArray* destination, const IntRect& rect)
    274 {
    275     // Initialize the destination to transparent black, if not entirely covered by the source.
    276     if (rect.x() < 0 || rect.y() < 0 || rect.maxX() > m_absolutePaintRect.width() || rect.maxY() > m_absolutePaintRect.height())
    277         memset(destination->data(), 0, destination->length());
    278 
    279     // Early return if the rect does not intersect with the source.
    280     if (rect.maxX() <= 0 || rect.maxY() <= 0 || rect.x() >= m_absolutePaintRect.width() || rect.y() >= m_absolutePaintRect.height())
    281         return;
    282 
    283     int xOrigin = rect.x();
    284     int xDest = 0;
    285     if (xOrigin < 0) {
    286         xDest = -xOrigin;
    287         xOrigin = 0;
    288     }
    289     int xEnd = rect.maxX();
    290     if (xEnd > m_absolutePaintRect.width())
    291         xEnd = m_absolutePaintRect.width();
    292 
    293     int yOrigin = rect.y();
    294     int yDest = 0;
    295     if (yOrigin < 0) {
    296         yDest = -yOrigin;
    297         yOrigin = 0;
    298     }
    299     int yEnd = rect.maxY();
    300     if (yEnd > m_absolutePaintRect.height())
    301         yEnd = m_absolutePaintRect.height();
    302 
    303     int size = (xEnd - xOrigin) * 4;
    304     int destinationScanline = rect.width() * 4;
    305     int sourceScanline = m_absolutePaintRect.width() * 4;
    306     unsigned char *destinationPixel = destination->data() + ((yDest * rect.width()) + xDest) * 4;
    307     unsigned char *sourcePixel = source->data() + ((yOrigin * m_absolutePaintRect.width()) + xOrigin) * 4;
    308 
    309     while (yOrigin < yEnd) {
    310         memcpy(destinationPixel, sourcePixel, size);
    311         destinationPixel += destinationScanline;
    312         sourcePixel += sourceScanline;
    313         ++yOrigin;
    314     }
    315 }
    316 
    317 void FilterEffect::copyUnmultipliedImage(Uint8ClampedArray* destination, const IntRect& rect)
    318 {
    319     ASSERT(hasResult());
    320 
    321     if (!m_unmultipliedImageResult) {
    322         // We prefer a conversion from the image buffer.
    323         if (m_imageBufferResult)
    324             m_unmultipliedImageResult = m_imageBufferResult->getUnmultipliedImageData(IntRect(IntPoint(), m_absolutePaintRect.size()));
    325         else {
    326             ASSERT(isFilterSizeValid(m_absolutePaintRect));
    327             m_unmultipliedImageResult = Uint8ClampedArray::createUninitialized(m_absolutePaintRect.width() * m_absolutePaintRect.height() * 4);
    328             unsigned char* sourceComponent = m_premultipliedImageResult->data();
    329             unsigned char* destinationComponent = m_unmultipliedImageResult->data();
    330             unsigned char* end = sourceComponent + (m_absolutePaintRect.width() * m_absolutePaintRect.height() * 4);
    331             while (sourceComponent < end) {
    332                 int alpha = sourceComponent[3];
    333                 if (alpha) {
    334                     destinationComponent[0] = static_cast<int>(sourceComponent[0]) * 255 / alpha;
    335                     destinationComponent[1] = static_cast<int>(sourceComponent[1]) * 255 / alpha;
    336                     destinationComponent[2] = static_cast<int>(sourceComponent[2]) * 255 / alpha;
    337                 } else {
    338                     destinationComponent[0] = 0;
    339                     destinationComponent[1] = 0;
    340                     destinationComponent[2] = 0;
    341                 }
    342                 destinationComponent[3] = alpha;
    343                 sourceComponent += 4;
    344                 destinationComponent += 4;
    345             }
    346         }
    347     }
    348     copyImageBytes(m_unmultipliedImageResult.get(), destination, rect);
    349 }
    350 
    351 void FilterEffect::copyPremultipliedImage(Uint8ClampedArray* destination, const IntRect& rect)
    352 {
    353     ASSERT(hasResult());
    354 
    355     if (!m_premultipliedImageResult) {
    356         // We prefer a conversion from the image buffer.
    357         if (m_imageBufferResult)
    358             m_premultipliedImageResult = m_imageBufferResult->getPremultipliedImageData(IntRect(IntPoint(), m_absolutePaintRect.size()));
    359         else {
    360             ASSERT(isFilterSizeValid(m_absolutePaintRect));
    361             m_premultipliedImageResult = Uint8ClampedArray::createUninitialized(m_absolutePaintRect.width() * m_absolutePaintRect.height() * 4);
    362             unsigned char* sourceComponent = m_unmultipliedImageResult->data();
    363             unsigned char* destinationComponent = m_premultipliedImageResult->data();
    364             unsigned char* end = sourceComponent + (m_absolutePaintRect.width() * m_absolutePaintRect.height() * 4);
    365             while (sourceComponent < end) {
    366                 int alpha = sourceComponent[3];
    367                 destinationComponent[0] = static_cast<int>(sourceComponent[0]) * alpha / 255;
    368                 destinationComponent[1] = static_cast<int>(sourceComponent[1]) * alpha / 255;
    369                 destinationComponent[2] = static_cast<int>(sourceComponent[2]) * alpha / 255;
    370                 destinationComponent[3] = alpha;
    371                 sourceComponent += 4;
    372                 destinationComponent += 4;
    373             }
    374         }
    375     }
    376     copyImageBytes(m_premultipliedImageResult.get(), destination, rect);
    377 }
    378 
    379 ImageBuffer* FilterEffect::createImageBufferResult()
    380 {
    381     // Only one result type is allowed.
    382     ASSERT(!hasResult());
    383     if (m_absolutePaintRect.isEmpty())
    384         return 0;
    385     m_imageBufferResult = ImageBuffer::create(m_absolutePaintRect.size(), 1, m_filter->renderingMode());
    386     if (!m_imageBufferResult)
    387         return 0;
    388     ASSERT(m_imageBufferResult->context());
    389     return m_imageBufferResult.get();
    390 }
    391 
    392 Uint8ClampedArray* FilterEffect::createUnmultipliedImageResult()
    393 {
    394     // Only one result type is allowed.
    395     ASSERT(!hasResult());
    396     ASSERT(isFilterSizeValid(m_absolutePaintRect));
    397 
    398     if (m_absolutePaintRect.isEmpty())
    399         return 0;
    400     m_unmultipliedImageResult = Uint8ClampedArray::createUninitialized(m_absolutePaintRect.width() * m_absolutePaintRect.height() * 4);
    401     return m_unmultipliedImageResult.get();
    402 }
    403 
    404 Uint8ClampedArray* FilterEffect::createPremultipliedImageResult()
    405 {
    406     // Only one result type is allowed.
    407     ASSERT(!hasResult());
    408     ASSERT(isFilterSizeValid(m_absolutePaintRect));
    409 
    410     if (m_absolutePaintRect.isEmpty())
    411         return 0;
    412     m_premultipliedImageResult = Uint8ClampedArray::createUninitialized(m_absolutePaintRect.width() * m_absolutePaintRect.height() * 4);
    413     return m_premultipliedImageResult.get();
    414 }
    415 
    416 void FilterEffect::transformResultColorSpace(ColorSpace dstColorSpace)
    417 {
    418     if (!hasResult() || dstColorSpace == m_resultColorSpace)
    419         return;
    420 
    421     // FIXME: We can avoid this potentially unnecessary ImageBuffer conversion by adding
    422     // color space transform support for the {pre,un}multiplied arrays.
    423     asImageBuffer()->transformColorSpace(m_resultColorSpace, dstColorSpace);
    424 
    425     m_resultColorSpace = dstColorSpace;
    426 
    427     if (m_unmultipliedImageResult)
    428         m_unmultipliedImageResult.clear();
    429     if (m_premultipliedImageResult)
    430         m_premultipliedImageResult.clear();
    431 }
    432 
    433 TextStream& FilterEffect::externalRepresentation(TextStream& ts, int) const
    434 {
    435     // FIXME: We should dump the subRegions of the filter primitives here later. This isn't
    436     // possible at the moment, because we need more detailed informations from the target object.
    437     return ts;
    438 }
    439 
    440 FloatRect FilterEffect::determineFilterPrimitiveSubregion()
    441 {
    442     ASSERT(filter());
    443 
    444     // FETile, FETurbulence, FEFlood don't have input effects, take the filter region as unite rect.
    445     FloatRect subregion;
    446     if (unsigned numberOfInputEffects = inputEffects().size()) {
    447         subregion = inputEffect(0)->determineFilterPrimitiveSubregion();
    448         for (unsigned i = 1; i < numberOfInputEffects; ++i)
    449             subregion.unite(inputEffect(i)->determineFilterPrimitiveSubregion());
    450     } else
    451         subregion = filter()->filterRegion();
    452 
    453     // After calling determineFilterPrimitiveSubregion on the target effect, reset the subregion again for <feTile>.
    454     if (filterEffectType() == FilterEffectTypeTile)
    455         subregion = filter()->filterRegion();
    456 
    457     subregion = mapRect(subregion);
    458 
    459     FloatRect boundaries = effectBoundaries();
    460     if (hasX())
    461         subregion.setX(boundaries.x());
    462     if (hasY())
    463         subregion.setY(boundaries.y());
    464     if (hasWidth())
    465         subregion.setWidth(boundaries.width());
    466     if (hasHeight())
    467         subregion.setHeight(boundaries.height());
    468 
    469     setFilterPrimitiveSubregion(subregion);
    470 
    471     FloatRect absoluteSubregion = filter()->absoluteTransform().mapRect(subregion);
    472     FloatSize filterResolution = filter()->filterResolution();
    473     absoluteSubregion.scale(filterResolution.width(), filterResolution.height());
    474 
    475     setMaxEffectRect(absoluteSubregion);
    476     return subregion;
    477 }
    478 
    479 PassRefPtr<SkImageFilter> FilterEffect::createImageFilter(SkiaImageFilterBuilder* builder)
    480 {
    481     return 0;
    482 }
    483 
    484 } // namespace WebCore
    485