1 /* 2 * rdppm.c 3 * 4 * This file was part of the Independent JPEG Group's software: 5 * Copyright (C) 1991-1997, Thomas G. Lane. 6 * Modified 2009 by Bill Allombert, Guido Vollbeding. 7 * libjpeg-turbo Modifications: 8 * Copyright (C) 2015, 2016, D. R. Commander. 9 * For conditions of distribution and use, see the accompanying README.ijg 10 * file. 11 * 12 * This file contains routines to read input 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 input from 19 * an ordinary stdio stream. They further assume that reading begins 20 * at the start of the file; start_input may need work if the 21 * user interface has already read some data (e.g., to determine that 22 * the file is indeed PPM format). 23 */ 24 25 #include "cdjpeg.h" /* Common decls for cjpeg/djpeg applications */ 26 27 #ifdef PPM_SUPPORTED 28 29 30 /* Portions of this code are based on the PBMPLUS library, which is: 31 ** 32 ** Copyright (C) 1988 by Jef Poskanzer. 33 ** 34 ** Permission to use, copy, modify, and distribute this software and its 35 ** documentation for any purpose and without fee is hereby granted, provided 36 ** that the above copyright notice appear in all copies and that both that 37 ** copyright notice and this permission notice appear in supporting 38 ** documentation. This software is provided "as is" without express or 39 ** implied warranty. 40 */ 41 42 43 /* Macros to deal with unsigned chars as efficiently as compiler allows */ 44 45 #ifdef HAVE_UNSIGNED_CHAR 46 typedef unsigned char U_CHAR; 47 #define UCH(x) ((int) (x)) 48 #else /* !HAVE_UNSIGNED_CHAR */ 49 #ifdef __CHAR_UNSIGNED__ 50 typedef char U_CHAR; 51 #define UCH(x) ((int) (x)) 52 #else 53 typedef char U_CHAR; 54 #define UCH(x) ((int) (x) & 0xFF) 55 #endif 56 #endif /* HAVE_UNSIGNED_CHAR */ 57 58 59 #define ReadOK(file,buffer,len) (JFREAD(file,buffer,len) == ((size_t) (len))) 60 61 62 /* Private version of data source object */ 63 64 typedef struct { 65 struct cjpeg_source_struct pub; /* public fields */ 66 67 /* Usually these two pointers point to the same place: */ 68 U_CHAR *iobuffer; /* fread's I/O buffer */ 69 JSAMPROW pixrow; /* compressor input buffer */ 70 size_t buffer_width; /* width of I/O buffer */ 71 JSAMPLE *rescale; /* => maxval-remapping array, or NULL */ 72 int maxval; 73 } ppm_source_struct; 74 75 typedef ppm_source_struct *ppm_source_ptr; 76 77 78 LOCAL(int) 79 pbm_getc (FILE *infile) 80 /* Read next char, skipping over any comments */ 81 /* A comment/newline sequence is returned as a newline */ 82 { 83 register int ch; 84 85 ch = getc(infile); 86 if (ch == '#') { 87 do { 88 ch = getc(infile); 89 } while (ch != '\n' && ch != EOF); 90 } 91 return ch; 92 } 93 94 95 LOCAL(unsigned int) 96 read_pbm_integer (j_compress_ptr cinfo, FILE *infile, unsigned int maxval) 97 /* Read an unsigned decimal integer from the PPM file */ 98 /* Swallows one trailing character after the integer */ 99 /* Note that on a 16-bit-int machine, only values up to 64k can be read. */ 100 /* This should not be a problem in practice. */ 101 { 102 register int ch; 103 register unsigned int val; 104 105 /* Skip any leading whitespace */ 106 do { 107 ch = pbm_getc(infile); 108 if (ch == EOF) 109 ERREXIT(cinfo, JERR_INPUT_EOF); 110 } while (ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r'); 111 112 if (ch < '0' || ch > '9') 113 ERREXIT(cinfo, JERR_PPM_NONNUMERIC); 114 115 val = ch - '0'; 116 while ((ch = pbm_getc(infile)) >= '0' && ch <= '9') { 117 val *= 10; 118 val += ch - '0'; 119 } 120 121 if (val > maxval) 122 ERREXIT(cinfo, JERR_PPM_TOOLARGE); 123 124 return val; 125 } 126 127 128 /* 129 * Read one row of pixels. 130 * 131 * We provide several different versions depending on input file format. 132 * In all cases, input is scaled to the size of JSAMPLE. 133 * 134 * A really fast path is provided for reading byte/sample raw files with 135 * maxval = MAXJSAMPLE, which is the normal case for 8-bit data. 136 */ 137 138 139 METHODDEF(JDIMENSION) 140 get_text_gray_row (j_compress_ptr cinfo, cjpeg_source_ptr sinfo) 141 /* This version is for reading text-format PGM files with any maxval */ 142 { 143 ppm_source_ptr source = (ppm_source_ptr) sinfo; 144 FILE *infile = source->pub.input_file; 145 register JSAMPROW ptr; 146 register JSAMPLE *rescale = source->rescale; 147 JDIMENSION col; 148 unsigned int maxval = source->maxval; 149 150 ptr = source->pub.buffer[0]; 151 for (col = cinfo->image_width; col > 0; col--) { 152 *ptr++ = rescale[read_pbm_integer(cinfo, infile, maxval)]; 153 } 154 return 1; 155 } 156 157 158 METHODDEF(JDIMENSION) 159 get_text_rgb_row (j_compress_ptr cinfo, cjpeg_source_ptr sinfo) 160 /* This version is for reading text-format PPM files with any maxval */ 161 { 162 ppm_source_ptr source = (ppm_source_ptr) sinfo; 163 FILE *infile = source->pub.input_file; 164 register JSAMPROW ptr; 165 register JSAMPLE *rescale = source->rescale; 166 JDIMENSION col; 167 unsigned int maxval = source->maxval; 168 169 ptr = source->pub.buffer[0]; 170 for (col = cinfo->image_width; col > 0; col--) { 171 *ptr++ = rescale[read_pbm_integer(cinfo, infile, maxval)]; 172 *ptr++ = rescale[read_pbm_integer(cinfo, infile, maxval)]; 173 *ptr++ = rescale[read_pbm_integer(cinfo, infile, maxval)]; 174 } 175 return 1; 176 } 177 178 179 METHODDEF(JDIMENSION) 180 get_scaled_gray_row (j_compress_ptr cinfo, cjpeg_source_ptr sinfo) 181 /* This version is for reading raw-byte-format PGM files with any maxval */ 182 { 183 ppm_source_ptr source = (ppm_source_ptr) sinfo; 184 register JSAMPROW ptr; 185 register U_CHAR *bufferptr; 186 register JSAMPLE *rescale = source->rescale; 187 JDIMENSION col; 188 189 if (! ReadOK(source->pub.input_file, source->iobuffer, source->buffer_width)) 190 ERREXIT(cinfo, JERR_INPUT_EOF); 191 ptr = source->pub.buffer[0]; 192 bufferptr = source->iobuffer; 193 for (col = cinfo->image_width; col > 0; col--) { 194 *ptr++ = rescale[UCH(*bufferptr++)]; 195 } 196 return 1; 197 } 198 199 200 METHODDEF(JDIMENSION) 201 get_scaled_rgb_row (j_compress_ptr cinfo, cjpeg_source_ptr sinfo) 202 /* This version is for reading raw-byte-format PPM files with any maxval */ 203 { 204 ppm_source_ptr source = (ppm_source_ptr) sinfo; 205 register JSAMPROW ptr; 206 register U_CHAR *bufferptr; 207 register JSAMPLE *rescale = source->rescale; 208 JDIMENSION col; 209 210 if (! ReadOK(source->pub.input_file, source->iobuffer, source->buffer_width)) 211 ERREXIT(cinfo, JERR_INPUT_EOF); 212 ptr = source->pub.buffer[0]; 213 bufferptr = source->iobuffer; 214 for (col = cinfo->image_width; col > 0; col--) { 215 *ptr++ = rescale[UCH(*bufferptr++)]; 216 *ptr++ = rescale[UCH(*bufferptr++)]; 217 *ptr++ = rescale[UCH(*bufferptr++)]; 218 } 219 return 1; 220 } 221 222 223 METHODDEF(JDIMENSION) 224 get_raw_row (j_compress_ptr cinfo, cjpeg_source_ptr sinfo) 225 /* This version is for reading raw-byte-format files with maxval = MAXJSAMPLE. 226 * In this case we just read right into the JSAMPLE buffer! 227 * Note that same code works for PPM and PGM files. 228 */ 229 { 230 ppm_source_ptr source = (ppm_source_ptr) sinfo; 231 232 if (! ReadOK(source->pub.input_file, source->iobuffer, source->buffer_width)) 233 ERREXIT(cinfo, JERR_INPUT_EOF); 234 return 1; 235 } 236 237 238 METHODDEF(JDIMENSION) 239 get_word_gray_row (j_compress_ptr cinfo, cjpeg_source_ptr sinfo) 240 /* This version is for reading raw-word-format PGM files with any maxval */ 241 { 242 ppm_source_ptr source = (ppm_source_ptr) sinfo; 243 register JSAMPROW ptr; 244 register U_CHAR *bufferptr; 245 register JSAMPLE *rescale = source->rescale; 246 JDIMENSION col; 247 unsigned int maxval = source->maxval; 248 249 if (! ReadOK(source->pub.input_file, source->iobuffer, source->buffer_width)) 250 ERREXIT(cinfo, JERR_INPUT_EOF); 251 ptr = source->pub.buffer[0]; 252 bufferptr = source->iobuffer; 253 for (col = cinfo->image_width; col > 0; col--) { 254 register unsigned int temp; 255 temp = UCH(*bufferptr++) << 8; 256 temp |= UCH(*bufferptr++); 257 if (temp > maxval) 258 ERREXIT(cinfo, JERR_PPM_TOOLARGE); 259 *ptr++ = rescale[temp]; 260 } 261 return 1; 262 } 263 264 265 METHODDEF(JDIMENSION) 266 get_word_rgb_row (j_compress_ptr cinfo, cjpeg_source_ptr sinfo) 267 /* This version is for reading raw-word-format PPM files with any maxval */ 268 { 269 ppm_source_ptr source = (ppm_source_ptr) sinfo; 270 register JSAMPROW ptr; 271 register U_CHAR *bufferptr; 272 register JSAMPLE *rescale = source->rescale; 273 JDIMENSION col; 274 unsigned int maxval = source->maxval; 275 276 if (! ReadOK(source->pub.input_file, source->iobuffer, source->buffer_width)) 277 ERREXIT(cinfo, JERR_INPUT_EOF); 278 ptr = source->pub.buffer[0]; 279 bufferptr = source->iobuffer; 280 for (col = cinfo->image_width; col > 0; col--) { 281 register unsigned int temp; 282 temp = UCH(*bufferptr++) << 8; 283 temp |= UCH(*bufferptr++); 284 if (temp > maxval) 285 ERREXIT(cinfo, JERR_PPM_TOOLARGE); 286 *ptr++ = rescale[temp]; 287 temp = UCH(*bufferptr++) << 8; 288 temp |= UCH(*bufferptr++); 289 if (temp > maxval) 290 ERREXIT(cinfo, JERR_PPM_TOOLARGE); 291 *ptr++ = rescale[temp]; 292 temp = UCH(*bufferptr++) << 8; 293 temp |= UCH(*bufferptr++); 294 if (temp > maxval) 295 ERREXIT(cinfo, JERR_PPM_TOOLARGE); 296 *ptr++ = rescale[temp]; 297 } 298 return 1; 299 } 300 301 302 /* 303 * Read the file header; return image size and component count. 304 */ 305 306 METHODDEF(void) 307 start_input_ppm (j_compress_ptr cinfo, cjpeg_source_ptr sinfo) 308 { 309 ppm_source_ptr source = (ppm_source_ptr) sinfo; 310 int c; 311 unsigned int w, h, maxval; 312 boolean need_iobuffer, use_raw_buffer, need_rescale; 313 314 if (getc(source->pub.input_file) != 'P') 315 ERREXIT(cinfo, JERR_PPM_NOT); 316 317 c = getc(source->pub.input_file); /* subformat discriminator character */ 318 319 /* detect unsupported variants (ie, PBM) before trying to read header */ 320 switch (c) { 321 case '2': /* it's a text-format PGM file */ 322 case '3': /* it's a text-format PPM file */ 323 case '5': /* it's a raw-format PGM file */ 324 case '6': /* it's a raw-format PPM file */ 325 break; 326 default: 327 ERREXIT(cinfo, JERR_PPM_NOT); 328 break; 329 } 330 331 /* fetch the remaining header info */ 332 w = read_pbm_integer(cinfo, source->pub.input_file, 65535); 333 h = read_pbm_integer(cinfo, source->pub.input_file, 65535); 334 maxval = read_pbm_integer(cinfo, source->pub.input_file, 65535); 335 336 if (w <= 0 || h <= 0 || maxval <= 0) /* error check */ 337 ERREXIT(cinfo, JERR_PPM_NOT); 338 339 cinfo->data_precision = BITS_IN_JSAMPLE; /* we always rescale data to this */ 340 cinfo->image_width = (JDIMENSION) w; 341 cinfo->image_height = (JDIMENSION) h; 342 source->maxval = maxval; 343 344 /* initialize flags to most common settings */ 345 need_iobuffer = TRUE; /* do we need an I/O buffer? */ 346 use_raw_buffer = FALSE; /* do we map input buffer onto I/O buffer? */ 347 need_rescale = TRUE; /* do we need a rescale array? */ 348 349 switch (c) { 350 case '2': /* it's a text-format PGM file */ 351 cinfo->input_components = 1; 352 cinfo->in_color_space = JCS_GRAYSCALE; 353 TRACEMS2(cinfo, 1, JTRC_PGM_TEXT, w, h); 354 source->pub.get_pixel_rows = get_text_gray_row; 355 need_iobuffer = FALSE; 356 break; 357 358 case '3': /* it's a text-format PPM file */ 359 cinfo->input_components = 3; 360 cinfo->in_color_space = JCS_RGB; 361 TRACEMS2(cinfo, 1, JTRC_PPM_TEXT, w, h); 362 source->pub.get_pixel_rows = get_text_rgb_row; 363 need_iobuffer = FALSE; 364 break; 365 366 case '5': /* it's a raw-format PGM file */ 367 cinfo->input_components = 1; 368 cinfo->in_color_space = JCS_GRAYSCALE; 369 TRACEMS2(cinfo, 1, JTRC_PGM, w, h); 370 if (maxval > 255) { 371 source->pub.get_pixel_rows = get_word_gray_row; 372 } else if (maxval == MAXJSAMPLE && sizeof(JSAMPLE) == sizeof(U_CHAR)) { 373 source->pub.get_pixel_rows = get_raw_row; 374 use_raw_buffer = TRUE; 375 need_rescale = FALSE; 376 } else { 377 source->pub.get_pixel_rows = get_scaled_gray_row; 378 } 379 break; 380 381 case '6': /* it's a raw-format PPM file */ 382 cinfo->input_components = 3; 383 cinfo->in_color_space = JCS_RGB; 384 TRACEMS2(cinfo, 1, JTRC_PPM, w, h); 385 if (maxval > 255) { 386 source->pub.get_pixel_rows = get_word_rgb_row; 387 } else if (maxval == MAXJSAMPLE && sizeof(JSAMPLE) == sizeof(U_CHAR)) { 388 source->pub.get_pixel_rows = get_raw_row; 389 use_raw_buffer = TRUE; 390 need_rescale = FALSE; 391 } else { 392 source->pub.get_pixel_rows = get_scaled_rgb_row; 393 } 394 break; 395 } 396 397 /* Allocate space for I/O buffer: 1 or 3 bytes or words/pixel. */ 398 if (need_iobuffer) { 399 source->buffer_width = (size_t) w * cinfo->input_components * 400 ((maxval <= 255) ? sizeof(U_CHAR) : (2 * sizeof(U_CHAR))); 401 source->iobuffer = (U_CHAR *) 402 (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, 403 source->buffer_width); 404 } 405 406 /* Create compressor input buffer. */ 407 if (use_raw_buffer) { 408 /* For unscaled raw-input case, we can just map it onto the I/O buffer. */ 409 /* Synthesize a JSAMPARRAY pointer structure */ 410 source->pixrow = (JSAMPROW) source->iobuffer; 411 source->pub.buffer = & source->pixrow; 412 source->pub.buffer_height = 1; 413 } else { 414 /* Need to translate anyway, so make a separate sample buffer. */ 415 source->pub.buffer = (*cinfo->mem->alloc_sarray) 416 ((j_common_ptr) cinfo, JPOOL_IMAGE, 417 (JDIMENSION) w * cinfo->input_components, (JDIMENSION) 1); 418 source->pub.buffer_height = 1; 419 } 420 421 /* Compute the rescaling array if required. */ 422 if (need_rescale) { 423 long val, half_maxval; 424 425 /* On 16-bit-int machines we have to be careful of maxval = 65535 */ 426 source->rescale = (JSAMPLE *) 427 (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, 428 (size_t) (((long) maxval + 1L) * 429 sizeof(JSAMPLE))); 430 half_maxval = maxval / 2; 431 for (val = 0; val <= (long) maxval; val++) { 432 /* The multiplication here must be done in 32 bits to avoid overflow */ 433 source->rescale[val] = (JSAMPLE) ((val * MAXJSAMPLE + half_maxval) / 434 maxval); 435 } 436 } 437 } 438 439 440 /* 441 * Finish up at the end of the file. 442 */ 443 444 METHODDEF(void) 445 finish_input_ppm (j_compress_ptr cinfo, cjpeg_source_ptr sinfo) 446 { 447 /* no work */ 448 } 449 450 451 /* 452 * The module selection routine for PPM format input. 453 */ 454 455 GLOBAL(cjpeg_source_ptr) 456 jinit_read_ppm (j_compress_ptr cinfo) 457 { 458 ppm_source_ptr source; 459 460 /* Create module interface object */ 461 source = (ppm_source_ptr) 462 (*cinfo->mem->alloc_small) ((j_common_ptr) cinfo, JPOOL_IMAGE, 463 sizeof(ppm_source_struct)); 464 /* Fill in method ptrs, except get_pixel_rows which start_input sets */ 465 source->pub.start_input = start_input_ppm; 466 source->pub.finish_input = finish_input_ppm; 467 468 return (cjpeg_source_ptr) source; 469 } 470 471 #endif /* PPM_SUPPORTED */ 472