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 FilterEffect::FilterEffect(Filter& filter)
46 , m_clipsToBounds(true)
47 , m_operatingColorSpace(ColorSpaceLinearRGB)
48 , m_resultColorSpace(ColorSpaceDeviceRGB)
52 FilterEffect::~FilterEffect()
56 void FilterEffect::determineAbsolutePaintRect()
58 m_absolutePaintRect = IntRect();
59 unsigned size = m_inputEffects.size();
60 for (unsigned i = 0; i < size; ++i)
61 m_absolutePaintRect.unite(m_inputEffects.at(i)->absolutePaintRect());
63 // Filters in SVG clip to primitive subregion, while CSS doesn't.
65 m_absolutePaintRect.intersect(enclosingIntRect(m_maxEffectRect));
67 m_absolutePaintRect.unite(enclosingIntRect(m_maxEffectRect));
71 IntRect FilterEffect::requestedRegionOfInputImageData(const IntRect& effectRect) const
74 IntPoint location = m_absolutePaintRect.location();
75 location.moveBy(-effectRect.location());
76 return IntRect(location, m_absolutePaintRect.size());
79 FloatRect FilterEffect::drawingRegionOfInputImage(const IntRect& srcRect) const
84 ImageBuffer::clampedSize(m_absolutePaintRect.size(), scale);
86 AffineTransform transform;
87 transform.scale(scale).translate(-m_absolutePaintRect.location());
88 return transform.mapRect(srcRect);
91 FilterEffect* FilterEffect::inputEffect(unsigned number) const
93 ASSERT_WITH_SECURITY_IMPLICATION(number < m_inputEffects.size());
94 return m_inputEffects.at(number).get();
97 static unsigned collectEffects(const FilterEffect*effect, HashSet<const FilterEffect*>& allEffects)
99 allEffects.add(effect);
100 unsigned size = effect->numberOfEffectInputs();
101 for (unsigned i = 0; i < size; ++i) {
102 FilterEffect* in = effect->inputEffect(i);
103 collectEffects(in, allEffects);
105 return allEffects.size();
108 unsigned FilterEffect::totalNumberOfEffectInputs() const
110 HashSet<const FilterEffect*> allEffects;
111 return collectEffects(this, allEffects);
115 void FilterEffect::applyAll()
119 FilterContextOpenCL* context = FilterContextOpenCL::context();
122 if (!context->inError())
124 clearResultsRecursive();
125 context->destroyContext();
127 // Software code path.
132 void FilterEffect::apply()
136 unsigned size = m_inputEffects.size();
137 for (unsigned i = 0; i < size; ++i) {
138 FilterEffect* in = m_inputEffects.at(i).get();
140 if (!in->hasResult())
143 // Convert input results to the current effect's color space.
144 transformResultColorSpace(in, i);
147 determineAbsolutePaintRect();
148 setResultColorSpace(m_operatingColorSpace);
150 if (m_absolutePaintRect.isEmpty() || !ImageBuffer::isSizeClamped(m_absolutePaintRect.size()))
153 if (requiresValidPreMultipliedPixels()) {
154 for (unsigned i = 0; i < size; ++i)
155 inputEffect(i)->correctFilterResultIfNeeded();
158 // Add platform specific apply functions here and return earlier.
160 if (platformApplyOpenCL())
163 platformApplySoftware();
167 // This function will be changed to abstract virtual when all filters are landed.
168 bool FilterEffect::platformApplyOpenCL()
170 if (!FilterContextOpenCL::context())
173 unsigned size = m_inputEffects.size();
174 for (unsigned i = 0; i < size; ++i) {
175 FilterEffect* in = m_inputEffects.at(i).get();
176 // Software code path expects that at least one of the following fileds is valid.
177 if (!in->m_imageBufferResult && !in->m_unmultipliedImageResult && !in->m_premultipliedImageResult)
181 platformApplySoftware();
182 ImageBuffer* sourceImage = asImageBuffer();
184 RefPtr<Uint8ClampedArray> sourceImageData = sourceImage->getUnmultipliedImageData(IntRect(IntPoint(), sourceImage->internalSize()));
185 createOpenCLImageResult(sourceImageData->data());
191 void FilterEffect::forceValidPreMultipliedPixels()
193 // Must operate on pre-multiplied results; other formats cannot have invalid pixels.
194 if (!m_premultipliedImageResult)
197 Uint8ClampedArray* imageArray = m_premultipliedImageResult.get();
198 unsigned char* pixelData = imageArray->data();
199 int pixelArrayLength = imageArray->length();
201 // We must have four bytes per pixel, and complete pixels
202 ASSERT(!(pixelArrayLength % 4));
204 #if HAVE(ARM_NEON_INTRINSICS)
205 if (pixelArrayLength >= 64) {
206 unsigned char* lastPixel = pixelData + (pixelArrayLength & ~0x3f);
208 // Increments pixelData by 64.
209 uint8x16x4_t sixteenPixels = vld4q_u8(pixelData);
210 sixteenPixels.val[0] = vminq_u8(sixteenPixels.val[0], sixteenPixels.val[3]);
211 sixteenPixels.val[1] = vminq_u8(sixteenPixels.val[1], sixteenPixels.val[3]);
212 sixteenPixels.val[2] = vminq_u8(sixteenPixels.val[2], sixteenPixels.val[3]);
213 vst4q_u8(pixelData, sixteenPixels);
215 } while (pixelData < lastPixel);
217 pixelArrayLength &= 0x3f;
218 if (!pixelArrayLength)
223 int numPixels = pixelArrayLength / 4;
225 // Iterate over each pixel, checking alpha and adjusting color components if necessary
226 while (--numPixels >= 0) {
227 // Alpha is the 4th byte in a pixel
228 unsigned char a = *(pixelData + 3);
229 // Clamp each component to alpha, and increment the pixel location
230 for (int i = 0; i < 3; ++i) {
235 // Increment for alpha
240 void FilterEffect::clearResult()
242 if (m_imageBufferResult)
243 m_imageBufferResult.reset();
244 if (m_unmultipliedImageResult)
245 m_unmultipliedImageResult.clear();
246 if (m_premultipliedImageResult)
247 m_premultipliedImageResult.clear();
249 if (m_openCLImageResult)
250 m_openCLImageResult.clear();
254 void FilterEffect::clearResultsRecursive()
256 // Clear all results, regardless that the current effect has
257 // a result. Can be used if an effect is in an erroneous state.
261 unsigned size = m_inputEffects.size();
262 for (unsigned i = 0; i < size; ++i)
263 m_inputEffects.at(i).get()->clearResultsRecursive();
266 ImageBuffer* FilterEffect::asImageBuffer()
270 if (m_imageBufferResult)
271 return m_imageBufferResult.get();
273 if (m_openCLImageResult)
274 return openCLImageToImageBuffer();
276 m_imageBufferResult = ImageBuffer::create(m_absolutePaintRect.size(), m_filter.filterScale(), m_resultColorSpace, m_filter.renderingMode());
277 IntRect destinationRect(IntPoint(), m_absolutePaintRect.size());
278 if (m_premultipliedImageResult)
279 m_imageBufferResult->putByteArray(Premultiplied, m_premultipliedImageResult.get(), destinationRect.size(), destinationRect, IntPoint());
281 m_imageBufferResult->putByteArray(Unmultiplied, m_unmultipliedImageResult.get(), destinationRect.size(), destinationRect, IntPoint());
282 return m_imageBufferResult.get();
286 ImageBuffer* FilterEffect::openCLImageToImageBuffer()
288 FilterContextOpenCL* context = FilterContextOpenCL::context();
291 if (context->inError())
294 size_t origin[3] = { 0, 0, 0 };
295 size_t region[3] = { m_absolutePaintRect.width(), m_absolutePaintRect.height(), 1 };
297 RefPtr<Uint8ClampedArray> destinationPixelArray = Uint8ClampedArray::create(m_absolutePaintRect.width() * m_absolutePaintRect.height() * 4);
299 if (context->isFailed(clFinish(context->commandQueue())))
302 if (context->isFailed(clEnqueueReadImage(context->commandQueue(), m_openCLImageResult, CL_TRUE, origin, region, 0, 0, destinationPixelArray->data(), 0, 0, 0)))
305 m_imageBufferResult = ImageBuffer::create(m_absolutePaintRect.size());
306 IntRect destinationRect(IntPoint(), m_absolutePaintRect.size());
307 m_imageBufferResult->putByteArray(Unmultiplied, destinationPixelArray.get(), destinationRect.size(), destinationRect, IntPoint());
309 return m_imageBufferResult.get();
313 PassRefPtr<Uint8ClampedArray> FilterEffect::asUnmultipliedImage(const IntRect& rect)
315 IntSize scaledSize(rect.size());
316 ASSERT(ImageBuffer::isSizeClamped(scaledSize));
317 scaledSize.scale(m_filter.filterScale());
318 RefPtr<Uint8ClampedArray> imageData = Uint8ClampedArray::createUninitialized(scaledSize.width() * scaledSize.height() * 4);
319 copyUnmultipliedImage(imageData.get(), rect);
320 return imageData.release();
323 PassRefPtr<Uint8ClampedArray> FilterEffect::asPremultipliedImage(const IntRect& rect)
325 IntSize scaledSize(rect.size());
326 ASSERT(ImageBuffer::isSizeClamped(scaledSize));
327 scaledSize.scale(m_filter.filterScale());
328 RefPtr<Uint8ClampedArray> imageData = Uint8ClampedArray::createUninitialized(scaledSize.width() * scaledSize.height() * 4);
329 copyPremultipliedImage(imageData.get(), rect);
330 return imageData.release();
333 inline void FilterEffect::copyImageBytes(Uint8ClampedArray* source, Uint8ClampedArray* destination, const IntRect& rect)
335 IntRect scaledRect(rect);
336 scaledRect.scale(m_filter.filterScale());
337 IntSize scaledPaintSize(m_absolutePaintRect.size());
338 scaledPaintSize.scale(m_filter.filterScale());
340 // Initialize the destination to transparent black, if not entirely covered by the source.
341 if (scaledRect.x() < 0 || scaledRect.y() < 0 || scaledRect.maxX() > scaledPaintSize.width() || scaledRect.maxY() > scaledPaintSize.height())
342 memset(destination->data(), 0, destination->length());
344 // Early return if the rect does not intersect with the source.
345 if (scaledRect.maxX() <= 0 || scaledRect.maxY() <= 0 || scaledRect.x() >= scaledPaintSize.width() || scaledRect.y() >= scaledPaintSize.height())
348 int xOrigin = scaledRect.x();
354 int xEnd = scaledRect.maxX();
355 if (xEnd > scaledPaintSize.width())
356 xEnd = scaledPaintSize.width();
358 int yOrigin = scaledRect.y();
364 int yEnd = scaledRect.maxY();
365 if (yEnd > scaledPaintSize.height())
366 yEnd = scaledPaintSize.height();
368 int size = (xEnd - xOrigin) * 4;
369 int destinationScanline = scaledRect.width() * 4;
370 int sourceScanline = scaledPaintSize.width() * 4;
371 unsigned char *destinationPixel = destination->data() + ((yDest * scaledRect.width()) + xDest) * 4;
372 unsigned char *sourcePixel = source->data() + ((yOrigin * scaledPaintSize.width()) + xOrigin) * 4;
374 while (yOrigin < yEnd) {
375 memcpy(destinationPixel, sourcePixel, size);
376 destinationPixel += destinationScanline;
377 sourcePixel += sourceScanline;
382 void FilterEffect::copyUnmultipliedImage(Uint8ClampedArray* destination, const IntRect& rect)
386 if (!m_unmultipliedImageResult) {
387 // We prefer a conversion from the image buffer.
388 if (m_imageBufferResult)
389 m_unmultipliedImageResult = m_imageBufferResult->getUnmultipliedImageData(IntRect(IntPoint(), m_absolutePaintRect.size()));
391 IntSize inputSize(m_absolutePaintRect.size());
392 ASSERT(ImageBuffer::isSizeClamped(inputSize));
393 inputSize.scale(m_filter.filterScale());
394 m_unmultipliedImageResult = Uint8ClampedArray::createUninitialized(inputSize.width() * inputSize.height() * 4);
395 unsigned char* sourceComponent = m_premultipliedImageResult->data();
396 unsigned char* destinationComponent = m_unmultipliedImageResult->data();
397 unsigned char* end = sourceComponent + (inputSize.width() * inputSize.height() * 4);
398 while (sourceComponent < end) {
399 int alpha = sourceComponent[3];
401 destinationComponent[0] = static_cast<int>(sourceComponent[0]) * 255 / alpha;
402 destinationComponent[1] = static_cast<int>(sourceComponent[1]) * 255 / alpha;
403 destinationComponent[2] = static_cast<int>(sourceComponent[2]) * 255 / alpha;
405 destinationComponent[0] = 0;
406 destinationComponent[1] = 0;
407 destinationComponent[2] = 0;
409 destinationComponent[3] = alpha;
410 sourceComponent += 4;
411 destinationComponent += 4;
415 copyImageBytes(m_unmultipliedImageResult.get(), destination, rect);
418 void FilterEffect::copyPremultipliedImage(Uint8ClampedArray* destination, const IntRect& rect)
422 if (!m_premultipliedImageResult) {
423 // We prefer a conversion from the image buffer.
424 if (m_imageBufferResult)
425 m_premultipliedImageResult = m_imageBufferResult->getPremultipliedImageData(IntRect(IntPoint(), m_absolutePaintRect.size()));
427 IntSize inputSize(m_absolutePaintRect.size());
428 ASSERT(ImageBuffer::isSizeClamped(inputSize));
429 inputSize.scale(m_filter.filterScale());
430 m_premultipliedImageResult = Uint8ClampedArray::createUninitialized(inputSize.width() * inputSize.height() * 4);
431 unsigned char* sourceComponent = m_unmultipliedImageResult->data();
432 unsigned char* destinationComponent = m_premultipliedImageResult->data();
433 unsigned char* end = sourceComponent + (inputSize.width() * inputSize.height() * 4);
434 while (sourceComponent < end) {
435 int alpha = sourceComponent[3];
436 destinationComponent[0] = static_cast<int>(sourceComponent[0]) * alpha / 255;
437 destinationComponent[1] = static_cast<int>(sourceComponent[1]) * alpha / 255;
438 destinationComponent[2] = static_cast<int>(sourceComponent[2]) * alpha / 255;
439 destinationComponent[3] = alpha;
440 sourceComponent += 4;
441 destinationComponent += 4;
445 copyImageBytes(m_premultipliedImageResult.get(), destination, rect);
448 ImageBuffer* FilterEffect::createImageBufferResult()
450 // Only one result type is allowed.
451 ASSERT(!hasResult());
452 if (m_absolutePaintRect.isEmpty())
455 FloatSize clampedSize = ImageBuffer::clampedSize(m_absolutePaintRect.size());
456 m_imageBufferResult = ImageBuffer::create(clampedSize, m_filter.filterScale(), m_resultColorSpace, m_filter.renderingMode());
457 if (!m_imageBufferResult)
460 ASSERT(m_imageBufferResult->context());
461 return m_imageBufferResult.get();
464 Uint8ClampedArray* FilterEffect::createUnmultipliedImageResult()
466 // Only one result type is allowed.
467 ASSERT(!hasResult());
468 if (m_absolutePaintRect.isEmpty())
471 IntSize resultSize(m_absolutePaintRect.size());
472 ASSERT(ImageBuffer::isSizeClamped(resultSize));
473 resultSize.scale(m_filter.filterScale());
474 m_unmultipliedImageResult = Uint8ClampedArray::createUninitialized(resultSize.width() * resultSize.height() * 4);
475 return m_unmultipliedImageResult.get();
478 Uint8ClampedArray* FilterEffect::createPremultipliedImageResult()
480 // Only one result type is allowed.
481 ASSERT(!hasResult());
482 if (m_absolutePaintRect.isEmpty())
485 IntSize resultSize(m_absolutePaintRect.size());
486 ASSERT(ImageBuffer::isSizeClamped(resultSize));
487 resultSize.scale(m_filter.filterScale());
488 m_premultipliedImageResult = Uint8ClampedArray::createUninitialized(resultSize.width() * resultSize.height() * 4);
489 return m_premultipliedImageResult.get();
493 OpenCLHandle FilterEffect::createOpenCLImageResult(uint8_t* source)
495 FilterContextOpenCL* context = FilterContextOpenCL::context();
498 if (context->inError())
501 ASSERT(!hasResult());
502 cl_image_format clImageFormat;
503 clImageFormat.image_channel_order = CL_RGBA;
504 clImageFormat.image_channel_data_type = CL_UNORM_INT8;
507 #ifdef CL_API_SUFFIX__VERSION_1_2
508 cl_image_desc imageDescriptor = { CL_MEM_OBJECT_IMAGE2D, m_absolutePaintRect.width(), m_absolutePaintRect.height(), 0, 0, 0, 0, 0, 0, 0};
509 m_openCLImageResult = clCreateImage(context->deviceContext(), CL_MEM_READ_WRITE | (source ? CL_MEM_COPY_HOST_PTR : 0),
510 &clImageFormat, &imageDescriptor, source, &errorCode);
512 m_openCLImageResult = clCreateImage2D(context->deviceContext(), CL_MEM_READ_WRITE | (source ? CL_MEM_COPY_HOST_PTR : 0),
513 &clImageFormat, m_absolutePaintRect.width(), m_absolutePaintRect.height(), 0, source, &errorCode);
515 if (context->isFailed(errorCode))
518 return m_openCLImageResult;
522 void FilterEffect::transformResultColorSpace(ColorSpace dstColorSpace)
525 // CG handles color space adjustments internally.
526 UNUSED_PARAM(dstColorSpace);
528 if (!hasResult() || dstColorSpace == m_resultColorSpace)
533 if (m_imageBufferResult)
534 m_imageBufferResult.clear();
535 FilterContextOpenCL* context = FilterContextOpenCL::context();
537 context->openCLTransformColorSpace(m_openCLImageResult, absolutePaintRect(), m_resultColorSpace, dstColorSpace);
541 // FIXME: We can avoid this potentially unnecessary ImageBuffer conversion by adding
542 // color space transform support for the {pre,un}multiplied arrays.
543 asImageBuffer()->transformColorSpace(m_resultColorSpace, dstColorSpace);
549 m_resultColorSpace = dstColorSpace;
551 if (m_unmultipliedImageResult)
552 m_unmultipliedImageResult.clear();
553 if (m_premultipliedImageResult)
554 m_premultipliedImageResult.clear();
558 TextStream& FilterEffect::externalRepresentation(TextStream& ts, int) const
560 // FIXME: We should dump the subRegions of the filter primitives here later. This isn't
561 // possible at the moment, because we need more detailed informations from the target object.
565 } // namespace WebCore