1 /* 2 * Copyright (C) 2008 Alex Mathews <possessedpenguinbob (at) gmail.com> 3 * Copyright (C) 2009 Dirk Schulze <krit (at) 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 #include "platform/graphics/gpu/AcceleratedImageBufferSurface.h" 32 33 #if HAVE(ARM_NEON_INTRINSICS) 34 #include <arm_neon.h> 35 #endif 36 37 namespace WebCore { 38 39 FilterEffect::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 53 FilterEffect::~FilterEffect() 54 { 55 } 56 57 inline 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 65 void 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 80 FloatRect FilterEffect::mapRectRecursive(const FloatRect& rect) 81 { 82 FloatRect result; 83 if (m_inputEffects.size() > 0) { 84 result = m_inputEffects.at(0)->mapRectRecursive(rect); 85 for (unsigned i = 1; i < m_inputEffects.size(); ++i) 86 result.unite(m_inputEffects.at(i)->mapRectRecursive(rect)); 87 } else 88 result = rect; 89 return mapRect(result); 90 } 91 92 FloatRect FilterEffect::getSourceRect(const FloatRect& destRect, const FloatRect& destClipRect) 93 { 94 FloatRect sourceRect = mapRect(destRect, false); 95 FloatRect sourceClipRect = mapRect(destClipRect, false); 96 97 FloatRect boundaries = effectBoundaries(); 98 if (hasX()) 99 sourceClipRect.setX(boundaries.x()); 100 if (hasY()) 101 sourceClipRect.setY(boundaries.y()); 102 if (hasWidth()) 103 sourceClipRect.setWidth(boundaries.width()); 104 if (hasHeight()) 105 sourceClipRect.setHeight(boundaries.height()); 106 107 FloatRect result; 108 if (m_inputEffects.size() > 0) { 109 result = m_inputEffects.at(0)->getSourceRect(sourceRect, sourceClipRect); 110 for (unsigned i = 1; i < m_inputEffects.size(); ++i) 111 result.unite(m_inputEffects.at(i)->getSourceRect(sourceRect, sourceClipRect)); 112 } else { 113 result = sourceRect; 114 result.intersect(sourceClipRect); 115 } 116 return result; 117 } 118 119 IntRect FilterEffect::requestedRegionOfInputImageData(const IntRect& effectRect) const 120 { 121 ASSERT(hasResult()); 122 IntPoint location = m_absolutePaintRect.location(); 123 location.moveBy(-effectRect.location()); 124 return IntRect(location, m_absolutePaintRect.size()); 125 } 126 127 IntRect FilterEffect::drawingRegionOfInputImage(const IntRect& srcRect) const 128 { 129 return IntRect(IntPoint(srcRect.x() - m_absolutePaintRect.x(), 130 srcRect.y() - m_absolutePaintRect.y()), srcRect.size()); 131 } 132 133 FilterEffect* FilterEffect::inputEffect(unsigned number) const 134 { 135 ASSERT_WITH_SECURITY_IMPLICATION(number < m_inputEffects.size()); 136 return m_inputEffects.at(number).get(); 137 } 138 139 void FilterEffect::apply() 140 { 141 if (hasResult()) 142 return; 143 unsigned size = m_inputEffects.size(); 144 for (unsigned i = 0; i < size; ++i) { 145 FilterEffect* in = m_inputEffects.at(i).get(); 146 in->apply(); 147 if (!in->hasResult()) 148 return; 149 150 // Convert input results to the current effect's color space. 151 transformResultColorSpace(in, i); 152 } 153 154 determineAbsolutePaintRect(); 155 setResultColorSpace(m_operatingColorSpace); 156 157 if (!isFilterSizeValid(m_absolutePaintRect)) 158 return; 159 160 if (requiresValidPreMultipliedPixels()) { 161 for (unsigned i = 0; i < size; ++i) 162 inputEffect(i)->correctFilterResultIfNeeded(); 163 } 164 165 if (applySkia()) 166 return; 167 168 applySoftware(); 169 } 170 171 void FilterEffect::forceValidPreMultipliedPixels() 172 { 173 // Must operate on pre-multiplied results; other formats cannot have invalid pixels. 174 if (!m_premultipliedImageResult) 175 return; 176 177 Uint8ClampedArray* imageArray = m_premultipliedImageResult.get(); 178 unsigned char* pixelData = imageArray->data(); 179 int pixelArrayLength = imageArray->length(); 180 181 // We must have four bytes per pixel, and complete pixels 182 ASSERT(!(pixelArrayLength % 4)); 183 184 #if HAVE(ARM_NEON_INTRINSICS) 185 if (pixelArrayLength >= 64) { 186 unsigned char* lastPixel = pixelData + (pixelArrayLength & ~0x3f); 187 do { 188 // Increments pixelData by 64. 189 uint8x16x4_t sixteenPixels = vld4q_u8(pixelData); 190 sixteenPixels.val[0] = vminq_u8(sixteenPixels.val[0], sixteenPixels.val[3]); 191 sixteenPixels.val[1] = vminq_u8(sixteenPixels.val[1], sixteenPixels.val[3]); 192 sixteenPixels.val[2] = vminq_u8(sixteenPixels.val[2], sixteenPixels.val[3]); 193 vst4q_u8(pixelData, sixteenPixels); 194 pixelData += 64; 195 } while (pixelData < lastPixel); 196 197 pixelArrayLength &= 0x3f; 198 if (!pixelArrayLength) 199 return; 200 } 201 #endif 202 203 int numPixels = pixelArrayLength / 4; 204 205 // Iterate over each pixel, checking alpha and adjusting color components if necessary 206 while (--numPixels >= 0) { 207 // Alpha is the 4th byte in a pixel 208 unsigned char a = *(pixelData + 3); 209 // Clamp each component to alpha, and increment the pixel location 210 for (int i = 0; i < 3; ++i) { 211 if (*pixelData > a) 212 *pixelData = a; 213 ++pixelData; 214 } 215 // Increment for alpha 216 ++pixelData; 217 } 218 } 219 220 void FilterEffect::clearResult() 221 { 222 if (m_imageBufferResult) 223 m_imageBufferResult.clear(); 224 if (m_unmultipliedImageResult) 225 m_unmultipliedImageResult.clear(); 226 if (m_premultipliedImageResult) 227 m_premultipliedImageResult.clear(); 228 } 229 230 void FilterEffect::clearResultsRecursive() 231 { 232 // Clear all results, regardless that the current effect has 233 // a result. Can be used if an effect is in an erroneous state. 234 if (hasResult()) 235 clearResult(); 236 237 unsigned size = m_inputEffects.size(); 238 for (unsigned i = 0; i < size; ++i) 239 m_inputEffects.at(i).get()->clearResultsRecursive(); 240 } 241 242 ImageBuffer* FilterEffect::asImageBuffer() 243 { 244 if (!hasResult()) 245 return 0; 246 if (m_imageBufferResult) 247 return m_imageBufferResult.get(); 248 OwnPtr<ImageBufferSurface> surface; 249 if (m_filter->isAccelerated()) 250 surface = adoptPtr(new AcceleratedImageBufferSurface(m_absolutePaintRect.size())); 251 if (!m_filter->isAccelerated() || !surface->isValid()) 252 surface = adoptPtr(new UnacceleratedImageBufferSurface(m_absolutePaintRect.size())); 253 m_imageBufferResult = ImageBuffer::create(surface.release()); 254 if (!m_imageBufferResult) 255 return 0; 256 257 IntRect destinationRect(IntPoint(), m_absolutePaintRect.size()); 258 if (m_premultipliedImageResult) 259 m_imageBufferResult->putByteArray(Premultiplied, m_premultipliedImageResult.get(), destinationRect.size(), destinationRect, IntPoint()); 260 else 261 m_imageBufferResult->putByteArray(Unmultiplied, m_unmultipliedImageResult.get(), destinationRect.size(), destinationRect, IntPoint()); 262 return m_imageBufferResult.get(); 263 } 264 265 PassRefPtr<Uint8ClampedArray> FilterEffect::asUnmultipliedImage(const IntRect& rect) 266 { 267 ASSERT(isFilterSizeValid(rect)); 268 RefPtr<Uint8ClampedArray> imageData = Uint8ClampedArray::createUninitialized(rect.width() * rect.height() * 4); 269 copyUnmultipliedImage(imageData.get(), rect); 270 return imageData.release(); 271 } 272 273 PassRefPtr<Uint8ClampedArray> FilterEffect::asPremultipliedImage(const IntRect& rect) 274 { 275 ASSERT(isFilterSizeValid(rect)); 276 RefPtr<Uint8ClampedArray> imageData = Uint8ClampedArray::createUninitialized(rect.width() * rect.height() * 4); 277 copyPremultipliedImage(imageData.get(), rect); 278 return imageData.release(); 279 } 280 281 inline void FilterEffect::copyImageBytes(Uint8ClampedArray* source, Uint8ClampedArray* destination, const IntRect& rect) 282 { 283 // Initialize the destination to transparent black, if not entirely covered by the source. 284 if (rect.x() < 0 || rect.y() < 0 || rect.maxX() > m_absolutePaintRect.width() || rect.maxY() > m_absolutePaintRect.height()) 285 memset(destination->data(), 0, destination->length()); 286 287 // Early return if the rect does not intersect with the source. 288 if (rect.maxX() <= 0 || rect.maxY() <= 0 || rect.x() >= m_absolutePaintRect.width() || rect.y() >= m_absolutePaintRect.height()) 289 return; 290 291 int xOrigin = rect.x(); 292 int xDest = 0; 293 if (xOrigin < 0) { 294 xDest = -xOrigin; 295 xOrigin = 0; 296 } 297 int xEnd = rect.maxX(); 298 if (xEnd > m_absolutePaintRect.width()) 299 xEnd = m_absolutePaintRect.width(); 300 301 int yOrigin = rect.y(); 302 int yDest = 0; 303 if (yOrigin < 0) { 304 yDest = -yOrigin; 305 yOrigin = 0; 306 } 307 int yEnd = rect.maxY(); 308 if (yEnd > m_absolutePaintRect.height()) 309 yEnd = m_absolutePaintRect.height(); 310 311 int size = (xEnd - xOrigin) * 4; 312 int destinationScanline = rect.width() * 4; 313 int sourceScanline = m_absolutePaintRect.width() * 4; 314 unsigned char *destinationPixel = destination->data() + ((yDest * rect.width()) + xDest) * 4; 315 unsigned char *sourcePixel = source->data() + ((yOrigin * m_absolutePaintRect.width()) + xOrigin) * 4; 316 317 while (yOrigin < yEnd) { 318 memcpy(destinationPixel, sourcePixel, size); 319 destinationPixel += destinationScanline; 320 sourcePixel += sourceScanline; 321 ++yOrigin; 322 } 323 } 324 325 void FilterEffect::copyUnmultipliedImage(Uint8ClampedArray* destination, const IntRect& rect) 326 { 327 ASSERT(hasResult()); 328 329 if (!m_unmultipliedImageResult) { 330 // We prefer a conversion from the image buffer. 331 if (m_imageBufferResult) 332 m_unmultipliedImageResult = m_imageBufferResult->getUnmultipliedImageData(IntRect(IntPoint(), m_absolutePaintRect.size())); 333 else { 334 ASSERT(isFilterSizeValid(m_absolutePaintRect)); 335 m_unmultipliedImageResult = Uint8ClampedArray::createUninitialized(m_absolutePaintRect.width() * m_absolutePaintRect.height() * 4); 336 unsigned char* sourceComponent = m_premultipliedImageResult->data(); 337 unsigned char* destinationComponent = m_unmultipliedImageResult->data(); 338 unsigned char* end = sourceComponent + (m_absolutePaintRect.width() * m_absolutePaintRect.height() * 4); 339 while (sourceComponent < end) { 340 int alpha = sourceComponent[3]; 341 if (alpha) { 342 destinationComponent[0] = static_cast<int>(sourceComponent[0]) * 255 / alpha; 343 destinationComponent[1] = static_cast<int>(sourceComponent[1]) * 255 / alpha; 344 destinationComponent[2] = static_cast<int>(sourceComponent[2]) * 255 / alpha; 345 } else { 346 destinationComponent[0] = 0; 347 destinationComponent[1] = 0; 348 destinationComponent[2] = 0; 349 } 350 destinationComponent[3] = alpha; 351 sourceComponent += 4; 352 destinationComponent += 4; 353 } 354 } 355 } 356 copyImageBytes(m_unmultipliedImageResult.get(), destination, rect); 357 } 358 359 void FilterEffect::copyPremultipliedImage(Uint8ClampedArray* destination, const IntRect& rect) 360 { 361 ASSERT(hasResult()); 362 363 if (!m_premultipliedImageResult) { 364 // We prefer a conversion from the image buffer. 365 if (m_imageBufferResult) 366 m_premultipliedImageResult = m_imageBufferResult->getPremultipliedImageData(IntRect(IntPoint(), m_absolutePaintRect.size())); 367 else { 368 ASSERT(isFilterSizeValid(m_absolutePaintRect)); 369 m_premultipliedImageResult = Uint8ClampedArray::createUninitialized(m_absolutePaintRect.width() * m_absolutePaintRect.height() * 4); 370 unsigned char* sourceComponent = m_unmultipliedImageResult->data(); 371 unsigned char* destinationComponent = m_premultipliedImageResult->data(); 372 unsigned char* end = sourceComponent + (m_absolutePaintRect.width() * m_absolutePaintRect.height() * 4); 373 while (sourceComponent < end) { 374 int alpha = sourceComponent[3]; 375 destinationComponent[0] = static_cast<int>(sourceComponent[0]) * alpha / 255; 376 destinationComponent[1] = static_cast<int>(sourceComponent[1]) * alpha / 255; 377 destinationComponent[2] = static_cast<int>(sourceComponent[2]) * alpha / 255; 378 destinationComponent[3] = alpha; 379 sourceComponent += 4; 380 destinationComponent += 4; 381 } 382 } 383 } 384 copyImageBytes(m_premultipliedImageResult.get(), destination, rect); 385 } 386 387 ImageBuffer* FilterEffect::createImageBufferResult() 388 { 389 // Only one result type is allowed. 390 if (m_absolutePaintRect.isEmpty()) 391 return 0; 392 OwnPtr<ImageBufferSurface> surface; 393 if (m_filter->isAccelerated()) 394 surface = adoptPtr(new AcceleratedImageBufferSurface(m_absolutePaintRect.size())); 395 if (!m_filter->isAccelerated() || !surface->isValid()) 396 surface = adoptPtr(new UnacceleratedImageBufferSurface(m_absolutePaintRect.size())); 397 m_imageBufferResult = ImageBuffer::create(surface.release()); 398 return m_imageBufferResult.get(); 399 } 400 401 Uint8ClampedArray* FilterEffect::createUnmultipliedImageResult() 402 { 403 // Only one result type is allowed. 404 ASSERT(!hasResult()); 405 ASSERT(isFilterSizeValid(m_absolutePaintRect)); 406 407 if (m_absolutePaintRect.isEmpty()) 408 return 0; 409 m_unmultipliedImageResult = Uint8ClampedArray::createUninitialized(m_absolutePaintRect.width() * m_absolutePaintRect.height() * 4); 410 return m_unmultipliedImageResult.get(); 411 } 412 413 Uint8ClampedArray* FilterEffect::createPremultipliedImageResult() 414 { 415 // Only one result type is allowed. 416 ASSERT(!hasResult()); 417 ASSERT(isFilterSizeValid(m_absolutePaintRect)); 418 419 if (m_absolutePaintRect.isEmpty()) 420 return 0; 421 m_premultipliedImageResult = Uint8ClampedArray::createUninitialized(m_absolutePaintRect.width() * m_absolutePaintRect.height() * 4); 422 return m_premultipliedImageResult.get(); 423 } 424 425 void FilterEffect::transformResultColorSpace(ColorSpace dstColorSpace) 426 { 427 if (!hasResult() || dstColorSpace == m_resultColorSpace) 428 return; 429 430 // FIXME: We can avoid this potentially unnecessary ImageBuffer conversion by adding 431 // color space transform support for the {pre,un}multiplied arrays. 432 asImageBuffer()->transformColorSpace(m_resultColorSpace, dstColorSpace); 433 434 m_resultColorSpace = dstColorSpace; 435 436 if (m_unmultipliedImageResult) 437 m_unmultipliedImageResult.clear(); 438 if (m_premultipliedImageResult) 439 m_premultipliedImageResult.clear(); 440 } 441 442 TextStream& FilterEffect::externalRepresentation(TextStream& ts, int) const 443 { 444 // FIXME: We should dump the subRegions of the filter primitives here later. This isn't 445 // possible at the moment, because we need more detailed informations from the target object. 446 return ts; 447 } 448 449 FloatRect FilterEffect::determineFilterPrimitiveSubregion(DetermineSubregionFlags flags) 450 { 451 ASSERT(filter()); 452 453 // FETile, FETurbulence, FEFlood don't have input effects, take the filter region as unite rect. 454 FloatRect subregion; 455 if (unsigned numberOfInputEffects = inputEffects().size()) { 456 subregion = inputEffect(0)->determineFilterPrimitiveSubregion(flags); 457 for (unsigned i = 1; i < numberOfInputEffects; ++i) 458 subregion.unite(inputEffect(i)->determineFilterPrimitiveSubregion(flags)); 459 } else 460 subregion = filter()->filterRegion(); 461 462 // After calling determineFilterPrimitiveSubregion on the target effect, reset the subregion again for <feTile>. 463 if (filterEffectType() == FilterEffectTypeTile) 464 subregion = filter()->filterRegion(); 465 466 if (flags & MapRectForward) 467 subregion = mapRect(subregion); 468 469 FloatRect boundaries = effectBoundaries(); 470 if (hasX()) 471 subregion.setX(boundaries.x()); 472 if (hasY()) 473 subregion.setY(boundaries.y()); 474 if (hasWidth()) 475 subregion.setWidth(boundaries.width()); 476 if (hasHeight()) 477 subregion.setHeight(boundaries.height()); 478 479 setFilterPrimitiveSubregion(subregion); 480 481 FloatRect absoluteSubregion = filter()->absoluteTransform().mapRect(subregion); 482 FloatSize filterResolution = filter()->filterResolution(); 483 absoluteSubregion.scale(filterResolution.width(), filterResolution.height()); 484 485 // Clip every filter effect to the filter region. 486 if (flags & ClipToFilterRegion) { 487 FloatRect absoluteScaledFilterRegion = filter()->absoluteFilterRegion(); 488 absoluteScaledFilterRegion.scale(filterResolution.width(), filterResolution.height()); 489 absoluteSubregion.intersect(absoluteScaledFilterRegion); 490 } 491 492 setMaxEffectRect(absoluteSubregion); 493 return subregion; 494 } 495 496 PassRefPtr<SkImageFilter> FilterEffect::createImageFilter(SkiaImageFilterBuilder* builder) 497 { 498 return 0; 499 } 500 501 SkImageFilter::CropRect FilterEffect::getCropRect(const FloatSize& cropOffset) const 502 { 503 SkRect rect = SkRect::MakeEmpty(); 504 uint32_t flags = 0; 505 FloatRect boundaries = effectBoundaries(); 506 FloatSize resolution = filter()->filterResolution(); 507 boundaries.scale(resolution.width(), resolution.height()); 508 boundaries.move(cropOffset); 509 if (hasX()) { 510 rect.fLeft = boundaries.x(); 511 flags |= SkImageFilter::CropRect::kHasLeft_CropEdge; 512 } 513 if (hasY()) { 514 rect.fTop = boundaries.y(); 515 flags |= SkImageFilter::CropRect::kHasTop_CropEdge; 516 } 517 if (hasWidth()) { 518 rect.fRight = rect.fLeft + boundaries.width(); 519 flags |= SkImageFilter::CropRect::kHasRight_CropEdge; 520 } 521 if (hasHeight()) { 522 rect.fBottom = rect.fTop + boundaries.height(); 523 flags |= SkImageFilter::CropRect::kHasBottom_CropEdge; 524 } 525 return SkImageFilter::CropRect(rect, flags); 526 } 527 528 } // namespace WebCore 529