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 "core/platform/graphics/filters/FEComposite.h" 28 29 #include "SkArithmeticMode.h" 30 #include "SkFlattenableBuffers.h" 31 #include "SkXfermodeImageFilter.h" 32 33 #include "core/platform/graphics/GraphicsContext.h" 34 #include "core/platform/graphics/cpu/arm/filters/FECompositeArithmeticNEON.h" 35 #include "core/platform/graphics/filters/Filter.h" 36 #include "core/platform/text/TextStream.h" 37 #include "core/rendering/RenderTreeAsText.h" 38 #include "third_party/skia/include/core/SkDevice.h" 39 40 #include "wtf/Uint8ClampedArray.h" 41 42 #include "core/platform/graphics/filters/SkiaImageFilterBuilder.h" 43 44 namespace WebCore { 45 46 class CompositeImageFilter : public SkImageFilter { 47 public: 48 CompositeImageFilter(SkXfermode::Mode mode, SkImageFilter* background, SkImageFilter* foreground) : SkImageFilter(background, foreground), m_mode(mode) 49 { 50 } 51 52 virtual bool onFilterImage(Proxy* proxy, const SkBitmap& src, const SkMatrix& ctm, SkBitmap* dst, SkIPoint* offset) 53 { 54 SkBitmap background = src; 55 SkBitmap foreground = src; 56 SkImageFilter* backgroundInput = getInput(0); 57 SkImageFilter* foregroundInput = getInput(1); 58 SkIPoint backgroundOffset = SkIPoint::Make(0, 0), foregroundOffset = SkIPoint::Make(0, 0); 59 if (backgroundInput && !backgroundInput->filterImage(proxy, src, ctm, &background, &backgroundOffset)) 60 return false; 61 62 if (foregroundInput && !foregroundInput->filterImage(proxy, src, ctm, &foreground, &foregroundOffset)) 63 return false; 64 65 SkAutoTUnref<SkDevice> device(proxy->createDevice(background.width(), background.height())); 66 SkCanvas canvas(device); 67 SkPaint paint; 68 paint.setXfermodeMode(SkXfermode::kSrc_Mode); 69 canvas.drawBitmap(background, backgroundOffset.fX, backgroundOffset.fY, &paint); 70 paint.setXfermodeMode(m_mode); 71 canvas.drawBitmap(foreground, foregroundOffset.fX, foregroundOffset.fY, &paint); 72 *dst = device->accessBitmap(false); 73 return true; 74 } 75 76 SK_DECLARE_PUBLIC_FLATTENABLE_DESERIALIZATION_PROCS(CompositeImageFilter) 77 78 protected: 79 explicit CompositeImageFilter(SkFlattenableReadBuffer& buffer) 80 : SkImageFilter(buffer) 81 { 82 m_mode = (SkXfermode::Mode) buffer.readInt(); 83 } 84 85 virtual void flatten(SkFlattenableWriteBuffer& buffer) const 86 { 87 this->SkImageFilter::flatten(buffer); 88 buffer.writeInt((int) m_mode); 89 } 90 91 private: 92 SkXfermode::Mode m_mode; 93 }; 94 95 FEComposite::FEComposite(Filter* filter, const CompositeOperationType& type, float k1, float k2, float k3, float k4) 96 : FilterEffect(filter) 97 , m_type(type) 98 , m_k1(k1) 99 , m_k2(k2) 100 , m_k3(k3) 101 , m_k4(k4) 102 { 103 } 104 105 PassRefPtr<FEComposite> FEComposite::create(Filter* filter, const CompositeOperationType& type, float k1, float k2, float k3, float k4) 106 { 107 return adoptRef(new FEComposite(filter, type, k1, k2, k3, k4)); 108 } 109 110 CompositeOperationType FEComposite::operation() const 111 { 112 return m_type; 113 } 114 115 bool FEComposite::setOperation(CompositeOperationType type) 116 { 117 if (m_type == type) 118 return false; 119 m_type = type; 120 return true; 121 } 122 123 float FEComposite::k1() const 124 { 125 return m_k1; 126 } 127 128 bool FEComposite::setK1(float k1) 129 { 130 if (m_k1 == k1) 131 return false; 132 m_k1 = k1; 133 return true; 134 } 135 136 float FEComposite::k2() const 137 { 138 return m_k2; 139 } 140 141 bool FEComposite::setK2(float k2) 142 { 143 if (m_k2 == k2) 144 return false; 145 m_k2 = k2; 146 return true; 147 } 148 149 float FEComposite::k3() const 150 { 151 return m_k3; 152 } 153 154 bool FEComposite::setK3(float k3) 155 { 156 if (m_k3 == k3) 157 return false; 158 m_k3 = k3; 159 return true; 160 } 161 162 float FEComposite::k4() const 163 { 164 return m_k4; 165 } 166 167 bool FEComposite::setK4(float k4) 168 { 169 if (m_k4 == k4) 170 return false; 171 m_k4 = k4; 172 return true; 173 } 174 175 void FEComposite::correctFilterResultIfNeeded() 176 { 177 if (m_type != FECOMPOSITE_OPERATOR_ARITHMETIC) 178 return; 179 180 forceValidPreMultipliedPixels(); 181 } 182 183 template <int b1, int b4> 184 static inline void computeArithmeticPixels(unsigned char* source, unsigned char* destination, int pixelArrayLength, 185 float k1, float k2, float k3, float k4) 186 { 187 float scaledK1; 188 float scaledK4; 189 if (b1) 190 scaledK1 = k1 / 255.0f; 191 if (b4) 192 scaledK4 = k4 * 255.0f; 193 194 while (--pixelArrayLength >= 0) { 195 unsigned char i1 = *source; 196 unsigned char i2 = *destination; 197 float result = k2 * i1 + k3 * i2; 198 if (b1) 199 result += scaledK1 * i1 * i2; 200 if (b4) 201 result += scaledK4; 202 203 if (result <= 0) 204 *destination = 0; 205 else if (result >= 255) 206 *destination = 255; 207 else 208 *destination = result; 209 ++source; 210 ++destination; 211 } 212 } 213 214 // computeArithmeticPixelsUnclamped is a faster version of computeArithmeticPixels for the common case where clamping 215 // is not necessary. This enables aggresive compiler optimizations such as auto-vectorization. 216 template <int b1, int b4> 217 static inline void computeArithmeticPixelsUnclamped(unsigned char* source, unsigned char* destination, int pixelArrayLength, float k1, float k2, float k3, float k4) 218 { 219 float scaledK1; 220 float scaledK4; 221 if (b1) 222 scaledK1 = k1 / 255.0f; 223 if (b4) 224 scaledK4 = k4 * 255.0f; 225 226 while (--pixelArrayLength >= 0) { 227 unsigned char i1 = *source; 228 unsigned char i2 = *destination; 229 float result = k2 * i1 + k3 * i2; 230 if (b1) 231 result += scaledK1 * i1 * i2; 232 if (b4) 233 result += scaledK4; 234 235 *destination = result; 236 ++source; 237 ++destination; 238 } 239 } 240 241 static inline void arithmeticSoftware(unsigned char* source, unsigned char* destination, int pixelArrayLength, float k1, float k2, float k3, float k4) 242 { 243 float upperLimit = std::max(0.0f, k1) + std::max(0.0f, k2) + std::max(0.0f, k3) + k4; 244 float lowerLimit = std::min(0.0f, k1) + std::min(0.0f, k2) + std::min(0.0f, k3) + k4; 245 if ((k4 >= 0.0f && k4 <= 1.0f) && (upperLimit >= 0.0f && upperLimit <= 1.0f) && (lowerLimit >= 0.0f && lowerLimit <= 1.0f)) { 246 if (k4) { 247 if (k1) 248 computeArithmeticPixelsUnclamped<1, 1>(source, destination, pixelArrayLength, k1, k2, k3, k4); 249 else 250 computeArithmeticPixelsUnclamped<0, 1>(source, destination, pixelArrayLength, k1, k2, k3, k4); 251 } else { 252 if (k1) 253 computeArithmeticPixelsUnclamped<1, 0>(source, destination, pixelArrayLength, k1, k2, k3, k4); 254 else 255 computeArithmeticPixelsUnclamped<0, 0>(source, destination, pixelArrayLength, k1, k2, k3, k4); 256 } 257 return; 258 } 259 260 if (k4) { 261 if (k1) 262 computeArithmeticPixels<1, 1>(source, destination, pixelArrayLength, k1, k2, k3, k4); 263 else 264 computeArithmeticPixels<0, 1>(source, destination, pixelArrayLength, k1, k2, k3, k4); 265 } else { 266 if (k1) 267 computeArithmeticPixels<1, 0>(source, destination, pixelArrayLength, k1, k2, k3, k4); 268 else 269 computeArithmeticPixels<0, 0>(source, destination, pixelArrayLength, k1, k2, k3, k4); 270 } 271 } 272 273 inline void FEComposite::platformArithmeticSoftware(Uint8ClampedArray* source, Uint8ClampedArray* destination, 274 float k1, float k2, float k3, float k4) 275 { 276 int length = source->length(); 277 ASSERT(length == static_cast<int>(destination->length())); 278 // The selection here eventually should happen dynamically. 279 #if HAVE(ARM_NEON_INTRINSICS) 280 ASSERT(!(length & 0x3)); 281 platformArithmeticNeon(source->data(), destination->data(), length, k1, k2, k3, k4); 282 #else 283 arithmeticSoftware(source->data(), destination->data(), length, k1, k2, k3, k4); 284 #endif 285 } 286 287 void FEComposite::determineAbsolutePaintRect() 288 { 289 switch (m_type) { 290 case FECOMPOSITE_OPERATOR_IN: 291 case FECOMPOSITE_OPERATOR_ATOP: 292 // For In and Atop the first effect just influences the result of 293 // the second effect. So just use the absolute paint rect of the second effect here. 294 setAbsolutePaintRect(inputEffect(1)->absolutePaintRect()); 295 return; 296 case FECOMPOSITE_OPERATOR_ARITHMETIC: 297 // Arithmetic may influnce the compele filter primitive region. So we can't 298 // optimize the paint region here. 299 setAbsolutePaintRect(enclosingIntRect(maxEffectRect())); 300 return; 301 default: 302 // Take the union of both input effects. 303 FilterEffect::determineAbsolutePaintRect(); 304 return; 305 } 306 } 307 308 void FEComposite::applySoftware() 309 { 310 FilterEffect* in = inputEffect(0); 311 FilterEffect* in2 = inputEffect(1); 312 313 if (m_type == FECOMPOSITE_OPERATOR_ARITHMETIC) { 314 Uint8ClampedArray* dstPixelArray = createPremultipliedImageResult(); 315 if (!dstPixelArray) 316 return; 317 318 IntRect effectADrawingRect = requestedRegionOfInputImageData(in->absolutePaintRect()); 319 RefPtr<Uint8ClampedArray> srcPixelArray = in->asPremultipliedImage(effectADrawingRect); 320 321 IntRect effectBDrawingRect = requestedRegionOfInputImageData(in2->absolutePaintRect()); 322 in2->copyPremultipliedImage(dstPixelArray, effectBDrawingRect); 323 324 platformArithmeticSoftware(srcPixelArray.get(), dstPixelArray, m_k1, m_k2, m_k3, m_k4); 325 return; 326 } 327 328 ImageBuffer* resultImage = createImageBufferResult(); 329 if (!resultImage) 330 return; 331 GraphicsContext* filterContext = resultImage->context(); 332 333 ImageBuffer* imageBuffer = in->asImageBuffer(); 334 ImageBuffer* imageBuffer2 = in2->asImageBuffer(); 335 ASSERT(imageBuffer); 336 ASSERT(imageBuffer2); 337 338 switch (m_type) { 339 case FECOMPOSITE_OPERATOR_OVER: 340 filterContext->drawImageBuffer(imageBuffer2, drawingRegionOfInputImage(in2->absolutePaintRect())); 341 filterContext->drawImageBuffer(imageBuffer, drawingRegionOfInputImage(in->absolutePaintRect())); 342 break; 343 case FECOMPOSITE_OPERATOR_IN: { 344 // Applies only to the intersected region. 345 IntRect destinationRect = in->absolutePaintRect(); 346 destinationRect.intersect(in2->absolutePaintRect()); 347 destinationRect.intersect(absolutePaintRect()); 348 if (destinationRect.isEmpty()) 349 break; 350 IntPoint destinationPoint(destinationRect.x() - absolutePaintRect().x(), destinationRect.y() - absolutePaintRect().y()); 351 IntRect sourceRect(IntPoint(destinationRect.x() - in->absolutePaintRect().x(), 352 destinationRect.y() - in->absolutePaintRect().y()), destinationRect.size()); 353 IntRect source2Rect(IntPoint(destinationRect.x() - in2->absolutePaintRect().x(), 354 destinationRect.y() - in2->absolutePaintRect().y()), destinationRect.size()); 355 filterContext->drawImageBuffer(imageBuffer2, destinationPoint, source2Rect); 356 filterContext->drawImageBuffer(imageBuffer, destinationPoint, sourceRect, CompositeSourceIn); 357 break; 358 } 359 case FECOMPOSITE_OPERATOR_OUT: 360 filterContext->drawImageBuffer(imageBuffer, drawingRegionOfInputImage(in->absolutePaintRect())); 361 filterContext->drawImageBuffer(imageBuffer2, drawingRegionOfInputImage(in2->absolutePaintRect()), IntRect(IntPoint(), imageBuffer2->logicalSize()), CompositeDestinationOut); 362 break; 363 case FECOMPOSITE_OPERATOR_ATOP: 364 filterContext->drawImageBuffer(imageBuffer2, drawingRegionOfInputImage(in2->absolutePaintRect())); 365 filterContext->drawImageBuffer(imageBuffer, drawingRegionOfInputImage(in->absolutePaintRect()), IntRect(IntPoint(), imageBuffer->logicalSize()), CompositeSourceAtop); 366 break; 367 case FECOMPOSITE_OPERATOR_XOR: 368 filterContext->drawImageBuffer(imageBuffer2, drawingRegionOfInputImage(in2->absolutePaintRect())); 369 filterContext->drawImageBuffer(imageBuffer, drawingRegionOfInputImage(in->absolutePaintRect()), IntRect(IntPoint(), imageBuffer->logicalSize()), CompositeXOR); 370 break; 371 default: 372 break; 373 } 374 } 375 376 SkXfermode::Mode toXfermode(WebCore::CompositeOperationType mode) 377 { 378 switch (mode) { 379 case WebCore::FECOMPOSITE_OPERATOR_OVER: 380 return SkXfermode::kSrcOver_Mode; 381 case WebCore::FECOMPOSITE_OPERATOR_IN: 382 return SkXfermode::kSrcIn_Mode; 383 case WebCore::FECOMPOSITE_OPERATOR_OUT: 384 return SkXfermode::kSrcOut_Mode; 385 case WebCore::FECOMPOSITE_OPERATOR_ATOP: 386 return SkXfermode::kSrcATop_Mode; 387 case WebCore::FECOMPOSITE_OPERATOR_XOR: 388 return SkXfermode::kXor_Mode; 389 default: 390 ASSERT_NOT_REACHED(); 391 return SkXfermode::kSrcOver_Mode; 392 } 393 } 394 395 PassRefPtr<SkImageFilter> FEComposite::createImageFilter(SkiaImageFilterBuilder* builder) 396 { 397 RefPtr<SkImageFilter> foreground(builder->build(inputEffect(0), operatingColorSpace())); 398 RefPtr<SkImageFilter> background(builder->build(inputEffect(1), operatingColorSpace())); 399 if (m_type == FECOMPOSITE_OPERATOR_ARITHMETIC) { 400 SkAutoTUnref<SkXfermode> mode(SkArithmeticMode::Create(SkFloatToScalar(m_k1), SkFloatToScalar(m_k2), SkFloatToScalar(m_k3), SkFloatToScalar(m_k4))); 401 return adoptRef(new SkXfermodeImageFilter(mode, background.get(), foreground.get())); 402 } 403 return adoptRef(new CompositeImageFilter(toXfermode(m_type), background.get(), foreground.get())); 404 } 405 406 static TextStream& operator<<(TextStream& ts, const CompositeOperationType& type) 407 { 408 switch (type) { 409 case FECOMPOSITE_OPERATOR_UNKNOWN: 410 ts << "UNKNOWN"; 411 break; 412 case FECOMPOSITE_OPERATOR_OVER: 413 ts << "OVER"; 414 break; 415 case FECOMPOSITE_OPERATOR_IN: 416 ts << "IN"; 417 break; 418 case FECOMPOSITE_OPERATOR_OUT: 419 ts << "OUT"; 420 break; 421 case FECOMPOSITE_OPERATOR_ATOP: 422 ts << "ATOP"; 423 break; 424 case FECOMPOSITE_OPERATOR_XOR: 425 ts << "XOR"; 426 break; 427 case FECOMPOSITE_OPERATOR_ARITHMETIC: 428 ts << "ARITHMETIC"; 429 break; 430 } 431 return ts; 432 } 433 434 TextStream& FEComposite::externalRepresentation(TextStream& ts, int indent) const 435 { 436 writeIndent(ts, indent); 437 ts << "[feComposite"; 438 FilterEffect::externalRepresentation(ts); 439 ts << " operation=\"" << m_type << "\""; 440 if (m_type == FECOMPOSITE_OPERATOR_ARITHMETIC) 441 ts << " k1=\"" << m_k1 << "\" k2=\"" << m_k2 << "\" k3=\"" << m_k3 << "\" k4=\"" << m_k4 << "\""; 442 ts << "]\n"; 443 inputEffect(0)->externalRepresentation(ts, indent + 1); 444 inputEffect(1)->externalRepresentation(ts, indent + 1); 445 return ts; 446 } 447 448 } // namespace WebCore 449