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 "core/platform/graphics/filters/FilterEffect.h" 27 28 #include "core/platform/graphics/ImageBuffer.h" 29 #include "core/platform/graphics/filters/Filter.h" 30 #include "third_party/skia/include/core/SkImageFilter.h" 31 #include "wtf/Uint8ClampedArray.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 m_imageBufferResult = ImageBuffer::create(m_absolutePaintRect.size(), 1, m_filter->renderingMode()); 249 IntRect destinationRect(IntPoint(), m_absolutePaintRect.size()); 250 if (m_premultipliedImageResult) 251 m_imageBufferResult->putByteArray(Premultiplied, m_premultipliedImageResult.get(), destinationRect.size(), destinationRect, IntPoint()); 252 else 253 m_imageBufferResult->putByteArray(Unmultiplied, m_unmultipliedImageResult.get(), destinationRect.size(), destinationRect, IntPoint()); 254 return m_imageBufferResult.get(); 255 } 256 257 PassRefPtr<Uint8ClampedArray> FilterEffect::asUnmultipliedImage(const IntRect& rect) 258 { 259 ASSERT(isFilterSizeValid(rect)); 260 RefPtr<Uint8ClampedArray> imageData = Uint8ClampedArray::createUninitialized(rect.width() * rect.height() * 4); 261 copyUnmultipliedImage(imageData.get(), rect); 262 return imageData.release(); 263 } 264 265 PassRefPtr<Uint8ClampedArray> FilterEffect::asPremultipliedImage(const IntRect& rect) 266 { 267 ASSERT(isFilterSizeValid(rect)); 268 RefPtr<Uint8ClampedArray> imageData = Uint8ClampedArray::createUninitialized(rect.width() * rect.height() * 4); 269 copyPremultipliedImage(imageData.get(), rect); 270 return imageData.release(); 271 } 272 273 inline void FilterEffect::copyImageBytes(Uint8ClampedArray* source, Uint8ClampedArray* destination, const IntRect& rect) 274 { 275 // Initialize the destination to transparent black, if not entirely covered by the source. 276 if (rect.x() < 0 || rect.y() < 0 || rect.maxX() > m_absolutePaintRect.width() || rect.maxY() > m_absolutePaintRect.height()) 277 memset(destination->data(), 0, destination->length()); 278 279 // Early return if the rect does not intersect with the source. 280 if (rect.maxX() <= 0 || rect.maxY() <= 0 || rect.x() >= m_absolutePaintRect.width() || rect.y() >= m_absolutePaintRect.height()) 281 return; 282 283 int xOrigin = rect.x(); 284 int xDest = 0; 285 if (xOrigin < 0) { 286 xDest = -xOrigin; 287 xOrigin = 0; 288 } 289 int xEnd = rect.maxX(); 290 if (xEnd > m_absolutePaintRect.width()) 291 xEnd = m_absolutePaintRect.width(); 292 293 int yOrigin = rect.y(); 294 int yDest = 0; 295 if (yOrigin < 0) { 296 yDest = -yOrigin; 297 yOrigin = 0; 298 } 299 int yEnd = rect.maxY(); 300 if (yEnd > m_absolutePaintRect.height()) 301 yEnd = m_absolutePaintRect.height(); 302 303 int size = (xEnd - xOrigin) * 4; 304 int destinationScanline = rect.width() * 4; 305 int sourceScanline = m_absolutePaintRect.width() * 4; 306 unsigned char *destinationPixel = destination->data() + ((yDest * rect.width()) + xDest) * 4; 307 unsigned char *sourcePixel = source->data() + ((yOrigin * m_absolutePaintRect.width()) + xOrigin) * 4; 308 309 while (yOrigin < yEnd) { 310 memcpy(destinationPixel, sourcePixel, size); 311 destinationPixel += destinationScanline; 312 sourcePixel += sourceScanline; 313 ++yOrigin; 314 } 315 } 316 317 void FilterEffect::copyUnmultipliedImage(Uint8ClampedArray* destination, const IntRect& rect) 318 { 319 ASSERT(hasResult()); 320 321 if (!m_unmultipliedImageResult) { 322 // We prefer a conversion from the image buffer. 323 if (m_imageBufferResult) 324 m_unmultipliedImageResult = m_imageBufferResult->getUnmultipliedImageData(IntRect(IntPoint(), m_absolutePaintRect.size())); 325 else { 326 ASSERT(isFilterSizeValid(m_absolutePaintRect)); 327 m_unmultipliedImageResult = Uint8ClampedArray::createUninitialized(m_absolutePaintRect.width() * m_absolutePaintRect.height() * 4); 328 unsigned char* sourceComponent = m_premultipliedImageResult->data(); 329 unsigned char* destinationComponent = m_unmultipliedImageResult->data(); 330 unsigned char* end = sourceComponent + (m_absolutePaintRect.width() * m_absolutePaintRect.height() * 4); 331 while (sourceComponent < end) { 332 int alpha = sourceComponent[3]; 333 if (alpha) { 334 destinationComponent[0] = static_cast<int>(sourceComponent[0]) * 255 / alpha; 335 destinationComponent[1] = static_cast<int>(sourceComponent[1]) * 255 / alpha; 336 destinationComponent[2] = static_cast<int>(sourceComponent[2]) * 255 / alpha; 337 } else { 338 destinationComponent[0] = 0; 339 destinationComponent[1] = 0; 340 destinationComponent[2] = 0; 341 } 342 destinationComponent[3] = alpha; 343 sourceComponent += 4; 344 destinationComponent += 4; 345 } 346 } 347 } 348 copyImageBytes(m_unmultipliedImageResult.get(), destination, rect); 349 } 350 351 void FilterEffect::copyPremultipliedImage(Uint8ClampedArray* destination, const IntRect& rect) 352 { 353 ASSERT(hasResult()); 354 355 if (!m_premultipliedImageResult) { 356 // We prefer a conversion from the image buffer. 357 if (m_imageBufferResult) 358 m_premultipliedImageResult = m_imageBufferResult->getPremultipliedImageData(IntRect(IntPoint(), m_absolutePaintRect.size())); 359 else { 360 ASSERT(isFilterSizeValid(m_absolutePaintRect)); 361 m_premultipliedImageResult = Uint8ClampedArray::createUninitialized(m_absolutePaintRect.width() * m_absolutePaintRect.height() * 4); 362 unsigned char* sourceComponent = m_unmultipliedImageResult->data(); 363 unsigned char* destinationComponent = m_premultipliedImageResult->data(); 364 unsigned char* end = sourceComponent + (m_absolutePaintRect.width() * m_absolutePaintRect.height() * 4); 365 while (sourceComponent < end) { 366 int alpha = sourceComponent[3]; 367 destinationComponent[0] = static_cast<int>(sourceComponent[0]) * alpha / 255; 368 destinationComponent[1] = static_cast<int>(sourceComponent[1]) * alpha / 255; 369 destinationComponent[2] = static_cast<int>(sourceComponent[2]) * alpha / 255; 370 destinationComponent[3] = alpha; 371 sourceComponent += 4; 372 destinationComponent += 4; 373 } 374 } 375 } 376 copyImageBytes(m_premultipliedImageResult.get(), destination, rect); 377 } 378 379 ImageBuffer* FilterEffect::createImageBufferResult() 380 { 381 // Only one result type is allowed. 382 ASSERT(!hasResult()); 383 if (m_absolutePaintRect.isEmpty()) 384 return 0; 385 m_imageBufferResult = ImageBuffer::create(m_absolutePaintRect.size(), 1, m_filter->renderingMode()); 386 if (!m_imageBufferResult) 387 return 0; 388 ASSERT(m_imageBufferResult->context()); 389 return m_imageBufferResult.get(); 390 } 391 392 Uint8ClampedArray* FilterEffect::createUnmultipliedImageResult() 393 { 394 // Only one result type is allowed. 395 ASSERT(!hasResult()); 396 ASSERT(isFilterSizeValid(m_absolutePaintRect)); 397 398 if (m_absolutePaintRect.isEmpty()) 399 return 0; 400 m_unmultipliedImageResult = Uint8ClampedArray::createUninitialized(m_absolutePaintRect.width() * m_absolutePaintRect.height() * 4); 401 return m_unmultipliedImageResult.get(); 402 } 403 404 Uint8ClampedArray* FilterEffect::createPremultipliedImageResult() 405 { 406 // Only one result type is allowed. 407 ASSERT(!hasResult()); 408 ASSERT(isFilterSizeValid(m_absolutePaintRect)); 409 410 if (m_absolutePaintRect.isEmpty()) 411 return 0; 412 m_premultipliedImageResult = Uint8ClampedArray::createUninitialized(m_absolutePaintRect.width() * m_absolutePaintRect.height() * 4); 413 return m_premultipliedImageResult.get(); 414 } 415 416 void FilterEffect::transformResultColorSpace(ColorSpace dstColorSpace) 417 { 418 if (!hasResult() || dstColorSpace == m_resultColorSpace) 419 return; 420 421 // FIXME: We can avoid this potentially unnecessary ImageBuffer conversion by adding 422 // color space transform support for the {pre,un}multiplied arrays. 423 asImageBuffer()->transformColorSpace(m_resultColorSpace, dstColorSpace); 424 425 m_resultColorSpace = dstColorSpace; 426 427 if (m_unmultipliedImageResult) 428 m_unmultipliedImageResult.clear(); 429 if (m_premultipliedImageResult) 430 m_premultipliedImageResult.clear(); 431 } 432 433 TextStream& FilterEffect::externalRepresentation(TextStream& ts, int) const 434 { 435 // FIXME: We should dump the subRegions of the filter primitives here later. This isn't 436 // possible at the moment, because we need more detailed informations from the target object. 437 return ts; 438 } 439 440 FloatRect FilterEffect::determineFilterPrimitiveSubregion() 441 { 442 ASSERT(filter()); 443 444 // FETile, FETurbulence, FEFlood don't have input effects, take the filter region as unite rect. 445 FloatRect subregion; 446 if (unsigned numberOfInputEffects = inputEffects().size()) { 447 subregion = inputEffect(0)->determineFilterPrimitiveSubregion(); 448 for (unsigned i = 1; i < numberOfInputEffects; ++i) 449 subregion.unite(inputEffect(i)->determineFilterPrimitiveSubregion()); 450 } else 451 subregion = filter()->filterRegion(); 452 453 // After calling determineFilterPrimitiveSubregion on the target effect, reset the subregion again for <feTile>. 454 if (filterEffectType() == FilterEffectTypeTile) 455 subregion = filter()->filterRegion(); 456 457 subregion = mapRect(subregion); 458 459 FloatRect boundaries = effectBoundaries(); 460 if (hasX()) 461 subregion.setX(boundaries.x()); 462 if (hasY()) 463 subregion.setY(boundaries.y()); 464 if (hasWidth()) 465 subregion.setWidth(boundaries.width()); 466 if (hasHeight()) 467 subregion.setHeight(boundaries.height()); 468 469 setFilterPrimitiveSubregion(subregion); 470 471 FloatRect absoluteSubregion = filter()->absoluteTransform().mapRect(subregion); 472 FloatSize filterResolution = filter()->filterResolution(); 473 absoluteSubregion.scale(filterResolution.width(), filterResolution.height()); 474 475 setMaxEffectRect(absoluteSubregion); 476 return subregion; 477 } 478 479 PassRefPtr<SkImageFilter> FilterEffect::createImageFilter(SkiaImageFilterBuilder* builder) 480 { 481 return 0; 482 } 483 484 } // namespace WebCore 485