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
7 * This library is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Library General Public
9 * License as published by the Free Software Foundation; either
10 * version 2 of the License, or (at your option) any later version.
12 * This library is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Library General Public License for more details.
17 * You should have received a copy of the GNU Library General Public License
18 * along with this library; see the file COPYING.LIB. If not, write to
19 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
20 * Boston, MA 02110-1301, USA.
24 #include "FilterEffect.h"
27 #include "ImageBuffer.h"
28 #include "TextStream.h"
29 #include <runtime/JSCInlines.h>
30 #include <runtime/TypedArrayInlines.h>
31 #include <runtime/Uint8ClampedArray.h>
33 #if HAVE(ARM_NEON_INTRINSICS)
39 static const float kMaxFilterArea = 4096 * 4096;
41 FilterEffect::FilterEffect(Filter& filter)
48 , m_clipsToBounds(true)
49 , m_operatingColorSpace(ColorSpaceLinearRGB)
50 , m_resultColorSpace(ColorSpaceDeviceRGB)
54 FilterEffect::~FilterEffect()
58 float FilterEffect::maxFilterArea()
60 return kMaxFilterArea;
63 bool FilterEffect::isFilterSizeValid(const FloatRect& rect)
65 if (rect.width() < 0 || rect.height() < 0
66 || (rect.height() * rect.width() > kMaxFilterArea))
71 void FilterEffect::determineAbsolutePaintRect()
73 m_absolutePaintRect = IntRect();
74 unsigned size = m_inputEffects.size();
75 for (unsigned i = 0; i < size; ++i)
76 m_absolutePaintRect.unite(m_inputEffects.at(i)->absolutePaintRect());
78 // Filters in SVG clip to primitive subregion, while CSS doesn't.
80 m_absolutePaintRect.intersect(enclosingIntRect(m_maxEffectRect));
82 m_absolutePaintRect.unite(enclosingIntRect(m_maxEffectRect));
86 IntRect FilterEffect::requestedRegionOfInputImageData(const IntRect& effectRect) const
89 IntPoint location = m_absolutePaintRect.location();
90 location.moveBy(-effectRect.location());
91 return IntRect(location, m_absolutePaintRect.size());
94 FloatRect FilterEffect::drawingRegionOfInputImage(const IntRect& srcRect) const
96 return FloatRect(FloatPoint(srcRect.x() - m_absolutePaintRect.x(),
97 srcRect.y() - m_absolutePaintRect.y()), srcRect.size());
100 FilterEffect* FilterEffect::inputEffect(unsigned number) const
102 ASSERT_WITH_SECURITY_IMPLICATION(number < m_inputEffects.size());
103 return m_inputEffects.at(number).get();
106 static unsigned collectEffects(const FilterEffect*effect, HashSet<const FilterEffect*>& allEffects)
108 allEffects.add(effect);
109 unsigned size = effect->numberOfEffectInputs();
110 for (unsigned i = 0; i < size; ++i) {
111 FilterEffect* in = effect->inputEffect(i);
112 collectEffects(in, allEffects);
114 return allEffects.size();
117 unsigned FilterEffect::totalNumberOfEffectInputs() const
119 HashSet<const FilterEffect*> allEffects;
120 return collectEffects(this, allEffects);
124 void FilterEffect::applyAll()
128 FilterContextOpenCL* context = FilterContextOpenCL::context();
131 if (!context->inError())
133 clearResultsRecursive();
134 context->destroyContext();
136 // Software code path.
141 void FilterEffect::apply()
145 unsigned size = m_inputEffects.size();
146 for (unsigned i = 0; i < size; ++i) {
147 FilterEffect* in = m_inputEffects.at(i).get();
149 if (!in->hasResult())
152 // Convert input results to the current effect's color space.
153 transformResultColorSpace(in, i);
156 determineAbsolutePaintRect();
157 setResultColorSpace(m_operatingColorSpace);
159 if (!isFilterSizeValid(m_absolutePaintRect))
162 if (requiresValidPreMultipliedPixels()) {
163 for (unsigned i = 0; i < size; ++i)
164 inputEffect(i)->correctFilterResultIfNeeded();
167 // Add platform specific apply functions here and return earlier.
169 if (platformApplyOpenCL())
172 platformApplySoftware();
176 // This function will be changed to abstract virtual when all filters are landed.
177 bool FilterEffect::platformApplyOpenCL()
179 if (!FilterContextOpenCL::context())
182 unsigned size = m_inputEffects.size();
183 for (unsigned i = 0; i < size; ++i) {
184 FilterEffect* in = m_inputEffects.at(i).get();
185 // Software code path expects that at least one of the following fileds is valid.
186 if (!in->m_imageBufferResult && !in->m_unmultipliedImageResult && !in->m_premultipliedImageResult)
190 platformApplySoftware();
191 ImageBuffer* sourceImage = asImageBuffer();
193 RefPtr<Uint8ClampedArray> sourceImageData = sourceImage->getUnmultipliedImageData(IntRect(IntPoint(), sourceImage->internalSize()));
194 createOpenCLImageResult(sourceImageData->data());
200 void FilterEffect::forceValidPreMultipliedPixels()
202 // Must operate on pre-multiplied results; other formats cannot have invalid pixels.
203 if (!m_premultipliedImageResult)
206 Uint8ClampedArray* imageArray = m_premultipliedImageResult.get();
207 unsigned char* pixelData = imageArray->data();
208 int pixelArrayLength = imageArray->length();
210 // We must have four bytes per pixel, and complete pixels
211 ASSERT(!(pixelArrayLength % 4));
213 #if HAVE(ARM_NEON_INTRINSICS)
214 if (pixelArrayLength >= 64) {
215 unsigned char* lastPixel = pixelData + (pixelArrayLength & ~0x3f);
217 // Increments pixelData by 64.
218 uint8x16x4_t sixteenPixels = vld4q_u8(pixelData);
219 sixteenPixels.val[0] = vminq_u8(sixteenPixels.val[0], sixteenPixels.val[3]);
220 sixteenPixels.val[1] = vminq_u8(sixteenPixels.val[1], sixteenPixels.val[3]);
221 sixteenPixels.val[2] = vminq_u8(sixteenPixels.val[2], sixteenPixels.val[3]);
222 vst4q_u8(pixelData, sixteenPixels);
224 } while (pixelData < lastPixel);
226 pixelArrayLength &= 0x3f;
227 if (!pixelArrayLength)
232 int numPixels = pixelArrayLength / 4;
234 // Iterate over each pixel, checking alpha and adjusting color components if necessary
235 while (--numPixels >= 0) {
236 // Alpha is the 4th byte in a pixel
237 unsigned char a = *(pixelData + 3);
238 // Clamp each component to alpha, and increment the pixel location
239 for (int i = 0; i < 3; ++i) {
244 // Increment for alpha
249 void FilterEffect::clearResult()
251 if (m_imageBufferResult)
252 m_imageBufferResult.reset();
253 if (m_unmultipliedImageResult)
254 m_unmultipliedImageResult.clear();
255 if (m_premultipliedImageResult)
256 m_premultipliedImageResult.clear();
258 if (m_openCLImageResult)
259 m_openCLImageResult.clear();
263 void FilterEffect::clearResultsRecursive()
265 // Clear all results, regardless that the current effect has
266 // a result. Can be used if an effect is in an erroneous state.
270 unsigned size = m_inputEffects.size();
271 for (unsigned i = 0; i < size; ++i)
272 m_inputEffects.at(i).get()->clearResultsRecursive();
275 ImageBuffer* FilterEffect::asImageBuffer()
279 if (m_imageBufferResult)
280 return m_imageBufferResult.get();
282 if (m_openCLImageResult)
283 return openCLImageToImageBuffer();
285 m_imageBufferResult = ImageBuffer::create(m_absolutePaintRect.size(), m_filter.filterScale(), m_resultColorSpace, m_filter.renderingMode());
286 IntRect destinationRect(IntPoint(), m_absolutePaintRect.size());
287 if (m_premultipliedImageResult)
288 m_imageBufferResult->putByteArray(Premultiplied, m_premultipliedImageResult.get(), destinationRect.size(), destinationRect, IntPoint());
290 m_imageBufferResult->putByteArray(Unmultiplied, m_unmultipliedImageResult.get(), destinationRect.size(), destinationRect, IntPoint());
291 return m_imageBufferResult.get();
295 ImageBuffer* FilterEffect::openCLImageToImageBuffer()
297 FilterContextOpenCL* context = FilterContextOpenCL::context();
300 if (context->inError())
303 size_t origin[3] = { 0, 0, 0 };
304 size_t region[3] = { m_absolutePaintRect.width(), m_absolutePaintRect.height(), 1 };
306 RefPtr<Uint8ClampedArray> destinationPixelArray = Uint8ClampedArray::create(m_absolutePaintRect.width() * m_absolutePaintRect.height() * 4);
308 if (context->isFailed(clFinish(context->commandQueue())))
311 if (context->isFailed(clEnqueueReadImage(context->commandQueue(), m_openCLImageResult, CL_TRUE, origin, region, 0, 0, destinationPixelArray->data(), 0, 0, 0)))
314 m_imageBufferResult = ImageBuffer::create(m_absolutePaintRect.size());
315 IntRect destinationRect(IntPoint(), m_absolutePaintRect.size());
316 m_imageBufferResult->putByteArray(Unmultiplied, destinationPixelArray.get(), destinationRect.size(), destinationRect, IntPoint());
318 return m_imageBufferResult.get();
322 PassRefPtr<Uint8ClampedArray> FilterEffect::asUnmultipliedImage(const IntRect& rect)
324 ASSERT(isFilterSizeValid(rect));
325 IntSize scaledSize(rect.size());
326 scaledSize.scale(m_filter.filterScale());
327 RefPtr<Uint8ClampedArray> imageData = Uint8ClampedArray::createUninitialized(scaledSize.width() * scaledSize.height() * 4);
328 copyUnmultipliedImage(imageData.get(), rect);
329 return imageData.release();
332 PassRefPtr<Uint8ClampedArray> FilterEffect::asPremultipliedImage(const IntRect& rect)
334 ASSERT(isFilterSizeValid(rect));
335 IntSize scaledSize(rect.size());
336 scaledSize.scale(m_filter.filterScale());
337 RefPtr<Uint8ClampedArray> imageData = Uint8ClampedArray::createUninitialized(scaledSize.width() * scaledSize.height() * 4);
338 copyPremultipliedImage(imageData.get(), rect);
339 return imageData.release();
342 inline void FilterEffect::copyImageBytes(Uint8ClampedArray* source, Uint8ClampedArray* destination, const IntRect& rect)
344 IntRect scaledRect(rect);
345 scaledRect.scale(m_filter.filterScale());
346 IntSize scaledPaintSize(m_absolutePaintRect.size());
347 scaledPaintSize.scale(m_filter.filterScale());
349 // Initialize the destination to transparent black, if not entirely covered by the source.
350 if (scaledRect.x() < 0 || scaledRect.y() < 0 || scaledRect.maxX() > scaledPaintSize.width() || scaledRect.maxY() > scaledPaintSize.height())
351 memset(destination->data(), 0, destination->length());
353 // Early return if the rect does not intersect with the source.
354 if (scaledRect.maxX() <= 0 || scaledRect.maxY() <= 0 || scaledRect.x() >= scaledPaintSize.width() || scaledRect.y() >= scaledPaintSize.height())
357 int xOrigin = scaledRect.x();
363 int xEnd = scaledRect.maxX();
364 if (xEnd > scaledPaintSize.width())
365 xEnd = scaledPaintSize.width();
367 int yOrigin = scaledRect.y();
373 int yEnd = scaledRect.maxY();
374 if (yEnd > scaledPaintSize.height())
375 yEnd = scaledPaintSize.height();
377 int size = (xEnd - xOrigin) * 4;
378 int destinationScanline = scaledRect.width() * 4;
379 int sourceScanline = scaledPaintSize.width() * 4;
380 unsigned char *destinationPixel = destination->data() + ((yDest * scaledRect.width()) + xDest) * 4;
381 unsigned char *sourcePixel = source->data() + ((yOrigin * scaledPaintSize.width()) + xOrigin) * 4;
383 while (yOrigin < yEnd) {
384 memcpy(destinationPixel, sourcePixel, size);
385 destinationPixel += destinationScanline;
386 sourcePixel += sourceScanline;
391 void FilterEffect::copyUnmultipliedImage(Uint8ClampedArray* destination, const IntRect& rect)
395 if (!m_unmultipliedImageResult) {
396 // We prefer a conversion from the image buffer.
397 if (m_imageBufferResult)
398 m_unmultipliedImageResult = m_imageBufferResult->getUnmultipliedImageData(IntRect(IntPoint(), m_absolutePaintRect.size()));
400 ASSERT(isFilterSizeValid(m_absolutePaintRect));
401 IntSize inputSize(m_absolutePaintRect.size());
402 inputSize.scale(m_filter.filterScale());
403 m_unmultipliedImageResult = Uint8ClampedArray::createUninitialized(inputSize.width() * inputSize.height() * 4);
404 unsigned char* sourceComponent = m_premultipliedImageResult->data();
405 unsigned char* destinationComponent = m_unmultipliedImageResult->data();
406 unsigned char* end = sourceComponent + (inputSize.width() * inputSize.height() * 4);
407 while (sourceComponent < end) {
408 int alpha = sourceComponent[3];
410 destinationComponent[0] = static_cast<int>(sourceComponent[0]) * 255 / alpha;
411 destinationComponent[1] = static_cast<int>(sourceComponent[1]) * 255 / alpha;
412 destinationComponent[2] = static_cast<int>(sourceComponent[2]) * 255 / alpha;
414 destinationComponent[0] = 0;
415 destinationComponent[1] = 0;
416 destinationComponent[2] = 0;
418 destinationComponent[3] = alpha;
419 sourceComponent += 4;
420 destinationComponent += 4;
424 copyImageBytes(m_unmultipliedImageResult.get(), destination, rect);
427 void FilterEffect::copyPremultipliedImage(Uint8ClampedArray* destination, const IntRect& rect)
431 if (!m_premultipliedImageResult) {
432 // We prefer a conversion from the image buffer.
433 if (m_imageBufferResult)
434 m_premultipliedImageResult = m_imageBufferResult->getPremultipliedImageData(IntRect(IntPoint(), m_absolutePaintRect.size()));
436 ASSERT(isFilterSizeValid(m_absolutePaintRect));
437 IntSize inputSize(m_absolutePaintRect.size());
438 inputSize.scale(m_filter.filterScale());
439 m_premultipliedImageResult = Uint8ClampedArray::createUninitialized(inputSize.width() * inputSize.height() * 4);
440 unsigned char* sourceComponent = m_unmultipliedImageResult->data();
441 unsigned char* destinationComponent = m_premultipliedImageResult->data();
442 unsigned char* end = sourceComponent + (inputSize.width() * inputSize.height() * 4);
443 while (sourceComponent < end) {
444 int alpha = sourceComponent[3];
445 destinationComponent[0] = static_cast<int>(sourceComponent[0]) * alpha / 255;
446 destinationComponent[1] = static_cast<int>(sourceComponent[1]) * alpha / 255;
447 destinationComponent[2] = static_cast<int>(sourceComponent[2]) * alpha / 255;
448 destinationComponent[3] = alpha;
449 sourceComponent += 4;
450 destinationComponent += 4;
454 copyImageBytes(m_premultipliedImageResult.get(), destination, rect);
457 ImageBuffer* FilterEffect::createImageBufferResult()
459 // Only one result type is allowed.
460 ASSERT(!hasResult());
461 if (m_absolutePaintRect.isEmpty())
463 m_imageBufferResult = ImageBuffer::create(m_absolutePaintRect.size(), m_filter.filterScale(), m_resultColorSpace, m_filter.renderingMode());
464 if (!m_imageBufferResult)
466 ASSERT(m_imageBufferResult->context());
467 return m_imageBufferResult.get();
470 Uint8ClampedArray* FilterEffect::createUnmultipliedImageResult()
472 // Only one result type is allowed.
473 ASSERT(!hasResult());
474 ASSERT(isFilterSizeValid(m_absolutePaintRect));
476 if (m_absolutePaintRect.isEmpty())
478 IntSize resultSize(m_absolutePaintRect.size());
479 resultSize.scale(m_filter.filterScale());
480 m_unmultipliedImageResult = Uint8ClampedArray::createUninitialized(resultSize.width() * resultSize.height() * 4);
481 return m_unmultipliedImageResult.get();
484 Uint8ClampedArray* FilterEffect::createPremultipliedImageResult()
486 // Only one result type is allowed.
487 ASSERT(!hasResult());
488 ASSERT(isFilterSizeValid(m_absolutePaintRect));
490 if (m_absolutePaintRect.isEmpty())
492 IntSize resultSize(m_absolutePaintRect.size());
493 resultSize.scale(m_filter.filterScale());
494 m_premultipliedImageResult = Uint8ClampedArray::createUninitialized(resultSize.width() * resultSize.height() * 4);
495 return m_premultipliedImageResult.get();
499 OpenCLHandle FilterEffect::createOpenCLImageResult(uint8_t* source)
501 FilterContextOpenCL* context = FilterContextOpenCL::context();
504 if (context->inError())
507 ASSERT(!hasResult());
508 cl_image_format clImageFormat;
509 clImageFormat.image_channel_order = CL_RGBA;
510 clImageFormat.image_channel_data_type = CL_UNORM_INT8;
513 #ifdef CL_API_SUFFIX__VERSION_1_2
514 cl_image_desc imageDescriptor = { CL_MEM_OBJECT_IMAGE2D, m_absolutePaintRect.width(), m_absolutePaintRect.height(), 0, 0, 0, 0, 0, 0, 0};
515 m_openCLImageResult = clCreateImage(context->deviceContext(), CL_MEM_READ_WRITE | (source ? CL_MEM_COPY_HOST_PTR : 0),
516 &clImageFormat, &imageDescriptor, source, &errorCode);
518 m_openCLImageResult = clCreateImage2D(context->deviceContext(), CL_MEM_READ_WRITE | (source ? CL_MEM_COPY_HOST_PTR : 0),
519 &clImageFormat, m_absolutePaintRect.width(), m_absolutePaintRect.height(), 0, source, &errorCode);
521 if (context->isFailed(errorCode))
524 return m_openCLImageResult;
528 void FilterEffect::transformResultColorSpace(ColorSpace dstColorSpace)
531 // CG handles color space adjustments internally.
532 UNUSED_PARAM(dstColorSpace);
534 if (!hasResult() || dstColorSpace == m_resultColorSpace)
539 if (m_imageBufferResult)
540 m_imageBufferResult.clear();
541 FilterContextOpenCL* context = FilterContextOpenCL::context();
543 context->openCLTransformColorSpace(m_openCLImageResult, absolutePaintRect(), m_resultColorSpace, dstColorSpace);
547 // FIXME: We can avoid this potentially unnecessary ImageBuffer conversion by adding
548 // color space transform support for the {pre,un}multiplied arrays.
549 asImageBuffer()->transformColorSpace(m_resultColorSpace, dstColorSpace);
555 m_resultColorSpace = dstColorSpace;
557 if (m_unmultipliedImageResult)
558 m_unmultipliedImageResult.clear();
559 if (m_premultipliedImageResult)
560 m_premultipliedImageResult.clear();
564 TextStream& FilterEffect::externalRepresentation(TextStream& ts, int) const
566 // FIXME: We should dump the subRegions of the filter primitives here later. This isn't
567 // possible at the moment, because we need more detailed informations from the target object.
571 } // namespace WebCore