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