BoxBlur.c 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324
  1. #include "Imaging.h"
  2. #define MAX(x, y) (((x) > (y)) ? (x) : (y))
  3. #define MIN(x, y) (((x) < (y)) ? (x) : (y))
  4. typedef UINT8 pixel[4];
  5. void static inline ImagingLineBoxBlur32(
  6. pixel *lineOut,
  7. pixel *lineIn,
  8. int lastx,
  9. int radius,
  10. int edgeA,
  11. int edgeB,
  12. UINT32 ww,
  13. UINT32 fw) {
  14. int x;
  15. UINT32 acc[4];
  16. UINT32 bulk[4];
  17. #define MOVE_ACC(acc, subtract, add) \
  18. acc[0] += lineIn[add][0] - lineIn[subtract][0]; \
  19. acc[1] += lineIn[add][1] - lineIn[subtract][1]; \
  20. acc[2] += lineIn[add][2] - lineIn[subtract][2]; \
  21. acc[3] += lineIn[add][3] - lineIn[subtract][3];
  22. #define ADD_FAR(bulk, acc, left, right) \
  23. bulk[0] = (acc[0] * ww) + (lineIn[left][0] + lineIn[right][0]) * fw; \
  24. bulk[1] = (acc[1] * ww) + (lineIn[left][1] + lineIn[right][1]) * fw; \
  25. bulk[2] = (acc[2] * ww) + (lineIn[left][2] + lineIn[right][2]) * fw; \
  26. bulk[3] = (acc[3] * ww) + (lineIn[left][3] + lineIn[right][3]) * fw;
  27. #define SAVE(x, bulk) \
  28. lineOut[x][0] = (UINT8)((bulk[0] + (1 << 23)) >> 24); \
  29. lineOut[x][1] = (UINT8)((bulk[1] + (1 << 23)) >> 24); \
  30. lineOut[x][2] = (UINT8)((bulk[2] + (1 << 23)) >> 24); \
  31. lineOut[x][3] = (UINT8)((bulk[3] + (1 << 23)) >> 24);
  32. /* Compute acc for -1 pixel (outside of image):
  33. From "-radius-1" to "-1" get first pixel,
  34. then from "0" to "radius-1". */
  35. acc[0] = lineIn[0][0] * (radius + 1);
  36. acc[1] = lineIn[0][1] * (radius + 1);
  37. acc[2] = lineIn[0][2] * (radius + 1);
  38. acc[3] = lineIn[0][3] * (radius + 1);
  39. /* As radius can be bigger than xsize, iterate to edgeA -1. */
  40. for (x = 0; x < edgeA - 1; x++) {
  41. acc[0] += lineIn[x][0];
  42. acc[1] += lineIn[x][1];
  43. acc[2] += lineIn[x][2];
  44. acc[3] += lineIn[x][3];
  45. }
  46. /* Then multiply remainder to last x. */
  47. acc[0] += lineIn[lastx][0] * (radius - edgeA + 1);
  48. acc[1] += lineIn[lastx][1] * (radius - edgeA + 1);
  49. acc[2] += lineIn[lastx][2] * (radius - edgeA + 1);
  50. acc[3] += lineIn[lastx][3] * (radius - edgeA + 1);
  51. if (edgeA <= edgeB) {
  52. /* Subtract pixel from left ("0").
  53. Add pixels from radius. */
  54. for (x = 0; x < edgeA; x++) {
  55. MOVE_ACC(acc, 0, x + radius);
  56. ADD_FAR(bulk, acc, 0, x + radius + 1);
  57. SAVE(x, bulk);
  58. }
  59. /* Subtract previous pixel from "-radius".
  60. Add pixels from radius. */
  61. for (x = edgeA; x < edgeB; x++) {
  62. MOVE_ACC(acc, x - radius - 1, x + radius);
  63. ADD_FAR(bulk, acc, x - radius - 1, x + radius + 1);
  64. SAVE(x, bulk);
  65. }
  66. /* Subtract previous pixel from "-radius".
  67. Add last pixel. */
  68. for (x = edgeB; x <= lastx; x++) {
  69. MOVE_ACC(acc, x - radius - 1, lastx);
  70. ADD_FAR(bulk, acc, x - radius - 1, lastx);
  71. SAVE(x, bulk);
  72. }
  73. } else {
  74. for (x = 0; x < edgeB; x++) {
  75. MOVE_ACC(acc, 0, x + radius);
  76. ADD_FAR(bulk, acc, 0, x + radius + 1);
  77. SAVE(x, bulk);
  78. }
  79. for (x = edgeB; x < edgeA; x++) {
  80. MOVE_ACC(acc, 0, lastx);
  81. ADD_FAR(bulk, acc, 0, lastx);
  82. SAVE(x, bulk);
  83. }
  84. for (x = edgeA; x <= lastx; x++) {
  85. MOVE_ACC(acc, x - radius - 1, lastx);
  86. ADD_FAR(bulk, acc, x - radius - 1, lastx);
  87. SAVE(x, bulk);
  88. }
  89. }
  90. #undef MOVE_ACC
  91. #undef ADD_FAR
  92. #undef SAVE
  93. }
  94. void static inline ImagingLineBoxBlur8(
  95. UINT8 *lineOut,
  96. UINT8 *lineIn,
  97. int lastx,
  98. int radius,
  99. int edgeA,
  100. int edgeB,
  101. UINT32 ww,
  102. UINT32 fw) {
  103. int x;
  104. UINT32 acc;
  105. UINT32 bulk;
  106. #define MOVE_ACC(acc, subtract, add) acc += lineIn[add] - lineIn[subtract];
  107. #define ADD_FAR(bulk, acc, left, right) \
  108. bulk = (acc * ww) + (lineIn[left] + lineIn[right]) * fw;
  109. #define SAVE(x, bulk) lineOut[x] = (UINT8)((bulk + (1 << 23)) >> 24)
  110. acc = lineIn[0] * (radius + 1);
  111. for (x = 0; x < edgeA - 1; x++) {
  112. acc += lineIn[x];
  113. }
  114. acc += lineIn[lastx] * (radius - edgeA + 1);
  115. if (edgeA <= edgeB) {
  116. for (x = 0; x < edgeA; x++) {
  117. MOVE_ACC(acc, 0, x + radius);
  118. ADD_FAR(bulk, acc, 0, x + radius + 1);
  119. SAVE(x, bulk);
  120. }
  121. for (x = edgeA; x < edgeB; x++) {
  122. MOVE_ACC(acc, x - radius - 1, x + radius);
  123. ADD_FAR(bulk, acc, x - radius - 1, x + radius + 1);
  124. SAVE(x, bulk);
  125. }
  126. for (x = edgeB; x <= lastx; x++) {
  127. MOVE_ACC(acc, x - radius - 1, lastx);
  128. ADD_FAR(bulk, acc, x - radius - 1, lastx);
  129. SAVE(x, bulk);
  130. }
  131. } else {
  132. for (x = 0; x < edgeB; x++) {
  133. MOVE_ACC(acc, 0, x + radius);
  134. ADD_FAR(bulk, acc, 0, x + radius + 1);
  135. SAVE(x, bulk);
  136. }
  137. for (x = edgeB; x < edgeA; x++) {
  138. MOVE_ACC(acc, 0, lastx);
  139. ADD_FAR(bulk, acc, 0, lastx);
  140. SAVE(x, bulk);
  141. }
  142. for (x = edgeA; x <= lastx; x++) {
  143. MOVE_ACC(acc, x - radius - 1, lastx);
  144. ADD_FAR(bulk, acc, x - radius - 1, lastx);
  145. SAVE(x, bulk);
  146. }
  147. }
  148. #undef MOVE_ACC
  149. #undef ADD_FAR
  150. #undef SAVE
  151. }
  152. Imaging
  153. ImagingHorizontalBoxBlur(Imaging imOut, Imaging imIn, float floatRadius) {
  154. ImagingSectionCookie cookie;
  155. int y;
  156. int radius = (int)floatRadius;
  157. UINT32 ww = (UINT32)(1 << 24) / (floatRadius * 2 + 1);
  158. UINT32 fw = ((1 << 24) - (radius * 2 + 1) * ww) / 2;
  159. int edgeA = MIN(radius + 1, imIn->xsize);
  160. int edgeB = MAX(imIn->xsize - radius - 1, 0);
  161. UINT32 *lineOut = calloc(imIn->xsize, sizeof(UINT32));
  162. if (lineOut == NULL) {
  163. return ImagingError_MemoryError();
  164. }
  165. // printf(">>> %d %d %d\n", radius, ww, fw);
  166. ImagingSectionEnter(&cookie);
  167. if (imIn->image8) {
  168. for (y = 0; y < imIn->ysize; y++) {
  169. ImagingLineBoxBlur8(
  170. (imIn == imOut ? (UINT8 *)lineOut : imOut->image8[y]),
  171. imIn->image8[y],
  172. imIn->xsize - 1,
  173. radius,
  174. edgeA,
  175. edgeB,
  176. ww,
  177. fw);
  178. if (imIn == imOut) {
  179. // Commit.
  180. memcpy(imOut->image8[y], lineOut, imIn->xsize);
  181. }
  182. }
  183. } else {
  184. for (y = 0; y < imIn->ysize; y++) {
  185. ImagingLineBoxBlur32(
  186. imIn == imOut ? (pixel *)lineOut : (pixel *)imOut->image32[y],
  187. (pixel *)imIn->image32[y],
  188. imIn->xsize - 1,
  189. radius,
  190. edgeA,
  191. edgeB,
  192. ww,
  193. fw);
  194. if (imIn == imOut) {
  195. // Commit.
  196. memcpy(imOut->image32[y], lineOut, imIn->xsize * 4);
  197. }
  198. }
  199. }
  200. ImagingSectionLeave(&cookie);
  201. free(lineOut);
  202. return imOut;
  203. }
  204. Imaging
  205. ImagingBoxBlur(Imaging imOut, Imaging imIn, float xradius, float yradius, int n) {
  206. int i;
  207. Imaging imTransposed;
  208. if (n < 1) {
  209. return ImagingError_ValueError("number of passes must be greater than zero");
  210. }
  211. if (xradius < 0 || yradius < 0) {
  212. return ImagingError_ValueError("radius must be >= 0");
  213. }
  214. if (strcmp(imIn->mode, imOut->mode) || imIn->type != imOut->type ||
  215. imIn->bands != imOut->bands || imIn->xsize != imOut->xsize ||
  216. imIn->ysize != imOut->ysize) {
  217. return ImagingError_Mismatch();
  218. }
  219. if (imIn->type != IMAGING_TYPE_UINT8) {
  220. return ImagingError_ModeError();
  221. }
  222. if (!(strcmp(imIn->mode, "RGB") == 0 || strcmp(imIn->mode, "RGBA") == 0 ||
  223. strcmp(imIn->mode, "RGBa") == 0 || strcmp(imIn->mode, "RGBX") == 0 ||
  224. strcmp(imIn->mode, "CMYK") == 0 || strcmp(imIn->mode, "L") == 0 ||
  225. strcmp(imIn->mode, "LA") == 0 || strcmp(imIn->mode, "La") == 0)) {
  226. return ImagingError_ModeError();
  227. }
  228. /* Apply blur in one dimension.
  229. Use imOut as a destination at first pass,
  230. then use imOut as a source too. */
  231. if (xradius != 0) {
  232. ImagingHorizontalBoxBlur(imOut, imIn, xradius);
  233. for (i = 1; i < n; i++) {
  234. ImagingHorizontalBoxBlur(imOut, imOut, xradius);
  235. }
  236. }
  237. if (yradius != 0) {
  238. imTransposed = ImagingNewDirty(imIn->mode, imIn->ysize, imIn->xsize);
  239. if (!imTransposed) {
  240. return NULL;
  241. }
  242. /* Transpose result for blur in another direction. */
  243. ImagingTranspose(imTransposed, xradius == 0 ? imIn : imOut);
  244. /* Reuse imTransposed as a source and destination there. */
  245. for (i = 0; i < n; i++) {
  246. ImagingHorizontalBoxBlur(imTransposed, imTransposed, yradius);
  247. }
  248. /* Restore original orientation. */
  249. ImagingTranspose(imOut, imTransposed);
  250. ImagingDelete(imTransposed);
  251. }
  252. if (xradius == 0 && yradius == 0) {
  253. if (!ImagingCopy2(imOut, imIn)) {
  254. return NULL;
  255. }
  256. }
  257. return imOut;
  258. }
  259. static float
  260. _gaussian_blur_radius(float radius, int passes) {
  261. float sigma2, L, l, a;
  262. sigma2 = radius * radius / passes;
  263. // from https://www.mia.uni-saarland.de/Publications/gwosdek-ssvm11.pdf
  264. // [7] Box length.
  265. L = sqrt(12.0 * sigma2 + 1.0);
  266. // [11] Integer part of box radius.
  267. l = floor((L - 1.0) / 2.0);
  268. // [14], [Fig. 2] Fractional part of box radius.
  269. a = (2 * l + 1) * (l * (l + 1) - 3 * sigma2);
  270. a /= 6 * (sigma2 - (l + 1) * (l + 1));
  271. return l + a;
  272. }
  273. Imaging
  274. ImagingGaussianBlur(Imaging imOut, Imaging imIn, float xradius, float yradius, int passes) {
  275. return ImagingBoxBlur(
  276. imOut,
  277. imIn,
  278. _gaussian_blur_radius(xradius, passes),
  279. _gaussian_blur_radius(yradius, passes),
  280. passes
  281. );
  282. }