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