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