Simple and fast tool to add a fancy blurred letterbox effect to images.
Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304
  1. #include <stdio.h>
  2. #include <string.h>
  3. #include <stdlib.h>
  4. #include <jpeglib.h>
  5. #include <omp.h>
  6. static int blur_radius = 15;
  7. static int blur_times = 5;
  8. static void blur_h(
  9. unsigned char *dest, unsigned char *src,
  10. int stride, int width, int height, int radius) {
  11. double coeff = 1.0 / (radius * 2 + 1);
  12. #pragma omp parallel for
  13. for (int i = 0; i < height; ++i) {
  14. int iwidth = i * stride * 3;
  15. double r_acc = 0.0;
  16. double g_acc = 0.0;
  17. double b_acc = 0.0;
  18. for (int j = -radius; j < width; ++j) {
  19. if (j - radius - 1 >= 0) {
  20. int index = iwidth + (j - radius - 1) * 3;
  21. r_acc -= coeff * src[index + 0];
  22. g_acc -= coeff * src[index + 1];
  23. b_acc -= coeff * src[index + 2];
  24. }
  25. if (j + radius < width) {
  26. int index = iwidth + (j + radius) * 3;
  27. r_acc += coeff * src[index + 0];
  28. g_acc += coeff * src[index + 1];
  29. b_acc += coeff * src[index + 2];
  30. }
  31. if (j < 0)
  32. continue;
  33. int index = iwidth + j * 3;
  34. dest[index + 0] = r_acc + 0.5;
  35. dest[index + 1] = g_acc + 0.5;
  36. dest[index + 2] = b_acc + 0.5;
  37. }
  38. }
  39. }
  40. static void blur_v(
  41. unsigned char *dest, unsigned char *src,
  42. int stride, int width, int height, int radius) {
  43. double coeff = 1.0 / (radius * 2 + 1);
  44. #pragma omp parallel for
  45. for (int j = 0; j < width; ++j) {
  46. double r_acc = 0.0;
  47. double g_acc = 0.0;
  48. double b_acc = 0.0;
  49. for (int i = -radius; i < height; ++i) {
  50. if (i - radius - 1 >= 0) {
  51. int index = (i - radius - 1) * stride * 3 + j * 3;
  52. r_acc -= coeff * src[index + 0];
  53. g_acc -= coeff * src[index + 1];
  54. b_acc -= coeff * src[index + 2];
  55. }
  56. if (i + radius < height) {
  57. int index = (i + radius) * stride * 3 + j * 3;
  58. r_acc += coeff * src[index + 0];
  59. g_acc += coeff * src[index + 1];
  60. b_acc += coeff * src[index + 2];
  61. }
  62. if (i < 0)
  63. continue;
  64. int index = i * stride * 3 + j * 3;
  65. dest[index + 0] = r_acc + 0.5;
  66. dest[index + 1] = g_acc + 0.5;
  67. dest[index + 2] = b_acc + 0.5;
  68. }
  69. }
  70. }
  71. static void blur_once(
  72. unsigned char *dest, unsigned char *src, unsigned char *scratch,
  73. int stride, int width, int height, int radius) {
  74. blur_h(scratch, src, stride, width, height, radius);
  75. blur_v(dest, scratch, stride, width, height, radius);
  76. }
  77. void blur_rect(
  78. unsigned char *image, unsigned char *scratch, unsigned char *scratch2,
  79. int stride, int x, int y, int w, int h, int radius, int times) {
  80. if (w == 0 || h == 0)
  81. return;
  82. unsigned char *orig = image;
  83. int idx = y * stride * 3 + x * 3;
  84. for (int i = 0; i < times; ++i) {
  85. blur_once(scratch + idx, image + idx, scratch2, stride, w, h, radius);
  86. if (i != times - 1) {
  87. unsigned char *tmp = image;
  88. image = scratch;
  89. scratch = tmp;
  90. }
  91. }
  92. if (image != orig)
  93. memcpy(scratch + idx, image + idx, stride * h * 3);
  94. }
  95. unsigned char *process_image(
  96. unsigned char *input_image, int input_width, int input_height,
  97. int desired_width, int desired_height) {
  98. unsigned char *output_image = calloc(3, desired_width * desired_height);
  99. double scale_w = (double)desired_width / (double)input_width;
  100. double scale_h = (double)desired_height / (double)input_height;
  101. double scale;
  102. int rect_x, rect_y, rect_w, rect_h;
  103. if (scale_w < scale_h)
  104. scale = scale_w;
  105. else
  106. scale = scale_h;
  107. rect_w = input_width * scale;
  108. rect_h = input_height * scale;
  109. rect_x = (desired_width - rect_w) / 2.0;
  110. rect_y = (desired_height - rect_h) / 2.0;
  111. // Write main image
  112. for (int y = 0; y < rect_h; ++y) {
  113. for (int x = 0; x < rect_w; ++x) {
  114. int input_y = y / scale;
  115. int input_x = x / scale;
  116. int input_idx = input_y * input_width * 3 + input_x * 3;
  117. int output_idx = (y + rect_y) * desired_width * 3 + (x + rect_x) * 3;
  118. output_image[output_idx + 0] = input_image[input_idx + 0];
  119. output_image[output_idx + 1] = input_image[input_idx + 1];
  120. output_image[output_idx + 2] = input_image[input_idx + 2];
  121. }
  122. }
  123. // Top/bottom
  124. for (int y = 0; y < rect_y; ++y) {
  125. for (int x = 0; x < rect_w; ++x) {
  126. int input_idx = (y + rect_y) * desired_width * 3 + (x + rect_x) * 3;
  127. int output_idx = y * desired_width * 3 + (x + rect_x) * 3;
  128. output_image[output_idx + 0] = output_image[input_idx + 0];
  129. output_image[output_idx + 1] = output_image[input_idx + 1];
  130. output_image[output_idx + 2] = output_image[input_idx + 2];
  131. input_idx = (y + rect_h) * desired_width * 3 + (x + rect_x) * 3;
  132. output_idx = (y + rect_h + rect_y) * desired_width * 3 + (x + rect_x) * 3;
  133. output_image[output_idx + 0] = output_image[input_idx + 0];
  134. output_image[output_idx + 1] = output_image[input_idx + 1];
  135. output_image[output_idx + 2] = output_image[input_idx + 2];
  136. }
  137. }
  138. // Left/right
  139. for (int x = 0; x < rect_x; ++x) {
  140. for (int y = 0; y < rect_h; ++y) {
  141. int input_idx_l = (y + rect_y) * desired_width * 3 + (x + rect_x) * 3;
  142. int output_idx_l = y * desired_width * 3 + (x) * 3;
  143. int input_idx_r = (y + rect_y) * desired_width * 3 + (x + rect_w) * 3;
  144. int output_idx_r = y * desired_width * 3 + (x + rect_w + rect_x) * 3;
  145. output_image[output_idx_l + 0] = output_image[input_idx_l + 0];
  146. output_image[output_idx_l + 1] = output_image[input_idx_l + 1];
  147. output_image[output_idx_l + 2] = output_image[input_idx_l + 2];
  148. output_image[output_idx_r + 0] = output_image[input_idx_r + 0];
  149. output_image[output_idx_r + 1] = output_image[input_idx_r + 1];
  150. output_image[output_idx_r + 2] = output_image[input_idx_r + 2];
  151. }
  152. }
  153. // Blur
  154. unsigned char *scratch = calloc(3, desired_width * desired_height);
  155. unsigned char *scratch2 = calloc(3, desired_width * desired_height);
  156. blur_rect(
  157. output_image, scratch, scratch2, desired_width,
  158. 0, 0, desired_width, rect_y, blur_radius, blur_times);
  159. blur_rect(
  160. output_image, scratch, scratch2, desired_width,
  161. 0, rect_y + rect_h, desired_width, rect_y, blur_radius, blur_times);
  162. blur_rect(
  163. output_image, scratch, scratch2, desired_width,
  164. 0, 0, rect_x, desired_height, blur_radius, blur_times);
  165. blur_rect(
  166. output_image, scratch, scratch2, desired_width,
  167. rect_x + rect_w, 0, rect_x, desired_height, blur_radius, blur_times);
  168. free(scratch);
  169. free(scratch2);
  170. return output_image;
  171. }
  172. unsigned char *read_jpg(FILE *in, int *width, int *height) {
  173. struct jpeg_decompress_struct cinfo = { 0 };
  174. struct jpeg_error_mgr jerr = { 0 };
  175. cinfo.err = jpeg_std_error(&jerr);
  176. jpeg_create_decompress(&cinfo);
  177. jpeg_stdio_src(&cinfo, in);
  178. jpeg_read_header(&cinfo, TRUE);
  179. jpeg_start_decompress(&cinfo);
  180. *width = cinfo.output_width;
  181. *height = cinfo.output_height;
  182. int bytes_per_line = cinfo.output_width * 3;
  183. unsigned char *image = malloc(bytes_per_line * cinfo.output_height);
  184. /* Make a one-row-high sample array that will go away when done with image */
  185. JSAMPARRAY buffer = (*cinfo.mem->alloc_sarray)
  186. ((j_common_ptr)&cinfo, JPOOL_IMAGE, bytes_per_line, 1);
  187. while (cinfo.output_scanline < cinfo.output_height) {
  188. jpeg_read_scanlines(&cinfo, buffer, 1);
  189. /* Assume put_scanline_someplace wants a pointer and sample count. */
  190. memcpy(&image[bytes_per_line * (cinfo.output_scanline - 1)], buffer[0], bytes_per_line);
  191. }
  192. jpeg_finish_decompress(&cinfo);
  193. jpeg_destroy_decompress(&cinfo);
  194. return image;
  195. }
  196. void write_jpg(FILE *out, unsigned char *image, int width, int height) {
  197. struct jpeg_compress_struct cinfo = { 0 };
  198. struct jpeg_error_mgr jerr = { 0 };
  199. cinfo.err = jpeg_std_error(&jerr);
  200. jpeg_create_compress(&cinfo);
  201. jpeg_stdio_dest(&cinfo, out);
  202. cinfo.image_width = width - blur_radius * 2;
  203. cinfo.image_height = height - blur_radius * 2;
  204. cinfo.input_components = 3;
  205. cinfo.in_color_space = JCS_RGB;
  206. jpeg_set_defaults(&cinfo);
  207. jpeg_set_quality(&cinfo, 90, FALSE);
  208. jpeg_start_compress(&cinfo, TRUE);
  209. int bytes_per_line = width * 3;
  210. JSAMPROW row_pointer[1];
  211. while (cinfo.next_scanline < cinfo.image_height) {
  212. row_pointer[0] = &image[(cinfo.next_scanline + blur_radius) * bytes_per_line + blur_radius * 3];
  213. jpeg_write_scanlines(&cinfo, row_pointer, 1);
  214. }
  215. jpeg_finish_compress(&cinfo);
  216. jpeg_destroy_compress(&cinfo);
  217. }
  218. int main(int argc, char **argv) {
  219. if (argc != 4) {
  220. printf("Usage: %s <resolution> <input> <output>\n", argv[0]);
  221. return EXIT_FAILURE;
  222. }
  223. int desired_width, desired_height;
  224. if (sscanf(argv[1], "%ix%i", &desired_width, &desired_height) != 2) {
  225. fprintf(stderr, "Error parsing resolution '%s'\n", argv[1]);
  226. return EXIT_FAILURE;
  227. }
  228. desired_width += blur_radius * 2;
  229. desired_height += blur_radius * 2;
  230. FILE *in;
  231. if (strcmp(argv[2], "-") == 0) {
  232. in = stdin;
  233. } else {
  234. in = fopen(argv[2], "r");
  235. if (in == NULL) {
  236. perror(argv[2]);
  237. return EXIT_FAILURE;
  238. }
  239. }
  240. int input_width, input_height;
  241. unsigned char *input_image = read_jpg(in, &input_width, &input_height);
  242. fclose(in);
  243. unsigned char *output_image = process_image(
  244. input_image, input_width, input_height, desired_width, desired_height);
  245. free(input_image);
  246. FILE *out;
  247. if (strcmp(argv[3], "-") == 0) {
  248. out = stdout;
  249. } else {
  250. out = fopen(argv[3], "w");
  251. if (out == NULL) {
  252. perror(argv[3]);
  253. free(output_image);
  254. return EXIT_FAILURE;
  255. }
  256. }
  257. write_jpg(out, output_image, desired_width, desired_height);
  258. free(output_image);
  259. return EXIT_SUCCESS;
  260. }