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) 2010 Zoltan Herczeg <zherczeg (at) webkit.org> 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 "FEConvolveMatrix.h" 28 29 #include "Filter.h" 30 #include "RenderTreeAsText.h" 31 #include "TextStream.h" 32 33 #include <wtf/ByteArray.h> 34 35 namespace WebCore { 36 37 FEConvolveMatrix::FEConvolveMatrix(Filter* filter, const IntSize& kernelSize, 38 float divisor, float bias, const IntPoint& targetOffset, EdgeModeType edgeMode, 39 const FloatPoint& kernelUnitLength, bool preserveAlpha, const Vector<float>& kernelMatrix) 40 : FilterEffect(filter) 41 , m_kernelSize(kernelSize) 42 , m_divisor(divisor) 43 , m_bias(bias) 44 , m_targetOffset(targetOffset) 45 , m_edgeMode(edgeMode) 46 , m_kernelUnitLength(kernelUnitLength) 47 , m_preserveAlpha(preserveAlpha) 48 , m_kernelMatrix(kernelMatrix) 49 { 50 } 51 52 PassRefPtr<FEConvolveMatrix> FEConvolveMatrix::create(Filter* filter, const IntSize& kernelSize, 53 float divisor, float bias, const IntPoint& targetOffset, EdgeModeType edgeMode, 54 const FloatPoint& kernelUnitLength, bool preserveAlpha, const Vector<float>& kernelMatrix) 55 { 56 return adoptRef(new FEConvolveMatrix(filter, kernelSize, divisor, bias, targetOffset, edgeMode, kernelUnitLength, 57 preserveAlpha, kernelMatrix)); 58 } 59 60 61 IntSize FEConvolveMatrix::kernelSize() const 62 { 63 return m_kernelSize; 64 } 65 66 void FEConvolveMatrix::setKernelSize(const IntSize& kernelSize) 67 { 68 m_kernelSize = kernelSize; 69 } 70 71 const Vector<float>& FEConvolveMatrix::kernel() const 72 { 73 return m_kernelMatrix; 74 } 75 76 void FEConvolveMatrix::setKernel(const Vector<float>& kernel) 77 { 78 m_kernelMatrix = kernel; 79 } 80 81 float FEConvolveMatrix::divisor() const 82 { 83 return m_divisor; 84 } 85 86 bool FEConvolveMatrix::setDivisor(float divisor) 87 { 88 if (m_divisor == divisor) 89 return false; 90 m_divisor = divisor; 91 return true; 92 } 93 94 float FEConvolveMatrix::bias() const 95 { 96 return m_bias; 97 } 98 99 bool FEConvolveMatrix::setBias(float bias) 100 { 101 if (m_bias == bias) 102 return false; 103 m_bias = bias; 104 return true; 105 } 106 107 IntPoint FEConvolveMatrix::targetOffset() const 108 { 109 return m_targetOffset; 110 } 111 112 bool FEConvolveMatrix::setTargetOffset(const IntPoint& targetOffset) 113 { 114 if (m_targetOffset == targetOffset) 115 return false; 116 m_targetOffset = targetOffset; 117 return true; 118 } 119 120 EdgeModeType FEConvolveMatrix::edgeMode() const 121 { 122 return m_edgeMode; 123 } 124 125 bool FEConvolveMatrix::setEdgeMode(EdgeModeType edgeMode) 126 { 127 if (m_edgeMode == edgeMode) 128 return false; 129 m_edgeMode = edgeMode; 130 return true; 131 } 132 133 FloatPoint FEConvolveMatrix::kernelUnitLength() const 134 { 135 return m_kernelUnitLength; 136 } 137 138 bool FEConvolveMatrix::setKernelUnitLength(const FloatPoint& kernelUnitLength) 139 { 140 if (m_kernelUnitLength == kernelUnitLength) 141 return false; 142 m_kernelUnitLength = kernelUnitLength; 143 return true; 144 } 145 146 bool FEConvolveMatrix::preserveAlpha() const 147 { 148 return m_preserveAlpha; 149 } 150 151 bool FEConvolveMatrix::setPreserveAlpha(bool preserveAlpha) 152 { 153 if (m_preserveAlpha == preserveAlpha) 154 return false; 155 m_preserveAlpha = preserveAlpha; 156 return true; 157 } 158 159 /* 160 ----------------------------------- 161 ConvolveMatrix implementation 162 ----------------------------------- 163 164 The image rectangle is split in the following way: 165 166 +---------------------+ 167 | A | 168 +---------------------+ 169 | | | | 170 | B | C | D | 171 | | | | 172 +---------------------+ 173 | E | 174 +---------------------+ 175 176 Where region C contains those pixels, whose values 177 can be calculated without crossing the edge of the rectangle. 178 179 Example: 180 Image size: width: 10, height: 10 181 182 Order (kernel matrix size): width: 3, height 4 183 Target: x:1, y:3 184 185 The following figure shows the target inside the kernel matrix: 186 187 ... 188 ... 189 ... 190 .X. 191 192 The regions in this case are the following: 193 Note: (x1, y1) top-left and (x2, y2) is the bottom-right corner 194 Note: row x2 and column y2 is not part of the region 195 only those (x, y) pixels, where x1 <= x < x2 and y1 <= y < y2 196 197 Region A: x1: 0, y1: 0, x2: 10, y2: 3 198 Region B: x1: 0, y1: 3, x2: 1, y2: 10 199 Region C: x1: 1, y1: 3, x2: 9, y2: 10 200 Region D: x1: 9, y1: 3, x2: 10, y2: 10 201 Region E: x1: 0, y1: 10, x2: 10, y2: 10 (empty region) 202 203 Since region C (often) contains most of the pixels, we implemented 204 a fast algoritm to calculate these values, called fastSetInteriorPixels. 205 For other regions, fastSetOuterPixels is used, which calls getPixelValue, 206 to handle pixels outside of the image. In a rare situations, when 207 kernel matrix is bigger than the image, all pixels are calculated by this 208 function. 209 210 Although these two functions have lot in common, I decided not to make 211 common a template for them, since there are key differences as well, 212 and would make it really hard to understand. 213 */ 214 215 static ALWAYS_INLINE unsigned char clampRGBAValue(float channel, unsigned char max = 255) 216 { 217 if (channel <= 0) 218 return 0; 219 if (channel >= max) 220 return max; 221 return channel; 222 } 223 224 template<bool preserveAlphaValues> 225 ALWAYS_INLINE void setDestinationPixels(ByteArray* image, int& pixel, float* totals, float divisor, float bias, ByteArray* src) 226 { 227 unsigned char maxAlpha = preserveAlphaValues ? 255 : clampRGBAValue(totals[3] / divisor + bias); 228 for (int i = 0; i < 3; ++i) 229 image->set(pixel++, clampRGBAValue(totals[i] / divisor + bias, maxAlpha)); 230 231 if (preserveAlphaValues) { 232 image->set(pixel, src->get(pixel)); 233 ++pixel; 234 } else 235 image->set(pixel++, maxAlpha); 236 } 237 238 // Only for region C 239 template<bool preserveAlphaValues> 240 ALWAYS_INLINE void FEConvolveMatrix::fastSetInteriorPixels(PaintingData& paintingData, int clipRight, int clipBottom) 241 { 242 // edge mode does not affect these pixels 243 int pixel = (m_targetOffset.y() * paintingData.width + m_targetOffset.x()) * 4; 244 int startKernelPixel = 0; 245 int kernelIncrease = clipRight * 4; 246 int xIncrease = (m_kernelSize.width() - 1) * 4; 247 // Contains the sum of rgb(a) components 248 float totals[3 + (preserveAlphaValues ? 0 : 1)]; 249 250 // m_divisor cannot be 0, SVGFEConvolveMatrixElement ensures this 251 ASSERT(m_divisor); 252 253 for (int y = clipBottom + 1; y > 0; --y) { 254 for (int x = clipRight + 1; x > 0; --x) { 255 int kernelValue = m_kernelMatrix.size() - 1; 256 int kernelPixel = startKernelPixel; 257 int width = m_kernelSize.width(); 258 259 totals[0] = 0; 260 totals[1] = 0; 261 totals[2] = 0; 262 if (!preserveAlphaValues) 263 totals[3] = 0; 264 265 while (kernelValue >= 0) { 266 totals[0] += m_kernelMatrix[kernelValue] * static_cast<float>(paintingData.srcPixelArray->get(kernelPixel++)); 267 totals[1] += m_kernelMatrix[kernelValue] * static_cast<float>(paintingData.srcPixelArray->get(kernelPixel++)); 268 totals[2] += m_kernelMatrix[kernelValue] * static_cast<float>(paintingData.srcPixelArray->get(kernelPixel++)); 269 if (!preserveAlphaValues) 270 totals[3] += m_kernelMatrix[kernelValue] * static_cast<float>(paintingData.srcPixelArray->get(kernelPixel)); 271 ++kernelPixel; 272 --kernelValue; 273 if (!--width) { 274 kernelPixel += kernelIncrease; 275 width = m_kernelSize.width(); 276 } 277 } 278 279 setDestinationPixels<preserveAlphaValues>(paintingData.dstPixelArray, pixel, totals, m_divisor, paintingData.bias, paintingData.srcPixelArray); 280 startKernelPixel += 4; 281 } 282 pixel += xIncrease; 283 startKernelPixel += xIncrease; 284 } 285 } 286 287 ALWAYS_INLINE int FEConvolveMatrix::getPixelValue(PaintingData& paintingData, int x, int y) 288 { 289 if (x >= 0 && x < paintingData.width && x >= 0 && y < paintingData.height) 290 return (y * paintingData.width + x) << 2; 291 292 switch (m_edgeMode) { 293 default: // EDGEMODE_NONE 294 return -1; 295 case EDGEMODE_DUPLICATE: 296 if (x < 0) 297 x = 0; 298 else if (x >= paintingData.width) 299 x = paintingData.width - 1; 300 if (y < 0) 301 y = 0; 302 else if (y >= paintingData.height) 303 y = paintingData.height - 1; 304 return (y * paintingData.width + x) << 2; 305 case EDGEMODE_WRAP: 306 while (x < 0) 307 x += paintingData.width; 308 x %= paintingData.width; 309 while (y < 0) 310 y += paintingData.height; 311 y %= paintingData.height; 312 return (y * paintingData.width + x) << 2; 313 } 314 } 315 316 // For other regions than C 317 template<bool preserveAlphaValues> 318 void FEConvolveMatrix::fastSetOuterPixels(PaintingData& paintingData, int x1, int y1, int x2, int y2) 319 { 320 int pixel = (y1 * paintingData.width + x1) * 4; 321 int height = y2 - y1; 322 int width = x2 - x1; 323 int beginKernelPixelX = x1 - m_targetOffset.x(); 324 int startKernelPixelX = beginKernelPixelX; 325 int startKernelPixelY = y1 - m_targetOffset.y(); 326 int xIncrease = (paintingData.width - width) * 4; 327 // Contains the sum of rgb(a) components 328 float totals[3 + (preserveAlphaValues ? 0 : 1)]; 329 330 // m_divisor cannot be 0, SVGFEConvolveMatrixElement ensures this 331 ASSERT(m_divisor); 332 333 for (int y = height; y > 0; --y) { 334 for (int x = width; x > 0; --x) { 335 int kernelValue = m_kernelMatrix.size() - 1; 336 int kernelPixelX = startKernelPixelX; 337 int kernelPixelY = startKernelPixelY; 338 int width = m_kernelSize.width(); 339 340 totals[0] = 0; 341 totals[1] = 0; 342 totals[2] = 0; 343 if (!preserveAlphaValues) 344 totals[3] = 0; 345 346 while (kernelValue >= 0) { 347 int pixelIndex = getPixelValue(paintingData, kernelPixelX, kernelPixelY); 348 if (pixelIndex >= 0) { 349 totals[0] += m_kernelMatrix[kernelValue] * static_cast<float>(paintingData.srcPixelArray->get(pixelIndex)); 350 totals[1] += m_kernelMatrix[kernelValue] * static_cast<float>(paintingData.srcPixelArray->get(pixelIndex + 1)); 351 totals[2] += m_kernelMatrix[kernelValue] * static_cast<float>(paintingData.srcPixelArray->get(pixelIndex + 2)); 352 } 353 if (!preserveAlphaValues && pixelIndex >= 0) 354 totals[3] += m_kernelMatrix[kernelValue] * static_cast<float>(paintingData.srcPixelArray->get(pixelIndex + 3)); 355 ++kernelPixelX; 356 --kernelValue; 357 if (!--width) { 358 kernelPixelX = startKernelPixelX; 359 ++kernelPixelY; 360 width = m_kernelSize.width(); 361 } 362 } 363 364 setDestinationPixels<preserveAlphaValues>(paintingData.dstPixelArray, pixel, totals, m_divisor, paintingData.bias, paintingData.srcPixelArray); 365 ++startKernelPixelX; 366 } 367 pixel += xIncrease; 368 startKernelPixelX = beginKernelPixelX; 369 ++startKernelPixelY; 370 } 371 } 372 373 ALWAYS_INLINE void FEConvolveMatrix::setInteriorPixels(PaintingData& paintingData, int clipRight, int clipBottom) 374 { 375 // Must be implemented here, since it refers another ALWAYS_INLINE 376 // function, which defined in this C++ source file as well 377 if (m_preserveAlpha) 378 fastSetInteriorPixels<true>(paintingData, clipRight, clipBottom); 379 else 380 fastSetInteriorPixels<false>(paintingData, clipRight, clipBottom); 381 } 382 383 ALWAYS_INLINE void FEConvolveMatrix::setOuterPixels(PaintingData& paintingData, int x1, int y1, int x2, int y2) 384 { 385 // Although this function can be moved to the header, it is implemented here 386 // because setInteriorPixels is also implemented here 387 if (m_preserveAlpha) 388 fastSetOuterPixels<true>(paintingData, x1, y1, x2, y2); 389 else 390 fastSetOuterPixels<false>(paintingData, x1, y1, x2, y2); 391 } 392 393 void FEConvolveMatrix::apply() 394 { 395 if (hasResult()) 396 return; 397 FilterEffect* in = inputEffect(0); 398 in->apply(); 399 if (!in->hasResult()) 400 return; 401 402 ByteArray* resultImage; 403 if (m_preserveAlpha) 404 resultImage = createUnmultipliedImageResult(); 405 else 406 resultImage = createPremultipliedImageResult(); 407 if (!resultImage) 408 return; 409 410 IntRect effectDrawingRect = requestedRegionOfInputImageData(in->absolutePaintRect()); 411 412 RefPtr<ByteArray> srcPixelArray; 413 if (m_preserveAlpha) 414 srcPixelArray = in->asUnmultipliedImage(effectDrawingRect); 415 else 416 srcPixelArray = in->asPremultipliedImage(effectDrawingRect); 417 418 IntSize paintSize = absolutePaintRect().size(); 419 PaintingData paintingData; 420 paintingData.srcPixelArray = srcPixelArray.get(); 421 paintingData.dstPixelArray = resultImage; 422 paintingData.width = paintSize.width(); 423 paintingData.height = paintSize.height(); 424 paintingData.bias = m_bias * 255; 425 426 // Drawing fully covered pixels 427 int clipRight = paintSize.width() - m_kernelSize.width(); 428 int clipBottom = paintSize.height() - m_kernelSize.height(); 429 430 if (clipRight >= 0 && clipBottom >= 0) { 431 setInteriorPixels(paintingData, clipRight, clipBottom); 432 433 clipRight += m_targetOffset.x() + 1; 434 clipBottom += m_targetOffset.y() + 1; 435 if (m_targetOffset.y() > 0) 436 setOuterPixels(paintingData, 0, 0, paintSize.width(), m_targetOffset.y()); 437 if (clipBottom < paintSize.height()) 438 setOuterPixels(paintingData, 0, clipBottom, paintSize.width(), paintSize.height()); 439 if (m_targetOffset.x() > 0) 440 setOuterPixels(paintingData, 0, m_targetOffset.y(), m_targetOffset.x(), clipBottom); 441 if (clipRight < paintSize.width()) 442 setOuterPixels(paintingData, clipRight, m_targetOffset.y(), paintSize.width(), clipBottom); 443 } else { 444 // Rare situation, not optimizied for speed 445 setOuterPixels(paintingData, 0, 0, paintSize.width(), paintSize.height()); 446 } 447 } 448 449 void FEConvolveMatrix::dump() 450 { 451 } 452 453 static TextStream& operator<<(TextStream& ts, const EdgeModeType& type) 454 { 455 switch (type) { 456 case EDGEMODE_UNKNOWN: 457 ts << "UNKNOWN"; 458 break; 459 case EDGEMODE_DUPLICATE: 460 ts << "DUPLICATE"; 461 break; 462 case EDGEMODE_WRAP: 463 ts << "WRAP"; 464 break; 465 case EDGEMODE_NONE: 466 ts << "NONE"; 467 break; 468 } 469 return ts; 470 } 471 472 TextStream& FEConvolveMatrix::externalRepresentation(TextStream& ts, int indent) const 473 { 474 writeIndent(ts, indent); 475 ts << "[feConvolveMatrix"; 476 FilterEffect::externalRepresentation(ts); 477 ts << " order=\"" << m_kernelSize << "\" " 478 << "kernelMatrix=\"" << m_kernelMatrix << "\" " 479 << "divisor=\"" << m_divisor << "\" " 480 << "bias=\"" << m_bias << "\" " 481 << "target=\"" << m_targetOffset << "\" " 482 << "edgeMode=\"" << m_edgeMode << "\" " 483 << "kernelUnitLength=\"" << m_kernelUnitLength << "\" " 484 << "preserveAlpha=\"" << m_preserveAlpha << "\"]\n"; 485 inputEffect(0)->externalRepresentation(ts, indent + 1); 486 return ts; 487 } 488 489 }; // namespace WebCore 490 491 #endif // ENABLE(FILTERS) 492