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 blink {
     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         m_imageFilters[i] = nullptr;
    270     }
    271 }
    272 
    273 void FilterEffect::clearResultsRecursive()
    274 {
    275     // Clear all results, regardless that the current effect has
    276     // a result. Can be used if an effect is in an erroneous state.
    277     if (hasResult())
    278         clearResult();
    279 
    280     unsigned size = m_inputEffects.size();
    281     for (unsigned i = 0; i < size; ++i)
    282         m_inputEffects.at(i).get()->clearResultsRecursive();
    283 }
    284 
    285 ImageBuffer* FilterEffect::asImageBuffer()
    286 {
    287     if (!hasResult())
    288         return 0;
    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)
    295         return 0;
    296 
    297     IntRect destinationRect(IntPoint(), m_absolutePaintRect.size());
    298     if (m_premultipliedImageResult)
    299         m_imageBufferResult->putByteArray(Premultiplied, m_premultipliedImageResult.get(), destinationRect.size(), destinationRect, IntPoint());
    300     else
    301         m_imageBufferResult->putByteArray(Unmultiplied, m_unmultipliedImageResult.get(), destinationRect.size(), destinationRect, IntPoint());
    302     return m_imageBufferResult.get();
    303 }
    304 
    305 PassRefPtr<Uint8ClampedArray> FilterEffect::asUnmultipliedImage(const IntRect& rect)
    306 {
    307     ASSERT(isFilterSizeValid(rect));
    308     RefPtr<Uint8ClampedArray> imageData = Uint8ClampedArray::createUninitialized(rect.width() * rect.height() * 4);
    309     copyUnmultipliedImage(imageData.get(), rect);
    310     return imageData.release();
    311 }
    312 
    313 PassRefPtr<Uint8ClampedArray> FilterEffect::asPremultipliedImage(const IntRect& rect)
    314 {
    315     ASSERT(isFilterSizeValid(rect));
    316     RefPtr<Uint8ClampedArray> imageData = Uint8ClampedArray::createUninitialized(rect.width() * rect.height() * 4);
    317     copyPremultipliedImage(imageData.get(), rect);
    318     return imageData.release();
    319 }
    320 
    321 inline void FilterEffect::copyImageBytes(Uint8ClampedArray* source, Uint8ClampedArray* destination, const IntRect& rect)
    322 {
    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());
    326 
    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())
    329         return;
    330 
    331     int xOrigin = rect.x();
    332     int xDest = 0;
    333     if (xOrigin < 0) {
    334         xDest = -xOrigin;
    335         xOrigin = 0;
    336     }
    337     int xEnd = rect.maxX();
    338     if (xEnd > m_absolutePaintRect.width())
    339         xEnd = m_absolutePaintRect.width();
    340 
    341     int yOrigin = rect.y();
    342     int yDest = 0;
    343     if (yOrigin < 0) {
    344         yDest = -yOrigin;
    345         yOrigin = 0;
    346     }
    347     int yEnd = rect.maxY();
    348     if (yEnd > m_absolutePaintRect.height())
    349         yEnd = m_absolutePaintRect.height();
    350 
    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;
    356 
    357     while (yOrigin < yEnd) {
    358         memcpy(destinationPixel, sourcePixel, size);
    359         destinationPixel += destinationScanline;
    360         sourcePixel += sourceScanline;
    361         ++yOrigin;
    362     }
    363 }
    364 
    365 void FilterEffect::copyUnmultipliedImage(Uint8ClampedArray* destination, const IntRect& rect)
    366 {
    367     ASSERT(hasResult());
    368 
    369     if (!m_unmultipliedImageResult) {
    370         // We prefer a conversion from the image buffer.
    371         if (m_imageBufferResult)
    372             m_unmultipliedImageResult = m_imageBufferResult->getImageData(Unmultiplied, IntRect(IntPoint(), m_absolutePaintRect.size()));
    373         else {
    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];
    381                 if (alpha) {
    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;
    385                 } else {
    386                     destinationComponent[0] = 0;
    387                     destinationComponent[1] = 0;
    388                     destinationComponent[2] = 0;
    389                 }
    390                 destinationComponent[3] = alpha;
    391                 sourceComponent += 4;
    392                 destinationComponent += 4;
    393             }
    394         }
    395     }
    396     copyImageBytes(m_unmultipliedImageResult.get(), destination, rect);
    397 }
    398 
    399 void FilterEffect::copyPremultipliedImage(Uint8ClampedArray* destination, const IntRect& rect)
    400 {
    401     ASSERT(hasResult());
    402 
    403     if (!m_premultipliedImageResult) {
    404         // We prefer a conversion from the image buffer.
    405         if (m_imageBufferResult)
    406             m_premultipliedImageResult = m_imageBufferResult->getImageData(Premultiplied, IntRect(IntPoint(), m_absolutePaintRect.size()));
    407         else {
    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;
    421             }
    422         }
    423     }
    424     copyImageBytes(m_premultipliedImageResult.get(), destination, rect);
    425 }
    426 
    427 ImageBuffer* FilterEffect::createImageBufferResult()
    428 {
    429     // Only one result type is allowed.
    430     ASSERT(!hasResult());
    431     ASSERT(isFilterSizeValid(m_absolutePaintRect));
    432 
    433     OwnPtr<ImageBufferSurface> surface;
    434     surface = adoptPtr(new UnacceleratedImageBufferSurface(m_absolutePaintRect.size()));
    435     m_imageBufferResult = ImageBuffer::create(surface.release());
    436     return m_imageBufferResult.get();
    437 }
    438 
    439 Uint8ClampedArray* FilterEffect::createUnmultipliedImageResult()
    440 {
    441     // Only one result type is allowed.
    442     ASSERT(!hasResult());
    443     ASSERT(isFilterSizeValid(m_absolutePaintRect));
    444 
    445     if (m_absolutePaintRect.isEmpty())
    446         return 0;
    447     m_unmultipliedImageResult = Uint8ClampedArray::createUninitialized(m_absolutePaintRect.width() * m_absolutePaintRect.height() * 4);
    448     return m_unmultipliedImageResult.get();
    449 }
    450 
    451 Uint8ClampedArray* FilterEffect::createPremultipliedImageResult()
    452 {
    453     // Only one result type is allowed.
    454     ASSERT(!hasResult());
    455     ASSERT(isFilterSizeValid(m_absolutePaintRect));
    456 
    457     if (m_absolutePaintRect.isEmpty())
    458         return 0;
    459     m_premultipliedImageResult = Uint8ClampedArray::createUninitialized(m_absolutePaintRect.width() * m_absolutePaintRect.height() * 4);
    460     return m_premultipliedImageResult.get();
    461 }
    462 
    463 Color FilterEffect::adaptColorToOperatingColorSpace(const Color& deviceColor)
    464 {
    465     // |deviceColor| is assumed to be DeviceRGB.
    466     return ColorSpaceUtilities::convertColor(deviceColor, operatingColorSpace());
    467 }
    468 
    469 void FilterEffect::transformResultColorSpace(ColorSpace dstColorSpace)
    470 {
    471     if (!hasResult() || dstColorSpace == m_resultColorSpace)
    472         return;
    473 
    474     // FIXME: We can avoid this potentially unnecessary ImageBuffer conversion by adding
    475     // color space transform support for the {pre,un}multiplied arrays.
    476     asImageBuffer()->transformColorSpace(m_resultColorSpace, dstColorSpace);
    477 
    478     m_resultColorSpace = dstColorSpace;
    479 
    480     if (m_unmultipliedImageResult)
    481         m_unmultipliedImageResult.clear();
    482     if (m_premultipliedImageResult)
    483         m_premultipliedImageResult.clear();
    484 }
    485 
    486 TextStream& FilterEffect::externalRepresentation(TextStream& ts, int) const
    487 {
    488     // FIXME: We should dump the subRegions of the filter primitives here later. This isn't
    489     // possible at the moment, because we need more detailed informations from the target object.
    490     return ts;
    491 }
    492 
    493 FloatRect FilterEffect::determineFilterPrimitiveSubregion(DetermineSubregionFlags flags)
    494 {
    495     Filter* filter = this->filter();
    496     ASSERT(filter);
    497 
    498     // FETile, FETurbulence, FEFlood don't have input effects, take the filter region as unite rect.
    499     FloatRect subregion;
    500     if (unsigned numberOfInputEffects = inputEffects().size()) {
    501         subregion = inputEffect(0)->determineFilterPrimitiveSubregion(flags);
    502         for (unsigned i = 1; i < numberOfInputEffects; ++i)
    503             subregion.unite(inputEffect(i)->determineFilterPrimitiveSubregion(flags));
    504     } else {
    505         subregion = filter->filterRegion();
    506     }
    507 
    508     // After calling determineFilterPrimitiveSubregion on the target effect, reset the subregion again for <feTile>.
    509     if (filterEffectType() == FilterEffectTypeTile)
    510         subregion = filter->filterRegion();
    511 
    512     if (flags & MapRectForward) {
    513         // mapRect works on absolute rectangles.
    514         subregion = filter->mapAbsoluteRectToLocalRect(mapRect(
    515             filter->mapLocalRectToAbsoluteRect(subregion)));
    516     }
    517 
    518     FloatRect boundaries = effectBoundaries();
    519     if (hasX())
    520         subregion.setX(boundaries.x());
    521     if (hasY())
    522         subregion.setY(boundaries.y());
    523     if (hasWidth())
    524         subregion.setWidth(boundaries.width());
    525     if (hasHeight())
    526         subregion.setHeight(boundaries.height());
    527 
    528     setFilterPrimitiveSubregion(subregion);
    529 
    530     FloatRect absoluteSubregion = filter->mapLocalRectToAbsoluteRect(subregion);
    531 
    532     // Clip every filter effect to the filter region.
    533     if (flags & ClipToFilterRegion) {
    534         absoluteSubregion.intersect(filter->absoluteFilterRegion());
    535     }
    536 
    537     setMaxEffectRect(absoluteSubregion);
    538     return subregion;
    539 }
    540 
    541 PassRefPtr<SkImageFilter> FilterEffect::createImageFilter(SkiaImageFilterBuilder* builder)
    542 {
    543     return nullptr;
    544 }
    545 
    546 PassRefPtr<SkImageFilter> FilterEffect::createImageFilterWithoutValidation(SkiaImageFilterBuilder* builder)
    547 {
    548     return createImageFilter(builder);
    549 }
    550 
    551 SkImageFilter::CropRect FilterEffect::getCropRect(const FloatSize& cropOffset) const
    552 {
    553     FloatRect rect = filter()->filterRegion();
    554     uint32_t flags = 0;
    555     FloatRect boundaries = effectBoundaries();
    556     boundaries.move(cropOffset);
    557     if (hasX()) {
    558         rect.setX(boundaries.x());
    559         flags |= SkImageFilter::CropRect::kHasLeft_CropEdge;
    560         flags |= SkImageFilter::CropRect::kHasRight_CropEdge;
    561     }
    562     if (hasY()) {
    563         rect.setY(boundaries.y());
    564         flags |= SkImageFilter::CropRect::kHasTop_CropEdge;
    565         flags |= SkImageFilter::CropRect::kHasBottom_CropEdge;
    566     }
    567     if (hasWidth()) {
    568         rect.setWidth(boundaries.width());
    569         flags |= SkImageFilter::CropRect::kHasRight_CropEdge;
    570     }
    571     if (hasHeight()) {
    572         rect.setHeight(boundaries.height());
    573         flags |= SkImageFilter::CropRect::kHasBottom_CropEdge;
    574     }
    575     rect.scale(filter()->absoluteTransform().a(), filter()->absoluteTransform().d());
    576     return SkImageFilter::CropRect(rect, flags);
    577 }
    578 
    579 static int getImageFilterIndex(ColorSpace colorSpace, bool requiresPMColorValidation)
    580 {
    581     // Map the (colorspace, bool) tuple to an integer index as follows:
    582     // 0 == linear colorspace, no PM validation
    583     // 1 == device colorspace, no PM validation
    584     // 2 == linear colorspace, PM validation
    585     // 3 == device colorspace, PM validation
    586     return (colorSpace == ColorSpaceLinearRGB ? 0x1 : 0x0) | (requiresPMColorValidation ? 0x2 : 0x0);
    587 }
    588 
    589 SkImageFilter* FilterEffect::getImageFilter(ColorSpace colorSpace, bool requiresPMColorValidation) const
    590 {
    591     int index = getImageFilterIndex(colorSpace, requiresPMColorValidation);
    592     return m_imageFilters[index].get();
    593 }
    594 
    595 void FilterEffect::setImageFilter(ColorSpace colorSpace, bool requiresPMColorValidation, PassRefPtr<SkImageFilter> imageFilter)
    596 {
    597     int index = getImageFilterIndex(colorSpace, requiresPMColorValidation);
    598     m_imageFilters[index] = imageFilter;
    599 }
    600 
    601 } // namespace blink
    602