1 /* 2 * Copyright (C) 2004, 2005, 2006, 2007 Nikolas Zimmermann <zimmermann (at) kde.org> 3 * Copyright (C) 2004, 2005 Rob Buis <buis (at) kde.org> 4 * Copyright (C) 2005 Eric Seidel <eric (at) webkit.org> 5 * Copyright (C) 2009 Dirk Schulze <krit (at) webkit.org> 6 * Copyright (C) Research In Motion Limited 2010. All rights reserved. 7 * Copyright (C) 2013 Google Inc. All rights reserved. 8 * 9 * This library is free software; you can redistribute it and/or 10 * modify it under the terms of the GNU Library General Public 11 * License as published by the Free Software Foundation; either 12 * version 2 of the License, or (at your option) any later version. 13 * 14 * This library is distributed in the hope that it will be useful, 15 * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 17 * Library General Public License for more details. 18 * 19 * You should have received a copy of the GNU Library General Public License 20 * along with this library; see the file COPYING.LIB. If not, write to 21 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 22 * Boston, MA 02110-1301, USA. 23 */ 24 25 #include "config.h" 26 27 #include "platform/graphics/filters/FEComposite.h" 28 29 #include "SkArithmeticMode.h" 30 #include "SkXfermodeImageFilter.h" 31 32 #include "platform/graphics/GraphicsContext.h" 33 #include "platform/graphics/cpu/arm/filters/FECompositeArithmeticNEON.h" 34 #include "platform/graphics/filters/SkiaImageFilterBuilder.h" 35 #include "platform/text/TextStream.h" 36 #include "third_party/skia/include/core/SkDevice.h" 37 38 #include "wtf/Uint8ClampedArray.h" 39 40 namespace blink { 41 42 FEComposite::FEComposite(Filter* filter, const CompositeOperationType& type, float k1, float k2, float k3, float k4) 43 : FilterEffect(filter) 44 , m_type(type) 45 , m_k1(k1) 46 , m_k2(k2) 47 , m_k3(k3) 48 , m_k4(k4) 49 { 50 } 51 52 PassRefPtr<FEComposite> FEComposite::create(Filter* filter, const CompositeOperationType& type, float k1, float k2, float k3, float k4) 53 { 54 return adoptRef(new FEComposite(filter, type, k1, k2, k3, k4)); 55 } 56 57 CompositeOperationType FEComposite::operation() const 58 { 59 return m_type; 60 } 61 62 bool FEComposite::setOperation(CompositeOperationType type) 63 { 64 if (m_type == type) 65 return false; 66 m_type = type; 67 return true; 68 } 69 70 float FEComposite::k1() const 71 { 72 return m_k1; 73 } 74 75 bool FEComposite::setK1(float k1) 76 { 77 if (m_k1 == k1) 78 return false; 79 m_k1 = k1; 80 return true; 81 } 82 83 float FEComposite::k2() const 84 { 85 return m_k2; 86 } 87 88 bool FEComposite::setK2(float k2) 89 { 90 if (m_k2 == k2) 91 return false; 92 m_k2 = k2; 93 return true; 94 } 95 96 float FEComposite::k3() const 97 { 98 return m_k3; 99 } 100 101 bool FEComposite::setK3(float k3) 102 { 103 if (m_k3 == k3) 104 return false; 105 m_k3 = k3; 106 return true; 107 } 108 109 float FEComposite::k4() const 110 { 111 return m_k4; 112 } 113 114 bool FEComposite::setK4(float k4) 115 { 116 if (m_k4 == k4) 117 return false; 118 m_k4 = k4; 119 return true; 120 } 121 122 void FEComposite::correctFilterResultIfNeeded() 123 { 124 if (m_type != FECOMPOSITE_OPERATOR_ARITHMETIC) 125 return; 126 127 forceValidPreMultipliedPixels(); 128 } 129 130 template <int b1, int b4> 131 static inline void computeArithmeticPixels(unsigned char* source, unsigned char* destination, int pixelArrayLength, 132 float k1, float k2, float k3, float k4) 133 { 134 float scaledK1; 135 float scaledK4; 136 if (b1) 137 scaledK1 = k1 / 255.0f; 138 if (b4) 139 scaledK4 = k4 * 255.0f; 140 141 while (--pixelArrayLength >= 0) { 142 unsigned char i1 = *source; 143 unsigned char i2 = *destination; 144 float result = k2 * i1 + k3 * i2; 145 if (b1) 146 result += scaledK1 * i1 * i2; 147 if (b4) 148 result += scaledK4; 149 150 if (result <= 0) 151 *destination = 0; 152 else if (result >= 255) 153 *destination = 255; 154 else 155 *destination = result; 156 ++source; 157 ++destination; 158 } 159 } 160 161 // computeArithmeticPixelsUnclamped is a faster version of computeArithmeticPixels for the common case where clamping 162 // is not necessary. This enables aggresive compiler optimizations such as auto-vectorization. 163 template <int b1, int b4> 164 static inline void computeArithmeticPixelsUnclamped(unsigned char* source, unsigned char* destination, int pixelArrayLength, float k1, float k2, float k3, float k4) 165 { 166 float scaledK1; 167 float scaledK4; 168 if (b1) 169 scaledK1 = k1 / 255.0f; 170 if (b4) 171 scaledK4 = k4 * 255.0f; 172 173 while (--pixelArrayLength >= 0) { 174 unsigned char i1 = *source; 175 unsigned char i2 = *destination; 176 float result = k2 * i1 + k3 * i2; 177 if (b1) 178 result += scaledK1 * i1 * i2; 179 if (b4) 180 result += scaledK4; 181 182 *destination = result; 183 ++source; 184 ++destination; 185 } 186 } 187 188 static inline void arithmeticSoftware(unsigned char* source, unsigned char* destination, int pixelArrayLength, float k1, float k2, float k3, float k4) 189 { 190 float upperLimit = std::max(0.0f, k1) + std::max(0.0f, k2) + std::max(0.0f, k3) + k4; 191 float lowerLimit = std::min(0.0f, k1) + std::min(0.0f, k2) + std::min(0.0f, k3) + k4; 192 if ((k4 >= 0.0f && k4 <= 1.0f) && (upperLimit >= 0.0f && upperLimit <= 1.0f) && (lowerLimit >= 0.0f && lowerLimit <= 1.0f)) { 193 if (k4) { 194 if (k1) 195 computeArithmeticPixelsUnclamped<1, 1>(source, destination, pixelArrayLength, k1, k2, k3, k4); 196 else 197 computeArithmeticPixelsUnclamped<0, 1>(source, destination, pixelArrayLength, k1, k2, k3, k4); 198 } else { 199 if (k1) 200 computeArithmeticPixelsUnclamped<1, 0>(source, destination, pixelArrayLength, k1, k2, k3, k4); 201 else 202 computeArithmeticPixelsUnclamped<0, 0>(source, destination, pixelArrayLength, k1, k2, k3, k4); 203 } 204 return; 205 } 206 207 if (k4) { 208 if (k1) 209 computeArithmeticPixels<1, 1>(source, destination, pixelArrayLength, k1, k2, k3, k4); 210 else 211 computeArithmeticPixels<0, 1>(source, destination, pixelArrayLength, k1, k2, k3, k4); 212 } else { 213 if (k1) 214 computeArithmeticPixels<1, 0>(source, destination, pixelArrayLength, k1, k2, k3, k4); 215 else 216 computeArithmeticPixels<0, 0>(source, destination, pixelArrayLength, k1, k2, k3, k4); 217 } 218 } 219 220 inline void FEComposite::platformArithmeticSoftware(Uint8ClampedArray* source, Uint8ClampedArray* destination, 221 float k1, float k2, float k3, float k4) 222 { 223 int length = source->length(); 224 ASSERT(length == static_cast<int>(destination->length())); 225 // The selection here eventually should happen dynamically. 226 #if HAVE(ARM_NEON_INTRINSICS) 227 ASSERT(!(length & 0x3)); 228 platformArithmeticNeon(source->data(), destination->data(), length, k1, k2, k3, k4); 229 #else 230 arithmeticSoftware(source->data(), destination->data(), length, k1, k2, k3, k4); 231 #endif 232 } 233 234 FloatRect FEComposite::determineAbsolutePaintRect(const FloatRect& originalRequestedRect) 235 { 236 FloatRect requestedRect = originalRequestedRect; 237 if (clipsToBounds()) 238 requestedRect.intersect(maxEffectRect()); 239 240 // We may be called multiple times if result is used more than once. Return 241 // quickly if nothing new is required. 242 if (absolutePaintRect().contains(enclosingIntRect(requestedRect))) 243 return requestedRect; 244 245 // No mapPaintRect required for FEComposite. 246 FloatRect input1Rect = inputEffect(1)->determineAbsolutePaintRect(requestedRect); 247 FloatRect affectedRect; 248 switch (m_type) { 249 case FECOMPOSITE_OPERATOR_IN: 250 // 'in' has output only in the intersection of both inputs. 251 affectedRect = intersection(input1Rect, inputEffect(0)->determineAbsolutePaintRect(input1Rect)); 252 break; 253 case FECOMPOSITE_OPERATOR_ATOP: 254 // 'atop' has output only in the extents of the second input. 255 // Make sure first input knows where it needs to produce output. 256 inputEffect(0)->determineAbsolutePaintRect(input1Rect); 257 affectedRect = input1Rect; 258 break; 259 case FECOMPOSITE_OPERATOR_ARITHMETIC: 260 if (k4() > 0) { 261 // Make sure first input knows where it needs to produce output. 262 inputEffect(0)->determineAbsolutePaintRect(requestedRect); 263 // Arithmetic with non-zero k4 may influnce the complete filter primitive 264 // region. So we can't optimize the paint region here. 265 affectedRect = requestedRect; 266 break; 267 } 268 if (k2() <= 0) { 269 // Input 0 does not appear where input 1 is not present. 270 FloatRect input0Rect = inputEffect(0)->determineAbsolutePaintRect(input1Rect); 271 if (k3() > 0) { 272 affectedRect = input1Rect; 273 } else { 274 // Just k1 is positive. Use intersection. 275 affectedRect = intersection(input1Rect, input0Rect); 276 } 277 break; 278 } 279 // else fall through to use union 280 default: 281 // Take the union of both input effects. 282 affectedRect = unionRect(input1Rect, inputEffect(0)->determineAbsolutePaintRect(requestedRect)); 283 break; 284 } 285 286 affectedRect.intersect(requestedRect); 287 addAbsolutePaintRect(affectedRect); 288 return affectedRect; 289 } 290 291 void FEComposite::applySoftware() 292 { 293 FilterEffect* in = inputEffect(0); 294 FilterEffect* in2 = inputEffect(1); 295 296 if (m_type == FECOMPOSITE_OPERATOR_ARITHMETIC) { 297 Uint8ClampedArray* dstPixelArray = createPremultipliedImageResult(); 298 if (!dstPixelArray) 299 return; 300 301 IntRect effectADrawingRect = requestedRegionOfInputImageData(in->absolutePaintRect()); 302 RefPtr<Uint8ClampedArray> srcPixelArray = in->asPremultipliedImage(effectADrawingRect); 303 304 IntRect effectBDrawingRect = requestedRegionOfInputImageData(in2->absolutePaintRect()); 305 in2->copyPremultipliedImage(dstPixelArray, effectBDrawingRect); 306 307 platformArithmeticSoftware(srcPixelArray.get(), dstPixelArray, m_k1, m_k2, m_k3, m_k4); 308 return; 309 } 310 311 ImageBuffer* resultImage = createImageBufferResult(); 312 if (!resultImage) 313 return; 314 GraphicsContext* filterContext = resultImage->context(); 315 316 ImageBuffer* imageBuffer = in->asImageBuffer(); 317 ImageBuffer* imageBuffer2 = in2->asImageBuffer(); 318 ASSERT(imageBuffer); 319 ASSERT(imageBuffer2); 320 321 switch (m_type) { 322 case FECOMPOSITE_OPERATOR_OVER: 323 filterContext->drawImageBuffer(imageBuffer2, drawingRegionOfInputImage(in2->absolutePaintRect())); 324 filterContext->drawImageBuffer(imageBuffer, drawingRegionOfInputImage(in->absolutePaintRect())); 325 break; 326 case FECOMPOSITE_OPERATOR_IN: { 327 // Applies only to the intersected region. 328 IntRect destinationRect = in->absolutePaintRect(); 329 destinationRect.intersect(in2->absolutePaintRect()); 330 destinationRect.intersect(absolutePaintRect()); 331 if (destinationRect.isEmpty()) 332 break; 333 FloatRect sourceRect(IntPoint(destinationRect.x() - in->absolutePaintRect().x(), 334 destinationRect.y() - in->absolutePaintRect().y()), destinationRect.size()); 335 FloatRect source2Rect(IntPoint(destinationRect.x() - in2->absolutePaintRect().x(), 336 destinationRect.y() - in2->absolutePaintRect().y()), destinationRect.size()); 337 destinationRect.move(-absolutePaintRect().x(), -absolutePaintRect().y()); 338 filterContext->drawImageBuffer(imageBuffer2, destinationRect, &source2Rect); 339 filterContext->drawImageBuffer(imageBuffer, destinationRect, &sourceRect, CompositeSourceIn); 340 break; 341 } 342 case FECOMPOSITE_OPERATOR_OUT: 343 filterContext->drawImageBuffer(imageBuffer, drawingRegionOfInputImage(in->absolutePaintRect())); 344 filterContext->drawImageBuffer(imageBuffer2, drawingRegionOfInputImage(in2->absolutePaintRect()), 0, CompositeDestinationOut); 345 break; 346 case FECOMPOSITE_OPERATOR_ATOP: 347 filterContext->drawImageBuffer(imageBuffer2, drawingRegionOfInputImage(in2->absolutePaintRect())); 348 filterContext->drawImageBuffer(imageBuffer, drawingRegionOfInputImage(in->absolutePaintRect()), 0, CompositeSourceAtop); 349 break; 350 case FECOMPOSITE_OPERATOR_XOR: 351 filterContext->drawImageBuffer(imageBuffer2, drawingRegionOfInputImage(in2->absolutePaintRect())); 352 filterContext->drawImageBuffer(imageBuffer, drawingRegionOfInputImage(in->absolutePaintRect()), 0, CompositeXOR); 353 break; 354 default: 355 break; 356 } 357 } 358 359 SkXfermode::Mode toXfermode(CompositeOperationType mode) 360 { 361 switch (mode) { 362 case FECOMPOSITE_OPERATOR_OVER: 363 return SkXfermode::kSrcOver_Mode; 364 case FECOMPOSITE_OPERATOR_IN: 365 return SkXfermode::kSrcIn_Mode; 366 case FECOMPOSITE_OPERATOR_OUT: 367 return SkXfermode::kSrcOut_Mode; 368 case FECOMPOSITE_OPERATOR_ATOP: 369 return SkXfermode::kSrcATop_Mode; 370 case FECOMPOSITE_OPERATOR_XOR: 371 return SkXfermode::kXor_Mode; 372 default: 373 ASSERT_NOT_REACHED(); 374 return SkXfermode::kSrcOver_Mode; 375 } 376 } 377 378 PassRefPtr<SkImageFilter> FEComposite::createImageFilter(SkiaImageFilterBuilder* builder) 379 { 380 return createImageFilterInternal(builder, true); 381 } 382 383 PassRefPtr<SkImageFilter> FEComposite::createImageFilterWithoutValidation(SkiaImageFilterBuilder* builder) 384 { 385 return createImageFilterInternal(builder, false); 386 } 387 388 PassRefPtr<SkImageFilter> FEComposite::createImageFilterInternal(SkiaImageFilterBuilder* builder, bool requiresPMColorValidation) 389 { 390 RefPtr<SkImageFilter> foreground(builder->build(inputEffect(0), operatingColorSpace(), !mayProduceInvalidPreMultipliedPixels())); 391 RefPtr<SkImageFilter> background(builder->build(inputEffect(1), operatingColorSpace(), !mayProduceInvalidPreMultipliedPixels())); 392 SkImageFilter::CropRect cropRect = getCropRect(builder->cropOffset()); 393 RefPtr<SkXfermode> mode; 394 if (m_type == FECOMPOSITE_OPERATOR_ARITHMETIC) 395 mode = adoptRef(SkArithmeticMode::Create(SkFloatToScalar(m_k1), SkFloatToScalar(m_k2), SkFloatToScalar(m_k3), SkFloatToScalar(m_k4), requiresPMColorValidation)); 396 else 397 mode = adoptRef(SkXfermode::Create(toXfermode(m_type))); 398 return adoptRef(SkXfermodeImageFilter::Create(mode.get(), background.get(), foreground.get(), &cropRect)); 399 } 400 401 static TextStream& operator<<(TextStream& ts, const CompositeOperationType& type) 402 { 403 switch (type) { 404 case FECOMPOSITE_OPERATOR_UNKNOWN: 405 ts << "UNKNOWN"; 406 break; 407 case FECOMPOSITE_OPERATOR_OVER: 408 ts << "OVER"; 409 break; 410 case FECOMPOSITE_OPERATOR_IN: 411 ts << "IN"; 412 break; 413 case FECOMPOSITE_OPERATOR_OUT: 414 ts << "OUT"; 415 break; 416 case FECOMPOSITE_OPERATOR_ATOP: 417 ts << "ATOP"; 418 break; 419 case FECOMPOSITE_OPERATOR_XOR: 420 ts << "XOR"; 421 break; 422 case FECOMPOSITE_OPERATOR_ARITHMETIC: 423 ts << "ARITHMETIC"; 424 break; 425 } 426 return ts; 427 } 428 429 TextStream& FEComposite::externalRepresentation(TextStream& ts, int indent) const 430 { 431 writeIndent(ts, indent); 432 ts << "[feComposite"; 433 FilterEffect::externalRepresentation(ts); 434 ts << " operation=\"" << m_type << "\""; 435 if (m_type == FECOMPOSITE_OPERATOR_ARITHMETIC) 436 ts << " k1=\"" << m_k1 << "\" k2=\"" << m_k2 << "\" k3=\"" << m_k3 << "\" k4=\"" << m_k4 << "\""; 437 ts << "]\n"; 438 inputEffect(0)->externalRepresentation(ts, indent + 1); 439 inputEffect(1)->externalRepresentation(ts, indent + 1); 440 return ts; 441 } 442 443 } // namespace blink 444