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 
     32 #if HAVE(ARM_NEON_INTRINSICS)
     33 #include <arm_neon.h>
     34 #endif
     35 
     36 namespace WebCore {
     37 
     38 static const float kMaxFilterArea = 4096 * 4096;
     39 
     40 FilterEffect::FilterEffect(Filter* filter)
     41     : m_alphaImage(false)
     42     , m_filter(filter)
     43     , m_hasX(false)
     44     , m_hasY(false)
     45     , m_hasWidth(false)
     46     , m_hasHeight(false)
     47     , m_clipsToBounds(true)
     48     , m_operatingColorSpace(ColorSpaceLinearRGB)
     49     , m_resultColorSpace(ColorSpaceDeviceRGB)
     50 {
     51     ASSERT(m_filter);
     52 }
     53 
     54 FilterEffect::~FilterEffect()
     55 {
     56 }
     57 
     58 float FilterEffect::maxFilterArea()
     59 {
     60     return kMaxFilterArea;
     61 }
     62 
     63 bool FilterEffect::isFilterSizeValid(const FloatRect& rect)
     64 {
     65     if (rect.width() < 0 || rect.height() < 0
     66         ||  (rect.height() * rect.width() > kMaxFilterArea))
     67         return false;
     68 
     69     return true;
     70 }
     71 
     72 FloatRect FilterEffect::determineAbsolutePaintRect(const FloatRect& originalRequestedRect)
     73 {
     74     FloatRect requestedRect = originalRequestedRect;
     75     // Filters in SVG clip to primitive subregion, while CSS doesn't.
     76     if (m_clipsToBounds)
     77         requestedRect.intersect(maxEffectRect());
     78 
     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)))
     82         return requestedRect;
     83 
     84     FloatRect inputRect = mapPaintRect(requestedRect, false);
     85     FloatRect inputUnion;
     86     unsigned size = m_inputEffects.size();
     87 
     88     for (unsigned i = 0; i < size; ++i)
     89         inputUnion.unite(m_inputEffects.at(i)->determineAbsolutePaintRect(inputRect));
     90     inputUnion = mapPaintRect(inputUnion, true);
     91 
     92     if (affectsTransparentPixels() || !size) {
     93         inputUnion = requestedRect;
     94     } else {
     95         // Rect may have inflated. Re-intersect with request.
     96         inputUnion.intersect(requestedRect);
     97     }
     98 
     99     addAbsolutePaintRect(inputUnion);
    100     return inputUnion;
    101 }
    102 
    103 FloatRect FilterEffect::mapRectRecursive(const FloatRect& rect)
    104 {
    105     FloatRect result;
    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));
    110     } else
    111         result = rect;
    112     return mapRect(result);
    113 }
    114 
    115 FloatRect FilterEffect::getSourceRect(const FloatRect& destRect, const FloatRect& destClipRect)
    116 {
    117     FloatRect sourceRect = mapRect(destRect, false);
    118     FloatRect sourceClipRect = mapRect(destClipRect, false);
    119 
    120     FloatRect boundaries = filter()->mapLocalRectToAbsoluteRect(effectBoundaries());
    121     if (hasX())
    122         sourceClipRect.setX(boundaries.x());
    123     if (hasY())
    124         sourceClipRect.setY(boundaries.y());
    125     if (hasWidth())
    126         sourceClipRect.setWidth(boundaries.width());
    127     if (hasHeight())
    128         sourceClipRect.setHeight(boundaries.height());
    129 
    130     FloatRect result;
    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));
    135     } else {
    136         result = sourceRect;
    137         result.intersect(sourceClipRect);
    138     }
    139     return result;
    140 }
    141 
    142 IntRect FilterEffect::requestedRegionOfInputImageData(const IntRect& effectRect) const
    143 {
    144     ASSERT(hasResult());
    145     IntPoint location = m_absolutePaintRect.location();
    146     location.moveBy(-effectRect.location());
    147     return IntRect(location, m_absolutePaintRect.size());
    148 }
    149 
    150 IntRect FilterEffect::drawingRegionOfInputImage(const IntRect& srcRect) const
    151 {
    152     return IntRect(IntPoint(srcRect.x() - m_absolutePaintRect.x(),
    153                             srcRect.y() - m_absolutePaintRect.y()), srcRect.size());
    154 }
    155 
    156 FilterEffect* FilterEffect::inputEffect(unsigned number) const
    157 {
    158     ASSERT_WITH_SECURITY_IMPLICATION(number < m_inputEffects.size());
    159     return m_inputEffects.at(number).get();
    160 }
    161 
    162 void FilterEffect::addAbsolutePaintRect(const FloatRect& paintRect)
    163 {
    164     IntRect intPaintRect(enclosingIntRect(paintRect));
    165     if (m_absolutePaintRect.contains(intPaintRect))
    166         return;
    167     intPaintRect.unite(m_absolutePaintRect);
    168     // Make sure we are not holding on to a smaller rendering.
    169     clearResult();
    170     m_absolutePaintRect = intPaintRect;
    171 }
    172 
    173 void FilterEffect::apply()
    174 {
    175     // Recursively determine paint rects first, so that we don't redraw images
    176     // if a smaller section is requested first.
    177     determineAbsolutePaintRect(maxEffectRect());
    178     applyRecursive();
    179 }
    180 
    181 void FilterEffect::applyRecursive()
    182 {
    183     if (hasResult())
    184         return;
    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())
    190             return;
    191 
    192         // Convert input results to the current effect's color space.
    193         transformResultColorSpace(in, i);
    194     }
    195 
    196     setResultColorSpace(m_operatingColorSpace);
    197 
    198     if (!isFilterSizeValid(m_absolutePaintRect))
    199         return;
    200 
    201     if (!mayProduceInvalidPreMultipliedPixels()) {
    202         for (unsigned i = 0; i < size; ++i)
    203             inputEffect(i)->correctFilterResultIfNeeded();
    204     }
    205 
    206     applySoftware();
    207 }
    208 
    209 void FilterEffect::forceValidPreMultipliedPixels()
    210 {
    211     // Must operate on pre-multiplied results; other formats cannot have invalid pixels.
    212     if (!m_premultipliedImageResult)
    213         return;
    214 
    215     Uint8ClampedArray* imageArray = m_premultipliedImageResult.get();
    216     unsigned char* pixelData = imageArray->data();
    217     int pixelArrayLength = imageArray->length();
    218 
    219     // We must have four bytes per pixel, and complete pixels
    220     ASSERT(!(pixelArrayLength % 4));
    221 
    222 #if HAVE(ARM_NEON_INTRINSICS)
    223     if (pixelArrayLength >= 64) {
    224         unsigned char* lastPixel = pixelData + (pixelArrayLength & ~0x3f);
    225         do {
    226             // Increments pixelData by 64.
    227             uint8x16x4_t sixteenPixels = vld4q_u8(pixelData);
    228             sixteenPixels.val[0] = vminq_u8(sixteenPixels.val[0], sixteenPixels.val[3]);
    229             sixteenPixels.val[1] = vminq_u8(sixteenPixels.val[1], sixteenPixels.val[3]);
    230             sixteenPixels.val[2] = vminq_u8(sixteenPixels.val[2], sixteenPixels.val[3]);
    231             vst4q_u8(pixelData, sixteenPixels);
    232             pixelData += 64;
    233         } while (pixelData < lastPixel);
    234 
    235         pixelArrayLength &= 0x3f;
    236         if (!pixelArrayLength)
    237             return;
    238     }
    239 #endif
    240 
    241     int numPixels = pixelArrayLength / 4;
    242 
    243     // Iterate over each pixel, checking alpha and adjusting color components if necessary
    244     while (--numPixels >= 0) {
    245         // Alpha is the 4th byte in a pixel
    246         unsigned char a = *(pixelData + 3);
    247         // Clamp each component to alpha, and increment the pixel location
    248         for (int i = 0; i < 3; ++i) {
    249             if (*pixelData > a)
    250                 *pixelData = a;
    251             ++pixelData;
    252         }
    253         // Increment for alpha
    254         ++pixelData;
    255     }
    256 }
    257 
    258 void FilterEffect::clearResult()
    259 {
    260     if (m_imageBufferResult)
    261         m_imageBufferResult.clear();
    262     if (m_unmultipliedImageResult)
    263         m_unmultipliedImageResult.clear();
    264     if (m_premultipliedImageResult)
    265         m_premultipliedImageResult.clear();
    266 
    267     m_absolutePaintRect = IntRect();
    268     for (int i = 0; i < 4; i++) {
    269         filter()->removeFromCache(m_imageFilters[i].get());
    270         m_imageFilters[i] = nullptr;
    271     }
    272 }
    273 
    274 void FilterEffect::clearResultsRecursive()
    275 {
    276     // Clear all results, regardless that the current effect has
    277     // a result. Can be used if an effect is in an erroneous state.
    278     if (hasResult())
    279         clearResult();
    280 
    281     unsigned size = m_inputEffects.size();
    282     for (unsigned i = 0; i < size; ++i)
    283         m_inputEffects.at(i).get()->clearResultsRecursive();
    284 }
    285 
    286 ImageBuffer* FilterEffect::asImageBuffer()
    287 {
    288     if (!hasResult())
    289         return 0;
    290     if (m_imageBufferResult)
    291         return m_imageBufferResult.get();
    292     OwnPtr<ImageBufferSurface> surface;
    293     surface = adoptPtr(new UnacceleratedImageBufferSurface(m_absolutePaintRect.size()));
    294     m_imageBufferResult = ImageBuffer::create(surface.release());
    295     if (!m_imageBufferResult)
    296         return 0;
    297 
    298     IntRect destinationRect(IntPoint(), m_absolutePaintRect.size());
    299     if (m_premultipliedImageResult)
    300         m_imageBufferResult->putByteArray(Premultiplied, m_premultipliedImageResult.get(), destinationRect.size(), destinationRect, IntPoint());
    301     else
    302         m_imageBufferResult->putByteArray(Unmultiplied, m_unmultipliedImageResult.get(), destinationRect.size(), destinationRect, IntPoint());
    303     return m_imageBufferResult.get();
    304 }
    305 
    306 PassRefPtr<Uint8ClampedArray> FilterEffect::asUnmultipliedImage(const IntRect& rect)
    307 {
    308     ASSERT(isFilterSizeValid(rect));
    309     RefPtr<Uint8ClampedArray> imageData = Uint8ClampedArray::createUninitialized(rect.width() * rect.height() * 4);
    310     copyUnmultipliedImage(imageData.get(), rect);
    311     return imageData.release();
    312 }
    313 
    314 PassRefPtr<Uint8ClampedArray> FilterEffect::asPremultipliedImage(const IntRect& rect)
    315 {
    316     ASSERT(isFilterSizeValid(rect));
    317     RefPtr<Uint8ClampedArray> imageData = Uint8ClampedArray::createUninitialized(rect.width() * rect.height() * 4);
    318     copyPremultipliedImage(imageData.get(), rect);
    319     return imageData.release();
    320 }
    321 
    322 inline void FilterEffect::copyImageBytes(Uint8ClampedArray* source, Uint8ClampedArray* destination, const IntRect& rect)
    323 {
    324     // Initialize the destination to transparent black, if not entirely covered by the source.
    325     if (rect.x() < 0 || rect.y() < 0 || rect.maxX() > m_absolutePaintRect.width() || rect.maxY() > m_absolutePaintRect.height())
    326         memset(destination->data(), 0, destination->length());
    327 
    328     // Early return if the rect does not intersect with the source.
    329     if (rect.maxX() <= 0 || rect.maxY() <= 0 || rect.x() >= m_absolutePaintRect.width() || rect.y() >= m_absolutePaintRect.height())
    330         return;
    331 
    332     int xOrigin = rect.x();
    333     int xDest = 0;
    334     if (xOrigin < 0) {
    335         xDest = -xOrigin;
    336         xOrigin = 0;
    337     }
    338     int xEnd = rect.maxX();
    339     if (xEnd > m_absolutePaintRect.width())
    340         xEnd = m_absolutePaintRect.width();
    341 
    342     int yOrigin = rect.y();
    343     int yDest = 0;
    344     if (yOrigin < 0) {
    345         yDest = -yOrigin;
    346         yOrigin = 0;
    347     }
    348     int yEnd = rect.maxY();
    349     if (yEnd > m_absolutePaintRect.height())
    350         yEnd = m_absolutePaintRect.height();
    351 
    352     int size = (xEnd - xOrigin) * 4;
    353     int destinationScanline = rect.width() * 4;
    354     int sourceScanline = m_absolutePaintRect.width() * 4;
    355     unsigned char *destinationPixel = destination->data() + ((yDest * rect.width()) + xDest) * 4;
    356     unsigned char *sourcePixel = source->data() + ((yOrigin * m_absolutePaintRect.width()) + xOrigin) * 4;
    357 
    358     while (yOrigin < yEnd) {
    359         memcpy(destinationPixel, sourcePixel, size);
    360         destinationPixel += destinationScanline;
    361         sourcePixel += sourceScanline;
    362         ++yOrigin;
    363     }
    364 }
    365 
    366 void FilterEffect::copyUnmultipliedImage(Uint8ClampedArray* destination, const IntRect& rect)
    367 {
    368     ASSERT(hasResult());
    369 
    370     if (!m_unmultipliedImageResult) {
    371         // We prefer a conversion from the image buffer.
    372         if (m_imageBufferResult)
    373             m_unmultipliedImageResult = m_imageBufferResult->getUnmultipliedImageData(IntRect(IntPoint(), m_absolutePaintRect.size()));
    374         else {
    375             ASSERT(isFilterSizeValid(m_absolutePaintRect));
    376             m_unmultipliedImageResult = Uint8ClampedArray::createUninitialized(m_absolutePaintRect.width() * m_absolutePaintRect.height() * 4);
    377             unsigned char* sourceComponent = m_premultipliedImageResult->data();
    378             unsigned char* destinationComponent = m_unmultipliedImageResult->data();
    379             unsigned char* end = sourceComponent + (m_absolutePaintRect.width() * m_absolutePaintRect.height() * 4);
    380             while (sourceComponent < end) {
    381                 int alpha = sourceComponent[3];
    382                 if (alpha) {
    383                     destinationComponent[0] = static_cast<int>(sourceComponent[0]) * 255 / alpha;
    384                     destinationComponent[1] = static_cast<int>(sourceComponent[1]) * 255 / alpha;
    385                     destinationComponent[2] = static_cast<int>(sourceComponent[2]) * 255 / alpha;
    386                 } else {
    387                     destinationComponent[0] = 0;
    388                     destinationComponent[1] = 0;
    389                     destinationComponent[2] = 0;
    390                 }
    391                 destinationComponent[3] = alpha;
    392                 sourceComponent += 4;
    393                 destinationComponent += 4;
    394             }
    395         }
    396     }
    397     copyImageBytes(m_unmultipliedImageResult.get(), destination, rect);
    398 }
    399 
    400 void FilterEffect::copyPremultipliedImage(Uint8ClampedArray* destination, const IntRect& rect)
    401 {
    402     ASSERT(hasResult());
    403 
    404     if (!m_premultipliedImageResult) {
    405         // We prefer a conversion from the image buffer.
    406         if (m_imageBufferResult)
    407             m_premultipliedImageResult = m_imageBufferResult->getPremultipliedImageData(IntRect(IntPoint(), m_absolutePaintRect.size()));
    408         else {
    409             ASSERT(isFilterSizeValid(m_absolutePaintRect));
    410             m_premultipliedImageResult = Uint8ClampedArray::createUninitialized(m_absolutePaintRect.width() * m_absolutePaintRect.height() * 4);
    411             unsigned char* sourceComponent = m_unmultipliedImageResult->data();
    412             unsigned char* destinationComponent = m_premultipliedImageResult->data();
    413             unsigned char* end = sourceComponent + (m_absolutePaintRect.width() * m_absolutePaintRect.height() * 4);
    414             while (sourceComponent < end) {
    415                 int alpha = sourceComponent[3];
    416                 destinationComponent[0] = static_cast<int>(sourceComponent[0]) * alpha / 255;
    417                 destinationComponent[1] = static_cast<int>(sourceComponent[1]) * alpha / 255;
    418                 destinationComponent[2] = static_cast<int>(sourceComponent[2]) * alpha / 255;
    419                 destinationComponent[3] = alpha;
    420                 sourceComponent += 4;
    421                 destinationComponent += 4;
    422             }
    423         }
    424     }
    425     copyImageBytes(m_premultipliedImageResult.get(), destination, rect);
    426 }
    427 
    428 ImageBuffer* FilterEffect::createImageBufferResult()
    429 {
    430     // Only one result type is allowed.
    431     ASSERT(!hasResult());
    432     ASSERT(isFilterSizeValid(m_absolutePaintRect));
    433 
    434     OwnPtr<ImageBufferSurface> surface;
    435     surface = adoptPtr(new UnacceleratedImageBufferSurface(m_absolutePaintRect.size()));
    436     m_imageBufferResult = ImageBuffer::create(surface.release());
    437     return m_imageBufferResult.get();
    438 }
    439 
    440 Uint8ClampedArray* FilterEffect::createUnmultipliedImageResult()
    441 {
    442     // Only one result type is allowed.
    443     ASSERT(!hasResult());
    444     ASSERT(isFilterSizeValid(m_absolutePaintRect));
    445 
    446     if (m_absolutePaintRect.isEmpty())
    447         return 0;
    448     m_unmultipliedImageResult = Uint8ClampedArray::createUninitialized(m_absolutePaintRect.width() * m_absolutePaintRect.height() * 4);
    449     return m_unmultipliedImageResult.get();
    450 }
    451 
    452 Uint8ClampedArray* FilterEffect::createPremultipliedImageResult()
    453 {
    454     // Only one result type is allowed.
    455     ASSERT(!hasResult());
    456     ASSERT(isFilterSizeValid(m_absolutePaintRect));
    457 
    458     if (m_absolutePaintRect.isEmpty())
    459         return 0;
    460     m_premultipliedImageResult = Uint8ClampedArray::createUninitialized(m_absolutePaintRect.width() * m_absolutePaintRect.height() * 4);
    461     return m_premultipliedImageResult.get();
    462 }
    463 
    464 Color FilterEffect::adaptColorToOperatingColorSpace(const Color& deviceColor)
    465 {
    466     // |deviceColor| is assumed to be DeviceRGB.
    467     return ColorSpaceUtilities::convertColor(deviceColor, operatingColorSpace());
    468 }
    469 
    470 void FilterEffect::transformResultColorSpace(ColorSpace dstColorSpace)
    471 {
    472     if (!hasResult() || dstColorSpace == m_resultColorSpace)
    473         return;
    474 
    475     // FIXME: We can avoid this potentially unnecessary ImageBuffer conversion by adding
    476     // color space transform support for the {pre,un}multiplied arrays.
    477     asImageBuffer()->transformColorSpace(m_resultColorSpace, dstColorSpace);
    478 
    479     m_resultColorSpace = dstColorSpace;
    480 
    481     if (m_unmultipliedImageResult)
    482         m_unmultipliedImageResult.clear();
    483     if (m_premultipliedImageResult)
    484         m_premultipliedImageResult.clear();
    485 }
    486 
    487 TextStream& FilterEffect::externalRepresentation(TextStream& ts, int) const
    488 {
    489     // FIXME: We should dump the subRegions of the filter primitives here later. This isn't
    490     // possible at the moment, because we need more detailed informations from the target object.
    491     return ts;
    492 }
    493 
    494 FloatRect FilterEffect::determineFilterPrimitiveSubregion(DetermineSubregionFlags flags)
    495 {
    496     Filter* filter = this->filter();
    497     ASSERT(filter);
    498 
    499     // FETile, FETurbulence, FEFlood don't have input effects, take the filter region as unite rect.
    500     FloatRect subregion;
    501     if (unsigned numberOfInputEffects = inputEffects().size()) {
    502         subregion = inputEffect(0)->determineFilterPrimitiveSubregion(flags);
    503         for (unsigned i = 1; i < numberOfInputEffects; ++i)
    504             subregion.unite(inputEffect(i)->determineFilterPrimitiveSubregion(flags));
    505     } else {
    506         subregion = filter->filterRegion();
    507     }
    508 
    509     // After calling determineFilterPrimitiveSubregion on the target effect, reset the subregion again for <feTile>.
    510     if (filterEffectType() == FilterEffectTypeTile)
    511         subregion = filter->filterRegion();
    512 
    513     if (flags & MapRectForward) {
    514         // mapRect works on absolute rectangles.
    515         subregion = filter->mapAbsoluteRectToLocalRect(mapRect(
    516             filter->mapLocalRectToAbsoluteRect(subregion)));
    517     }
    518 
    519     FloatRect boundaries = effectBoundaries();
    520     if (hasX())
    521         subregion.setX(boundaries.x());
    522     if (hasY())
    523         subregion.setY(boundaries.y());
    524     if (hasWidth())
    525         subregion.setWidth(boundaries.width());
    526     if (hasHeight())
    527         subregion.setHeight(boundaries.height());
    528 
    529     setFilterPrimitiveSubregion(subregion);
    530 
    531     FloatRect absoluteSubregion = filter->mapLocalRectToAbsoluteRect(subregion);
    532 
    533     // Clip every filter effect to the filter region.
    534     if (flags & ClipToFilterRegion) {
    535         absoluteSubregion.intersect(filter->absoluteFilterRegion());
    536     }
    537 
    538     setMaxEffectRect(absoluteSubregion);
    539     return subregion;
    540 }
    541 
    542 PassRefPtr<SkImageFilter> FilterEffect::createImageFilter(SkiaImageFilterBuilder* builder)
    543 {
    544     return nullptr;
    545 }
    546 
    547 PassRefPtr<SkImageFilter> FilterEffect::createImageFilterWithoutValidation(SkiaImageFilterBuilder* builder)
    548 {
    549     return createImageFilter(builder);
    550 }
    551 
    552 SkImageFilter::CropRect FilterEffect::getCropRect(const FloatSize& cropOffset) const
    553 {
    554     FloatRect rect = filter()->filterRegion();
    555     uint32_t flags = 0;
    556     FloatRect boundaries = effectBoundaries();
    557     boundaries.move(cropOffset);
    558     if (hasX()) {
    559         rect.setX(boundaries.x());
    560         flags |= SkImageFilter::CropRect::kHasLeft_CropEdge;
    561         flags |= SkImageFilter::CropRect::kHasRight_CropEdge;
    562     }
    563     if (hasY()) {
    564         rect.setY(boundaries.y());
    565         flags |= SkImageFilter::CropRect::kHasTop_CropEdge;
    566         flags |= SkImageFilter::CropRect::kHasBottom_CropEdge;
    567     }
    568     if (hasWidth()) {
    569         rect.setWidth(boundaries.width());
    570         flags |= SkImageFilter::CropRect::kHasRight_CropEdge;
    571     }
    572     if (hasHeight()) {
    573         rect.setHeight(boundaries.height());
    574         flags |= SkImageFilter::CropRect::kHasBottom_CropEdge;
    575     }
    576     rect = filter()->mapLocalRectToAbsoluteRect(rect);
    577     return SkImageFilter::CropRect(rect, flags);
    578 }
    579 
    580 static int getImageFilterIndex(ColorSpace colorSpace, bool requiresPMColorValidation)
    581 {
    582     // Map the (colorspace, bool) tuple to an integer index as follows:
    583     // 0 == linear colorspace, no PM validation
    584     // 1 == device colorspace, no PM validation
    585     // 2 == linear colorspace, PM validation
    586     // 3 == device colorspace, PM validation
    587     return (colorSpace == ColorSpaceLinearRGB ? 0x1 : 0x0) | (requiresPMColorValidation ? 0x2 : 0x0);
    588 }
    589 
    590 SkImageFilter* FilterEffect::getImageFilter(ColorSpace colorSpace, bool requiresPMColorValidation) const
    591 {
    592     int index = getImageFilterIndex(colorSpace, requiresPMColorValidation);
    593     return m_imageFilters[index].get();
    594 }
    595 
    596 void FilterEffect::setImageFilter(ColorSpace colorSpace, bool requiresPMColorValidation, PassRefPtr<SkImageFilter> imageFilter)
    597 {
    598     int index = getImageFilterIndex(colorSpace, requiresPMColorValidation);
    599     filter()->removeFromCache(m_imageFilters[index].get());
    600     m_imageFilters[index] = imageFilter;
    601 }
    602 
    603 } // namespace WebCore
    604