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