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 * 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. 11 * 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. 16 * 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. 21 */ 22 23#include "config.h" 24 25#if ENABLE(FILTERS) 26#include "FilterEffect.h" 27 28#include "Filter.h" 29#include "ImageBuffer.h" 30#include "TextStream.h" 31#include <wtf/Uint8ClampedArray.h> 32 33#if HAVE(ARM_NEON_INTRINSICS) 34#include <arm_neon.h> 35#endif 36 37namespace WebCore { 38 39FilterEffect::FilterEffect(Filter* filter) 40 : m_alphaImage(false) 41 , m_filter(filter) 42 , m_hasX(false) 43 , m_hasY(false) 44 , m_hasWidth(false) 45 , m_hasHeight(false) 46 , m_clipsToBounds(true) 47 , m_operatingColorSpace(ColorSpaceLinearRGB) 48 , m_resultColorSpace(ColorSpaceDeviceRGB) 49{ 50 ASSERT(m_filter); 51} 52 53FilterEffect::~FilterEffect() 54{ 55} 56 57inline bool isFilterSizeValid(IntRect rect) 58{ 59 if (rect.width() < 0 || rect.width() > kMaxFilterSize 60 || rect.height() < 0 || rect.height() > kMaxFilterSize) 61 return false; 62 return true; 63} 64 65void FilterEffect::determineAbsolutePaintRect() 66{ 67 m_absolutePaintRect = IntRect(); 68 unsigned size = m_inputEffects.size(); 69 for (unsigned i = 0; i < size; ++i) 70 m_absolutePaintRect.unite(m_inputEffects.at(i)->absolutePaintRect()); 71 72 // Filters in SVG clip to primitive subregion, while CSS doesn't. 73 if (m_clipsToBounds) 74 m_absolutePaintRect.intersect(enclosingIntRect(m_maxEffectRect)); 75 else 76 m_absolutePaintRect.unite(enclosingIntRect(m_maxEffectRect)); 77 78} 79 80IntRect FilterEffect::requestedRegionOfInputImageData(const IntRect& effectRect) const 81{ 82 ASSERT(hasResult()); 83 IntPoint location = m_absolutePaintRect.location(); 84 location.moveBy(-effectRect.location()); 85 return IntRect(location, m_absolutePaintRect.size()); 86} 87 88IntRect FilterEffect::drawingRegionOfInputImage(const IntRect& srcRect) const 89{ 90 return IntRect(IntPoint(srcRect.x() - m_absolutePaintRect.x(), 91 srcRect.y() - m_absolutePaintRect.y()), srcRect.size()); 92} 93 94FilterEffect* FilterEffect::inputEffect(unsigned number) const 95{ 96 ASSERT_WITH_SECURITY_IMPLICATION(number < m_inputEffects.size()); 97 return m_inputEffects.at(number).get(); 98} 99 100#if ENABLE(OPENCL) 101void FilterEffect::applyAll() 102{ 103 if (hasResult()) 104 return; 105 FilterContextOpenCL* context = FilterContextOpenCL::context(); 106 if (context) { 107 apply(); 108 if (!context->inError()) 109 return; 110 clearResultsRecursive(); 111 context->destroyContext(); 112 } 113 // Software code path. 114 apply(); 115} 116#endif 117 118void FilterEffect::apply() 119{ 120 if (hasResult()) 121 return; 122 unsigned size = m_inputEffects.size(); 123 for (unsigned i = 0; i < size; ++i) { 124 FilterEffect* in = m_inputEffects.at(i).get(); 125 in->apply(); 126 if (!in->hasResult()) 127 return; 128 129 // Convert input results to the current effect's color space. 130 transformResultColorSpace(in, i); 131 } 132 133 determineAbsolutePaintRect(); 134 setResultColorSpace(m_operatingColorSpace); 135 136 if (!isFilterSizeValid(m_absolutePaintRect)) 137 return; 138 139 if (requiresValidPreMultipliedPixels()) { 140 for (unsigned i = 0; i < size; ++i) 141 inputEffect(i)->correctFilterResultIfNeeded(); 142 } 143 144 // Add platform specific apply functions here and return earlier. 145#if ENABLE(OPENCL) 146 if (platformApplyOpenCL()) 147 return; 148#endif 149 platformApplySoftware(); 150} 151 152#if ENABLE(OPENCL) 153// This function will be changed to abstract virtual when all filters are landed. 154bool FilterEffect::platformApplyOpenCL() 155{ 156 if (!FilterContextOpenCL::context()) 157 return false; 158 159 unsigned size = m_inputEffects.size(); 160 for (unsigned i = 0; i < size; ++i) { 161 FilterEffect* in = m_inputEffects.at(i).get(); 162 // Software code path expects that at least one of the following fileds is valid. 163 if (!in->m_imageBufferResult && !in->m_unmultipliedImageResult && !in->m_premultipliedImageResult) 164 in->asImageBuffer(); 165 } 166 167 platformApplySoftware(); 168 ImageBuffer* sourceImage = asImageBuffer(); 169 if (sourceImage) { 170 RefPtr<Uint8ClampedArray> sourceImageData = sourceImage->getUnmultipliedImageData(IntRect(IntPoint(), sourceImage->internalSize())); 171 createOpenCLImageResult(sourceImageData->data()); 172 } 173 return true; 174} 175#endif 176 177void FilterEffect::forceValidPreMultipliedPixels() 178{ 179 // Must operate on pre-multiplied results; other formats cannot have invalid pixels. 180 if (!m_premultipliedImageResult) 181 return; 182 183 Uint8ClampedArray* imageArray = m_premultipliedImageResult.get(); 184 unsigned char* pixelData = imageArray->data(); 185 int pixelArrayLength = imageArray->length(); 186 187 // We must have four bytes per pixel, and complete pixels 188 ASSERT(!(pixelArrayLength % 4)); 189 190#if HAVE(ARM_NEON_INTRINSICS) 191 if (pixelArrayLength >= 64) { 192 unsigned char* lastPixel = pixelData + (pixelArrayLength & ~0x3f); 193 do { 194 // Increments pixelData by 64. 195 uint8x16x4_t sixteenPixels = vld4q_u8(pixelData); 196 sixteenPixels.val[0] = vminq_u8(sixteenPixels.val[0], sixteenPixels.val[3]); 197 sixteenPixels.val[1] = vminq_u8(sixteenPixels.val[1], sixteenPixels.val[3]); 198 sixteenPixels.val[2] = vminq_u8(sixteenPixels.val[2], sixteenPixels.val[3]); 199 vst4q_u8(pixelData, sixteenPixels); 200 pixelData += 64; 201 } while (pixelData < lastPixel); 202 203 pixelArrayLength &= 0x3f; 204 if (!pixelArrayLength) 205 return; 206 } 207#endif 208 209 int numPixels = pixelArrayLength / 4; 210 211 // Iterate over each pixel, checking alpha and adjusting color components if necessary 212 while (--numPixels >= 0) { 213 // Alpha is the 4th byte in a pixel 214 unsigned char a = *(pixelData + 3); 215 // Clamp each component to alpha, and increment the pixel location 216 for (int i = 0; i < 3; ++i) { 217 if (*pixelData > a) 218 *pixelData = a; 219 ++pixelData; 220 } 221 // Increment for alpha 222 ++pixelData; 223 } 224} 225 226void FilterEffect::clearResult() 227{ 228 if (m_imageBufferResult) 229 m_imageBufferResult.clear(); 230 if (m_unmultipliedImageResult) 231 m_unmultipliedImageResult.clear(); 232 if (m_premultipliedImageResult) 233 m_premultipliedImageResult.clear(); 234#if ENABLE(OPENCL) 235 if (m_openCLImageResult) 236 m_openCLImageResult.clear(); 237#endif 238} 239 240void FilterEffect::clearResultsRecursive() 241{ 242 // Clear all results, regardless that the current effect has 243 // a result. Can be used if an effect is in an erroneous state. 244 if (hasResult()) 245 clearResult(); 246 247 unsigned size = m_inputEffects.size(); 248 for (unsigned i = 0; i < size; ++i) 249 m_inputEffects.at(i).get()->clearResultsRecursive(); 250} 251 252ImageBuffer* FilterEffect::asImageBuffer() 253{ 254 if (!hasResult()) 255 return 0; 256 if (m_imageBufferResult) 257 return m_imageBufferResult.get(); 258#if ENABLE(OPENCL) 259 if (m_openCLImageResult) 260 return openCLImageToImageBuffer(); 261#endif 262 m_imageBufferResult = ImageBuffer::create(m_absolutePaintRect.size(), 1, m_resultColorSpace, m_filter->renderingMode()); 263 IntRect destinationRect(IntPoint(), m_absolutePaintRect.size()); 264 if (m_premultipliedImageResult) 265 m_imageBufferResult->putByteArray(Premultiplied, m_premultipliedImageResult.get(), destinationRect.size(), destinationRect, IntPoint()); 266 else 267 m_imageBufferResult->putByteArray(Unmultiplied, m_unmultipliedImageResult.get(), destinationRect.size(), destinationRect, IntPoint()); 268 return m_imageBufferResult.get(); 269} 270 271#if ENABLE(OPENCL) 272ImageBuffer* FilterEffect::openCLImageToImageBuffer() 273{ 274 FilterContextOpenCL* context = FilterContextOpenCL::context(); 275 ASSERT(context); 276 277 if (context->inError()) 278 return 0; 279 280 size_t origin[3] = { 0, 0, 0 }; 281 size_t region[3] = { m_absolutePaintRect.width(), m_absolutePaintRect.height(), 1 }; 282 283 RefPtr<Uint8ClampedArray> destinationPixelArray = Uint8ClampedArray::create(m_absolutePaintRect.width() * m_absolutePaintRect.height() * 4); 284 285 if (context->isFailed(clFinish(context->commandQueue()))) 286 return 0; 287 288 if (context->isFailed(clEnqueueReadImage(context->commandQueue(), m_openCLImageResult, CL_TRUE, origin, region, 0, 0, destinationPixelArray->data(), 0, 0, 0))) 289 return 0; 290 291 m_imageBufferResult = ImageBuffer::create(m_absolutePaintRect.size()); 292 IntRect destinationRect(IntPoint(), m_absolutePaintRect.size()); 293 m_imageBufferResult->putByteArray(Unmultiplied, destinationPixelArray.get(), destinationRect.size(), destinationRect, IntPoint()); 294 295 return m_imageBufferResult.get(); 296} 297#endif 298 299PassRefPtr<Uint8ClampedArray> FilterEffect::asUnmultipliedImage(const IntRect& rect) 300{ 301 ASSERT(isFilterSizeValid(rect)); 302 RefPtr<Uint8ClampedArray> imageData = Uint8ClampedArray::createUninitialized(rect.width() * rect.height() * 4); 303 copyUnmultipliedImage(imageData.get(), rect); 304 return imageData.release(); 305} 306 307PassRefPtr<Uint8ClampedArray> FilterEffect::asPremultipliedImage(const IntRect& rect) 308{ 309 ASSERT(isFilterSizeValid(rect)); 310 RefPtr<Uint8ClampedArray> imageData = Uint8ClampedArray::createUninitialized(rect.width() * rect.height() * 4); 311 copyPremultipliedImage(imageData.get(), rect); 312 return imageData.release(); 313} 314 315inline void FilterEffect::copyImageBytes(Uint8ClampedArray* source, Uint8ClampedArray* destination, const IntRect& rect) 316{ 317 // Initialize the destination to transparent black, if not entirely covered by the source. 318 if (rect.x() < 0 || rect.y() < 0 || rect.maxX() > m_absolutePaintRect.width() || rect.maxY() > m_absolutePaintRect.height()) 319 memset(destination->data(), 0, destination->length()); 320 321 // Early return if the rect does not intersect with the source. 322 if (rect.maxX() <= 0 || rect.maxY() <= 0 || rect.x() >= m_absolutePaintRect.width() || rect.y() >= m_absolutePaintRect.height()) 323 return; 324 325 int xOrigin = rect.x(); 326 int xDest = 0; 327 if (xOrigin < 0) { 328 xDest = -xOrigin; 329 xOrigin = 0; 330 } 331 int xEnd = rect.maxX(); 332 if (xEnd > m_absolutePaintRect.width()) 333 xEnd = m_absolutePaintRect.width(); 334 335 int yOrigin = rect.y(); 336 int yDest = 0; 337 if (yOrigin < 0) { 338 yDest = -yOrigin; 339 yOrigin = 0; 340 } 341 int yEnd = rect.maxY(); 342 if (yEnd > m_absolutePaintRect.height()) 343 yEnd = m_absolutePaintRect.height(); 344 345 int size = (xEnd - xOrigin) * 4; 346 int destinationScanline = rect.width() * 4; 347 int sourceScanline = m_absolutePaintRect.width() * 4; 348 unsigned char *destinationPixel = destination->data() + ((yDest * rect.width()) + xDest) * 4; 349 unsigned char *sourcePixel = source->data() + ((yOrigin * m_absolutePaintRect.width()) + xOrigin) * 4; 350 351 while (yOrigin < yEnd) { 352 memcpy(destinationPixel, sourcePixel, size); 353 destinationPixel += destinationScanline; 354 sourcePixel += sourceScanline; 355 ++yOrigin; 356 } 357} 358 359void FilterEffect::copyUnmultipliedImage(Uint8ClampedArray* destination, const IntRect& rect) 360{ 361 ASSERT(hasResult()); 362 363 if (!m_unmultipliedImageResult) { 364 // We prefer a conversion from the image buffer. 365 if (m_imageBufferResult) 366 m_unmultipliedImageResult = m_imageBufferResult->getUnmultipliedImageData(IntRect(IntPoint(), m_absolutePaintRect.size())); 367 else { 368 ASSERT(isFilterSizeValid(m_absolutePaintRect)); 369 m_unmultipliedImageResult = Uint8ClampedArray::createUninitialized(m_absolutePaintRect.width() * m_absolutePaintRect.height() * 4); 370 unsigned char* sourceComponent = m_premultipliedImageResult->data(); 371 unsigned char* destinationComponent = m_unmultipliedImageResult->data(); 372 unsigned char* end = sourceComponent + (m_absolutePaintRect.width() * m_absolutePaintRect.height() * 4); 373 while (sourceComponent < end) { 374 int alpha = sourceComponent[3]; 375 if (alpha) { 376 destinationComponent[0] = static_cast<int>(sourceComponent[0]) * 255 / alpha; 377 destinationComponent[1] = static_cast<int>(sourceComponent[1]) * 255 / alpha; 378 destinationComponent[2] = static_cast<int>(sourceComponent[2]) * 255 / alpha; 379 } else { 380 destinationComponent[0] = 0; 381 destinationComponent[1] = 0; 382 destinationComponent[2] = 0; 383 } 384 destinationComponent[3] = alpha; 385 sourceComponent += 4; 386 destinationComponent += 4; 387 } 388 } 389 } 390 copyImageBytes(m_unmultipliedImageResult.get(), destination, rect); 391} 392 393void FilterEffect::copyPremultipliedImage(Uint8ClampedArray* destination, const IntRect& rect) 394{ 395 ASSERT(hasResult()); 396 397 if (!m_premultipliedImageResult) { 398 // We prefer a conversion from the image buffer. 399 if (m_imageBufferResult) 400 m_premultipliedImageResult = m_imageBufferResult->getPremultipliedImageData(IntRect(IntPoint(), m_absolutePaintRect.size())); 401 else { 402 ASSERT(isFilterSizeValid(m_absolutePaintRect)); 403 m_premultipliedImageResult = Uint8ClampedArray::createUninitialized(m_absolutePaintRect.width() * m_absolutePaintRect.height() * 4); 404 unsigned char* sourceComponent = m_unmultipliedImageResult->data(); 405 unsigned char* destinationComponent = m_premultipliedImageResult->data(); 406 unsigned char* end = sourceComponent + (m_absolutePaintRect.width() * m_absolutePaintRect.height() * 4); 407 while (sourceComponent < end) { 408 int alpha = sourceComponent[3]; 409 destinationComponent[0] = static_cast<int>(sourceComponent[0]) * alpha / 255; 410 destinationComponent[1] = static_cast<int>(sourceComponent[1]) * alpha / 255; 411 destinationComponent[2] = static_cast<int>(sourceComponent[2]) * alpha / 255; 412 destinationComponent[3] = alpha; 413 sourceComponent += 4; 414 destinationComponent += 4; 415 } 416 } 417 } 418 copyImageBytes(m_premultipliedImageResult.get(), destination, rect); 419} 420 421ImageBuffer* FilterEffect::createImageBufferResult() 422{ 423 // Only one result type is allowed. 424 ASSERT(!hasResult()); 425 if (m_absolutePaintRect.isEmpty()) 426 return 0; 427 m_imageBufferResult = ImageBuffer::create(m_absolutePaintRect.size(), 1, m_resultColorSpace, m_filter->renderingMode()); 428 if (!m_imageBufferResult) 429 return 0; 430 ASSERT(m_imageBufferResult->context()); 431 return m_imageBufferResult.get(); 432} 433 434Uint8ClampedArray* FilterEffect::createUnmultipliedImageResult() 435{ 436 // Only one result type is allowed. 437 ASSERT(!hasResult()); 438 ASSERT(isFilterSizeValid(m_absolutePaintRect)); 439 440 if (m_absolutePaintRect.isEmpty()) 441 return 0; 442 m_unmultipliedImageResult = Uint8ClampedArray::createUninitialized(m_absolutePaintRect.width() * m_absolutePaintRect.height() * 4); 443 return m_unmultipliedImageResult.get(); 444} 445 446Uint8ClampedArray* FilterEffect::createPremultipliedImageResult() 447{ 448 // Only one result type is allowed. 449 ASSERT(!hasResult()); 450 ASSERT(isFilterSizeValid(m_absolutePaintRect)); 451 452 if (m_absolutePaintRect.isEmpty()) 453 return 0; 454 m_premultipliedImageResult = Uint8ClampedArray::createUninitialized(m_absolutePaintRect.width() * m_absolutePaintRect.height() * 4); 455 return m_premultipliedImageResult.get(); 456} 457 458#if ENABLE(OPENCL) 459OpenCLHandle FilterEffect::createOpenCLImageResult(uint8_t* source) 460{ 461 FilterContextOpenCL* context = FilterContextOpenCL::context(); 462 ASSERT(context); 463 464 if (context->inError()) 465 return 0; 466 467 ASSERT(!hasResult()); 468 cl_image_format clImageFormat; 469 clImageFormat.image_channel_order = CL_RGBA; 470 clImageFormat.image_channel_data_type = CL_UNORM_INT8; 471 472 int errorCode = 0; 473#ifdef CL_API_SUFFIX__VERSION_1_2 474 cl_image_desc imageDescriptor = { CL_MEM_OBJECT_IMAGE2D, m_absolutePaintRect.width(), m_absolutePaintRect.height(), 0, 0, 0, 0, 0, 0, 0}; 475 m_openCLImageResult = clCreateImage(context->deviceContext(), CL_MEM_READ_WRITE | (source ? CL_MEM_COPY_HOST_PTR : 0), 476 &clImageFormat, &imageDescriptor, source, &errorCode); 477#else 478 m_openCLImageResult = clCreateImage2D(context->deviceContext(), CL_MEM_READ_WRITE | (source ? CL_MEM_COPY_HOST_PTR : 0), 479 &clImageFormat, m_absolutePaintRect.width(), m_absolutePaintRect.height(), 0, source, &errorCode); 480#endif 481 if (context->isFailed(errorCode)) 482 return 0; 483 484 return m_openCLImageResult; 485} 486#endif 487 488void FilterEffect::transformResultColorSpace(ColorSpace dstColorSpace) 489{ 490#if USE(CG) 491 // CG handles color space adjustments internally. 492 UNUSED_PARAM(dstColorSpace); 493#else 494 if (!hasResult() || dstColorSpace == m_resultColorSpace) 495 return; 496 497#if ENABLE(OPENCL) 498 if (openCLImage()) { 499 if (m_imageBufferResult) 500 m_imageBufferResult.clear(); 501 FilterContextOpenCL* context = FilterContextOpenCL::context(); 502 ASSERT(context); 503 context->openCLTransformColorSpace(m_openCLImageResult, absolutePaintRect(), m_resultColorSpace, dstColorSpace); 504 } else { 505#endif 506 507 // FIXME: We can avoid this potentially unnecessary ImageBuffer conversion by adding 508 // color space transform support for the {pre,un}multiplied arrays. 509 asImageBuffer()->transformColorSpace(m_resultColorSpace, dstColorSpace); 510 511#if ENABLE(OPENCL) 512 } 513#endif 514 515 m_resultColorSpace = dstColorSpace; 516 517 if (m_unmultipliedImageResult) 518 m_unmultipliedImageResult.clear(); 519 if (m_premultipliedImageResult) 520 m_premultipliedImageResult.clear(); 521#endif 522} 523 524TextStream& FilterEffect::externalRepresentation(TextStream& ts, int) const 525{ 526 // FIXME: We should dump the subRegions of the filter primitives here later. This isn't 527 // possible at the moment, because we need more detailed informations from the target object. 528 return ts; 529} 530 531} // namespace WebCore 532 533#endif // ENABLE(FILTERS) 534