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