Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / platform / graphics / filters / FilterEffect.cpp
1 /*
2  * Copyright (C) 2008 Alex Mathews <possessedpenguinbob@gmail.com>
3  * Copyright (C) 2009 Dirk Schulze <krit@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