• 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, 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     /* emit header for raw PPM format */
260     fprintf(dest->pub.output_file, "P6\n%ld %ld\n%d\n",
261             (long)cinfo->output_width, (long)cinfo->output_height, PPM_MAXVAL);
262     break;
263   default:
264     ERREXIT(cinfo, JERR_PPM_COLORSPACE);
265   }
266 }
267 
268 
269 /*
270  * Finish up at the end of the file.
271  */
272 
273 METHODDEF(void)
finish_output_ppm(j_decompress_ptr cinfo,djpeg_dest_ptr dinfo)274 finish_output_ppm(j_decompress_ptr cinfo, djpeg_dest_ptr dinfo)
275 {
276   /* Make sure we wrote the output file OK */
277   fflush(dinfo->output_file);
278   if (ferror(dinfo->output_file))
279     ERREXIT(cinfo, JERR_FILE_WRITE);
280 }
281 
282 
283 /*
284  * Re-calculate buffer dimensions based on output dimensions.
285  */
286 
287 METHODDEF(void)
calc_buffer_dimensions_ppm(j_decompress_ptr cinfo,djpeg_dest_ptr dinfo)288 calc_buffer_dimensions_ppm(j_decompress_ptr cinfo, djpeg_dest_ptr dinfo)
289 {
290   ppm_dest_ptr dest = (ppm_dest_ptr)dinfo;
291 
292   if (cinfo->out_color_space == JCS_GRAYSCALE)
293     dest->samples_per_row = cinfo->output_width * cinfo->out_color_components;
294   else
295     dest->samples_per_row = cinfo->output_width * 3;
296   dest->buffer_width = dest->samples_per_row * (BYTESPERSAMPLE * sizeof(char));
297 }
298 
299 
300 /*
301  * The module selection routine for PPM format output.
302  */
303 
304 GLOBAL(djpeg_dest_ptr)
jinit_write_ppm(j_decompress_ptr cinfo)305 jinit_write_ppm(j_decompress_ptr cinfo)
306 {
307   ppm_dest_ptr dest;
308 
309   /* Create module interface object, fill in method pointers */
310   dest = (ppm_dest_ptr)
311       (*cinfo->mem->alloc_small) ((j_common_ptr)cinfo, JPOOL_IMAGE,
312                                   sizeof(ppm_dest_struct));
313   dest->pub.start_output = start_output_ppm;
314   dest->pub.finish_output = finish_output_ppm;
315   dest->pub.calc_buffer_dimensions = calc_buffer_dimensions_ppm;
316 
317   /* Calculate output image dimensions so we can allocate space */
318   jpeg_calc_output_dimensions(cinfo);
319 
320   /* Create physical I/O buffer */
321   dest->pub.calc_buffer_dimensions(cinfo, (djpeg_dest_ptr)dest);
322   dest->iobuffer = (char *)(*cinfo->mem->alloc_small)
323     ((j_common_ptr)cinfo, JPOOL_IMAGE, dest->buffer_width);
324 
325   if (cinfo->quantize_colors || BITS_IN_JSAMPLE != 8 ||
326       sizeof(JSAMPLE) != sizeof(char) ||
327       (cinfo->out_color_space != JCS_EXT_RGB
328 #if RGB_RED == 0 && RGB_GREEN == 1 && RGB_BLUE == 2 && RGB_PIXELSIZE == 3
329        && cinfo->out_color_space != JCS_RGB
330 #endif
331       )) {
332     /* When quantizing, we need an output buffer for colormap indexes
333      * that's separate from the physical I/O buffer.  We also need a
334      * separate buffer if pixel format translation must take place.
335      */
336     dest->pub.buffer = (*cinfo->mem->alloc_sarray)
337       ((j_common_ptr)cinfo, JPOOL_IMAGE,
338        cinfo->output_width * cinfo->output_components, (JDIMENSION)1);
339     dest->pub.buffer_height = 1;
340     if (IsExtRGB(cinfo->out_color_space))
341       dest->pub.put_pixel_rows = put_rgb;
342     else if (cinfo->out_color_space == JCS_CMYK)
343       dest->pub.put_pixel_rows = put_cmyk;
344     else if (!cinfo->quantize_colors)
345       dest->pub.put_pixel_rows = copy_pixel_rows;
346     else if (cinfo->out_color_space == JCS_GRAYSCALE)
347       dest->pub.put_pixel_rows = put_demapped_gray;
348     else
349       dest->pub.put_pixel_rows = put_demapped_rgb;
350   } else {
351     /* We will fwrite() directly from decompressor output buffer. */
352     /* Synthesize a JSAMPARRAY pointer structure */
353     dest->pixrow = (JSAMPROW)dest->iobuffer;
354     dest->pub.buffer = &dest->pixrow;
355     dest->pub.buffer_height = 1;
356     dest->pub.put_pixel_rows = put_pixel_rows;
357   }
358 
359   return (djpeg_dest_ptr)dest;
360 }
361 
362 #endif /* PPM_SUPPORTED */
363