You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

268 lines
8.1 KiB

  1. #include <math.h>
  2. #include <stdbool.h>
  3. #include <stdint.h>
  4. #include <stdlib.h>
  5. #include <string.h>
  6. #include "image.h"
  7. #include "jpeglib.h"
  8. #include "lodepng/lodepng.h"
  9. #include "utils.h"
  10. struct image_data* image_make(int width, int height)
  11. {
  12. struct image_data* target = malloc(sizeof(struct image_data));
  13. target->size = width * height * 3;
  14. target->data = malloc(target->size);
  15. target->width = width;
  16. target->height = height;
  17. return target;
  18. }
  19. void image_free(struct image_data* img)
  20. {
  21. free(img->data);
  22. free(img);
  23. }
  24. struct image_data* image_load_jpeg_file(char* filename)
  25. {
  26. FILE* file;
  27. if ((file = fopen(filename, "rb")) == NULL) {
  28. fprintf(stderr, "can't open %s\n", filename);
  29. return 0;
  30. }
  31. struct jpeg_decompress_struct cinfo;
  32. struct jpeg_error_mgr jerr;
  33. cinfo.err = jpeg_std_error(&jerr);
  34. jpeg_create_decompress(&cinfo);
  35. jpeg_stdio_src(&cinfo, file);
  36. // only ignore return value on reading file.
  37. // see example.c L348 for more detail.
  38. (void)jpeg_read_header(&cinfo, TRUE);
  39. // max 4mbyte memory usage.
  40. // cinfo.mem->max_memory_to_use = 4000000L;
  41. cinfo.out_color_space = JCS_RGB;
  42. (void)jpeg_start_decompress(&cinfo);
  43. int row_stride = cinfo.output_width * cinfo.output_components;
  44. /* Make a one-row-high sample array that will go away when done with image */
  45. JSAMPARRAY buffer = (*cinfo.mem->alloc_sarray)((j_common_ptr)&cinfo, JPOOL_IMAGE, row_stride, 1);
  46. struct image_data* img = image_make(cinfo.output_width, cinfo.output_height);
  47. while (cinfo.output_scanline < cinfo.output_height) {
  48. int i = cinfo.output_scanline;
  49. (void)jpeg_read_scanlines(&cinfo, buffer, 1);
  50. int base = i * img->width * 3;
  51. if (cinfo.output_components == 3) {
  52. memcpy(img->data + base, buffer[0], row_stride);
  53. } else {
  54. for (int x = 0; x < cinfo.output_width; x++) {
  55. img->data[base + x * 3 + 0] = buffer[0][x * cinfo.output_components + 0];
  56. img->data[base + x * 3 + 1] = buffer[0][x * cinfo.output_components + 1];
  57. img->data[base + x * 3 + 2] = buffer[0][x * cinfo.output_components + 2];
  58. }
  59. }
  60. }
  61. (void)jpeg_finish_decompress(&cinfo);
  62. jpeg_destroy_decompress(&cinfo);
  63. fclose(file);
  64. return img;
  65. }
  66. struct image_data* image_load_png_file(char* filename)
  67. {
  68. struct image_data* data = malloc(sizeof(struct image_data));
  69. memset(data, 0, sizeof(struct image_data));
  70. unsigned int err = lodepng_decode24_file(&data->data, &data->width, &data->height, filename);
  71. if (err != 0) {
  72. free(data);
  73. printf("LoadPNG error %d: %s\n", err, lodepng_error_text(err));
  74. return NULL;
  75. }
  76. return data;
  77. }
  78. struct image_data* image_load_file(char* filename, char* name)
  79. {
  80. if (string_ends_with(name, "jpg"))
  81. return image_load_jpeg_file(filename);
  82. else if (string_ends_with(name, "png"))
  83. return image_load_png_file(filename);
  84. else {
  85. printf("File %s extension is not 'jpg' or 'png'.\n", name);
  86. return NULL;
  87. }
  88. }
  89. int image_load_file_resize(char* filename, char* name, struct image_data* result)
  90. {
  91. struct image_data* buf = image_load_file(filename, name);
  92. if (!buf)
  93. return -1;
  94. image_resize(buf, result);
  95. free(buf);
  96. return 0;
  97. }
  98. void image_write_jpeg_file(struct image_data* data, char* filename)
  99. {
  100. struct jpeg_compress_struct cinfo;
  101. struct jpeg_error_mgr jerr;
  102. FILE* outfile = fopen(filename, "wb");
  103. if (outfile == NULL) {
  104. fprintf(stderr, "can't open %s\n", filename);
  105. return;
  106. }
  107. cinfo.err = jpeg_std_error(&jerr);
  108. jpeg_create_compress(&cinfo);
  109. jpeg_stdio_dest(&cinfo, outfile);
  110. cinfo.image_width = data->width; /* image width and height, in pixels */
  111. cinfo.image_height = data->height;
  112. cinfo.input_components = 3; /* # of color components per pixel */
  113. cinfo.in_color_space = JCS_RGB; /* colorspace of input image */
  114. jpeg_set_defaults(&cinfo);
  115. jpeg_set_quality(&cinfo, 90, TRUE /* limit to baseline-JPEG values */);
  116. jpeg_start_compress(&cinfo, TRUE);
  117. JSAMPROW row_pointer[1];
  118. int row_stride = data->width * 3; /* JSAMPLEs per row in image_buffer */
  119. while (cinfo.next_scanline < cinfo.image_height) {
  120. row_pointer[0] = &data->data[cinfo.next_scanline * row_stride];
  121. (void)jpeg_write_scanlines(&cinfo, row_pointer, 1);
  122. }
  123. jpeg_finish_compress(&cinfo);
  124. jpeg_destroy_compress(&cinfo);
  125. fclose(outfile);
  126. }
  127. /**
  128. * mix color a and b to c, with rate
  129. **/
  130. void image_bilinear_mix(uint8_t* pa, uint8_t* pb, uint8_t* pc, float rate)
  131. {
  132. for (uint8_t i = 0; i < 3; i++) {
  133. pc[i] = *pb++ * rate + *pa++ * (1 - rate);
  134. }
  135. }
  136. void image_bilinear_mix_fast(uint8_t* pa, uint8_t* pb, uint8_t* pc)
  137. {
  138. for (uint8_t i = 0; i < 3; i++) {
  139. pc[i] = (*pb++ >> 1) + (*pa++ >> 1);
  140. }
  141. }
  142. void image_bilinear_get(struct image_data* img, float x, float y, uint8_t* output)
  143. {
  144. int pixel_size = 3;
  145. float px = x * (img->width - 1), py = y * (img->height - 1);
  146. int x0 = floor(px), x1 = ceil(px),
  147. y0 = floor(py), y1 = ceil(py);
  148. uint8_t pixels[2][3];
  149. image_bilinear_mix(img->data + (y0 * img->width + x0) * pixel_size,
  150. img->data + (y0 * img->width + x1) * pixel_size,
  151. pixels[0],
  152. px - x0);
  153. image_bilinear_mix(img->data + (y1 * img->width + x0) * pixel_size,
  154. img->data + (y1 * img->width + x1) * pixel_size,
  155. pixels[1],
  156. px - x0);
  157. image_bilinear_mix(pixels[0], pixels[1], output, py - y0);
  158. }
  159. void image_bilinear_get_fast(struct image_data* img, int x, int y, uint8_t* output)
  160. {
  161. int pixel_size = 3;
  162. int x0 = x * 2, x1 = x * 2 + 1,
  163. y0 = y * 2, y1 = y * 2 + 1;
  164. uint8_t pixels[2][3];
  165. image_bilinear_mix_fast(img->data + (y0 * img->width + x0) * pixel_size,
  166. img->data + (y0 * img->width + x1) * pixel_size,
  167. pixels[0]);
  168. image_bilinear_mix_fast(img->data + (y1 * img->width + x0) * pixel_size,
  169. img->data + (y1 * img->width + x1) * pixel_size,
  170. pixels[1]);
  171. image_bilinear_mix_fast(pixels[0], pixels[1], output);
  172. }
  173. struct image_data* image_bilinear_downscale_fast(struct image_data* original, int width, int height)
  174. {
  175. if (original->width < width * 2 || original->height < height * 2)
  176. return original;
  177. struct image_data* target = image_make(original->width / 2, original->height / 2);
  178. for (int y = 0; y < target->height; y++) {
  179. for (int x = 0; x < target->width; x++) {
  180. image_bilinear_get_fast(original, x, y, target->data + (y * target->width + x) * 3);
  181. }
  182. }
  183. struct image_data* result = image_bilinear_downscale_fast(target, width, height);
  184. if (result != target)
  185. image_free(target);
  186. return result;
  187. }
  188. void image_resize(struct image_data* original, struct image_data* target)
  189. {
  190. if (original->height == target->height
  191. && original->width == target->width) {
  192. memcpy(target->data, original->data, original->size);
  193. return;
  194. }
  195. struct image_data* rescale = image_bilinear_downscale_fast(original, target->width, target->height);
  196. double rate_orig = (double)rescale->height / (double)rescale->width,
  197. rate_targ = (double)target->height / (double)target->width;
  198. double scale = 0, offset = 0;
  199. bool scaleY = FALSE;
  200. if (rate_orig >= rate_targ) {
  201. scale = rate_targ / rate_orig;
  202. offset = (rate_orig - rate_targ) / rate_targ / 2;
  203. scaleY = TRUE;
  204. } else {
  205. scale = rate_orig / rate_targ; // (1/targ) / (1/orig)
  206. offset = (1 - rate_orig / rate_targ) / 2; // ((1/orig) - (1/targ))/(1/orig)/2;
  207. scaleY = FALSE;
  208. }
  209. for (int y = 0; y < target->height; y++) {
  210. float py = (float)y / target->height;
  211. if (scaleY)
  212. py = py * scale + offset;
  213. for (int x = 0; x < target->width; x++) {
  214. image_bilinear_get(rescale,
  215. !scaleY ? ((float)x / target->width * scale) + offset : (float)x / target->width,
  216. py, target->data + (y * target->width + x) * 3);
  217. }
  218. }
  219. if (rescale != original)
  220. image_free(rescale);
  221. }