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