xref: /plan9/sys/src/cmd/gs/jpeg/wrppm.c (revision 7dd7cddf99dd7472612f1413b4da293630e6b1bc)
1*7dd7cddfSDavid du Colombier /*
2*7dd7cddfSDavid du Colombier  * wrppm.c
3*7dd7cddfSDavid du Colombier  *
4*7dd7cddfSDavid du Colombier  * Copyright (C) 1991-1996, Thomas G. Lane.
5*7dd7cddfSDavid du Colombier  * This file is part of the Independent JPEG Group's software.
6*7dd7cddfSDavid du Colombier  * For conditions of distribution and use, see the accompanying README file.
7*7dd7cddfSDavid du Colombier  *
8*7dd7cddfSDavid du Colombier  * This file contains routines to write output images in PPM/PGM format.
9*7dd7cddfSDavid du Colombier  * The extended 2-byte-per-sample raw PPM/PGM formats are supported.
10*7dd7cddfSDavid du Colombier  * The PBMPLUS library is NOT required to compile this software
11*7dd7cddfSDavid du Colombier  * (but it is highly useful as a set of PPM image manipulation programs).
12*7dd7cddfSDavid du Colombier  *
13*7dd7cddfSDavid du Colombier  * These routines may need modification for non-Unix environments or
14*7dd7cddfSDavid du Colombier  * specialized applications.  As they stand, they assume output to
15*7dd7cddfSDavid du Colombier  * an ordinary stdio stream.
16*7dd7cddfSDavid du Colombier  */
17*7dd7cddfSDavid du Colombier 
18*7dd7cddfSDavid du Colombier #include "cdjpeg.h"		/* Common decls for cjpeg/djpeg applications */
19*7dd7cddfSDavid du Colombier 
20*7dd7cddfSDavid du Colombier #ifdef PPM_SUPPORTED
21*7dd7cddfSDavid du Colombier 
22*7dd7cddfSDavid du Colombier 
23*7dd7cddfSDavid du Colombier /*
24*7dd7cddfSDavid du Colombier  * For 12-bit JPEG data, we either downscale the values to 8 bits
25*7dd7cddfSDavid du Colombier  * (to write standard byte-per-sample PPM/PGM files), or output
26*7dd7cddfSDavid du Colombier  * nonstandard word-per-sample PPM/PGM files.  Downscaling is done
27*7dd7cddfSDavid du Colombier  * if PPM_NORAWWORD is defined (this can be done in the Makefile
28*7dd7cddfSDavid du Colombier  * or in jconfig.h).
29*7dd7cddfSDavid du Colombier  * (When the core library supports data precision reduction, a cleaner
30*7dd7cddfSDavid du Colombier  * implementation will be to ask for that instead.)
31*7dd7cddfSDavid du Colombier  */
32*7dd7cddfSDavid du Colombier 
33*7dd7cddfSDavid du Colombier #if BITS_IN_JSAMPLE == 8
34*7dd7cddfSDavid du Colombier #define PUTPPMSAMPLE(ptr,v)  *ptr++ = (char) (v)
35*7dd7cddfSDavid du Colombier #define BYTESPERSAMPLE 1
36*7dd7cddfSDavid du Colombier #define PPM_MAXVAL 255
37*7dd7cddfSDavid du Colombier #else
38*7dd7cddfSDavid du Colombier #ifdef PPM_NORAWWORD
39*7dd7cddfSDavid du Colombier #define PUTPPMSAMPLE(ptr,v)  *ptr++ = (char) ((v) >> (BITS_IN_JSAMPLE-8))
40*7dd7cddfSDavid du Colombier #define BYTESPERSAMPLE 1
41*7dd7cddfSDavid du Colombier #define PPM_MAXVAL 255
42*7dd7cddfSDavid du Colombier #else
43*7dd7cddfSDavid du Colombier /* The word-per-sample format always puts the LSB first. */
44*7dd7cddfSDavid du Colombier #define PUTPPMSAMPLE(ptr,v)			\
45*7dd7cddfSDavid du Colombier 	{ register int val_ = v;		\
46*7dd7cddfSDavid du Colombier 	  *ptr++ = (char) (val_ & 0xFF);	\
47*7dd7cddfSDavid du Colombier 	  *ptr++ = (char) ((val_ >> 8) & 0xFF);	\
48*7dd7cddfSDavid du Colombier 	}
49*7dd7cddfSDavid du Colombier #define BYTESPERSAMPLE 2
50*7dd7cddfSDavid du Colombier #define PPM_MAXVAL ((1<<BITS_IN_JSAMPLE)-1)
51*7dd7cddfSDavid du Colombier #endif
52*7dd7cddfSDavid du Colombier #endif
53*7dd7cddfSDavid du Colombier 
54*7dd7cddfSDavid du Colombier 
55*7dd7cddfSDavid du Colombier /*
56*7dd7cddfSDavid du Colombier  * When JSAMPLE is the same size as char, we can just fwrite() the
57*7dd7cddfSDavid du Colombier  * decompressed data to the PPM or PGM file.  On PCs, in order to make this
58*7dd7cddfSDavid du Colombier  * work the output buffer must be allocated in near data space, because we are
59*7dd7cddfSDavid du Colombier  * assuming small-data memory model wherein fwrite() can't reach far memory.
60*7dd7cddfSDavid du Colombier  * If you need to process very wide images on a PC, you might have to compile
61*7dd7cddfSDavid du Colombier  * in large-memory model, or else replace fwrite() with a putc() loop ---
62*7dd7cddfSDavid du Colombier  * which will be much slower.
63*7dd7cddfSDavid du Colombier  */
64*7dd7cddfSDavid du Colombier 
65*7dd7cddfSDavid du Colombier 
66*7dd7cddfSDavid du Colombier /* Private version of data destination object */
67*7dd7cddfSDavid du Colombier 
68*7dd7cddfSDavid du Colombier typedef struct {
69*7dd7cddfSDavid du Colombier   struct djpeg_dest_struct pub;	/* public fields */
70*7dd7cddfSDavid du Colombier 
71*7dd7cddfSDavid du Colombier   /* Usually these two pointers point to the same place: */
72*7dd7cddfSDavid du Colombier   char *iobuffer;		/* fwrite's I/O buffer */
73*7dd7cddfSDavid du Colombier   JSAMPROW pixrow;		/* decompressor output buffer */
74*7dd7cddfSDavid du Colombier   size_t buffer_width;		/* width of I/O buffer */
75*7dd7cddfSDavid du Colombier   JDIMENSION samples_per_row;	/* JSAMPLEs per output row */
76*7dd7cddfSDavid du Colombier } ppm_dest_struct;
77*7dd7cddfSDavid du Colombier 
78*7dd7cddfSDavid du Colombier typedef ppm_dest_struct * ppm_dest_ptr;
79*7dd7cddfSDavid du Colombier 
80*7dd7cddfSDavid du Colombier 
81*7dd7cddfSDavid du Colombier /*
82*7dd7cddfSDavid du Colombier  * Write some pixel data.
83*7dd7cddfSDavid du Colombier  * In this module rows_supplied will always be 1.
84*7dd7cddfSDavid du Colombier  *
85*7dd7cddfSDavid du Colombier  * put_pixel_rows handles the "normal" 8-bit case where the decompressor
86*7dd7cddfSDavid du Colombier  * output buffer is physically the same as the fwrite buffer.
87*7dd7cddfSDavid du Colombier  */
88*7dd7cddfSDavid du Colombier 
89*7dd7cddfSDavid du Colombier METHODDEF(void)
put_pixel_rows(j_decompress_ptr cinfo,djpeg_dest_ptr dinfo,JDIMENSION rows_supplied)90*7dd7cddfSDavid du Colombier put_pixel_rows (j_decompress_ptr cinfo, djpeg_dest_ptr dinfo,
91*7dd7cddfSDavid du Colombier 		JDIMENSION rows_supplied)
92*7dd7cddfSDavid du Colombier {
93*7dd7cddfSDavid du Colombier   ppm_dest_ptr dest = (ppm_dest_ptr) dinfo;
94*7dd7cddfSDavid du Colombier 
95*7dd7cddfSDavid du Colombier   (void) JFWRITE(dest->pub.output_file, dest->iobuffer, dest->buffer_width);
96*7dd7cddfSDavid du Colombier }
97*7dd7cddfSDavid du Colombier 
98*7dd7cddfSDavid du Colombier 
99*7dd7cddfSDavid du Colombier /*
100*7dd7cddfSDavid du Colombier  * This code is used when we have to copy the data and apply a pixel
101*7dd7cddfSDavid du Colombier  * format translation.  Typically this only happens in 12-bit mode.
102*7dd7cddfSDavid du Colombier  */
103*7dd7cddfSDavid du Colombier 
104*7dd7cddfSDavid du Colombier METHODDEF(void)
copy_pixel_rows(j_decompress_ptr cinfo,djpeg_dest_ptr dinfo,JDIMENSION rows_supplied)105*7dd7cddfSDavid du Colombier copy_pixel_rows (j_decompress_ptr cinfo, djpeg_dest_ptr dinfo,
106*7dd7cddfSDavid du Colombier 		 JDIMENSION rows_supplied)
107*7dd7cddfSDavid du Colombier {
108*7dd7cddfSDavid du Colombier   ppm_dest_ptr dest = (ppm_dest_ptr) dinfo;
109*7dd7cddfSDavid du Colombier   register char * bufferptr;
110*7dd7cddfSDavid du Colombier   register JSAMPROW ptr;
111*7dd7cddfSDavid du Colombier   register JDIMENSION col;
112*7dd7cddfSDavid du Colombier 
113*7dd7cddfSDavid du Colombier   ptr = dest->pub.buffer[0];
114*7dd7cddfSDavid du Colombier   bufferptr = dest->iobuffer;
115*7dd7cddfSDavid du Colombier   for (col = dest->samples_per_row; col > 0; col--) {
116*7dd7cddfSDavid du Colombier     PUTPPMSAMPLE(bufferptr, GETJSAMPLE(*ptr++));
117*7dd7cddfSDavid du Colombier   }
118*7dd7cddfSDavid du Colombier   (void) JFWRITE(dest->pub.output_file, dest->iobuffer, dest->buffer_width);
119*7dd7cddfSDavid du Colombier }
120*7dd7cddfSDavid du Colombier 
121*7dd7cddfSDavid du Colombier 
122*7dd7cddfSDavid du Colombier /*
123*7dd7cddfSDavid du Colombier  * Write some pixel data when color quantization is in effect.
124*7dd7cddfSDavid du Colombier  * We have to demap the color index values to straight data.
125*7dd7cddfSDavid du Colombier  */
126*7dd7cddfSDavid du Colombier 
127*7dd7cddfSDavid du Colombier METHODDEF(void)
put_demapped_rgb(j_decompress_ptr cinfo,djpeg_dest_ptr dinfo,JDIMENSION rows_supplied)128*7dd7cddfSDavid du Colombier put_demapped_rgb (j_decompress_ptr cinfo, djpeg_dest_ptr dinfo,
129*7dd7cddfSDavid du Colombier 		  JDIMENSION rows_supplied)
130*7dd7cddfSDavid du Colombier {
131*7dd7cddfSDavid du Colombier   ppm_dest_ptr dest = (ppm_dest_ptr) dinfo;
132*7dd7cddfSDavid du Colombier   register char * bufferptr;
133*7dd7cddfSDavid du Colombier   register int pixval;
134*7dd7cddfSDavid du Colombier   register JSAMPROW ptr;
135*7dd7cddfSDavid du Colombier   register JSAMPROW color_map0 = cinfo->colormap[0];
136*7dd7cddfSDavid du Colombier   register JSAMPROW color_map1 = cinfo->colormap[1];
137*7dd7cddfSDavid du Colombier   register JSAMPROW color_map2 = cinfo->colormap[2];
138*7dd7cddfSDavid du Colombier   register JDIMENSION col;
139*7dd7cddfSDavid du Colombier 
140*7dd7cddfSDavid du Colombier   ptr = dest->pub.buffer[0];
141*7dd7cddfSDavid du Colombier   bufferptr = dest->iobuffer;
142*7dd7cddfSDavid du Colombier   for (col = cinfo->output_width; col > 0; col--) {
143*7dd7cddfSDavid du Colombier     pixval = GETJSAMPLE(*ptr++);
144*7dd7cddfSDavid du Colombier     PUTPPMSAMPLE(bufferptr, GETJSAMPLE(color_map0[pixval]));
145*7dd7cddfSDavid du Colombier     PUTPPMSAMPLE(bufferptr, GETJSAMPLE(color_map1[pixval]));
146*7dd7cddfSDavid du Colombier     PUTPPMSAMPLE(bufferptr, GETJSAMPLE(color_map2[pixval]));
147*7dd7cddfSDavid du Colombier   }
148*7dd7cddfSDavid du Colombier   (void) JFWRITE(dest->pub.output_file, dest->iobuffer, dest->buffer_width);
149*7dd7cddfSDavid du Colombier }
150*7dd7cddfSDavid du Colombier 
151*7dd7cddfSDavid du Colombier 
152*7dd7cddfSDavid du Colombier METHODDEF(void)
put_demapped_gray(j_decompress_ptr cinfo,djpeg_dest_ptr dinfo,JDIMENSION rows_supplied)153*7dd7cddfSDavid du Colombier put_demapped_gray (j_decompress_ptr cinfo, djpeg_dest_ptr dinfo,
154*7dd7cddfSDavid du Colombier 		   JDIMENSION rows_supplied)
155*7dd7cddfSDavid du Colombier {
156*7dd7cddfSDavid du Colombier   ppm_dest_ptr dest = (ppm_dest_ptr) dinfo;
157*7dd7cddfSDavid du Colombier   register char * bufferptr;
158*7dd7cddfSDavid du Colombier   register JSAMPROW ptr;
159*7dd7cddfSDavid du Colombier   register JSAMPROW color_map = cinfo->colormap[0];
160*7dd7cddfSDavid du Colombier   register JDIMENSION col;
161*7dd7cddfSDavid du Colombier 
162*7dd7cddfSDavid du Colombier   ptr = dest->pub.buffer[0];
163*7dd7cddfSDavid du Colombier   bufferptr = dest->iobuffer;
164*7dd7cddfSDavid du Colombier   for (col = cinfo->output_width; col > 0; col--) {
165*7dd7cddfSDavid du Colombier     PUTPPMSAMPLE(bufferptr, GETJSAMPLE(color_map[GETJSAMPLE(*ptr++)]));
166*7dd7cddfSDavid du Colombier   }
167*7dd7cddfSDavid du Colombier   (void) JFWRITE(dest->pub.output_file, dest->iobuffer, dest->buffer_width);
168*7dd7cddfSDavid du Colombier }
169*7dd7cddfSDavid du Colombier 
170*7dd7cddfSDavid du Colombier 
171*7dd7cddfSDavid du Colombier /*
172*7dd7cddfSDavid du Colombier  * Startup: write the file header.
173*7dd7cddfSDavid du Colombier  */
174*7dd7cddfSDavid du Colombier 
175*7dd7cddfSDavid du Colombier METHODDEF(void)
start_output_ppm(j_decompress_ptr cinfo,djpeg_dest_ptr dinfo)176*7dd7cddfSDavid du Colombier start_output_ppm (j_decompress_ptr cinfo, djpeg_dest_ptr dinfo)
177*7dd7cddfSDavid du Colombier {
178*7dd7cddfSDavid du Colombier   ppm_dest_ptr dest = (ppm_dest_ptr) dinfo;
179*7dd7cddfSDavid du Colombier 
180*7dd7cddfSDavid du Colombier   /* Emit file header */
181*7dd7cddfSDavid du Colombier   switch (cinfo->out_color_space) {
182*7dd7cddfSDavid du Colombier   case JCS_GRAYSCALE:
183*7dd7cddfSDavid du Colombier     /* emit header for raw PGM format */
184*7dd7cddfSDavid du Colombier     fprintf(dest->pub.output_file, "P5\n%ld %ld\n%d\n",
185*7dd7cddfSDavid du Colombier 	    (long) cinfo->output_width, (long) cinfo->output_height,
186*7dd7cddfSDavid du Colombier 	    PPM_MAXVAL);
187*7dd7cddfSDavid du Colombier     break;
188*7dd7cddfSDavid du Colombier   case JCS_RGB:
189*7dd7cddfSDavid du Colombier     /* emit header for raw PPM format */
190*7dd7cddfSDavid du Colombier     fprintf(dest->pub.output_file, "P6\n%ld %ld\n%d\n",
191*7dd7cddfSDavid du Colombier 	    (long) cinfo->output_width, (long) cinfo->output_height,
192*7dd7cddfSDavid du Colombier 	    PPM_MAXVAL);
193*7dd7cddfSDavid du Colombier     break;
194*7dd7cddfSDavid du Colombier   default:
195*7dd7cddfSDavid du Colombier     ERREXIT(cinfo, JERR_PPM_COLORSPACE);
196*7dd7cddfSDavid du Colombier   }
197*7dd7cddfSDavid du Colombier }
198*7dd7cddfSDavid du Colombier 
199*7dd7cddfSDavid du Colombier 
200*7dd7cddfSDavid du Colombier /*
201*7dd7cddfSDavid du Colombier  * Finish up at the end of the file.
202*7dd7cddfSDavid du Colombier  */
203*7dd7cddfSDavid du Colombier 
204*7dd7cddfSDavid du Colombier METHODDEF(void)
finish_output_ppm(j_decompress_ptr cinfo,djpeg_dest_ptr dinfo)205*7dd7cddfSDavid du Colombier finish_output_ppm (j_decompress_ptr cinfo, djpeg_dest_ptr dinfo)
206*7dd7cddfSDavid du Colombier {
207*7dd7cddfSDavid du Colombier   /* Make sure we wrote the output file OK */
208*7dd7cddfSDavid du Colombier   fflush(dinfo->output_file);
209*7dd7cddfSDavid du Colombier   if (ferror(dinfo->output_file))
210*7dd7cddfSDavid du Colombier     ERREXIT(cinfo, JERR_FILE_WRITE);
211*7dd7cddfSDavid du Colombier }
212*7dd7cddfSDavid du Colombier 
213*7dd7cddfSDavid du Colombier 
214*7dd7cddfSDavid du Colombier /*
215*7dd7cddfSDavid du Colombier  * The module selection routine for PPM format output.
216*7dd7cddfSDavid du Colombier  */
217*7dd7cddfSDavid du Colombier 
218*7dd7cddfSDavid du Colombier GLOBAL(djpeg_dest_ptr)
jinit_write_ppm(j_decompress_ptr cinfo)219*7dd7cddfSDavid du Colombier jinit_write_ppm (j_decompress_ptr cinfo)
220*7dd7cddfSDavid du Colombier {
221*7dd7cddfSDavid du Colombier   ppm_dest_ptr dest;
222*7dd7cddfSDavid du Colombier 
223*7dd7cddfSDavid du Colombier   /* Create module interface object, fill in method pointers */
224*7dd7cddfSDavid du Colombier   dest = (ppm_dest_ptr)
225*7dd7cddfSDavid du Colombier       (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE,
226*7dd7cddfSDavid du Colombier 				  SIZEOF(ppm_dest_struct));
227*7dd7cddfSDavid du Colombier   dest->pub.start_output = start_output_ppm;
228*7dd7cddfSDavid du Colombier   dest->pub.finish_output = finish_output_ppm;
229*7dd7cddfSDavid du Colombier 
230*7dd7cddfSDavid du Colombier   /* Calculate output image dimensions so we can allocate space */
231*7dd7cddfSDavid du Colombier   jpeg_calc_output_dimensions(cinfo);
232*7dd7cddfSDavid du Colombier 
233*7dd7cddfSDavid du Colombier   /* Create physical I/O buffer.  Note we make this near on a PC. */
234*7dd7cddfSDavid du Colombier   dest->samples_per_row = cinfo->output_width * cinfo->out_color_components;
235*7dd7cddfSDavid du Colombier   dest->buffer_width = dest->samples_per_row * (BYTESPERSAMPLE * SIZEOF(char));
236*7dd7cddfSDavid du Colombier   dest->iobuffer = (char *) (*cinfo->mem->alloc_small)
237*7dd7cddfSDavid du Colombier     ((j_common_ptr) cinfo, JPOOL_IMAGE, dest->buffer_width);
238*7dd7cddfSDavid du Colombier 
239*7dd7cddfSDavid du Colombier   if (cinfo->quantize_colors || BITS_IN_JSAMPLE != 8 ||
240*7dd7cddfSDavid du Colombier       SIZEOF(JSAMPLE) != SIZEOF(char)) {
241*7dd7cddfSDavid du Colombier     /* When quantizing, we need an output buffer for colormap indexes
242*7dd7cddfSDavid du Colombier      * that's separate from the physical I/O buffer.  We also need a
243*7dd7cddfSDavid du Colombier      * separate buffer if pixel format translation must take place.
244*7dd7cddfSDavid du Colombier      */
245*7dd7cddfSDavid du Colombier     dest->pub.buffer = (*cinfo->mem->alloc_sarray)
246*7dd7cddfSDavid du Colombier       ((j_common_ptr) cinfo, JPOOL_IMAGE,
247*7dd7cddfSDavid du Colombier        cinfo->output_width * cinfo->output_components, (JDIMENSION) 1);
248*7dd7cddfSDavid du Colombier     dest->pub.buffer_height = 1;
249*7dd7cddfSDavid du Colombier     if (! cinfo->quantize_colors)
250*7dd7cddfSDavid du Colombier       dest->pub.put_pixel_rows = copy_pixel_rows;
251*7dd7cddfSDavid du Colombier     else if (cinfo->out_color_space == JCS_GRAYSCALE)
252*7dd7cddfSDavid du Colombier       dest->pub.put_pixel_rows = put_demapped_gray;
253*7dd7cddfSDavid du Colombier     else
254*7dd7cddfSDavid du Colombier       dest->pub.put_pixel_rows = put_demapped_rgb;
255*7dd7cddfSDavid du Colombier   } else {
256*7dd7cddfSDavid du Colombier     /* We will fwrite() directly from decompressor output buffer. */
257*7dd7cddfSDavid du Colombier     /* Synthesize a JSAMPARRAY pointer structure */
258*7dd7cddfSDavid du Colombier     /* Cast here implies near->far pointer conversion on PCs */
259*7dd7cddfSDavid du Colombier     dest->pixrow = (JSAMPROW) dest->iobuffer;
260*7dd7cddfSDavid du Colombier     dest->pub.buffer = & dest->pixrow;
261*7dd7cddfSDavid du Colombier     dest->pub.buffer_height = 1;
262*7dd7cddfSDavid du Colombier     dest->pub.put_pixel_rows = put_pixel_rows;
263*7dd7cddfSDavid du Colombier   }
264*7dd7cddfSDavid du Colombier 
265*7dd7cddfSDavid du Colombier   return (djpeg_dest_ptr) dest;
266*7dd7cddfSDavid du Colombier }
267*7dd7cddfSDavid du Colombier 
268*7dd7cddfSDavid du Colombier #endif /* PPM_SUPPORTED */
269