/src/3rdparty/webkit/Source/WebCore/platform/graphics/filters/FEConvolveMatrix.cpp

https://bitbucket.org/ultra_iter/qt-vtl · C++ · 491 lines · 344 code · 57 blank · 90 comment · 58 complexity · bda9c22bbe7a0bffc51df14867e4ae37 MD5 · raw file

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