PageRenderTime 70ms CodeModel.GetById 19ms RepoModel.GetById 1ms app.codeStats 0ms

/src/lib/cairo_box_blur.cpp

http://infekt.googlecode.com/
C++ | 269 lines | 174 code | 45 blank | 50 comment | 18 complexity | e8b214e5fd95f328b8f7869b3010d22b MD5 | raw file
Possible License(s): GPL-2.0
  1. /**
  2. * Copyright (C) 2010 cxxjoe
  3. *
  4. * This program is free software; you can redistribute it and/or
  5. * modify it under the terms of the GNU General Public License
  6. * as published by the Free Software Foundation; either version 2
  7. * of the License, or (at your option) any later version.
  8. *
  9. * This program is distributed in the hope that it will be useful,
  10. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. * GNU General Public License for more details.
  13. **/
  14. #include "stdafx.h" // remove if you don't have one
  15. /* #include <cairo.h> */
  16. #include <math.h>
  17. #include "cairo_box_blur.h"
  18. #include "util.h"
  19. #include <omp.h>
  20. // largely based on
  21. // http://mxr.mozilla.org/mozilla1.9.2/source/gfx/thebes/src/gfxBlur.cpp
  22. // (GPL v2)
  23. #ifndef M_PI
  24. #define M_PI 3.141592653589793238462643
  25. #endif
  26. // mozilla code stuff
  27. typedef int32_t PRInt32;
  28. #define PR_MAX(a, b) ((a) > (b) ? (a) : (b))
  29. #define PR_MIN(a, b) ((a) < (b) ? (a) : (b))
  30. #define NS_ASSERTION(a, b) _ASSERT(a)
  31. CCairoBoxBlur::CCairoBoxBlur(int a_width, int a_height, int a_blurRadius, bool a_allowHwAccel)
  32. {
  33. m_blurRadius = a_blurRadius;
  34. m_useCpu = true;
  35. #if defined(_WIN32) && !defined(COMPACT_RELEASE)
  36. if(a_allowHwAccel && CCudaUtil::GetInstance()->IsCudaUsable())
  37. {
  38. if(CCudaUtil::GetInstance()->IsCudaThreadInitialized() ||
  39. CCudaUtil::GetInstance()->InitCudaThread())
  40. {
  41. m_useCpu = false;
  42. }
  43. }
  44. #endif /* _WIN32 */
  45. m_imgSurface = cairo_image_surface_create(m_useCpu ? CAIRO_FORMAT_A8 : CAIRO_FORMAT_ARGB32, a_width, a_height);
  46. m_context = cairo_create(m_imgSurface);
  47. m_width = a_width;
  48. m_height = a_height;
  49. }
  50. /**
  51. * Box blur involves looking at one pixel, and setting its value to the average
  52. * of its neighbouring pixels.
  53. * @param aInput The input buffer.
  54. * @param aOutput The output buffer.
  55. * @param aLeftLobe The number of pixels to blend on the left.
  56. * @param aRightLobe The number of pixels to blend on the right.
  57. * @param aStride The stride of the buffers.
  58. * @param aRows The number of rows in the buffers.
  59. */
  60. static void
  61. BoxBlurHorizontal(unsigned char* aInput,
  62. unsigned char* aOutput,
  63. PRInt32 aLeftLobe,
  64. PRInt32 aRightLobe,
  65. PRInt32 aStride,
  66. PRInt32 aRows)
  67. {
  68. PRInt32 boxSize = aLeftLobe + aRightLobe + 1;
  69. #pragma omp parallel for
  70. for (PRInt32 y = 0; y < aRows; y++) {
  71. PRInt32 alphaSum = 0;
  72. for (PRInt32 i = 0; i < boxSize; i++) {
  73. PRInt32 pos = i - aLeftLobe;
  74. pos = PR_MAX(pos, 0);
  75. pos = PR_MIN(pos, aStride - 1);
  76. alphaSum += aInput[aStride * y + pos];
  77. }
  78. for (PRInt32 x = 0; x < aStride; x++) {
  79. PRInt32 tmp = x - aLeftLobe;
  80. PRInt32 last = PR_MAX(tmp, 0);
  81. PRInt32 next = PR_MIN(tmp + boxSize, aStride - 1);
  82. aOutput[aStride * y + x] = alphaSum/boxSize;
  83. alphaSum += aInput[aStride * y + next] -
  84. aInput[aStride * y + last];
  85. }
  86. }
  87. }
  88. /**
  89. * Identical to BoxBlurHorizontal, except it blurs top and bottom instead of
  90. * left and right.
  91. */
  92. static void
  93. BoxBlurVertical(unsigned char* aInput,
  94. unsigned char* aOutput,
  95. PRInt32 aTopLobe,
  96. PRInt32 aBottomLobe,
  97. PRInt32 aStride,
  98. PRInt32 aRows)
  99. {
  100. PRInt32 boxSize = aTopLobe + aBottomLobe + 1;
  101. #pragma omp parallel for
  102. for (PRInt32 x = 0; x < aStride; x++) {
  103. PRInt32 alphaSum = 0;
  104. for (PRInt32 i = 0; i < boxSize; i++) {
  105. PRInt32 pos = i - aTopLobe;
  106. pos = PR_MAX(pos, 0);
  107. pos = PR_MIN(pos, aRows - 1);
  108. alphaSum += aInput[aStride * pos + x];
  109. }
  110. for (PRInt32 y = 0; y < aRows; y++) {
  111. PRInt32 tmp = y - aTopLobe;
  112. PRInt32 last = PR_MAX(tmp, 0);
  113. PRInt32 next = PR_MIN(tmp + boxSize, aRows - 1);
  114. aOutput[aStride * y + x] = alphaSum/boxSize;
  115. alphaSum += aInput[aStride * next + x] -
  116. aInput[aStride * last + x];
  117. }
  118. }
  119. }
  120. static void ComputeLobes(PRInt32 aRadius, PRInt32 aLobes[3][2])
  121. {
  122. PRInt32 major, minor, final;
  123. /* See http://www.w3.org/TR/SVG/filters.html#feGaussianBlur for
  124. * some notes about approximating the Gaussian blur with box-blurs.
  125. * The comments below are in the terminology of that page.
  126. */
  127. PRInt32 z = aRadius/3;
  128. switch (aRadius % 3) {
  129. case 0:
  130. // aRadius = z*3; choose d = 2*z + 1
  131. major = minor = final = z;
  132. break;
  133. case 1:
  134. // aRadius = z*3 + 1
  135. // This is a tricky case since there is no value of d which will
  136. // yield a radius of exactly aRadius. If d is odd, i.e. d=2*k + 1
  137. // for some integer k, then the radius will be 3*k. If d is even,
  138. // i.e. d=2*k, then the radius will be 3*k - 1.
  139. // So we have to choose values that don't match the standard
  140. // algorithm.
  141. major = z + 1;
  142. minor = final = z;
  143. break;
  144. case 2:
  145. // aRadius = z*3 + 2; choose d = 2*z + 2
  146. major = final = z + 1;
  147. minor = z;
  148. break;
  149. }
  150. NS_ASSERTION(major + minor + final == aRadius,
  151. "Lobes don't sum to the right length");
  152. aLobes[0][0] = major;
  153. aLobes[0][1] = minor;
  154. aLobes[1][0] = minor;
  155. aLobes[1][1] = major;
  156. aLobes[2][0] = final;
  157. aLobes[2][1] = final;
  158. }
  159. bool CCairoBoxBlur::Paint(cairo_t* a_destination)
  160. {
  161. bool l_ok = false;
  162. cairo_surface_flush(m_imgSurface);
  163. unsigned char* l_boxData = cairo_image_surface_get_data(m_imgSurface);
  164. if(!l_boxData)
  165. {
  166. // too large or something...
  167. return false;
  168. }
  169. #if defined(_WIN32) && !defined(COMPACT_RELEASE)
  170. if(!m_useCpu)
  171. {
  172. l_ok = CCudaUtil::GetInstance()->DoCudaGaussianBlurRGBA(reinterpret_cast<unsigned int*>(l_boxData),
  173. cairo_image_surface_get_width(m_imgSurface),
  174. cairo_image_surface_get_height(m_imgSurface), m_blurRadius / 5.0f + 2);
  175. if(!l_ok)
  176. {
  177. // must retry!
  178. m_useCpu = true;
  179. cairo_destroy(m_context);
  180. cairo_surface_destroy(m_imgSurface);
  181. m_imgSurface = cairo_image_surface_create(CAIRO_FORMAT_A8, m_width, m_height);
  182. m_context = cairo_create(m_imgSurface);
  183. return false;
  184. }
  185. }
  186. #endif /* _WIN32 */
  187. if(!l_ok)
  188. {
  189. // CPU or CPU fallback.
  190. const PRInt32 l_stride = cairo_image_surface_get_stride(m_imgSurface);
  191. const PRInt32 l_rows = cairo_image_surface_get_height(m_imgSurface);
  192. PRInt32 l_lobes[3][2];
  193. ComputeLobes(m_blurRadius, l_lobes);
  194. unsigned char* l_tmpData = new unsigned char[l_stride * l_rows];
  195. BoxBlurHorizontal(l_boxData, l_tmpData, l_lobes[0][0], l_lobes[0][1], l_stride, l_rows);
  196. BoxBlurHorizontal(l_tmpData, l_boxData, l_lobes[1][0], l_lobes[1][1], l_stride, l_rows);
  197. BoxBlurHorizontal(l_boxData, l_tmpData, l_lobes[2][0], l_lobes[2][1], l_stride, l_rows);
  198. BoxBlurVertical(l_tmpData, l_boxData, l_lobes[0][0], l_lobes[0][1], l_stride, l_rows);
  199. BoxBlurVertical(l_boxData, l_tmpData, l_lobes[1][0], l_lobes[1][1], l_stride, l_rows);
  200. BoxBlurVertical(l_tmpData, l_boxData, l_lobes[2][0], l_lobes[2][1], l_stride, l_rows);
  201. delete[] l_tmpData;
  202. }
  203. cairo_surface_mark_dirty(m_imgSurface);
  204. if(m_useCpu)
  205. {
  206. // 8 bit mask
  207. cairo_mask_surface(a_destination, m_imgSurface, 0, 0);
  208. }
  209. else
  210. {
  211. // 32 bit RGBA processed shit
  212. cairo_save(a_destination);
  213. cairo_set_operator(a_destination, CAIRO_OPERATOR_ATOP);
  214. cairo_set_source_surface(a_destination, m_imgSurface, 0, 0);
  215. cairo_paint(a_destination);
  216. cairo_restore(a_destination);
  217. }
  218. return true;
  219. }
  220. CCairoBoxBlur::~CCairoBoxBlur()
  221. {
  222. cairo_destroy(m_context);
  223. cairo_surface_destroy(m_imgSurface);
  224. }