1/* 2 * Copyright (C) 2004, 2005, 2006, 2007 Nikolas Zimmermann <zimmermann@kde.org> 3 * Copyright (C) 2004, 2005 Rob Buis <buis@kde.org> 4 * Copyright (C) 2005 Eric Seidel <eric@webkit.org> 5 * Copyright (C) 2009 Dirk Schulze <krit@webkit.org> 6 * Copyright (C) Research In Motion Limited 2010. 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#if ENABLE(FILTERS) 27#include "FEComposite.h" 28 29#include "FECompositeArithmeticNEON.h" 30#include "Filter.h" 31#include "GraphicsContext.h" 32#include "TextStream.h" 33 34#include <runtime/Uint8ClampedArray.h> 35 36namespace WebCore { 37 38FEComposite::FEComposite(Filter* filter, const CompositeOperationType& type, float k1, float k2, float k3, float k4) 39 : FilterEffect(filter) 40 , m_type(type) 41 , m_k1(k1) 42 , m_k2(k2) 43 , m_k3(k3) 44 , m_k4(k4) 45{ 46} 47 48PassRefPtr<FEComposite> FEComposite::create(Filter* filter, const CompositeOperationType& type, float k1, float k2, float k3, float k4) 49{ 50 return adoptRef(new FEComposite(filter, type, k1, k2, k3, k4)); 51} 52 53CompositeOperationType FEComposite::operation() const 54{ 55 return m_type; 56} 57 58bool FEComposite::setOperation(CompositeOperationType type) 59{ 60 if (m_type == type) 61 return false; 62 m_type = type; 63 return true; 64} 65 66float FEComposite::k1() const 67{ 68 return m_k1; 69} 70 71bool FEComposite::setK1(float k1) 72{ 73 if (m_k1 == k1) 74 return false; 75 m_k1 = k1; 76 return true; 77} 78 79float FEComposite::k2() const 80{ 81 return m_k2; 82} 83 84bool FEComposite::setK2(float k2) 85{ 86 if (m_k2 == k2) 87 return false; 88 m_k2 = k2; 89 return true; 90} 91 92float FEComposite::k3() const 93{ 94 return m_k3; 95} 96 97bool FEComposite::setK3(float k3) 98{ 99 if (m_k3 == k3) 100 return false; 101 m_k3 = k3; 102 return true; 103} 104 105float FEComposite::k4() const 106{ 107 return m_k4; 108} 109 110bool FEComposite::setK4(float k4) 111{ 112 if (m_k4 == k4) 113 return false; 114 m_k4 = k4; 115 return true; 116} 117 118void FEComposite::correctFilterResultIfNeeded() 119{ 120 if (m_type != FECOMPOSITE_OPERATOR_ARITHMETIC) 121 return; 122 123 forceValidPreMultipliedPixels(); 124} 125 126template <int b1, int b4> 127static inline void computeArithmeticPixels(unsigned char* source, unsigned char* destination, int pixelArrayLength, 128 float k1, float k2, float k3, float k4) 129{ 130 float scaledK1; 131 float scaledK4; 132 if (b1) 133 scaledK1 = k1 / 255.0f; 134 if (b4) 135 scaledK4 = k4 * 255.0f; 136 137 while (--pixelArrayLength >= 0) { 138 unsigned char i1 = *source; 139 unsigned char i2 = *destination; 140 float result = k2 * i1 + k3 * i2; 141 if (b1) 142 result += scaledK1 * i1 * i2; 143 if (b4) 144 result += scaledK4; 145 146 if (result <= 0) 147 *destination = 0; 148 else if (result >= 255) 149 *destination = 255; 150 else 151 *destination = result; 152 ++source; 153 ++destination; 154 } 155} 156 157// computeArithmeticPixelsUnclamped is a faster version of computeArithmeticPixels for the common case where clamping 158// is not necessary. This enables aggresive compiler optimizations such as auto-vectorization. 159template <int b1, int b4> 160static inline void computeArithmeticPixelsUnclamped(unsigned char* source, unsigned char* destination, int pixelArrayLength, float k1, float k2, float k3, float k4) 161{ 162 float scaledK1; 163 float scaledK4; 164 if (b1) 165 scaledK1 = k1 / 255.0f; 166 if (b4) 167 scaledK4 = k4 * 255.0f; 168 169 while (--pixelArrayLength >= 0) { 170 unsigned char i1 = *source; 171 unsigned char i2 = *destination; 172 float result = k2 * i1 + k3 * i2; 173 if (b1) 174 result += scaledK1 * i1 * i2; 175 if (b4) 176 result += scaledK4; 177 178 *destination = result; 179 ++source; 180 ++destination; 181 } 182} 183 184#if !HAVE(ARM_NEON_INTRINSICS) 185static inline void arithmeticSoftware(unsigned char* source, unsigned char* destination, int pixelArrayLength, float k1, float k2, float k3, float k4) 186{ 187 float upperLimit = std::max(0.0f, k1) + std::max(0.0f, k2) + std::max(0.0f, k3) + k4; 188 float lowerLimit = std::min(0.0f, k1) + std::min(0.0f, k2) + std::min(0.0f, k3) + k4; 189 if ((k4 >= 0.0f && k4 <= 1.0f) && (upperLimit >= 0.0f && upperLimit <= 1.0f) && (lowerLimit >= 0.0f && lowerLimit <= 1.0f)) { 190 if (k4) { 191 if (k1) 192 computeArithmeticPixelsUnclamped<1, 1>(source, destination, pixelArrayLength, k1, k2, k3, k4); 193 else 194 computeArithmeticPixelsUnclamped<0, 1>(source, destination, pixelArrayLength, k1, k2, k3, k4); 195 } else { 196 if (k1) 197 computeArithmeticPixelsUnclamped<1, 0>(source, destination, pixelArrayLength, k1, k2, k3, k4); 198 else 199 computeArithmeticPixelsUnclamped<0, 0>(source, destination, pixelArrayLength, k1, k2, k3, k4); 200 } 201 return; 202 } 203 204 if (k4) { 205 if (k1) 206 computeArithmeticPixels<1, 1>(source, destination, pixelArrayLength, k1, k2, k3, k4); 207 else 208 computeArithmeticPixels<0, 1>(source, destination, pixelArrayLength, k1, k2, k3, k4); 209 } else { 210 if (k1) 211 computeArithmeticPixels<1, 0>(source, destination, pixelArrayLength, k1, k2, k3, k4); 212 else 213 computeArithmeticPixels<0, 0>(source, destination, pixelArrayLength, k1, k2, k3, k4); 214 } 215} 216#endif 217 218inline void FEComposite::platformArithmeticSoftware(Uint8ClampedArray* source, Uint8ClampedArray* destination, 219 float k1, float k2, float k3, float k4) 220{ 221 int length = source->length(); 222 ASSERT(length == static_cast<int>(destination->length())); 223 // The selection here eventually should happen dynamically. 224#if HAVE(ARM_NEON_INTRINSICS) 225 ASSERT(!(length & 0x3)); 226 platformArithmeticNeon(source->data(), destination->data(), length, k1, k2, k3, k4); 227#else 228 arithmeticSoftware(source->data(), destination->data(), length, k1, k2, k3, k4); 229#endif 230} 231 232void FEComposite::determineAbsolutePaintRect() 233{ 234 switch (m_type) { 235 case FECOMPOSITE_OPERATOR_IN: 236 case FECOMPOSITE_OPERATOR_ATOP: 237 // For In and Atop the first effect just influences the result of 238 // the second effect. So just use the absolute paint rect of the second effect here. 239 setAbsolutePaintRect(inputEffect(1)->absolutePaintRect()); 240 return; 241 case FECOMPOSITE_OPERATOR_ARITHMETIC: 242 // Arithmetic may influnce the compele filter primitive region. So we can't 243 // optimize the paint region here. 244 setAbsolutePaintRect(enclosingIntRect(maxEffectRect())); 245 return; 246 default: 247 // Take the union of both input effects. 248 FilterEffect::determineAbsolutePaintRect(); 249 return; 250 } 251} 252 253void FEComposite::platformApplySoftware() 254{ 255 FilterEffect* in = inputEffect(0); 256 FilterEffect* in2 = inputEffect(1); 257 258 if (m_type == FECOMPOSITE_OPERATOR_ARITHMETIC) { 259 Uint8ClampedArray* dstPixelArray = createPremultipliedImageResult(); 260 if (!dstPixelArray) 261 return; 262 263 IntRect effectADrawingRect = requestedRegionOfInputImageData(in->absolutePaintRect()); 264 RefPtr<Uint8ClampedArray> srcPixelArray = in->asPremultipliedImage(effectADrawingRect); 265 266 IntRect effectBDrawingRect = requestedRegionOfInputImageData(in2->absolutePaintRect()); 267 in2->copyPremultipliedImage(dstPixelArray, effectBDrawingRect); 268 269 platformArithmeticSoftware(srcPixelArray.get(), dstPixelArray, m_k1, m_k2, m_k3, m_k4); 270 return; 271 } 272 273 ImageBuffer* resultImage = createImageBufferResult(); 274 if (!resultImage) 275 return; 276 GraphicsContext* filterContext = resultImage->context(); 277 278 ImageBuffer* imageBuffer = in->asImageBuffer(); 279 ImageBuffer* imageBuffer2 = in2->asImageBuffer(); 280 ASSERT(imageBuffer); 281 ASSERT(imageBuffer2); 282 283 switch (m_type) { 284 case FECOMPOSITE_OPERATOR_OVER: 285 filterContext->drawImageBuffer(imageBuffer2, ColorSpaceDeviceRGB, drawingRegionOfInputImage(in2->absolutePaintRect())); 286 filterContext->drawImageBuffer(imageBuffer, ColorSpaceDeviceRGB, drawingRegionOfInputImage(in->absolutePaintRect())); 287 break; 288 case FECOMPOSITE_OPERATOR_IN: { 289 // Applies only to the intersected region. 290 IntRect destinationRect = in->absolutePaintRect(); 291 destinationRect.intersect(in2->absolutePaintRect()); 292 destinationRect.intersect(absolutePaintRect()); 293 if (destinationRect.isEmpty()) 294 break; 295 IntPoint destinationPoint(destinationRect.x() - absolutePaintRect().x(), destinationRect.y() - absolutePaintRect().y()); 296 IntRect sourceRect(IntPoint(destinationRect.x() - in->absolutePaintRect().x(), 297 destinationRect.y() - in->absolutePaintRect().y()), destinationRect.size()); 298 IntRect source2Rect(IntPoint(destinationRect.x() - in2->absolutePaintRect().x(), 299 destinationRect.y() - in2->absolutePaintRect().y()), destinationRect.size()); 300 filterContext->drawImageBuffer(imageBuffer2, ColorSpaceDeviceRGB, IntRect(destinationPoint, source2Rect.size()), source2Rect); 301 filterContext->drawImageBuffer(imageBuffer, ColorSpaceDeviceRGB, IntRect(destinationPoint, sourceRect.size()), sourceRect, CompositeSourceIn); 302 break; 303 } 304 case FECOMPOSITE_OPERATOR_OUT: 305 filterContext->drawImageBuffer(imageBuffer, ColorSpaceDeviceRGB, drawingRegionOfInputImage(in->absolutePaintRect())); 306 filterContext->drawImageBuffer(imageBuffer2, ColorSpaceDeviceRGB, drawingRegionOfInputImage(in2->absolutePaintRect()), IntRect(IntPoint(), imageBuffer2->logicalSize()), CompositeDestinationOut); 307 break; 308 case FECOMPOSITE_OPERATOR_ATOP: 309 filterContext->drawImageBuffer(imageBuffer2, ColorSpaceDeviceRGB, drawingRegionOfInputImage(in2->absolutePaintRect())); 310 filterContext->drawImageBuffer(imageBuffer, ColorSpaceDeviceRGB, drawingRegionOfInputImage(in->absolutePaintRect()), IntRect(IntPoint(), imageBuffer->logicalSize()), CompositeSourceAtop); 311 break; 312 case FECOMPOSITE_OPERATOR_XOR: 313 filterContext->drawImageBuffer(imageBuffer2, ColorSpaceDeviceRGB, drawingRegionOfInputImage(in2->absolutePaintRect())); 314 filterContext->drawImageBuffer(imageBuffer, ColorSpaceDeviceRGB, drawingRegionOfInputImage(in->absolutePaintRect()), IntRect(IntPoint(), imageBuffer->logicalSize()), CompositeXOR); 315 break; 316 default: 317 break; 318 } 319} 320 321void FEComposite::dump() 322{ 323} 324 325static TextStream& operator<<(TextStream& ts, const CompositeOperationType& type) 326{ 327 switch (type) { 328 case FECOMPOSITE_OPERATOR_UNKNOWN: 329 ts << "UNKNOWN"; 330 break; 331 case FECOMPOSITE_OPERATOR_OVER: 332 ts << "OVER"; 333 break; 334 case FECOMPOSITE_OPERATOR_IN: 335 ts << "IN"; 336 break; 337 case FECOMPOSITE_OPERATOR_OUT: 338 ts << "OUT"; 339 break; 340 case FECOMPOSITE_OPERATOR_ATOP: 341 ts << "ATOP"; 342 break; 343 case FECOMPOSITE_OPERATOR_XOR: 344 ts << "XOR"; 345 break; 346 case FECOMPOSITE_OPERATOR_ARITHMETIC: 347 ts << "ARITHMETIC"; 348 break; 349 } 350 return ts; 351} 352 353TextStream& FEComposite::externalRepresentation(TextStream& ts, int indent) const 354{ 355 writeIndent(ts, indent); 356 ts << "[feComposite"; 357 FilterEffect::externalRepresentation(ts); 358 ts << " operation=\"" << m_type << "\""; 359 if (m_type == FECOMPOSITE_OPERATOR_ARITHMETIC) 360 ts << " k1=\"" << m_k1 << "\" k2=\"" << m_k2 << "\" k3=\"" << m_k3 << "\" k4=\"" << m_k4 << "\""; 361 ts << "]\n"; 362 inputEffect(0)->externalRepresentation(ts, indent + 1); 363 inputEffect(1)->externalRepresentation(ts, indent + 1); 364 return ts; 365} 366 367} // namespace WebCore 368 369#endif // ENABLE(FILTERS) 370