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