1/* This code is ripped from Autotrace-0.29. Small modifications by pts. */ 2 3/* input-pnm.ci: 4 * The pnm reading and writing code was written from scratch by Erik Nygren 5 * (nygren@mit.edu) based on the specifications in the man pages and 6 * does not contain any code from the netpbm or pbmplus distributions. 7 */ 8 9#ifdef __GNUC__ 10#ifndef __clang__ 11#pragma implementation 12#endif 13#endif 14 15/* #include "types.h" */ 16#include "at_bitmap.h" 17/* #include "input-pnm.h" */ 18/* #include "message.h" */ 19/* #include "xstd.h" */ 20 21/* #include <math.h> -- ceil(); emulated */ 22#include <stdlib.h> /* atoi(...) */ 23 24/**** pts ****/ 25/* #include <ctype.h> */ 26#define isspace(c) ((c)=='\0' || (c)==' ' || ((unsigned char)((c)-011)<=(unsigned char)(015-011))) 27/* ^^^ not strictly POSIX C locale */ 28#define isdigit(c) ((unsigned char)(c-'0')<=(unsigned char)('9'-'0')) 29 30#if 0 31# define PNMFILE FILE 32# define fread_PNMFILE(s,slen,f) fread(s, 1, slen, f) 33#else 34# define PNMFILE /*Filter::UngetFILED*/GenBuffer::Readable 35# define fread_PNMFILE(s,slen,f) f->vi_read(s, slen) 36#endif 37 38/* Declare local data types 39 */ 40 41typedef struct _PNMScanner 42{ 43 PNMFILE *fd; /* The file descriptor of the file being read */ 44 char cur; /* The current character in the input stream */ 45 int eof; /* Have we reached end of file? */ 46} PNMScanner; 47 48typedef struct _PNMInfo 49{ 50 unsigned int xres, yres; /* The size of the image */ 51 int maxval; /* For ascii image files, the max value 52 * which we need to normalize to */ 53 int np; /* Number of image planes (0 for pbm) */ 54 int asciibody; /* 1 if ascii body, 0 if raw body */ 55 /* Routine to use to load the pnm body */ 56 void (* loader) (PNMScanner *, struct _PNMInfo *, unsigned char *); 57} PNMInfo; 58 59/* Contains the information needed to write out PNM rows */ 60typedef struct _PNMRowInfo 61{ 62 PNMFILE *fd; /* File descriptor */ 63 char *rowbuf; /* Buffer for writing out rows */ 64 int xres; /* X resolution */ 65 int np; /* Number of planes */ 66 unsigned char *red; /* Colormap red */ 67 unsigned char *grn; /* Colormap green */ 68 unsigned char *blu; /* Colormap blue */ 69} PNMRowInfo; 70 71/* Save info */ 72typedef struct 73{ 74 int raw; /* raw or ascii */ 75} PNMSaveVals; 76 77typedef struct 78{ 79 int run; /* run */ 80} PNMSaveInterface; 81 82#define PNM_BUFLEN 512 /* The input buffer size for data returned 83 * from the scanner. Note that lines 84 * aren't allowed to be over 256 characters 85 * by the spec anyways so this shouldn't 86 * be an issue. */ 87 88#define SAVE_COMMENT_STRING "# CREATOR: The GIMP's PNM Filter Version 1.0\n" 89 90/* Declare some local functions. 91 */ 92 93static void pnm_load_ascii (PNMScanner *scan, 94 PNMInfo *info, 95 unsigned char *pixel_rgn); 96static void pnm_load_raw (PNMScanner *scan, 97 PNMInfo *info, 98 unsigned char *pixel_rgn); 99static void pnm_load_rawpbm (PNMScanner *scan, 100 PNMInfo *info, 101 unsigned char *pixel_rgn); 102 103static void pnmscanner_destroy (PNMScanner *s); 104#if 0 105static void pnmscanner_createbuffer (PNMScanner *s, 106 unsigned int bufsize); 107static void pnmscanner_getchar (PNMScanner *s); 108static void pnmscanner_getsmalltoken (PNMScanner *s, unsigned char *buf); 109#endif 110static void pnmscanner_eatwhitespace (PNMScanner *s); 111static void pnmscanner_gettoken (PNMScanner *s, 112 unsigned char *buf, 113 unsigned int bufsize); 114static unsigned pnmscanner_getint(PNMScanner *s); 115 116static PNMScanner * pnmscanner_create (PNMFILE *fd); 117 118 119#define pnmscanner_eof(s) ((s)->eof) 120#define pnmscanner_fd(s) ((s)->fd) 121 122#if 0 123/* pnmscanner_getchar --- 124 * Reads a character from the input stream 125 */ 126static void 127pnmscanner_getchar (PNMScanner *s) 128{ 129 if (s->inbuf) 130 { 131 s->cur = s->inbuf[s->inbufpos++]; 132 if (s->inbufpos >= s->inbufvalidsize) 133 { 134 if (s->inbufsize > s->inbufvalidsize) 135 s->eof = 1; 136 else 137 s->inbufvalidsize = fread(s->inbuf, 1, s->inbufsize, s->fd); 138 s->inbufpos = 0; 139 } 140 } 141 else 142 s->eof = !fread(&(s->cur), 1, 1, s->fd); 143} 144#endif 145 146#define pnmscanner_getchar(s) do { s->eof = !fread_PNMFILE(&(s->cur), 1, s->fd); } while(0) 147 148/* pnmscanner_eatwhitespace --- 149 * Eats up whitespace from the input and returns when done or eof. 150 * Also deals with comments. 151 */ 152static inline void pnmscanner_eatwhitespace(PNMScanner *s) { /**** pts ****/ 153 while (1) { 154 if (s->cur=='#') { 155 do pnmscanner_getchar(s); while (s->cur!='\n'); 156 } else if (!isspace(s->cur)) { 157 break; 158 } 159 pnmscanner_getchar(s); 160 } 161} 162 163 164static struct struct_pnm_types 165{ 166 char name; 167 int np; 168 int asciibody; 169 int maxval; 170 void (* loader) (PNMScanner *, struct _PNMInfo *, unsigned char *pixel_rgn); 171} pnm_types[] = 172{ 173 { '1', 0, 1, 1, pnm_load_ascii }, /* ASCII PBM */ 174 { '2', 1, 1, 255, pnm_load_ascii }, /* ASCII PGM */ 175 { '3', 3, 1, 255, pnm_load_ascii }, /* ASCII PPM */ 176 { '4', 0, 0, 1, pnm_load_rawpbm }, /* RAW PBM */ 177 { '5', 1, 0, 255, pnm_load_raw }, /* RAW PGM */ 178 { '6', 3, 0, 255, pnm_load_raw }, /* RAW PPM */ 179 { 0 , 0, 0, 0, NULL} 180}; 181 182#if PTS_SAM2P 183bitmap_type pnm_load_image (PNMFILE* filename) 184#else 185bitmap_type pnm_load_image (at_string filename) 186#endif 187{ 188 char buf[PNM_BUFLEN]; /* buffer for random things like scanning */ 189 PNMInfo *pnminfo; 190 PNMScanner * volatile scan; 191 int ctr; 192 PNMFILE* fd; 193 bitmap_type bitmap; 194 195 #if PTS_SAM2P /**** pts ****/ 196 fd=filename; 197 #else 198 /* open the file */ 199 fd = xfopen (filename, "rb"); 200 if (fd == NULL) 201 { 202 FATAL("PNM: can't open file\n"); 203 BITMAP_BITS (bitmap) = NULL; 204 BITMAP_WIDTH (bitmap) = 0; 205 BITMAP_HEIGHT (bitmap) = 0; 206 BITMAP_PLANES (bitmap) = 0; 207 return (bitmap); 208 } 209 #endif 210 211 212 /* allocate the necessary structures */ 213 /* pnminfo = (PNMInfo *) malloc (sizeof (PNMInfo)); */ 214 XMALLOCT(pnminfo, PNMInfo*, sizeof(PNMInfo)); 215 216 scan = NULL; 217 /* set error handling */ 218 219 scan = pnmscanner_create(fd); 220 221 /* Get magic number */ 222 pnmscanner_gettoken (scan, (unsigned char *)buf, PNM_BUFLEN); 223 if (pnmscanner_eof(scan)) 224 FATALP ("PNM: premature end of file"); 225 if (buf[0] != 'P' || buf[2]) 226 FATALP ("PNM: is not a valid file"); 227 228 /* Look up magic number to see what type of PNM this is */ 229 for (ctr=0; pnm_types[ctr].name; ctr++) 230 if (buf[1] == pnm_types[ctr].name) 231 { 232 pnminfo->np = pnm_types[ctr].np; 233 pnminfo->asciibody = pnm_types[ctr].asciibody; 234 pnminfo->maxval = pnm_types[ctr].maxval; 235 pnminfo->loader = pnm_types[ctr].loader; 236 } 237 if (!pnminfo->loader) 238 FATALP ("PNM: file not in a supported format"); 239 240 pnmscanner_gettoken(scan, (unsigned char *)buf, PNM_BUFLEN); 241 if (pnmscanner_eof(scan)) 242 FATALP ("PNM: premature end of file"); 243 pnminfo->xres = isdigit(*buf)?atoi(buf):0; 244 if (pnminfo->xres<=0) 245 FATALP ("PNM: invalid xres while loading"); 246 247 pnmscanner_gettoken(scan, (unsigned char *)buf, PNM_BUFLEN); 248 if (pnmscanner_eof(scan)) 249 FATALP ("PNM: premature end of file"); 250 pnminfo->yres = isdigit(*buf)?atoi(buf):0; 251 if (pnminfo->yres<=0) 252 FATALP ("PNM: invalid yres while loading"); 253 254 if (pnminfo->np != 0) /* pbm's don't have a maxval field */ 255 { 256 pnmscanner_gettoken(scan, (unsigned char *)buf, PNM_BUFLEN); 257 if (pnmscanner_eof(scan)) 258 FATALP ("PNM: premature end of file"); 259 260 pnminfo->maxval = isdigit(*buf)?atoi(buf):0; 261 if ((pnminfo->maxval<=0) 262 || (pnminfo->maxval>255 && !pnminfo->asciibody)) 263 FATALP ("PNM: invalid maxval while loading"); 264 } 265 266 BITMAP_WIDTH (bitmap) = (at_dimen_t) pnminfo->xres; 267 BITMAP_HEIGHT (bitmap) = (at_dimen_t) pnminfo->yres; 268 269 BITMAP_PLANES (bitmap) = (pnminfo->np)?(pnminfo->np):1; 270 /* BITMAP_BITS (bitmap) = (unsigned char *) malloc (pnminfo->yres * pnminfo->xres * BITMAP_PLANES (bitmap)); */ 271 XMALLOCT(BITMAP_BITS (bitmap), unsigned char *, pnminfo->yres * pnminfo->xres * BITMAP_PLANES (bitmap)); 272 pnminfo->loader (scan, pnminfo, BITMAP_BITS (bitmap)); 273 /* vvv Dat: We detect truncation late truncated files will just have garbage :-( */ 274 if (pnmscanner_eof(scan)) 275 FATALP ("PNM: truncated image data"); 276 277 /* Destroy the scanner */ 278 pnmscanner_destroy (scan); 279 280 /* free the structures */ 281 /* free (pnminfo); */ 282 XFREE(pnminfo); 283 284 /* close the file */ 285 /* xfclose (fd, filename); */ 286 287 return (bitmap); 288} 289 290static void 291pnm_load_ascii (PNMScanner *scan, 292 PNMInfo *info, 293 unsigned char *data) 294{ 295 register unsigned char *d, *dend; 296 unsigned u, s; 297 #if 0 /**** pts ****/ 298 /* Buffer reads to increase performance */ 299 /* !! convert(1) is faster -- maybe buffering helps? */ 300 pnmscanner_createbuffer(scan, 4096); 301 #endif 302 d = data; 303 if (info->np==0) { /* PBM */ 304 dend=d+info->xres*info->yres; 305 while (d!=dend) { 306 /* pnmscanner_getsmalltoken(scan, (unsigned char *)buf); */ 307 pnmscanner_eatwhitespace(scan); 308 *d++=-(scan->cur=='0'); 309 pnmscanner_getchar(scan); 310 } 311 } else { /* PGM or PPM */ /**** pts ****/ 312 dend=d+info->xres*info->yres*info->np; 313 switch (s=info->maxval) { 314 case 255: 315 while (d!=dend) { 316 *d++=pnmscanner_getint(scan); /* Dat: removed isdigit() */ 317 } 318 break; 319 case 15: 320 while (d!=dend) { 321 *d++ = pnmscanner_getint(scan)*17; 322 } 323 break; 324 case 3: 325 while (d!=dend) { 326 *d++ = pnmscanner_getint(scan)*85; 327 } 328 break; 329 case 0: /* avoid division by 0 */ 330 case 1: 331 while (d!=dend) { 332 *d++ = -(0==pnmscanner_getint(scan)); /* (*buf=='0')?0xff:0x00; */ 333 } 334 default: 335 while (d!=dend) { 336 u=pnmscanner_getint(scan); 337 *d++ = (0UL+u*255UL+(s>>1))/s; /* always <= 255 */ 338 } 339 } 340 } 341} 342 343static void 344pnm_load_raw (PNMScanner *scan, 345 PNMInfo *info, 346 unsigned char *data) 347{ 348 unsigned char *d, *dend; 349 unsigned s=info->maxval; 350 slen_t delta, scanlines; 351 PNMFILE *fd=pnmscanner_fd(scan); 352 353 scanlines = info->yres; 354 d = data; 355 delta=info->xres * info->np; 356 dend=d+delta*scanlines; 357 while (d!=dend) { 358 if (info->xres*info->np != fread_PNMFILE((char*)d, delta, fd)) return; 359 d+=delta; 360 } 361 d=data; 362 switch (s=info->maxval) { /**** pts ****/ 363 case 1: case 0: 364 for (; d!=dend; d++) if (*d!=0) *d=255; 365 break; 366 case 3: 367 while (d!=dend) *d++*=85; 368 break; 369 case 15: 370 while (d!=dend) *d++*=17; 371 break; 372 case 255: 373 break; 374 default: 375 for (; d!=dend; d++) *d=(0UL+*d*255UL+(s>>1))/s; /* always <= 255 */ 376 break; 377 } 378} 379 380static void 381pnm_load_rawpbm (PNMScanner *scan, 382 PNMInfo *info, 383 unsigned char *data) 384{ 385 unsigned char *buf; 386 unsigned char curbyte; 387 unsigned char *d; 388 unsigned int x, i; 389 unsigned int start, end, scanlines; 390 PNMFILE *fd; 391 unsigned int rowlen, bufpos; 392 393 fd = pnmscanner_fd(scan); 394 /****pts****/ /* rowlen = (unsigned int)ceil((double)(info->xres)/8.0);*/ 395 rowlen=(info->xres+7)>>3; 396 /* buf = (unsigned char *)malloc(rowlen*sizeof(unsigned char)); */ 397 XMALLOCT(buf, unsigned char*, rowlen*sizeof(unsigned char)); 398 399 start = 0; 400 end = info->yres; 401 scanlines = end - start; 402 d = data; 403 404 for (i = 0; i < scanlines; i++) 405 { 406 if (rowlen != fread_PNMFILE((char*)buf, rowlen, fd)) 407 FATALP ("PNM: error reading file"); 408 bufpos = 0; 409 curbyte = buf[0]; 410 411 for (x = 0; x < info->xres; x++) 412 { 413 if ((x % 8) == 0) { 414 curbyte = buf[bufpos++]; 415 /* // if (curbyte!=0) printf("%d <%u>\n", x, curbyte); */ 416 } 417 /* // if (curbyte!=0) printf("[%u]\n", curbyte); */ 418 d[x] = (curbyte&0x80) ? 0x00 : 0xff; 419 curbyte <<= 1; 420 } 421 422 d += info->xres; 423 } 424 425 XFREE(buf); 426} 427 428/**************** FILE SCANNER UTILITIES **************/ 429 430/* pnmscanner_create --- 431 * Creates a new scanner based on a file descriptor. The 432 * look ahead buffer is one character initially. 433 */ 434static PNMScanner * 435pnmscanner_create (PNMFILE *fd) 436{ 437 PNMScanner *s; 438 439 XMALLOCT (s, PNMScanner*, sizeof(PNMScanner)); 440 s->fd = fd; 441 s->eof = !fread_PNMFILE(&(s->cur), 1, s->fd); 442 return s; 443} 444 445/* pnmscanner_destroy --- 446 * Destroys a scanner and its resources. Doesn't close the fd. 447 */ 448static void 449pnmscanner_destroy (PNMScanner *s) 450{ 451 XFREE(s); 452} 453 454#if 0 /**** pts ****/ 455/* pnmscanner_createbuffer --- 456 * Creates a buffer so we can do buffered reads. 457 */ 458static void 459pnmscanner_createbuffer (PNMScanner *s, 460 unsigned int bufsize) 461{ 462 /* s->inbuf = (char *)malloc(sizeof(char)*bufsize); */ 463 XMALLOCT(s->inbuf, char*, sizeof(char)*bufsize); 464 s->inbufsize = bufsize; 465 s->inbufpos = 0; 466 s->inbufvalidsize = fread(s->inbuf, 1, bufsize, s->fd); 467} 468#endif 469 470/* pnmscanner_gettoken --- 471 * Gets the next token, eating any leading whitespace. 472 */ 473static void pnmscanner_gettoken (PNMScanner *s, 474 unsigned char *buf, 475 unsigned int bufsize) 476{ 477 unsigned char *bufend=buf+bufsize-1; 478 pnmscanner_eatwhitespace(s); 479 while (!pnmscanner_eof(s) && !isspace(s->cur) && s->cur!='#') { 480 if (buf!=bufend) *buf++=s->cur; 481 pnmscanner_getchar(s); 482 } 483 *buf='\0'; 484} 485 486static unsigned pnmscanner_getint(PNMScanner *s) { 487 unsigned ret=0; 488 pnmscanner_eatwhitespace(s); 489 while (!pnmscanner_eof(s)) { 490 if (isdigit(s->cur)) ret=10*ret+s->cur-'0'; 491 else if (isspace(s->cur) || s->cur=='#') break; 492 pnmscanner_getchar(s); 493 } 494 return ret; 495} 496