wrppm.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366
  1. /*
  2. * wrppm.c
  3. *
  4. * This file was part of the Independent JPEG Group's software:
  5. * Copyright (C) 1991-1996, Thomas G. Lane.
  6. * Modified 2009 by Guido Vollbeding.
  7. * libjpeg-turbo Modifications:
  8. * Copyright (C) 2017, 2019-2020, 2022, D. R. Commander.
  9. * For conditions of distribution and use, see the accompanying README.ijg
  10. * file.
  11. *
  12. * This file contains routines to write output images in PPM/PGM format.
  13. * The extended 2-byte-per-sample raw PPM/PGM formats are supported.
  14. * The PBMPLUS library is NOT required to compile this software
  15. * (but it is highly useful as a set of PPM image manipulation programs).
  16. *
  17. * These routines may need modification for non-Unix environments or
  18. * specialized applications. As they stand, they assume output to
  19. * an ordinary stdio stream.
  20. */
  21. #include "cmyk.h"
  22. #include "cdjpeg.h" /* Common decls for cjpeg/djpeg applications */
  23. #ifdef PPM_SUPPORTED
  24. /*
  25. * For 12-bit JPEG data, we either downscale the values to 8 bits
  26. * (to write standard byte-per-sample PPM/PGM files), or output
  27. * nonstandard word-per-sample PPM/PGM files. Downscaling is done
  28. * if PPM_NORAWWORD is defined (this can be done in the Makefile
  29. * or in jconfig.h).
  30. * (When the core library supports data precision reduction, a cleaner
  31. * implementation will be to ask for that instead.)
  32. */
  33. #if BITS_IN_JSAMPLE == 8
  34. #define PUTPPMSAMPLE(ptr, v) *ptr++ = (char)(v)
  35. #define BYTESPERSAMPLE 1
  36. #define PPM_MAXVAL 255
  37. #else
  38. #ifdef PPM_NORAWWORD
  39. #define PUTPPMSAMPLE(ptr, v) *ptr++ = (char)((v) >> (BITS_IN_JSAMPLE - 8))
  40. #define BYTESPERSAMPLE 1
  41. #define PPM_MAXVAL 255
  42. #else
  43. /* The word-per-sample format always puts the MSB first. */
  44. #define PUTPPMSAMPLE(ptr, v) { \
  45. register int val_ = v; \
  46. *ptr++ = (char)((val_ >> 8) & 0xFF); \
  47. *ptr++ = (char)(val_ & 0xFF); \
  48. }
  49. #define BYTESPERSAMPLE 2
  50. #define PPM_MAXVAL ((1 << BITS_IN_JSAMPLE) - 1)
  51. #endif
  52. #endif
  53. /*
  54. * When JSAMPLE is the same size as char, we can just fwrite() the
  55. * decompressed data to the PPM or PGM file.
  56. */
  57. /* Private version of data destination object */
  58. typedef struct {
  59. struct djpeg_dest_struct pub; /* public fields */
  60. /* Usually these two pointers point to the same place: */
  61. char *iobuffer; /* fwrite's I/O buffer */
  62. JSAMPROW pixrow; /* decompressor output buffer */
  63. size_t buffer_width; /* width of I/O buffer */
  64. JDIMENSION samples_per_row; /* JSAMPLEs per output row */
  65. } ppm_dest_struct;
  66. typedef ppm_dest_struct *ppm_dest_ptr;
  67. /*
  68. * Write some pixel data.
  69. * In this module rows_supplied will always be 1.
  70. *
  71. * put_pixel_rows handles the "normal" 8-bit case where the decompressor
  72. * output buffer is physically the same as the fwrite buffer.
  73. */
  74. METHODDEF(void)
  75. put_pixel_rows(j_decompress_ptr cinfo, djpeg_dest_ptr dinfo,
  76. JDIMENSION rows_supplied)
  77. {
  78. ppm_dest_ptr dest = (ppm_dest_ptr)dinfo;
  79. fwrite(dest->iobuffer, 1, dest->buffer_width, dest->pub.output_file);
  80. }
  81. /*
  82. * This code is used when we have to copy the data and apply a pixel
  83. * format translation. Typically this only happens in 12-bit mode.
  84. */
  85. METHODDEF(void)
  86. copy_pixel_rows(j_decompress_ptr cinfo, djpeg_dest_ptr dinfo,
  87. JDIMENSION rows_supplied)
  88. {
  89. ppm_dest_ptr dest = (ppm_dest_ptr)dinfo;
  90. register char *bufferptr;
  91. register JSAMPROW ptr;
  92. #if BITS_IN_JSAMPLE != 8
  93. register JDIMENSION col;
  94. #endif
  95. ptr = dest->pub.buffer[0];
  96. bufferptr = dest->iobuffer;
  97. #if BITS_IN_JSAMPLE == 8
  98. memcpy(bufferptr, ptr, dest->samples_per_row);
  99. #else
  100. for (col = dest->samples_per_row; col > 0; col--) {
  101. PUTPPMSAMPLE(bufferptr, *ptr++);
  102. }
  103. #endif
  104. fwrite(dest->iobuffer, 1, dest->buffer_width, dest->pub.output_file);
  105. }
  106. /*
  107. * Convert extended RGB to RGB.
  108. */
  109. METHODDEF(void)
  110. put_rgb(j_decompress_ptr cinfo, djpeg_dest_ptr dinfo, JDIMENSION rows_supplied)
  111. {
  112. ppm_dest_ptr dest = (ppm_dest_ptr)dinfo;
  113. register char *bufferptr;
  114. register JSAMPROW ptr;
  115. register JDIMENSION col;
  116. register int rindex = rgb_red[cinfo->out_color_space];
  117. register int gindex = rgb_green[cinfo->out_color_space];
  118. register int bindex = rgb_blue[cinfo->out_color_space];
  119. register int ps = rgb_pixelsize[cinfo->out_color_space];
  120. ptr = dest->pub.buffer[0];
  121. bufferptr = dest->iobuffer;
  122. for (col = cinfo->output_width; col > 0; col--) {
  123. PUTPPMSAMPLE(bufferptr, ptr[rindex]);
  124. PUTPPMSAMPLE(bufferptr, ptr[gindex]);
  125. PUTPPMSAMPLE(bufferptr, ptr[bindex]);
  126. ptr += ps;
  127. }
  128. fwrite(dest->iobuffer, 1, dest->buffer_width, dest->pub.output_file);
  129. }
  130. /*
  131. * Convert CMYK to RGB.
  132. */
  133. METHODDEF(void)
  134. put_cmyk(j_decompress_ptr cinfo, djpeg_dest_ptr dinfo,
  135. JDIMENSION rows_supplied)
  136. {
  137. ppm_dest_ptr dest = (ppm_dest_ptr)dinfo;
  138. register char *bufferptr;
  139. register JSAMPROW ptr;
  140. register JDIMENSION col;
  141. ptr = dest->pub.buffer[0];
  142. bufferptr = dest->iobuffer;
  143. for (col = cinfo->output_width; col > 0; col--) {
  144. JSAMPLE r, g, b, c = *ptr++, m = *ptr++, y = *ptr++, k = *ptr++;
  145. cmyk_to_rgb(c, m, y, k, &r, &g, &b);
  146. PUTPPMSAMPLE(bufferptr, r);
  147. PUTPPMSAMPLE(bufferptr, g);
  148. PUTPPMSAMPLE(bufferptr, b);
  149. }
  150. fwrite(dest->iobuffer, 1, dest->buffer_width, dest->pub.output_file);
  151. }
  152. /*
  153. * Write some pixel data when color quantization is in effect.
  154. * We have to demap the color index values to straight data.
  155. */
  156. METHODDEF(void)
  157. put_demapped_rgb(j_decompress_ptr cinfo, djpeg_dest_ptr dinfo,
  158. JDIMENSION rows_supplied)
  159. {
  160. ppm_dest_ptr dest = (ppm_dest_ptr)dinfo;
  161. register char *bufferptr;
  162. register int pixval;
  163. register JSAMPROW ptr;
  164. register JSAMPROW color_map0 = cinfo->colormap[0];
  165. register JSAMPROW color_map1 = cinfo->colormap[1];
  166. register JSAMPROW color_map2 = cinfo->colormap[2];
  167. register JDIMENSION col;
  168. ptr = dest->pub.buffer[0];
  169. bufferptr = dest->iobuffer;
  170. for (col = cinfo->output_width; col > 0; col--) {
  171. pixval = *ptr++;
  172. PUTPPMSAMPLE(bufferptr, color_map0[pixval]);
  173. PUTPPMSAMPLE(bufferptr, color_map1[pixval]);
  174. PUTPPMSAMPLE(bufferptr, color_map2[pixval]);
  175. }
  176. fwrite(dest->iobuffer, 1, dest->buffer_width, dest->pub.output_file);
  177. }
  178. METHODDEF(void)
  179. put_demapped_gray(j_decompress_ptr cinfo, djpeg_dest_ptr dinfo,
  180. JDIMENSION rows_supplied)
  181. {
  182. ppm_dest_ptr dest = (ppm_dest_ptr)dinfo;
  183. register char *bufferptr;
  184. register JSAMPROW ptr;
  185. register JSAMPROW color_map = cinfo->colormap[0];
  186. register JDIMENSION col;
  187. ptr = dest->pub.buffer[0];
  188. bufferptr = dest->iobuffer;
  189. for (col = cinfo->output_width; col > 0; col--) {
  190. PUTPPMSAMPLE(bufferptr, color_map[*ptr++]);
  191. }
  192. fwrite(dest->iobuffer, 1, dest->buffer_width, dest->pub.output_file);
  193. }
  194. /*
  195. * Startup: write the file header.
  196. */
  197. METHODDEF(void)
  198. start_output_ppm(j_decompress_ptr cinfo, djpeg_dest_ptr dinfo)
  199. {
  200. ppm_dest_ptr dest = (ppm_dest_ptr)dinfo;
  201. /* Emit file header */
  202. switch (cinfo->out_color_space) {
  203. case JCS_GRAYSCALE:
  204. /* emit header for raw PGM format */
  205. fprintf(dest->pub.output_file, "P5\n%ld %ld\n%d\n",
  206. (long)cinfo->output_width, (long)cinfo->output_height, PPM_MAXVAL);
  207. break;
  208. case JCS_RGB:
  209. case JCS_EXT_RGB:
  210. case JCS_EXT_RGBX:
  211. case JCS_EXT_BGR:
  212. case JCS_EXT_BGRX:
  213. case JCS_EXT_XBGR:
  214. case JCS_EXT_XRGB:
  215. case JCS_EXT_RGBA:
  216. case JCS_EXT_BGRA:
  217. case JCS_EXT_ABGR:
  218. case JCS_EXT_ARGB:
  219. case JCS_CMYK:
  220. if (!IsExtRGB(cinfo->out_color_space) && cinfo->quantize_colors)
  221. ERREXIT(cinfo, JERR_PPM_COLORSPACE);
  222. /* emit header for raw PPM format */
  223. fprintf(dest->pub.output_file, "P6\n%ld %ld\n%d\n",
  224. (long)cinfo->output_width, (long)cinfo->output_height, PPM_MAXVAL);
  225. break;
  226. default:
  227. ERREXIT(cinfo, JERR_PPM_COLORSPACE);
  228. }
  229. }
  230. /*
  231. * Finish up at the end of the file.
  232. */
  233. METHODDEF(void)
  234. finish_output_ppm(j_decompress_ptr cinfo, djpeg_dest_ptr dinfo)
  235. {
  236. /* Make sure we wrote the output file OK */
  237. fflush(dinfo->output_file);
  238. if (ferror(dinfo->output_file))
  239. ERREXIT(cinfo, JERR_FILE_WRITE);
  240. }
  241. /*
  242. * Re-calculate buffer dimensions based on output dimensions.
  243. */
  244. METHODDEF(void)
  245. calc_buffer_dimensions_ppm(j_decompress_ptr cinfo, djpeg_dest_ptr dinfo)
  246. {
  247. ppm_dest_ptr dest = (ppm_dest_ptr)dinfo;
  248. if (cinfo->out_color_space == JCS_GRAYSCALE)
  249. dest->samples_per_row = cinfo->output_width * cinfo->out_color_components;
  250. else
  251. dest->samples_per_row = cinfo->output_width * 3;
  252. dest->buffer_width = dest->samples_per_row * (BYTESPERSAMPLE * sizeof(char));
  253. }
  254. /*
  255. * The module selection routine for PPM format output.
  256. */
  257. GLOBAL(djpeg_dest_ptr)
  258. jinit_write_ppm(j_decompress_ptr cinfo)
  259. {
  260. ppm_dest_ptr dest;
  261. /* Create module interface object, fill in method pointers */
  262. dest = (ppm_dest_ptr)
  263. (*cinfo->mem->alloc_small) ((j_common_ptr)cinfo, JPOOL_IMAGE,
  264. sizeof(ppm_dest_struct));
  265. dest->pub.start_output = start_output_ppm;
  266. dest->pub.finish_output = finish_output_ppm;
  267. dest->pub.calc_buffer_dimensions = calc_buffer_dimensions_ppm;
  268. /* Calculate output image dimensions so we can allocate space */
  269. jpeg_calc_output_dimensions(cinfo);
  270. /* Create physical I/O buffer */
  271. dest->pub.calc_buffer_dimensions(cinfo, (djpeg_dest_ptr)dest);
  272. dest->iobuffer = (char *)(*cinfo->mem->alloc_small)
  273. ((j_common_ptr)cinfo, JPOOL_IMAGE, dest->buffer_width);
  274. if (cinfo->quantize_colors || BITS_IN_JSAMPLE != 8 ||
  275. sizeof(JSAMPLE) != sizeof(char) ||
  276. #if RGB_RED == 0 && RGB_GREEN == 1 && RGB_BLUE == 2 && RGB_PIXELSIZE == 3
  277. (cinfo->out_color_space != JCS_EXT_RGB &&
  278. cinfo->out_color_space != JCS_RGB)) {
  279. #else
  280. cinfo->out_color_space != JCS_EXT_RGB) {
  281. #endif
  282. /* When quantizing, we need an output buffer for colormap indexes
  283. * that's separate from the physical I/O buffer. We also need a
  284. * separate buffer if pixel format translation must take place.
  285. */
  286. dest->pub.buffer = (*cinfo->mem->alloc_sarray)
  287. ((j_common_ptr)cinfo, JPOOL_IMAGE,
  288. cinfo->output_width * cinfo->output_components, (JDIMENSION)1);
  289. dest->pub.buffer_height = 1;
  290. if (!cinfo->quantize_colors) {
  291. if (IsExtRGB(cinfo->out_color_space))
  292. dest->pub.put_pixel_rows = put_rgb;
  293. else if (cinfo->out_color_space == JCS_CMYK)
  294. dest->pub.put_pixel_rows = put_cmyk;
  295. else
  296. dest->pub.put_pixel_rows = copy_pixel_rows;
  297. } else if (cinfo->out_color_space == JCS_GRAYSCALE)
  298. dest->pub.put_pixel_rows = put_demapped_gray;
  299. else
  300. dest->pub.put_pixel_rows = put_demapped_rgb;
  301. } else {
  302. /* We will fwrite() directly from decompressor output buffer. */
  303. /* Synthesize a JSAMPARRAY pointer structure */
  304. dest->pixrow = (JSAMPROW)dest->iobuffer;
  305. dest->pub.buffer = &dest->pixrow;
  306. dest->pub.buffer_height = 1;
  307. dest->pub.put_pixel_rows = put_pixel_rows;
  308. }
  309. return (djpeg_dest_ptr)dest;
  310. }
  311. #endif /* PPM_SUPPORTED */