1 /* libpnmrw.c - PBM/PGM/PPM read/write library
2 **
3 ** Copyright (C) 1988, 1989, 1991, 1992 by Jef Poskanzer.
4 **
5 ** Permission to use, copy, modify, and distribute this software and its
6 ** documentation for any purpose and without fee is hereby granted, provided
7 ** that the above copyright notice appear in all copies and that both that
8 ** copyright notice and this permission notice appear in supporting
9 ** documentation.  This software is provided "as is" without express or
10 ** implied warranty.
11  */
12 
13 /* $Id: libpnmrw.c,v 1.22 2005/03/20 20:15:34 demailly Exp $ */
14 
15 #include <stdlib.h>
16 #include <string.h>
17 
18 #if defined(HAVE_PARAM_H)
19 #include <sys/param.h>
20 #endif
21 
22 #if defined(SVR2) || defined(SVR3) || defined(SVR4) || defined(linux)
23 #ifndef SYSV
24 #define SYSV
25 #endif
26 #endif
27 #if ! ( defined(BSD) || defined(SYSV) || defined(MSDOS) )
28 /* CONFIGURE: If your system is >= 4.2BSD, set the BSD option; if you're a
29 ** System V site, set the SYSV option; and if you're IBM-compatible, set
30 ** MSDOS.  If your compiler is ANSI C, you're probably better off setting
31 ** SYSV - all it affects is string handling.
32  */
33 #define BSD
34 /* #define SYSV */
35 /* #define MSDOS */
36 #endif
37 
38 #include <stdio.h>
39 #include "libpnmrw.h"
40 
41 typedef char *String;
42 #include "../messages.h"
43 
44 #ifdef SYSV
45 #include <string.h>
46 #else				/*SYSV */
47 #include <strings.h>
48 #endif				/*SYSV */
49 
50 #include <errno.h>
51 
52 /* Definitions. */
53 
54 extern void *xmalloc(size_t n);
55 
56 #define pbm_allocarray( cols, rows ) \
57 	((bit**) pm_allocarray( cols, rows, sizeof(bit) ))
58 #define pbm_allocrow( cols ) ((bit*) pm_allocrow( cols, sizeof(bit) ))
59 #define pbm_freearray( bits, rows ) pm_freearray( (char**) bits, rows )
60 #define pbm_freerow( bitrow ) pm_freerow( (char*) bitrow )
61 #define pgm_allocarray( cols, rows ) \
62 	((gray**) pm_allocarray( cols, rows, sizeof(gray) ))
63 #define pgm_allocrow( cols ) ((gray*) pm_allocrow( cols, sizeof(gray) ))
64 #define pgm_freearray( grays, rows ) pm_freearray( (char**) grays, rows )
65 #define pgm_freerow( grayrow ) pm_freerow( (char*) grayrow )
66 #define ppm_allocarray( cols, rows ) \
67 	((pixel**) pm_allocarray( cols, rows, sizeof(pixel) ))
68 #define ppm_allocrow( cols ) ((pixel*) pm_allocrow( cols, sizeof(pixel) ))
69 #define ppm_freearray( pixels, rows ) pm_freearray( (char**) pixels, rows )
70 #define ppm_freerow( pixelrow ) pm_freerow( (char*) pixelrow )
71 
72 
73 /* Variables. */
74 
75 static char *progname;
76 
77 
78 /* Variable-sized arrays. */
79 
80 char *
pm_allocrow(int cols,int size)81 pm_allocrow(int cols, int size)
82 {
83     register char *itrow;
84 
85     itrow = (char *) xmalloc(cols * size);
86     if (itrow == (char *) 0) {
87 	fprintf(stderr, msgText[OUT_OF_MEMORY_ALLOCATING_A_ROW], progname);
88 	return (char *) 0;
89     }
90     return itrow;
91 }
92 
93 void
pm_freerow(char * itrow)94 pm_freerow(char *itrow)
95 {
96     free(itrow);
97 }
98 
99 char **
pm_allocarray(int cols,int rows,int size)100 pm_allocarray(int cols, int rows, int size)
101 {
102     char **its;
103     int i;
104 
105     its = (char **) xmalloc(rows * sizeof(char *));
106     if (its == (char **) 0) {
107 	fprintf(stderr, msgText[OUT_OF_MEMORY_ALLOCATING_AN_ARRAY], progname);
108 	return (char **) 0;
109     }
110     its[0] = (char *) xmalloc(rows * cols * size);
111     if (its[0] == (char *) 0) {
112 	fprintf(stderr, msgText[OUT_OF_MEMORY_ALLOCATING_AN_ARRAY], progname);
113 	free((char *) its);
114 	return (char **) 0;
115     }
116     for (i = 1; i < rows; ++i)
117 	its[i] = &(its[0][i * cols * size]);
118     return its;
119 }
120 
121 void
pm_freearray(char ** its,int rows)122 pm_freearray(char **its, int rows)
123 {
124     free(its[0]);
125     free(its);
126 }
127 
128 
129 /* File open/close that handles "-" as stdin and checks errors. */
130 
131 static void
pm_perror(char * reason)132 pm_perror(char *reason)
133 {
134 #if !defined(__NetBSD__)
135 #if defined(BSD4_4)
136     __const extern char *__const sys_errlist[];
137 #else
138 #ifndef __GLIBC__
139 #ifndef SYS_ERRLIST_DEFINED
140 #ifdef __CYGWIN__
141 #  define sys_errlist _sys_errlist
142 #else
143 #ifdef MISSING_STRERROR
144     extern char *sys_errlist[];
145 #endif
146 #endif
147 #endif
148 #endif
149 #endif
150 #endif
151     char *e;
152 
153 #ifdef MISSING_STRERROR
154     e = (char *)sys_errlist[errno];
155 #else
156     e = (char *)strerror(errno);
157 #endif
158 
159     if (reason != 0 && reason[0] != '\0')
160 	fprintf(stderr, "%s: %s - %s\n", progname, reason, e);
161     else
162 	fprintf(stderr, "%s: %s\n", progname, e);
163 }
164 
165 FILE *
pm_openr(char * name)166 pm_openr(char *name)
167 {
168     FILE *f;
169 
170     if (strcmp(name, "-") == 0)
171 	f = stdin;
172     else {
173 	f = fopen(name, "r");
174 	if (f == NULL) {
175 	    pm_perror(name);
176 	    return (FILE *) 0;
177 	}
178     }
179     return f;
180 }
181 
182 FILE *
pm_openw(char * name)183 pm_openw(char *name)
184 {
185     FILE *f;
186 
187     f = fopen(name, "w");
188     if (f == NULL) {
189 	pm_perror(name);
190 	return (FILE *) 0;
191     }
192     return f;
193 }
194 
195 int
pm_closer(FILE * f)196 pm_closer(FILE * f)
197 {
198     if (ferror(f)) {
199 	fprintf(stderr, msgText[A_FILE_READ_ERROR_OCCURRED_AT_SOME_POINT],
200                         progname);
201 	return -1;
202     }
203     if (f != stdin)
204 	if (fclose(f) != 0) {
205 	    pm_perror("fclose");
206 	    return -1;
207 	}
208     return 0;
209 }
210 
211 int
pm_closew(FILE * f)212 pm_closew(FILE * f)
213 {
214     fflush(f);
215     if (ferror(f)) {
216 	fprintf(stderr, msgText[A_FILE_WRITE_ERROR_OCCURRED_AT_SOME_POINT],
217 		progname);
218 	return -1;
219     }
220     if (f != stdout)
221 	if (fclose(f) != 0) {
222 	    pm_perror("fclose");
223 	    return -1;
224 	}
225     return 0;
226 }
227 
228 static int
pbm_getc(FILE * file)229 pbm_getc(FILE * file)
230 {
231     register int ich;
232 
233     ich = getc(file);
234     if (ich == EOF) {
235 	fprintf(stderr, msgText[READ_ERROR], progname);
236 	return EOF;
237     }
238     if (ich == '#') {
239 	do {
240 	    ich = getc(file);
241 	    if (ich == EOF) {
242 		fprintf(stderr, msgText[READ_ERROR], progname);
243 		return EOF;
244 	    }
245 	}
246 	while (ich != '\n' && ich != '\r');
247     }
248     return ich;
249 }
250 
251 static bit
pbm_getbit(FILE * file)252 pbm_getbit(FILE * file)
253 {
254     register int ich;
255 
256     do {
257 	ich = pbm_getc(file);
258 	if (ich == EOF)
259 	    return -1;
260     }
261     while (ich == ' ' || ich == '\t' || ich == '\n' || ich == '\r');
262 
263     if (ich != '0' && ich != '1') {
264 	fprintf(stderr, msgText[JUNK_IN_FILE_WHERE_BITS_SHOULD_BE], progname);
265 	return -1;
266     }
267     return (ich == '1') ? 1 : 0;
268 }
269 
270 static int
pbm_readmagicnumber(FILE * file)271 pbm_readmagicnumber(FILE * file)
272 {
273     int ich1, ich2;
274 
275     ich1 = getc(file);
276     if (ich1 == EOF) {
277 	fprintf(stderr, msgText[READ_ERROR_READING_MAGIC_NUMBER], progname);
278 	return -1;
279     }
280     ich2 = getc(file);
281     if (ich2 == EOF) {
282 	fprintf(stderr, msgText[READ_ERROR_READING_MAGIC_NUMBER], progname);
283 	return -1;
284     }
285     return ich1 * 256 + ich2;
286 }
287 
288 static int
pbm_getint(FILE * file)289 pbm_getint(FILE * file)
290 {
291     register char ich;
292     register int i;
293 
294     do {
295 	ich = pbm_getc(file);
296 	if (ich == EOF)
297 	    return -1;
298     }
299     while (ich == ' ' || ich == '\t' || ich == '\n' || ich == '\r');
300 
301     if (ich < '0' || ich > '9') {
302 	fprintf(stderr, msgText[JUNK_IN_FILE_WHERE_AN_INTEGER_SHOULD_BE],
303                 progname);
304 	return -1;
305     }
306     i = 0;
307     do {
308 	i = i * 10 + ich - '0';
309 	ich = pbm_getc(file);
310 	if (ich == EOF)
311 	    return -1;
312     }
313     while (ich >= '0' && ich <= '9');
314 
315     return i;
316 }
317 
318 static int
pbm_readpbminitrest(FILE * file,int * colsP,int * rowsP)319 pbm_readpbminitrest(FILE * file, int *colsP, int *rowsP)
320 {
321     /* Read size. */
322     *colsP = pbm_getint(file);
323     *rowsP = pbm_getint(file);
324     if (*colsP == -1 || *rowsP == -1)
325 	return -1;
326     return 0;
327 }
328 
329 static int
pbm_getrawbyte(FILE * file)330 pbm_getrawbyte(FILE * file)
331 {
332     register int iby;
333 
334     iby = getc(file);
335     if (iby == EOF) {
336 	fprintf(stderr, msgText[READ_ERROR], progname);
337 	return -1;
338     }
339     return iby;
340 }
341 
342 static int
pbm_readpbmrow(FILE * file,bit * bitrow,int cols,int format)343 pbm_readpbmrow(FILE * file, bit * bitrow, int cols, int format)
344 {
345     register int col, bitshift, b;
346     register int item = 0;
347     register bit *bP;
348 
349     switch (format) {
350     case PBM_FORMAT:
351 	for (col = 0, bP = bitrow; col < cols; ++col, ++bP) {
352 	    b = pbm_getbit(file);
353 	    if (b == -1)
354 		return -1;
355 	    *bP = b;
356 	}
357 	break;
358 
359     case RPBM_FORMAT:
360 	bitshift = -1;
361 	for (col = 0, bP = bitrow; col < cols; ++col, ++bP) {
362 	    if (bitshift == -1) {
363 		item = pbm_getrawbyte(file);
364 		if (item == -1)
365 		    return -1;
366 		bitshift = 7;
367 	    }
368 	    *bP = (item >> bitshift) & 1;
369 	    --bitshift;
370 	}
371 	break;
372 
373     default:
374 	fprintf(stderr, msgText[CANT_HAPPEN], progname);
375 	return -1;
376     }
377     return 0;
378 }
379 
380 static void
pbm_writepbminit(FILE * file,int cols,int rows,int forceplain)381 pbm_writepbminit(FILE * file, int cols, int rows, int forceplain)
382 {
383     if (!forceplain)
384 	fprintf(file, "%c%c\n%d %d\n", PBM_MAGIC1, RPBM_MAGIC2, cols, rows);
385     else
386 	fprintf(file, "%c%c\n%d %d\n", PBM_MAGIC1, PBM_MAGIC2, cols, rows);
387 }
388 
389 static void
pbm_writepbmrowraw(FILE * file,bit * bitrow,int cols)390 pbm_writepbmrowraw(FILE * file, bit * bitrow, int cols)
391 {
392     register int col, bitshift;
393     register unsigned char item;
394     register bit *bP;
395 
396     bitshift = 7;
397     item = 0;
398     for (col = 0, bP = bitrow; col < cols; ++col, ++bP) {
399 	if (*bP)
400 	    item += 1 << bitshift;
401 	--bitshift;
402 	if (bitshift == -1) {
403 	    putc(item, file);
404 	    bitshift = 7;
405 	    item = 0;
406 	}
407     }
408     if (bitshift != 7)
409 	putc(item, file);
410 }
411 
412 static void
pbm_writepbmrowplain(FILE * file,bit * bitrow,int cols)413 pbm_writepbmrowplain(FILE * file, bit * bitrow, int cols)
414 {
415     register int col, charcount;
416     register bit *bP;
417 
418     charcount = 0;
419     for (col = 0, bP = bitrow; col < cols; ++col, ++bP) {
420 	if (charcount >= 70) {
421 	    putc('\n', file);
422 	    charcount = 0;
423 	}
424 	putc(*bP ? '1' : '0', file);
425 	++charcount;
426     }
427     putc('\n', file);
428 }
429 
430 static void
pbm_writepbmrow(FILE * file,bit * bitrow,int cols,int forceplain)431 pbm_writepbmrow(FILE * file, bit * bitrow, int cols, int forceplain)
432 {
433     if (!forceplain)
434 	pbm_writepbmrowraw(file, bitrow, cols);
435     else
436 	pbm_writepbmrowplain(file, bitrow, cols);
437 }
438 
439 static int
pgm_readpgminitrest(FILE * file,int * colsP,int * rowsP,gray * maxvalP)440 pgm_readpgminitrest(FILE * file, int *colsP, int *rowsP, gray * maxvalP)
441 {
442     int maxval;
443 
444     /* Read size. */
445     *colsP = pbm_getint(file);
446     *rowsP = pbm_getint(file);
447     if (*colsP == -1 || *rowsP == -1)
448 	return -1;
449 
450     /* Read maxval. */
451     maxval = pbm_getint(file);
452     if (maxval == -1)
453 	return -1;
454     if (maxval > PGM_MAXMAXVAL) {
455 	fprintf(stderr, msgText[MAXVAL_IS_TOO_LARGE], progname);
456 	return -1;
457     }
458     *maxvalP = maxval;
459     return 0;
460 }
461 
462 static int
pgm_readpgmrow(FILE * file,gray * grayrow,int cols,gray maxval,int format)463 pgm_readpgmrow(FILE * file, gray * grayrow, int cols, gray maxval, int format)
464 {
465     register int col, val;
466     register gray *gP;
467 
468     switch (format) {
469     case PGM_FORMAT:
470 	for (col = 0, gP = grayrow; col < cols; ++col, ++gP) {
471 	    val = pbm_getint(file);
472 	    if (val == -1)
473 		return -1;
474 	    *gP = val;
475 	}
476 	break;
477 
478     case RPGM_FORMAT:
479 	if (fread(grayrow, 1, cols, file) != cols) {
480 	    fprintf(stderr, msgText[READ_ERROR], progname);
481 	    return -1;
482 	}
483 	break;
484 
485     default:
486 	fprintf(stderr, msgText[CANT_HAPPEN], progname);
487 	return -1;
488     }
489     return 0;
490 }
491 
492 static void
pgm_writepgminit(FILE * file,int cols,int rows,gray maxval,int forceplain)493 pgm_writepgminit(FILE * file, int cols, int rows, gray maxval, int forceplain)
494 {
495     if (!forceplain)
496 	fprintf(
497 		   file, "%c%c\n%d %d\n%d\n", PGM_MAGIC1, RPGM_MAGIC2,
498 		   cols, rows, maxval);
499     else
500 	fprintf(
501 		   file, "%c%c\n%d %d\n%d\n", PGM_MAGIC1, PGM_MAGIC2,
502 		   cols, rows, maxval);
503 }
504 
505 static void
putus(short unsigned int n,FILE * file)506 putus(short unsigned int n, FILE * file)
507 {
508     if (n >= 10)
509 	putus(n / 10, file);
510     putc(n % 10 + '0', file);
511 }
512 
513 static int
pgm_writepgmrowraw(FILE * file,gray * grayrow,int cols,gray maxval)514 pgm_writepgmrowraw(FILE * file, gray * grayrow, int cols, gray maxval)
515 {
516     if (fwrite(grayrow, 1, cols, file) != cols) {
517 	fprintf(stderr, msgText[WRITE_ERROR], progname);
518 	return -1;
519     }
520     return 0;
521 }
522 
523 static int
pgm_writepgmrowplain(FILE * file,gray * grayrow,int cols,gray maxval)524 pgm_writepgmrowplain(FILE * file, gray * grayrow, int cols, gray maxval)
525 {
526     register int col, charcount;
527     register gray *gP;
528 
529     charcount = 0;
530     for (col = 0, gP = grayrow; col < cols; ++col, ++gP) {
531 	if (charcount >= 65) {
532 	    putc('\n', file);
533 	    charcount = 0;
534 	} else if (charcount > 0) {
535 	    putc(' ', file);
536 	    ++charcount;
537 	}
538 	putus((unsigned short) *gP, file);
539 	charcount += 3;
540     }
541     if (charcount > 0)
542 	putc('\n', file);
543     return 0;
544 }
545 
546 static int
pgm_writepgmrow(FILE * file,gray * grayrow,int cols,gray maxval,int forceplain)547 pgm_writepgmrow(FILE * file, gray * grayrow, int cols, gray maxval, int forceplain)
548 {
549     if (!forceplain)
550 	return pgm_writepgmrowraw(file, grayrow, cols, maxval);
551     else
552 	return pgm_writepgmrowplain(file, grayrow, cols, maxval);
553 }
554 
555 static int
ppm_readppminitrest(FILE * file,int * colsP,int * rowsP,pixval * maxvalP)556 ppm_readppminitrest(FILE * file, int *colsP, int *rowsP, pixval * maxvalP)
557 {
558     int maxval;
559 
560     /* Read size. */
561     *colsP = pbm_getint(file);
562     *rowsP = pbm_getint(file);
563     if (*colsP == -1 || *rowsP == -1)
564 	return -1;
565 
566     /* Read maxval. */
567     maxval = pbm_getint(file);
568     if (maxval == -1)
569 	return -1;
570     if (maxval > PPM_MAXMAXVAL) {
571 	fprintf(stderr, msgText[MAXVAL_IS_TOO_LARGE], progname);
572 	return -1;
573     }
574     *maxvalP = maxval;
575     return 0;
576 }
577 
578 static int
ppm_readppmrow(FILE * file,pixel * pixelrow,int cols,pixval maxval,int format)579 ppm_readppmrow(FILE * file, pixel * pixelrow, int cols,
580 	       pixval maxval, int format)
581 {
582     register int col;
583     register pixel *pP;
584     register int r, g, b;
585     gray *grayrow;
586     register gray *gP;
587 
588     switch (format) {
589     case PPM_FORMAT:
590 	for (col = 0, pP = pixelrow; col < cols; ++col, ++pP) {
591 	    r = pbm_getint(file);
592 	    g = pbm_getint(file);
593 	    b = pbm_getint(file);
594 	    if (r == -1 || g == -1 || b == -1)
595 		return -1;
596 	    PPM_ASSIGN(*pP, r, g, b);
597 	}
598 	break;
599 
600     case RPPM_FORMAT:
601 	grayrow = pgm_allocrow(3 * cols);
602 	if (grayrow == (gray *) 0)
603 	    return -1;
604 	if (fread(grayrow, 1, 3 * cols, file) != 3 * cols) {
605 	    fprintf(stderr, msgText[READ_ERROR], progname);
606 	    return -1;
607 	}
608 	for (col = 0, gP = grayrow, pP = pixelrow; col < cols; ++col, ++pP) {
609 	    r = *gP++;
610 	    g = *gP++;
611 	    b = *gP++;
612 	    PPM_ASSIGN(*pP, r, g, b);
613 	}
614 	pgm_freerow(grayrow);
615 	break;
616 
617     default:
618 	fprintf(stderr, msgText[CANT_HAPPEN], progname);
619 	return -1;
620     }
621     return 0;
622 }
623 
624 static void
ppm_writeppminit(FILE * file,int cols,int rows,pixval maxval,int forceplain)625 ppm_writeppminit(FILE * file, int cols, int rows, pixval maxval, int forceplain)
626 {
627     if (!forceplain)
628 	fprintf(
629 		   file, "%c%c\n%d %d\n%d\n", PPM_MAGIC1, RPPM_MAGIC2,
630 		   cols, rows, maxval);
631     else
632 	fprintf(
633 		   file, "%c%c\n%d %d\n%d\n", PPM_MAGIC1, PPM_MAGIC2,
634 		   cols, rows, maxval);
635 }
636 
637 static int
ppm_writeppmrowraw(FILE * file,pixel * pixelrow,int cols,pixval maxval)638 ppm_writeppmrowraw(FILE * file, pixel * pixelrow, int cols, pixval maxval)
639 {
640     register int col;
641     register pixel *pP;
642     gray *grayrow;
643     register gray *gP;
644 
645     grayrow = pgm_allocrow(3 * cols);
646     if (grayrow == (gray *) 0)
647 	return -1;
648     for (col = 0, pP = pixelrow, gP = grayrow; col < cols; ++col, ++pP) {
649 	*gP++ = PPM_GETR(*pP);
650 	*gP++ = PPM_GETG(*pP);
651 	*gP++ = PPM_GETB(*pP);
652     }
653     if (fwrite(grayrow, 1, 3 * cols, file) != 3 * cols) {
654 	fprintf(stderr, msgText[WRITE_ERROR], progname);
655 	return -1;
656     }
657     pgm_freerow(grayrow);
658     return 0;
659 }
660 
661 static int
ppm_writeppmrowplain(FILE * file,pixel * pixelrow,int cols,pixval maxval)662 ppm_writeppmrowplain(FILE * file, pixel * pixelrow, int cols, pixval maxval)
663 {
664     register int col, charcount;
665     register pixel *pP;
666     register pixval val;
667 
668     charcount = 0;
669     for (col = 0, pP = pixelrow; col < cols; ++col, ++pP) {
670 	if (charcount >= 65) {
671 	    putc('\n', file);
672 	    charcount = 0;
673 	} else if (charcount > 0) {
674 	    putc(' ', file);
675 	    putc(' ', file);
676 	    charcount += 2;
677 	}
678 	val = PPM_GETR(*pP);
679 	putus(val, file);
680 	putc(' ', file);
681 	val = PPM_GETG(*pP);
682 	putus(val, file);
683 	putc(' ', file);
684 	val = PPM_GETB(*pP);
685 	putus(val, file);
686 	charcount += 11;
687     }
688     if (charcount > 0)
689 	putc('\n', file);
690     return 0;
691 }
692 
693 static int
ppm_writeppmrow(FILE * file,pixel * pixelrow,int cols,pixval maxval,int forceplain)694 ppm_writeppmrow(FILE * file, pixel * pixelrow, int cols,
695 		pixval maxval, int forceplain)
696 {
697     if (!forceplain)
698 	return ppm_writeppmrowraw(file, pixelrow, cols, maxval);
699     else
700 	return ppm_writeppmrowplain(file, pixelrow, cols, maxval);
701 }
702 
703 void
pnm_init2(char * pn)704 pnm_init2(char *pn)
705 {
706     /* Save program name. */
707     progname = pn;
708 }
709 
710 xelval pnm_pbmmaxval = 1;
711 
712 int
pnm_readpnminit(FILE * file,int * colsP,int * rowsP,xelval * maxvalP,int * formatP)713 pnm_readpnminit(FILE * file, int *colsP, int *rowsP,
714 		xelval * maxvalP, int *formatP)
715 {
716     gray gmaxval;
717 
718     /* Check magic number. */
719     *formatP = pbm_readmagicnumber(file);
720     if (*formatP == -1)
721 	return -1;
722     switch (PNM_FORMAT_TYPE(*formatP)) {
723     case PPM_TYPE:
724 	if (ppm_readppminitrest(file, colsP, rowsP, (pixval *) maxvalP) < 0)
725 	    return -1;
726 	break;
727 
728     case PGM_TYPE:
729 	if (pgm_readpgminitrest(file, colsP, rowsP, &gmaxval) < 0)
730 	    return -1;
731 	*maxvalP = (xelval) gmaxval;
732 	break;
733 
734     case PBM_TYPE:
735 	if (pbm_readpbminitrest(file, colsP, rowsP) < 0)
736 	    return -1;
737 	*maxvalP = pnm_pbmmaxval;
738 	break;
739 
740     default:
741 	fprintf(
742 	  stderr, msgText[BAD_MAGIC_NUMBER_NOT_A_PPM_PGM_OR_PBM_FILE],
743 		  progname);
744 	return -1;
745     }
746     return 0;
747 }
748 
749 int
pnm_readpnmrow(FILE * file,xel * xelrow,int cols,xelval maxval,int format)750 pnm_readpnmrow(FILE * file, xel * xelrow, int cols, xelval maxval, int format)
751 {
752     register int col;
753     register xel *xP;
754     gray *grayrow;
755     register gray *gP;
756     bit *bitrow;
757     register bit *bP;
758 
759     switch (PNM_FORMAT_TYPE(format)) {
760     case PPM_TYPE:
761 	if (ppm_readppmrow(file, (pixel *) xelrow, cols,
762 			   (pixval) maxval, format) < 0)
763 	    return -1;
764 	break;
765 
766     case PGM_TYPE:
767 	grayrow = pgm_allocrow(cols);
768 	if (grayrow == (gray *) 0)
769 	    return -1;
770 	if (pgm_readpgmrow(file, grayrow, cols, (gray) maxval, format) < 0)
771 	    return -1;
772 	for (col = 0, xP = xelrow, gP = grayrow; col < cols; ++col, ++xP, ++gP)
773 	    PNM_ASSIGN1(*xP, *gP);
774 	pgm_freerow(grayrow);
775 	break;
776 
777     case PBM_TYPE:
778 	bitrow = pbm_allocrow(cols);
779 	if (bitrow == (bit *) 0)
780 	    return -1;
781 	if (pbm_readpbmrow(file, bitrow, cols, format) < 0) {
782 	    pbm_freerow(bitrow);
783 	    return -1;
784 	}
785 	for (col = 0, xP = xelrow, bP = bitrow; col < cols; ++col, ++xP, ++bP)
786 	    PNM_ASSIGN1(*xP, *bP == PBM_BLACK ? 0 : pnm_pbmmaxval);
787 	pbm_freerow(bitrow);
788 	break;
789 
790     default:
791 	fprintf(stderr, msgText[CANT_HAPPEN], progname);
792 	return -1;
793     }
794     return 0;
795 }
796 
797 xel **
pnm_readpnm(FILE * file,int * colsP,int * rowsP,xelval * maxvalP,int * formatP)798 pnm_readpnm(FILE * file, int *colsP, int *rowsP, xelval * maxvalP, int *formatP)
799 {
800     xel **xels;
801     int row;
802 
803     if (pnm_readpnminit(file, colsP, rowsP, maxvalP, formatP) < 0)
804 	return (xel **) 0;
805 
806     xels = pnm_allocarray(*colsP, *rowsP);
807     if (xels == (xel **) 0)
808 	return (xel **) 0;
809 
810     for (row = 0; row < *rowsP; ++row)
811 	if (pnm_readpnmrow(file, xels[row], *colsP, *maxvalP, *formatP) < 0) {
812 	    pnm_freearray(xels, *rowsP);
813 	    return (xel **) 0;
814 	}
815     return xels;
816 }
817 
818 int
pnm_writepnminit(FILE * file,int cols,int rows,xelval maxval,int format,int forceplain)819 pnm_writepnminit(FILE * file, int cols, int rows, xelval maxval,
820 		 int format, int forceplain)
821 {
822     switch (PNM_FORMAT_TYPE(format)) {
823     case PPM_TYPE:
824 	ppm_writeppminit(file, cols, rows, (pixval) maxval, forceplain);
825 	break;
826 
827     case PGM_TYPE:
828 	pgm_writepgminit(file, cols, rows, (gray) maxval, forceplain);
829 	break;
830 
831     case PBM_TYPE:
832 	pbm_writepbminit(file, cols, rows, forceplain);
833 	break;
834 
835     default:
836 	fprintf(stderr, msgText[CANT_HAPPEN], progname);
837 	return -1;
838     }
839     return 0;
840 }
841 
842 int
pnm_writepnmrow(FILE * file,xel * xelrow,int cols,xelval maxval,int format,int forceplain)843 pnm_writepnmrow(FILE * file, xel * xelrow, int cols,
844 		xelval maxval, int format, int forceplain)
845 {
846     register int col;
847     register xel *xP;
848     gray *grayrow;
849     register gray *gP;
850     bit *bitrow;
851     register bit *bP;
852 
853     switch (PNM_FORMAT_TYPE(format)) {
854     case PPM_TYPE:
855 	if (ppm_writeppmrow(file, (pixel *) xelrow, cols,
856 			    (pixval) maxval, forceplain) < 0)
857 	    return -1;
858 	break;
859 
860     case PGM_TYPE:
861 	grayrow = pgm_allocrow(cols);
862 	if (grayrow == (gray *) 0)
863 	    return -1;
864 	for (col = 0, gP = grayrow, xP = xelrow; col < cols; ++col, ++gP, ++xP)
865 	    *gP = PNM_GET1(*xP);
866 	if (pgm_writepgmrow(file, grayrow, cols,
867 			    (gray) maxval, forceplain) < 0) {
868 	    pgm_freerow(grayrow);
869 	    return -1;
870 	}
871 	pgm_freerow(grayrow);
872 	break;
873 
874     case PBM_TYPE:
875 	bitrow = pbm_allocrow(cols);
876 	if (bitrow == (bit *) 0)
877 	    return -1;
878 	for (col = 0, bP = bitrow, xP = xelrow; col < cols; ++col, ++bP, ++xP)
879 	    *bP = PNM_GET1(*xP) == 0 ? PBM_BLACK : PBM_WHITE;
880 	pbm_writepbmrow(file, bitrow, cols, forceplain);
881 	pbm_freerow(bitrow);
882 	break;
883 
884     default:
885 	fprintf(stderr, msgText[CANT_HAPPEN], progname);
886 	return -1;
887     }
888     return 0;
889 }
890 
891 int
pnm_writepnm(FILE * file,xel ** xels,int cols,int rows,xelval maxval,int format,int forceplain)892 pnm_writepnm(FILE * file, xel ** xels, int cols, int rows,
893 	     xelval maxval, int format, int forceplain)
894 {
895     int row;
896 
897     if (pnm_writepnminit(file, cols, rows, maxval, format, forceplain) < 0)
898 	return -1;
899 
900     for (row = 0; row < rows; ++row)
901 	if (pnm_writepnmrow(file, xels[row], cols,
902 			    maxval, format, forceplain) < 0)
903 	    return -1;
904     return 0;
905 }
906 
907 
908 /* Colormap stuff. */
909 
910 #define HASH_SIZE 20023
911 
912 #define ppm_hashpixel(p) ((((long) PPM_GETR(p)*33023 + \
913 			    (long) PPM_GETG(p)*30013 + \
914 			    (long) PPM_GETB(p)*27011) & 0x7fffffff) % HASH_SIZE)
915 
916 colorhist_vector
ppm_computecolorhist(pixel ** pixels,int cols,int rows,int maxcolors,int * colorsP)917 ppm_computecolorhist(pixel ** pixels, int cols, int rows,
918 		     int maxcolors, int *colorsP)
919 {
920     colorhash_table cht;
921     colorhist_vector chv;
922 
923     cht = ppm_computecolorhash(pixels, cols, rows, maxcolors, colorsP);
924     if (cht == (colorhash_table) 0)
925 	return (colorhist_vector) 0;
926     chv = ppm_colorhashtocolorhist(cht, maxcolors);
927     ppm_freecolorhash(cht);
928     return chv;
929 }
930 
931 void
ppm_addtocolorhist(colorhist_vector chv,int * colorsP,int maxcolors,pixel * colorP,int value,int position)932 ppm_addtocolorhist(colorhist_vector chv, int *colorsP,
933 		   int maxcolors, pixel * colorP, int value, int position)
934 {
935     int i, j;
936 
937     /* Search colorhist for the color. */
938     for (i = 0; i < *colorsP; ++i)
939 	if (PPM_EQUAL(chv[i].color, *colorP)) {
940 	    /* Found it - move to new slot. */
941 	    if (position > i) {
942 		for (j = i; j < position; ++j)
943 		    chv[j] = chv[j + 1];
944 	    } else if (position < i) {
945 		for (j = i; j > position; --j)
946 		    chv[j] = chv[j - 1];
947 	    }
948 	    chv[position].color = *colorP;
949 	    chv[position].value = value;
950 	    return;
951 	}
952     if (*colorsP < maxcolors) {
953 	/* Didn't find it, but there's room to add it; so do so. */
954 	for (i = *colorsP; i > position; --i)
955 	    chv[i] = chv[i - 1];
956 	chv[position].color = *colorP;
957 	chv[position].value = value;
958 	++(*colorsP);
959     }
960 }
961 
962 colorhash_table
ppm_computecolorhash(pixel ** pixels,int cols,int rows,int maxcolors,int * colorsP)963 ppm_computecolorhash(pixel ** pixels, int cols, int rows,
964 		     int maxcolors, int *colorsP)
965 {
966     colorhash_table cht;
967     register pixel *pP;
968     colorhist_list chl;
969     int col, row, hash;
970 
971     cht = ppm_alloccolorhash();
972     if (cht == (colorhash_table) 0)
973 	return (colorhash_table) 0;
974     *colorsP = 0;
975 
976     /* Go through the entire image, building a hash table of colors. */
977     for (row = 0; row < rows; ++row)
978 	for (col = 0, pP = pixels[row]; col < cols; ++col, ++pP) {
979 	    hash = ppm_hashpixel(*pP);
980 	    for (chl = cht[hash]; chl != (colorhist_list) 0; chl = chl->next)
981 		if (PPM_EQUAL(chl->ch.color, *pP))
982 		    break;
983 	    if (chl != (colorhist_list) 0)
984 		++(chl->ch.value);
985 	    else {
986 		if (++(*colorsP) > maxcolors) {
987 		    ppm_freecolorhash(cht);
988 		    return (colorhash_table) 0;
989 		}
990 		chl = (colorhist_list)
991 		    xmalloc(sizeof(struct colorhist_list_item));
992 		if (chl == 0) {
993 		    fprintf(stderr,
994 			    msgText[OUT_OF_MEMORY_COMPUTING_HASH_TABLE],
995 			    progname);
996 		    ppm_freecolorhash(cht);
997 		    return (colorhash_table) 0;
998 		}
999 		chl->ch.color = *pP;
1000 		chl->ch.value = 1;
1001 		chl->next = cht[hash];
1002 		cht[hash] = chl;
1003 	    }
1004 	}
1005 
1006     return cht;
1007 }
1008 
1009 colorhash_table
ppm_alloccolorhash(void)1010 ppm_alloccolorhash(void)
1011 {
1012     colorhash_table cht;
1013     int i;
1014 
1015     cht = (colorhash_table) xmalloc(HASH_SIZE * sizeof(colorhist_list));
1016     if (cht == 0) {
1017 	fprintf(stderr, msgText[OUT_OF_MEMORY_ALLOCATING_HASH_TABLE],
1018                 progname);
1019 	return (colorhash_table) 0;
1020     }
1021     for (i = 0; i < HASH_SIZE; ++i)
1022 	cht[i] = (colorhist_list) 0;
1023 
1024     return cht;
1025 }
1026 
1027 int
ppm_addtocolorhash(colorhash_table cht,pixel * colorP,int value)1028 ppm_addtocolorhash(colorhash_table cht, pixel * colorP, int value)
1029 {
1030     register int hash;
1031     register colorhist_list chl;
1032 
1033     chl = (colorhist_list) xmalloc(sizeof(struct colorhist_list_item));
1034     if (chl == 0)
1035 	return -1;
1036     hash = ppm_hashpixel(*colorP);
1037     chl->ch.color = *colorP;
1038     chl->ch.value = value;
1039     chl->next = cht[hash];
1040     cht[hash] = chl;
1041     return 0;
1042 }
1043 
1044 colorhist_vector
ppm_colorhashtocolorhist(colorhash_table cht,int maxcolors)1045 ppm_colorhashtocolorhist(colorhash_table cht, int maxcolors)
1046 {
1047     colorhist_vector chv;
1048     colorhist_list chl;
1049     int i, j;
1050 
1051     /* Now collate the hash table into a simple colorhist array. */
1052     chv = (colorhist_vector) xmalloc(maxcolors * sizeof(struct colorhist_item));
1053     /* (Leave room for expansion by caller.) */
1054     if (chv == (colorhist_vector) 0) {
1055 	fprintf(stderr, msgText[OUT_OF_MEMORY_GENERATING_HISTOGRAM], progname);
1056 	return (colorhist_vector) 0;
1057     }
1058     /* Loop through the hash table. */
1059     j = 0;
1060     for (i = 0; i < HASH_SIZE; ++i)
1061 	for (chl = cht[i]; chl != (colorhist_list) 0; chl = chl->next) {
1062 	    /* Add the new entry. */
1063 	    chv[j] = chl->ch;
1064 	    ++j;
1065 	}
1066 
1067     /* All done. */
1068     return chv;
1069 }
1070 
1071 colorhash_table
ppm_colorhisttocolorhash(colorhist_vector chv,int colors)1072 ppm_colorhisttocolorhash(colorhist_vector chv, int colors)
1073 {
1074     colorhash_table cht;
1075     int i, hash;
1076     pixel color;
1077     colorhist_list chl;
1078 
1079     cht = ppm_alloccolorhash();
1080     if (cht == (colorhash_table) 0)
1081 	return (colorhash_table) 0;
1082 
1083     for (i = 0; i < colors; ++i) {
1084 	color = chv[i].color;
1085 	hash = ppm_hashpixel(color);
1086 	for (chl = cht[hash]; chl != (colorhist_list) 0; chl = chl->next)
1087 	    if (PPM_EQUAL(chl->ch.color, color)) {
1088 		fprintf(stderr, "%s %s - %d %d %d\n", progname,
1089 		      msgText[SAME_COLOR_FOUND_TWICE],
1090 		      PPM_GETR(color), PPM_GETG(color), PPM_GETB(color));
1091 		ppm_freecolorhash(cht);
1092 		return (colorhash_table) 0;
1093 	    }
1094 	chl = (colorhist_list) xmalloc(sizeof(struct colorhist_list_item));
1095 	if (chl == (colorhist_list) 0) {
1096 	    fprintf(stderr, msgText[COLORHIST_OUT_OF_MEMORY], progname);
1097 	    ppm_freecolorhash(cht);
1098 	    return (colorhash_table) 0;
1099 	}
1100 	chl->ch.color = color;
1101 	chl->ch.value = i;
1102 	chl->next = cht[hash];
1103 	cht[hash] = chl;
1104     }
1105 
1106     return cht;
1107 }
1108 
1109 int
ppm_lookupcolor(colorhash_table cht,pixel * colorP)1110 ppm_lookupcolor(colorhash_table cht, pixel * colorP)
1111 {
1112     int hash;
1113     colorhist_list chl;
1114 
1115     hash = ppm_hashpixel(*colorP);
1116     for (chl = cht[hash]; chl != (colorhist_list) 0; chl = chl->next)
1117 	if (PPM_EQUAL(chl->ch.color, *colorP))
1118 	    return chl->ch.value;
1119 
1120     return -1;
1121 }
1122 
1123 void
ppm_freecolorhist(colorhist_vector chv)1124 ppm_freecolorhist(colorhist_vector chv)
1125 {
1126     free((char *) chv);
1127 }
1128 
1129 void
ppm_freecolorhash(colorhash_table cht)1130 ppm_freecolorhash(colorhash_table cht)
1131 {
1132     int i;
1133     colorhist_list chl, chlnext;
1134 
1135     for (i = 0; i < HASH_SIZE; ++i)
1136 	for (chl = cht[i]; chl != (colorhist_list) 0; chl = chlnext) {
1137 	    chlnext = chl->next;
1138 	    free((char *) chl);
1139 	}
1140     free((char *) cht);
1141 }
1142