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
FilterEffect(Filter * filter)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
~FilterEffect()54 FilterEffect::~FilterEffect()
55 {
56 }
57
maxFilterArea()58 float FilterEffect::maxFilterArea()
59 {
60 return kMaxFilterArea;
61 }
62
isFilterSizeValid(const FloatRect & rect)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
determineAbsolutePaintRect(const FloatRect & originalRequestedRect)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
mapRectRecursive(const FloatRect & rect)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
getSourceRect(const FloatRect & destRect,const FloatRect & destClipRect)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
requestedRegionOfInputImageData(const IntRect & effectRect) const142 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
drawingRegionOfInputImage(const IntRect & srcRect) const150 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
inputEffect(unsigned number) const156 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
addAbsolutePaintRect(const FloatRect & paintRect)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
apply()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
applyRecursive()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
forceValidPreMultipliedPixels()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
clearResult()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
clearResultsRecursive()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
asImageBuffer()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
asUnmultipliedImage(const IntRect & rect)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
asPremultipliedImage(const IntRect & rect)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
copyImageBytes(Uint8ClampedArray * source,Uint8ClampedArray * destination,const IntRect & rect)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
copyUnmultipliedImage(Uint8ClampedArray * destination,const IntRect & rect)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
copyPremultipliedImage(Uint8ClampedArray * destination,const IntRect & rect)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
createImageBufferResult()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
createUnmultipliedImageResult()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
createPremultipliedImageResult()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
adaptColorToOperatingColorSpace(const Color & deviceColor)464 Color FilterEffect::adaptColorToOperatingColorSpace(const Color& deviceColor)
465 {
466 // |deviceColor| is assumed to be DeviceRGB.
467 return ColorSpaceUtilities::convertColor(deviceColor, operatingColorSpace());
468 }
469
transformResultColorSpace(ColorSpace dstColorSpace)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
externalRepresentation(TextStream & ts,int) const487 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
determineFilterPrimitiveSubregion(DetermineSubregionFlags flags)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
createImageFilter(SkiaImageFilterBuilder * builder)542 PassRefPtr<SkImageFilter> FilterEffect::createImageFilter(SkiaImageFilterBuilder* builder)
543 {
544 return nullptr;
545 }
546
createImageFilterWithoutValidation(SkiaImageFilterBuilder * builder)547 PassRefPtr<SkImageFilter> FilterEffect::createImageFilterWithoutValidation(SkiaImageFilterBuilder* builder)
548 {
549 return createImageFilter(builder);
550 }
551
getCropRect(const FloatSize & cropOffset) const552 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
getImageFilterIndex(ColorSpace colorSpace,bool requiresPMColorValidation)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
getImageFilter(ColorSpace colorSpace,bool requiresPMColorValidation) const590 SkImageFilter* FilterEffect::getImageFilter(ColorSpace colorSpace, bool requiresPMColorValidation) const
591 {
592 int index = getImageFilterIndex(colorSpace, requiresPMColorValidation);
593 return m_imageFilters[index].get();
594 }
595
setImageFilter(ColorSpace colorSpace,bool requiresPMColorValidation,PassRefPtr<SkImageFilter> imageFilter)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