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
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).
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.
23 #include "cdjpeg.h" /* Common decls for cjpeg/djpeg applications */
25 #if defined(PPM_SUPPORTED) && \
26 (BITS_IN_JSAMPLE != 16 || defined(D_LOSSLESS_SUPPORTED))
30 * For 12-bit JPEG data, we either downscale the values to 8 bits
31 * (to write standard byte-per-sample PPM/PGM files), or output
32 * nonstandard word-per-sample PPM/PGM files. Downscaling is done
33 * if PPM_NORAWWORD is defined (this can be done in the Makefile
35 * (When the core library supports data precision reduction, a cleaner
36 * implementation will be to ask for that instead.)
39 #if BITS_IN_JSAMPLE == 8
40 #define PUTPPMSAMPLE(ptr, v) *ptr++ = (char)(v)
41 #define BYTESPERSAMPLE 1
42 #define PPM_MAXVAL 255
45 #define PUTPPMSAMPLE(ptr, v) *ptr++ = (char)((v) >> (BITS_IN_JSAMPLE - 8))
46 #define BYTESPERSAMPLE 1
47 #define PPM_MAXVAL 255
49 /* The word-per-sample format always puts the MSB first. */
50 #define PUTPPMSAMPLE(ptr, v) { \
51 register int val_ = v; \
52 *ptr++ = (char)((val_ >> 8) & 0xFF); \
53 *ptr++ = (char)(val_ & 0xFF); \
55 #define BYTESPERSAMPLE 2
56 #define PPM_MAXVAL ((1 << BITS_IN_JSAMPLE) - 1)
62 * When _JSAMPLE is the same size as char, we can just fwrite() the
63 * decompressed data to the PPM or PGM file.
67 /* Private version of data destination object */
70 struct djpeg_dest_struct pub; /* public fields */
72 /* Usually these two pointers point to the same place: */
73 char *iobuffer; /* fwrite's I/O buffer */
74 _JSAMPROW pixrow; /* decompressor output buffer */
75 size_t buffer_width; /* width of I/O buffer */
76 JDIMENSION samples_per_row; /* _JSAMPLEs per output row */
79 typedef ppm_dest_struct *ppm_dest_ptr;
83 * Write some pixel data.
84 * In this module rows_supplied will always be 1.
86 * put_pixel_rows handles the "normal" 8-bit case where the decompressor
87 * output buffer is physically the same as the fwrite buffer.
91 put_pixel_rows(j_decompress_ptr cinfo, djpeg_dest_ptr dinfo,
92 JDIMENSION rows_supplied)
94 ppm_dest_ptr dest = (ppm_dest_ptr)dinfo;
96 fwrite(dest->iobuffer, 1, dest->buffer_width, dest->pub.output_file);
101 * This code is used when we have to copy the data and apply a pixel
102 * format translation. Typically this only happens in 12-bit mode.
106 copy_pixel_rows(j_decompress_ptr cinfo, djpeg_dest_ptr dinfo,
107 JDIMENSION rows_supplied)
109 ppm_dest_ptr dest = (ppm_dest_ptr)dinfo;
110 register char *bufferptr;
111 register _JSAMPROW ptr;
112 #if BITS_IN_JSAMPLE != 8
113 register JDIMENSION col;
116 ptr = dest->pub._buffer[0];
117 bufferptr = dest->iobuffer;
118 #if BITS_IN_JSAMPLE == 8
119 memcpy(bufferptr, ptr, dest->samples_per_row);
121 for (col = dest->samples_per_row; col > 0; col--) {
122 PUTPPMSAMPLE(bufferptr, *ptr++);
125 fwrite(dest->iobuffer, 1, dest->buffer_width, dest->pub.output_file);
130 * Convert extended RGB to RGB.
134 put_rgb(j_decompress_ptr cinfo, djpeg_dest_ptr dinfo, JDIMENSION rows_supplied)
136 ppm_dest_ptr dest = (ppm_dest_ptr)dinfo;
137 register char *bufferptr;
138 register _JSAMPROW ptr;
139 register JDIMENSION col;
140 register int rindex = rgb_red[cinfo->out_color_space];
141 register int gindex = rgb_green[cinfo->out_color_space];
142 register int bindex = rgb_blue[cinfo->out_color_space];
143 register int ps = rgb_pixelsize[cinfo->out_color_space];
145 ptr = dest->pub._buffer[0];
146 bufferptr = dest->iobuffer;
147 for (col = cinfo->output_width; col > 0; col--) {
148 PUTPPMSAMPLE(bufferptr, ptr[rindex]);
149 PUTPPMSAMPLE(bufferptr, ptr[gindex]);
150 PUTPPMSAMPLE(bufferptr, ptr[bindex]);
153 fwrite(dest->iobuffer, 1, dest->buffer_width, dest->pub.output_file);
158 * Convert CMYK to RGB.
162 put_cmyk(j_decompress_ptr cinfo, djpeg_dest_ptr dinfo,
163 JDIMENSION rows_supplied)
165 ppm_dest_ptr dest = (ppm_dest_ptr)dinfo;
166 register char *bufferptr;
167 register _JSAMPROW ptr;
168 register JDIMENSION col;
170 ptr = dest->pub._buffer[0];
171 bufferptr = dest->iobuffer;
172 for (col = cinfo->output_width; col > 0; col--) {
173 _JSAMPLE r, g, b, c = *ptr++, m = *ptr++, y = *ptr++, k = *ptr++;
174 cmyk_to_rgb(c, m, y, k, &r, &g, &b);
175 PUTPPMSAMPLE(bufferptr, r);
176 PUTPPMSAMPLE(bufferptr, g);
177 PUTPPMSAMPLE(bufferptr, b);
179 fwrite(dest->iobuffer, 1, dest->buffer_width, dest->pub.output_file);
184 * Write some pixel data when color quantization is in effect.
185 * We have to demap the color index values to straight data.
189 put_demapped_rgb(j_decompress_ptr cinfo, djpeg_dest_ptr dinfo,
190 JDIMENSION rows_supplied)
192 ppm_dest_ptr dest = (ppm_dest_ptr)dinfo;
193 register char *bufferptr;
195 register _JSAMPROW ptr;
196 register _JSAMPROW color_map0 = ((_JSAMPARRAY)cinfo->colormap)[0];
197 register _JSAMPROW color_map1 = ((_JSAMPARRAY)cinfo->colormap)[1];
198 register _JSAMPROW color_map2 = ((_JSAMPARRAY)cinfo->colormap)[2];
199 register JDIMENSION col;
201 ptr = dest->pub._buffer[0];
202 bufferptr = dest->iobuffer;
203 for (col = cinfo->output_width; col > 0; col--) {
205 PUTPPMSAMPLE(bufferptr, color_map0[pixval]);
206 PUTPPMSAMPLE(bufferptr, color_map1[pixval]);
207 PUTPPMSAMPLE(bufferptr, color_map2[pixval]);
209 fwrite(dest->iobuffer, 1, dest->buffer_width, dest->pub.output_file);
214 put_demapped_gray(j_decompress_ptr cinfo, djpeg_dest_ptr dinfo,
215 JDIMENSION rows_supplied)
217 ppm_dest_ptr dest = (ppm_dest_ptr)dinfo;
218 register char *bufferptr;
219 register _JSAMPROW ptr;
220 register _JSAMPROW color_map = ((_JSAMPARRAY)cinfo->colormap)[0];
221 register JDIMENSION col;
223 ptr = dest->pub._buffer[0];
224 bufferptr = dest->iobuffer;
225 for (col = cinfo->output_width; col > 0; col--) {
226 PUTPPMSAMPLE(bufferptr, color_map[*ptr++]);
228 fwrite(dest->iobuffer, 1, dest->buffer_width, dest->pub.output_file);
233 * Startup: write the file header.
237 start_output_ppm(j_decompress_ptr cinfo, djpeg_dest_ptr dinfo)
239 ppm_dest_ptr dest = (ppm_dest_ptr)dinfo;
241 /* Emit file header */
242 switch (cinfo->out_color_space) {
244 /* emit header for raw PGM format */
245 fprintf(dest->pub.output_file, "P5\n%ld %ld\n%d\n",
246 (long)cinfo->output_width, (long)cinfo->output_height, PPM_MAXVAL);
260 if (!IsExtRGB(cinfo->out_color_space) && cinfo->quantize_colors)
261 ERREXIT(cinfo, JERR_PPM_COLORSPACE);
262 /* emit header for raw PPM format */
263 fprintf(dest->pub.output_file, "P6\n%ld %ld\n%d\n",
264 (long)cinfo->output_width, (long)cinfo->output_height, PPM_MAXVAL);
267 ERREXIT(cinfo, JERR_PPM_COLORSPACE);
273 * Finish up at the end of the file.
277 finish_output_ppm(j_decompress_ptr cinfo, djpeg_dest_ptr dinfo)
279 /* Make sure we wrote the output file OK */
280 fflush(dinfo->output_file);
281 if (ferror(dinfo->output_file))
282 ERREXIT(cinfo, JERR_FILE_WRITE);
287 * Re-calculate buffer dimensions based on output dimensions.
291 calc_buffer_dimensions_ppm(j_decompress_ptr cinfo, djpeg_dest_ptr dinfo)
293 ppm_dest_ptr dest = (ppm_dest_ptr)dinfo;
295 if (cinfo->out_color_space == JCS_GRAYSCALE)
296 dest->samples_per_row = cinfo->output_width * cinfo->out_color_components;
298 dest->samples_per_row = cinfo->output_width * 3;
299 dest->buffer_width = dest->samples_per_row * (BYTESPERSAMPLE * sizeof(char));
304 * The module selection routine for PPM format output.
307 GLOBAL(djpeg_dest_ptr)
308 _jinit_write_ppm(j_decompress_ptr cinfo)
312 if (cinfo->data_precision != BITS_IN_JSAMPLE)
313 ERREXIT1(cinfo, JERR_BAD_PRECISION, cinfo->data_precision);
315 /* Create module interface object, fill in method pointers */
316 dest = (ppm_dest_ptr)
317 (*cinfo->mem->alloc_small) ((j_common_ptr)cinfo, JPOOL_IMAGE,
318 sizeof(ppm_dest_struct));
319 dest->pub.start_output = start_output_ppm;
320 dest->pub.finish_output = finish_output_ppm;
321 dest->pub.calc_buffer_dimensions = calc_buffer_dimensions_ppm;
323 /* Calculate output image dimensions so we can allocate space */
324 jpeg_calc_output_dimensions(cinfo);
326 /* Create physical I/O buffer */
327 dest->pub.calc_buffer_dimensions(cinfo, (djpeg_dest_ptr)dest);
328 dest->iobuffer = (char *)(*cinfo->mem->alloc_small)
329 ((j_common_ptr)cinfo, JPOOL_IMAGE, dest->buffer_width);
331 if (cinfo->quantize_colors || BITS_IN_JSAMPLE != 8 ||
332 sizeof(_JSAMPLE) != sizeof(char) ||
333 #if RGB_RED == 0 && RGB_GREEN == 1 && RGB_BLUE == 2 && RGB_PIXELSIZE == 3
334 (cinfo->out_color_space != JCS_EXT_RGB &&
335 cinfo->out_color_space != JCS_RGB)) {
337 cinfo->out_color_space != JCS_EXT_RGB) {
339 /* When quantizing, we need an output buffer for colormap indexes
340 * that's separate from the physical I/O buffer. We also need a
341 * separate buffer if pixel format translation must take place.
343 dest->pub._buffer = (_JSAMPARRAY)(*cinfo->mem->alloc_sarray)
344 ((j_common_ptr)cinfo, JPOOL_IMAGE,
345 cinfo->output_width * cinfo->output_components, (JDIMENSION)1);
346 dest->pub.buffer_height = 1;
347 if (!cinfo->quantize_colors) {
348 if (IsExtRGB(cinfo->out_color_space))
349 dest->pub.put_pixel_rows = put_rgb;
350 else if (cinfo->out_color_space == JCS_CMYK)
351 dest->pub.put_pixel_rows = put_cmyk;
353 dest->pub.put_pixel_rows = copy_pixel_rows;
354 } else if (cinfo->out_color_space == JCS_GRAYSCALE)
355 dest->pub.put_pixel_rows = put_demapped_gray;
357 dest->pub.put_pixel_rows = put_demapped_rgb;
359 /* We will fwrite() directly from decompressor output buffer. */
360 /* Synthesize a _JSAMPARRAY pointer structure */
361 dest->pixrow = (_JSAMPROW)dest->iobuffer;
362 dest->pub._buffer = &dest->pixrow;
363 dest->pub.buffer_height = 1;
364 dest->pub.put_pixel_rows = put_pixel_rows;
367 return (djpeg_dest_ptr)dest;
370 #endif /* defined(PPM_SUPPORTED) &&
371 (BITS_IN_JSAMPLE != 16 || defined(D_LOSSLESS_SUPPORTED)) */