1 /* ilbmtoppm.c - read an IFF ILBM file and produce a PPM
2 **
3 ** Copyright (C) 1989 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 ** Modified by Mark Thompson on 10/4/90 to accommodate 24-bit IFF files
13 ** as used by ASDG, NewTek, etc.
14 **
15 ** Modified by Ingo Wilken (Ingo.Wilken@informatik.uni-oldenburg.de)
16 **  20/Jun/93:
17 **  - row-by-row operation
18 **  - better de-interleave algorithm
19 **  - colormap files
20 **  - direct color
21 **  04/Oct/93:
22 **  - multipalette capability (PCHG chunk)
23 **  - options -ignore, -isham, -isehb and -adjustcolors
24 **  22/May/94:
25 **  - minor change: check first for 24 planes, then for HAM
26 **  21/Sep/94:
27 **  - write mask plane to a file if -maskfile option used
28 **  - write colormap file
29 **  - added sliced HAM/dynamic HAM/dynamic Hires multipalette formats (SHAM, CTBL chunk)
30 **  - added color lookup tables (CLUT chunk)
31 **  - major rework of colormap/multipalette handling
32 **  - now uses numeric IFF IDs
33 **  24/Oct/94:
34 **  - transparentColor capability
35 **  - added RGBN/RGB8 image types
36 **  - 24-bit & direct color modified to n-bit deep ILBM
37 **  22/Feb/95:
38 **  - direct color (DCOL) reimplemented
39 **  29/Mar/95
40 **  - added IFF-PBM format
41 */
42 
43 #include <string.h>
44 
45 #include "pm_c_util.h"
46 #include "mallocvar.h"
47 #include "intcode.h"
48 #include "ilbm.h"
49 #include "ppm.h"
50 
51 typedef struct {
52     int reg;            /* color register to change */
53     pixval r, g, b;     /* new colors for register */
54 } PaletteChange;
55 
56 typedef struct {
57     pixel *color;
58     int    ncolors;
59     /* lookup tables */
60     unsigned char *redlut;
61     unsigned char *greenlut;
62     unsigned char *bluelut;
63     unsigned char *monolut;
64     /* multipalette stuff */
65     PaletteChange *mp_init;
66     PaletteChange **mp_change;
67     int mp_rows;                /* # of rows in change array */
68     int mp_type;                /* see below, higher types preferred */
69     int mp_flags;
70     IFF_ID  mp_id;
71 } ColorMap;
72 
73 #define HAS_COLORMAP(cmap)      ((cmap) && (cmap)->color)
74 #define HAS_COLORLUT(cmap)      ((cmap) && ((cmap)->redlut || (cmap)->greenlut || (cmap)->bluelut))
75 #define HAS_MONOLUT(cmap)       ((cmap) && (cmap)->monolut)
76 #define HAS_MULTIPALETTE(cmap)  (HAS_COLORMAP(cmap) && (cmap)->mp_type)
77 #define MP_TYPE_SHAM        1
78 #define MP_TYPE_CTBL        2
79 #define MP_TYPE_PCHG        3
80 #define MP_REG_IGNORE       -1
81 #define MP_REG_END          -2
82 #define MP_FLAGS_SKIPLACED   (1<<0)
83 
84 #define FACTOR_4BIT     17      /* scale factor maxval 15 -> maxval 255 */
85 
86 static short verbose = 0;
87 static short adjustcolors = 0;
88 static unsigned char *ilbmrow;
89 static pixel *pixelrow;
90 static FILE *maskfile = NULL;
91 static bit *maskrow = NULL;
92 static bool wrotemask;
93 static IFF_ID typeid;       /* ID_ILBM, ID_RGBN, ID_RGB8 */
94 
95 static char *transpName = NULL;  /* -transparent option value */
96 
97 static bool debug = FALSE;
98 
99 
100 
101 static char *
ID2string(IFF_ID const id)102 ID2string(IFF_ID const id) {
103 
104     static char str[4];
105 
106     str[0] = (char)(id >> 24 & 0xff);
107     str[1] = (char)(id >> 16 & 0xff);
108     str[2] = (char)(id >>  8 & 0xff);
109     str[3] = (char)(id >>  0 & 0xff);
110 
111     return str;
112 }
113 
114 
115 /****************************************************************************
116  Memory allocation
117  ****************************************************************************/
118 static ColorMap *
alloc_cmap(void)119 alloc_cmap(void) {
120 
121     ColorMap * cmap;
122 
123     MALLOCVAR_NOFAIL(cmap);
124 
125     cmap->color     = NULL;
126     cmap->ncolors   = 0;
127     cmap->monolut   = NULL;
128     cmap->redlut    = NULL;
129     cmap->greenlut  = NULL;
130     cmap->bluelut   = NULL;
131     cmap->mp_init   = NULL;
132     cmap->mp_change = NULL;
133     cmap->mp_rows   = 0;
134     cmap->mp_type   = 0;
135     cmap->mp_flags  = 0;
136 
137     return cmap;
138 }
139 
140 
141 
142 static rawtype *
alloc_rawrow(unsigned int const cols)143 alloc_rawrow(unsigned int const cols) {
144 
145     rawtype * r;
146     unsigned int col;
147 
148     MALLOCARRAY_NOFAIL(r, cols);
149 
150     for (col = 0; col < cols; ++col)
151         r[col] = 0;
152 
153     return r;
154 }
155 
156 
157 /****************************************************************************
158  Basic I/O functions
159  ****************************************************************************/
160 
161 static void
readerr(FILE * const fP,IFF_ID const iffId)162 readerr(FILE * const fP,
163         IFF_ID const iffId) {
164 
165     if (ferror(fP))
166         pm_error("read error");
167     else
168         pm_error("premature EOF in %s chunk", ID2string(iffId));
169 }
170 
171 
172 
173 static void
read_bytes(FILE * const ifP,int const bytes,unsigned char * const buffer,IFF_ID const iffid,unsigned long * const counterP)174 read_bytes(FILE *          const ifP,
175            int             const bytes,
176            unsigned char * const buffer,
177            IFF_ID          const iffid,
178            unsigned long * const counterP) {
179 
180     if (counterP) {
181         if (*counterP < bytes)
182             pm_error("insufficient data in %s chunk", ID2string(iffid));
183         *counterP -= bytes;
184     }
185     if (fread(buffer, 1, bytes, ifP) != bytes)
186         readerr(ifP, iffid);
187 }
188 
189 
190 
191 static unsigned char
get_byte(FILE * const ifP,IFF_ID const iffId,unsigned long * const counterP)192 get_byte(FILE *          const ifP,
193          IFF_ID          const iffId,
194          unsigned long * const counterP) {
195 
196     int i;
197 
198     if (counterP) {
199         if (*counterP == 0 )
200             pm_error("insufficient data in %s chunk", ID2string(iffId));
201         --(*counterP);
202     }
203     i = getc(ifP);
204     if (i == EOF)
205         readerr(ifP, iffId);
206 
207     return (unsigned char) i;
208 }
209 
210 static long
get_big_long(FILE * const ifP,IFF_ID const iffid,unsigned long * const counterP)211 get_big_long(FILE *          const ifP,
212              IFF_ID          const iffid,
213              unsigned long * const counterP) {
214 
215     long l;
216 
217     if (counterP) {
218         if (*counterP < 4)
219             pm_error("insufficient data in %s chunk", ID2string(iffid));
220         *counterP -= 4;
221     }
222     if (pm_readbiglong(ifP, &l) == -1)
223         readerr(ifP, iffid);
224 
225     return l;
226 }
227 
228 
229 
230 static short
get_big_short(FILE * const ifP,IFF_ID const iffid,unsigned long * const counterP)231 get_big_short(FILE *          const ifP,
232               IFF_ID          const iffid,
233               unsigned long * const counterP) {
234 
235     short s;
236 
237     if (counterP) {
238         if (*counterP < 2)
239             pm_error("insufficient data in %s chunk", ID2string(iffid));
240         *counterP -= 2;
241     }
242     if (pm_readbigshort(ifP, &s) == -1)
243         readerr(ifP, iffid);
244 
245     return s;
246 }
247 
248 
249 
250 /****************************************************************************
251  Chunk reader
252  ****************************************************************************/
253 
254 static void
chunk_end(FILE * const ifP,IFF_ID const iffid,unsigned long const chunksize)255 chunk_end(FILE *        const ifP,
256           IFF_ID        const iffid,
257           unsigned long const chunksize) {
258 
259     if (chunksize > 0) {
260         unsigned long remainingChunksize;
261         pm_message("warning - %ld extraneous byte%s in %s chunk",
262                     chunksize, (chunksize == 1 ? "" : "s"), ID2string(iffid));
263         remainingChunksize = chunksize;  /* initial value */
264         while (remainingChunksize > 0)
265             get_byte(ifP, iffid, &remainingChunksize);
266     }
267 }
268 
269 
270 
271 static void
skip_chunk(FILE * const ifP,IFF_ID const iffId,unsigned long const chunkSize)272 skip_chunk(FILE *        const ifP,
273            IFF_ID        const iffId,
274            unsigned long const chunkSize) {
275 
276     unsigned long remainingChunkSize;
277 
278     for (remainingChunkSize = chunkSize; remainingChunkSize > 0; )
279         get_byte(ifP, iffId, &remainingChunkSize);
280 }
281 
282 
283 
284 static void
display_chunk(FILE * const ifP,IFF_ID const iffId,unsigned long const chunkSize)285 display_chunk(FILE *        const ifP,
286               IFF_ID        const iffId,
287               unsigned long const chunkSize) {
288 
289     int byte;
290     unsigned long remainingChunkSize;
291 
292     pm_message("contents of %s chunk:", ID2string(iffId));
293 
294     for (remainingChunkSize = chunkSize, byte = '\0';
295          remainingChunkSize > 0; ) {
296 
297         byte = get_byte(ifP, iffId, &remainingChunkSize);
298         if (fputc(byte, stderr) == EOF)
299             pm_error("write error");
300     }
301     if (byte != '\n') {
302         int rc;
303         rc = fputc('\n', stderr);
304         if (rc == EOF)
305             pm_error("write error");
306     }
307 }
308 
309 
310 
311 static void
read_cmap(FILE * const ifP,IFF_ID const iffId,unsigned long const chunkSize,ColorMap * const cmapP)312 read_cmap(FILE *        const ifP,
313           IFF_ID        const iffId,
314           unsigned long const chunkSize,
315           ColorMap *    const cmapP) {
316 
317     unsigned long const colorCt = chunkSize / 3;
318 
319     if (colorCt == 0) {
320         pm_error("warning - empty %s colormap", ID2string(iffId));
321         skip_chunk(ifP, iffId, chunkSize);
322     } else {
323         unsigned int i;
324         unsigned long remainingChunkSize;
325 
326         if (cmapP->color)               /* prefer CMAP-chunk over CMYK-chunk */
327             ppm_freerow(cmapP->color);
328         cmapP->color   = ppm_allocrow(colorCt);
329         cmapP->ncolors = colorCt;
330 
331         for (i = 0, remainingChunkSize = chunkSize; i < colorCt; ++i ) {
332             int const r = get_byte(ifP, iffId, &remainingChunkSize);
333             int const g = get_byte(ifP, iffId, &remainingChunkSize);
334             int const b = get_byte(ifP, iffId, &remainingChunkSize);
335             PPM_ASSIGN(cmapP->color[i], r, g, b);
336         }
337         chunk_end(ifP, iffId, remainingChunkSize);
338     }
339 }
340 
341 
342 
343 static void
read_cmyk(FILE * const ifP,IFF_ID const iffId,unsigned long const chunkSize,ColorMap * const cmapP)344 read_cmyk(FILE *        const ifP,
345           IFF_ID        const iffId,
346           unsigned long const chunkSize,
347           ColorMap *    const cmapP) {
348 
349     if (HAS_COLORMAP(cmapP)) {      /* prefer RGB color map */
350         skip_chunk(ifP, iffId, chunkSize);
351     } else {
352         unsigned long const colorCt = chunkSize / 4;
353         if (colorCt == 0 ) {
354             pm_error("warning - empty %s colormap", ID2string(iffId));
355             skip_chunk(ifP, iffId, chunkSize);
356         } else {
357             unsigned int i;
358             unsigned long remainingChunkSize;
359 
360             cmapP->color   = ppm_allocrow(colorCt);
361             cmapP->ncolors = colorCt;
362 
363             for (i = 0, remainingChunkSize = chunkSize; i < colorCt; ++i) {
364                 int const c = get_byte(ifP, iffId, &remainingChunkSize);
365                 int const m = get_byte(ifP, iffId, &remainingChunkSize);
366                 int const y = get_byte(ifP, iffId, &remainingChunkSize);
367                 int const k = get_byte(ifP, iffId, &remainingChunkSize);
368 
369                 {
370                     pixval const red =
371                         MAXCOLVAL - MIN(MAXCOLVAL,
372                                         c * (MAXCOLVAL-k) / MAXCOLVAL+k);
373                     pixval const green =
374                         MAXCOLVAL - MIN(MAXCOLVAL,
375                                         m * (MAXCOLVAL-k) / MAXCOLVAL+k);
376                     pixval const blue =
377                         MAXCOLVAL - MIN(MAXCOLVAL,
378                                         y * (MAXCOLVAL-k) / MAXCOLVAL+k);
379 
380                     PPM_ASSIGN(cmapP->color[i], red, green, blue);
381                 }
382             }
383             chunk_end(ifP, iffId, remainingChunkSize);
384         }
385     }
386 }
387 
388 
389 
390 static void
read_clut(FILE * const ifP,IFF_ID const iffId,unsigned long const chunkSize,ColorMap * const cmapP)391 read_clut(FILE *        const ifP,
392           IFF_ID        const iffId,
393           unsigned long const chunkSize,
394           ColorMap *    const cmapP) {
395 
396     if (chunkSize != CLUTSize) {
397         pm_message("invalid size for %s chunk - skipping it",
398                    ID2string(iffId));
399         skip_chunk(ifP, iffId, chunkSize);
400     } else {
401         long type;
402         unsigned char * lut;
403         unsigned long remainingChunkSize;
404         unsigned int i;
405 
406         remainingChunkSize = chunkSize;  /* initial value */
407 
408         type = get_big_long(ifP, iffId, &remainingChunkSize);
409         get_big_long(ifP, iffId, &remainingChunkSize); /* skip reserved fld */
410 
411         MALLOCARRAY_NOFAIL(lut, 256);
412         for( i = 0; i < 256; ++i )
413             lut[i] = get_byte(ifP, iffId, &remainingChunkSize);
414 
415         switch( type ) {
416         case CLUT_MONO:
417             cmapP->monolut  = lut;
418             break;
419         case CLUT_RED:
420             cmapP->redlut   = lut;
421             break;
422         case CLUT_GREEN:
423             cmapP->greenlut = lut;
424             break;
425         case CLUT_BLUE:
426             cmapP->bluelut  = lut;
427             break;
428         default:
429             pm_message("warning - %s type %ld not recognized",
430                        ID2string(iffId), type);
431             free(lut);
432         }
433     }
434 }
435 
436 
437 
438 static void
warnNonsquarePixels(uint8_t const xAspect,uint8_t const yAspect)439 warnNonsquarePixels(uint8_t const xAspect,
440                     uint8_t const yAspect) {
441 
442     if (xAspect != yAspect) {
443         const char * const baseMsg = "warning - non-square pixels";
444 
445         if (pm_have_float_format())
446             pm_message("%s; to fix do a 'pamscale -%cscale %g'",
447                        baseMsg,
448                        xAspect > yAspect ? 'x' : 'y',
449                        xAspect > yAspect ?
450                        (float)xAspect/yAspect :
451                        (float)yAspect/xAspect);
452         else
453             pm_message("%s", baseMsg);
454     }
455 }
456 
457 
458 
459 static BitMapHeader *
read_bmhd(FILE * const ifP,IFF_ID const iffid,unsigned long const chunksize)460 read_bmhd(FILE *        const ifP,
461           IFF_ID        const iffid,
462           unsigned long const chunksize) {
463 
464     BitMapHeader * bmhdP;
465 
466     if (chunksize != BitMapHeaderSize) {
467         pm_message("invalid size for %s chunk - skipping it",
468                    ID2string(iffid));
469         skip_chunk(ifP, iffid, chunksize);
470         bmhdP = NULL;
471     } else {
472         unsigned long remainingChunksize;
473 
474         MALLOCVAR_NOFAIL(bmhdP);
475 
476         remainingChunksize = chunksize;  /* initial value */
477 
478         bmhdP->w = get_big_short(ifP, iffid, &remainingChunksize);
479         bmhdP->h = get_big_short(ifP, iffid, &remainingChunksize);
480         bmhdP->x = get_big_short(ifP, iffid, &remainingChunksize);
481         bmhdP->y = get_big_short(ifP, iffid, &remainingChunksize);
482         bmhdP->nPlanes = get_byte(ifP, iffid, &remainingChunksize);
483         bmhdP->masking = get_byte(ifP, iffid, &remainingChunksize);
484         bmhdP->compression = get_byte(ifP, iffid, &remainingChunksize);
485         bmhdP->flags = get_byte(ifP, iffid, &remainingChunksize);
486         bmhdP->transparentColor =
487             get_big_short(ifP, iffid, &remainingChunksize);
488         bmhdP->xAspect = get_byte(ifP, iffid, &remainingChunksize);
489         bmhdP->yAspect = get_byte(ifP, iffid, &remainingChunksize);
490         bmhdP->pageWidth  = get_big_short(ifP, iffid, &remainingChunksize);
491         bmhdP->pageHeight = get_big_short(ifP, iffid, &remainingChunksize);
492 
493         if (verbose) {
494             if (typeid == ID_ILBM)
495                 pm_message("dimensions: %dx%d, %d planes",
496                            bmhdP->w, bmhdP->h, bmhdP->nPlanes);
497             else
498                 pm_message("dimensions: %dx%d", bmhdP->w, bmhdP->h);
499 
500             if (typeid == ID_ILBM || typeid == ID_PBM) {
501                 pm_message("compression: %s",
502                            bmhdP->compression <= cmpMAXKNOWN ?
503                            cmpNAME[bmhdP->compression] : "unknown");
504 
505                 switch(bmhdP->masking) {
506                 case mskNone:
507                     break;
508                 case mskHasMask:
509                 case mskHasTransparentColor:
510                     if (!maskfile)
511                         pm_message("use '-maskfile <filename>' "
512                                    "to generate a PBM mask file from %s",
513                                    mskNAME[bmhdP->masking]);
514                     break;
515                 case mskLasso:
516                     pm_message("warning - masking type '%s' not recognized",
517                                mskNAME[bmhdP->masking]);
518                     break;
519                 default:
520                     pm_error("unknown masking type %d", bmhdP->masking);
521                 }
522             }
523             else    /* RGBN/RGB8 */
524                 if (!maskfile)
525                     pm_message("use '-maskfile <filename>' "
526                                "to generate a PBM mask file "
527                                "from genlock bits");
528         }
529 
530         /* fix aspect ratio */
531         if (bmhdP->xAspect == 0 || bmhdP->yAspect == 0) {
532             pm_message("warning - illegal aspect ratio %d:%d, using 1:1",
533                        bmhdP->xAspect, bmhdP->yAspect);
534             bmhdP->xAspect = bmhdP->yAspect = 1;
535         }
536 
537         warnNonsquarePixels(bmhdP->xAspect, bmhdP->yAspect);
538     }
539     return bmhdP;
540 }
541 
542 
543 /****************************************************************************
544  ILBM functions
545  ****************************************************************************/
546 
547 
548 static void
read_ilbm_plane(FILE * const ifP,unsigned long * const remainingChunksizeP,int const bytes,int const compression)549 read_ilbm_plane(FILE *          const ifP,
550                 unsigned long * const remainingChunksizeP,
551                 int             const bytes,
552                 int             const compression) {
553 
554     unsigned char *ubp;
555     int j, byte;
556     int bytesRemaining;
557 
558     bytesRemaining = bytes;  /* initial value */
559 
560     switch(compression) {
561         case cmpNone:
562             read_bytes(ifP, bytesRemaining, ilbmrow,
563                        ID_BODY, remainingChunksizeP);
564             break;
565         case cmpByteRun1:
566             ubp = ilbmrow;
567             do {
568                 byte = (int)get_byte(ifP, ID_BODY, remainingChunksizeP);
569                 if( byte <= 127 ) {
570                     j = byte;
571                     bytesRemaining -= (j+1);
572                     if( bytesRemaining < 0 )
573                         pm_error("error doing ByteRun1 decompression");
574                     for( ; j >= 0; j-- )
575                         *ubp++ = get_byte(ifP, ID_BODY, remainingChunksizeP);
576                 }
577                 else
578                 if ( byte != 128 ) {
579                     j = 256 - byte;
580                     bytesRemaining -= (j+1);
581                     if( bytesRemaining < 0 )
582                         pm_error("error doing ByteRun1 decompression");
583                     byte = (int)get_byte(ifP, ID_BODY, remainingChunksizeP);
584                     for( ; j >= 0; j-- )
585                         *ubp++ = (unsigned char)byte;
586                 }
587                 /* 128 is a NOP */
588             }
589             while( bytesRemaining > 0 );
590             break;
591         default:
592             pm_error("unknown compression type %d", compression);
593     }
594 }
595 
596 
597 static const unsigned char bit_mask[] = {1, 2, 4, 8, 16, 32, 64, 128};
598 
599 static void
decode_row(FILE * const ifP,unsigned long * const remainingChunksizeP,rawtype * const chunkyrow,int const nPlanes,BitMapHeader * const bmhdP)600 decode_row(FILE *          const ifP,
601            unsigned long * const remainingChunksizeP,
602            rawtype *       const chunkyrow,
603            int             const nPlanes,
604            BitMapHeader *  const bmhdP) {
605 
606     int plane, col, cols, cbit, bytes;
607     unsigned char *ilp;
608     rawtype *chp;
609 
610     cols = bmhdP->w;
611     bytes = RowBytes(cols);
612     for( plane = 0; plane < nPlanes; plane++ ) {
613         int mask;
614 
615         mask = 1 << plane;
616         read_ilbm_plane(ifP, remainingChunksizeP, bytes, bmhdP->compression);
617 
618         ilp = ilbmrow;
619         chp = chunkyrow;
620 
621         cbit = 7;
622         for( col = 0; col < cols; col++, cbit--, chp++ ) {
623             if( cbit < 0 ) {
624                 cbit = 7;
625                 ilp++;
626             }
627             if( *ilp & bit_mask[cbit] )
628                 *chp |= mask;
629             else
630                 *chp &= ~mask;
631         }
632     }
633 }
634 
635 
636 static void
decode_mask(FILE * const ifP,unsigned long * const remainingChunksizeP,rawtype * const chunkyrow,BitMapHeader * const bmhdP)637 decode_mask(FILE *          const ifP,
638             unsigned long * const remainingChunksizeP,
639             rawtype *       const chunkyrow,
640             BitMapHeader *  const bmhdP) {
641 
642     int col, cols, cbit;
643     unsigned char *ilp;
644 
645     cols = bmhdP->w;
646     switch (bmhdP->masking) {
647     case mskNone:
648         break;
649     case mskHasMask:        /* mask plane */
650         read_ilbm_plane(ifP, remainingChunksizeP, RowBytes(cols),
651                         bmhdP->compression);
652         if (maskfile) {
653             ilp = ilbmrow;
654             cbit = 7;
655             for (col = 0; col < cols; ++col, --cbit) {
656                 if (cbit < 0) {
657                     cbit = 7;
658                     ++ilp;
659                 }
660                 if (*ilp & bit_mask[cbit])
661                     maskrow[col] = PBM_BLACK;
662                 else
663                     maskrow[col] = PBM_WHITE;
664             }
665             pbm_writepbmrow(maskfile, maskrow, cols, 0);
666             wrotemask = true;
667         }
668         break;
669     case mskHasTransparentColor:
670         if (!chunkyrow)
671             pm_error("decode_mask(): chunkyrow == NULL - can't happen");
672 
673         if (maskfile) {
674             for (col = 0; col < cols; ++col) {
675                 if (chunkyrow[col] == bmhdP->transparentColor)
676                     maskrow[col] = PBM_WHITE;
677                 else
678                     maskrow[col] = PBM_BLACK;
679             }
680             pbm_writepbmrow(maskfile, maskrow, cols, 0);
681                 wrotemask = true;
682         }
683         break;
684     case mskLasso:
685         pm_error("This program does not know how to process Lasso masking");
686         break;
687     default:
688         pm_error("decode_mask(): unknown masking type %d - can't happen",
689                  bmhdP->masking);
690     }
691 }
692 
693 
694 /****************************************************************************
695  Multipalette handling
696  ****************************************************************************/
697 
698 
699 static void
multi_adjust(ColorMap * const cmapP,unsigned int const row,const PaletteChange * const palchange)700 multi_adjust(ColorMap *            const cmapP,
701              unsigned int          const row,
702              const PaletteChange * const palchange) {
703 
704     unsigned int i;
705 
706     for (i = 0; palchange[i].reg != MP_REG_END; ++i) {
707         int const reg = palchange[i].reg;
708         if (reg >= cmapP->ncolors) {
709             pm_message("warning - palette change register out of range");
710             pm_message("    row %u  change structure %d  reg=%d (max %d)",
711                        row, i, reg, cmapP->ncolors-1);
712             pm_message("    ignoring it...  "
713                        "colors might get messed up from here");
714         } else {
715             if (reg != MP_REG_IGNORE) {
716                 PPM_ASSIGN(cmapP->color[reg],
717                            palchange[i].r, palchange[i].g, palchange[i].b);
718             }
719         }
720     }
721 }
722 
723 
724 
725 static void
multi_init(ColorMap * const cmapP,long const viewportmodes)726 multi_init(ColorMap * const cmapP,
727            long       const viewportmodes) {
728 
729     if (cmapP->mp_init)
730         multi_adjust(cmapP, -1, cmapP->mp_init);
731     if (!(viewportmodes & vmLACE) )
732         cmapP->mp_flags &= ~(MP_FLAGS_SKIPLACED);
733 }
734 
735 
736 
737 static void
multi_update(ColorMap * const cmapP,unsigned int const row)738 multi_update(ColorMap *   const cmapP,
739              unsigned int const row) {
740 
741     if (cmapP->mp_flags & MP_FLAGS_SKIPLACED) {
742         if (ODD(row))
743             return;
744         if (row/2 < cmapP->mp_rows && cmapP->mp_change[row/2])
745             multi_adjust(cmapP, row, cmapP->mp_change[row/2]);
746     } else {
747         if (row < cmapP->mp_rows && cmapP->mp_change[row])
748             multi_adjust(cmapP, row, cmapP->mp_change[row]);
749     }
750 }
751 
752 
753 
754 static void
multi_free(ColorMap * const cmapP)755 multi_free(ColorMap * const cmapP) {
756 
757     if (cmapP->mp_init) {
758         free(cmapP->mp_init);
759         cmapP->mp_init = NULL;
760     }
761 
762     if (cmapP->mp_change) {
763         unsigned int i;
764 
765         for (i = 0; i < cmapP->mp_rows; ++i) {
766             if (cmapP->mp_change[i])
767                 free(cmapP->mp_change[i]);
768         }
769         free(cmapP->mp_change);
770         cmapP->mp_change = NULL;
771     }
772 
773     cmapP->mp_rows  = 0;
774     cmapP->mp_type  = 0;
775     cmapP->mp_flags = 0;
776 }
777 
778 
779 
780 /****************************************************************************
781  Colormap handling
782  ****************************************************************************/
783 
784 
785 
786 static void
analyzeCmapSamples(const ColorMap * const cmapP,pixval * const maxSampleP,bool * const shiftedP)787 analyzeCmapSamples(const ColorMap * const cmapP,
788                    pixval *         const maxSampleP,
789                    bool *           const shiftedP) {
790 
791     pixval       maxSample;
792     bool         shifted;
793     unsigned int i;
794 
795     for (i = 0, maxSample = 0, shifted = true; i < cmapP->ncolors; ++i) {
796         pixval const r = PPM_GETR(cmapP->color[i]);
797         pixval const g = PPM_GETG(cmapP->color[i]);
798         pixval const b = PPM_GETB(cmapP->color[i]);
799 
800         maxSample = MAX(maxSample, MAX(r, MAX(g, b)));
801 
802         if (r & 0x0f || g & 0x0f || b & 0x0f)
803             shifted = false;
804     }
805     *shiftedP   = shifted;
806     *maxSampleP = maxSample;
807 }
808 
809 
810 
811 static void
transformCmap(ColorMap * const cmapP)812 transformCmap(ColorMap * const cmapP) {
813 
814     pixval maxSample;
815         /* The maximum sample value in *cmapP input */
816     bool shifted;
817         /* Samples in the *cmapP input appear to be 4 bit (maxval 15) original
818            values shifted left 4 places to make 8 bit (maxval 255) samples.
819         */
820 
821     analyzeCmapSamples(cmapP, &maxSample, &shifted);
822 
823     if (maxSample == 0)
824         pm_message("warning - black colormap");
825     else if (shifted || maxSample <= 15) {
826         if (!adjustcolors) {
827             pm_message("warning - probably %s4-bit colormap",
828                        shifted ? "shifted " : "");
829             pm_message("Use '-adjustcolors' to scale colormap to 8 bits");
830         } else {
831             unsigned int i;
832             pm_message("scaling colormap to 8 bits");
833             for (i = 0; i < cmapP->ncolors; ++i) {
834                 pixval r, g, b;
835                 r = PPM_GETR(cmapP->color[i]);
836                 g = PPM_GETG(cmapP->color[i]);
837                 b = PPM_GETB(cmapP->color[i]);
838                 if (shifted) {
839                     r >>= 4;
840                     g >>= 4;
841                     b >>= 4;
842                 }
843                 r *= FACTOR_4BIT;
844                 g *= FACTOR_4BIT;
845                 b *= FACTOR_4BIT;
846                 PPM_ASSIGN(cmapP->color[i], r, g, b);
847             }
848         }
849     }
850 }
851 
852 
853 
854 static pixel *
transpColor(const BitMapHeader * const bmhdP,ColorMap * const cmapP,const char * const transpName,pixval const maxval)855 transpColor(const BitMapHeader * const bmhdP,
856             ColorMap *           const cmapP,
857             const char *         const transpName,
858             pixval               const maxval) {
859 
860     pixel * transpColorP;
861 
862     if (bmhdP) {
863         if (bmhdP->masking == mskHasTransparentColor ||
864             bmhdP->masking == mskLasso) {
865             MALLOCVAR_NOFAIL(transpColorP);
866 
867             if (transpName)
868                 *transpColorP = ppm_parsecolor(transpName, maxval);
869             else {
870                 unsigned short const transpIdx = bmhdP->transparentColor;
871                 if (HAS_COLORMAP(cmapP)) {
872                     if (transpIdx >= cmapP->ncolors) {
873                         pm_message("using default transparent color (black)");
874                         PPM_ASSIGN(*transpColorP, 0, 0, 0);
875                     } else
876                         *transpColorP = cmapP->color[transpIdx];
877                 } else {
878                     /* The color index is just a direct gray level */
879                     PPM_ASSIGN(*transpColorP, transpIdx, transpIdx, transpIdx);
880                 }
881             }
882         } else
883             transpColorP = NULL;
884     } else
885         transpColorP = NULL;
886 
887     return transpColorP;
888 }
889 
890 
891 
892 static void
prepareCmap(const BitMapHeader * const bmhdP,ColorMap * const cmapP)893 prepareCmap(const BitMapHeader * const bmhdP,
894             ColorMap *           const cmapP) {
895 /*----------------------------------------------------------------------------
896    This is a really ugly subroutine that 1) analyzes a colormap and its
897    context (returning the analysis in global variables); and 2) modifies that
898    color map, because it's really one type of data structure as input and
899    another as output.
900 -----------------------------------------------------------------------------*/
901     bool bmhdCmapOk;
902 
903     if (bmhdP)
904         bmhdCmapOk = (bmhdP->flags & BMHD_FLAGS_CMAPOK);
905     else
906         bmhdCmapOk = false;
907 
908     if (HAS_COLORMAP(cmapP) && !bmhdCmapOk)
909         transformCmap(cmapP);
910 }
911 
912 
913 
914 static pixval
lookup_red(ColorMap * const cmapP,unsigned int const oldval)915 lookup_red(ColorMap *   const cmapP,
916            unsigned int const oldval) {
917 
918     if (cmapP && cmapP->redlut && oldval < 256)
919         return cmapP->redlut[oldval];
920     else
921         return oldval;
922 }
923 
924 
925 
926 static pixval
lookup_green(ColorMap * const cmapP,unsigned int const oldval)927 lookup_green(ColorMap *   const cmapP,
928              unsigned int const oldval) {
929 
930     if (cmapP && cmapP->greenlut && oldval < 256)
931         return cmapP->greenlut[oldval];
932     else
933         return oldval;
934 }
935 
936 
937 
938 static pixval
lookup_blue(ColorMap * const cmapP,unsigned int const oldval)939 lookup_blue(ColorMap *   const cmapP,
940             unsigned int const oldval) {
941 
942     if (cmapP && cmapP->bluelut && oldval < 256)
943         return cmapP->bluelut[oldval];
944     else
945         return oldval;
946 }
947 
948 
949 
950 static pixval
lookup_mono(ColorMap * const cmapP,unsigned int const oldval)951 lookup_mono(ColorMap *   const cmapP,
952             unsigned int const oldval) {
953 
954     if (cmapP && cmapP->monolut && oldval < 256)
955         return cmapP->monolut[oldval];
956     else
957         return oldval;
958 }
959 
960 
961 
962 static ColorMap *
ehbcmap(ColorMap * const cmapP)963 ehbcmap(ColorMap * const cmapP) {
964 
965     pixel * tempcolor;
966     unsigned int i;
967 
968     tempcolor = ppm_allocrow(cmapP->ncolors * 2);
969 
970     for (i = 0; i < cmapP->ncolors; ++i) {
971         tempcolor[i] = cmapP->color[i];
972         PPM_ASSIGN(tempcolor[cmapP->ncolors + i],
973                    PPM_GETR(cmapP->color[i]) / 2,
974                    PPM_GETG(cmapP->color[i]) / 2,
975                    PPM_GETB(cmapP->color[i]) / 2);
976     }
977     ppm_freerow(cmapP->color);
978     cmapP->color = tempcolor;
979     cmapP->ncolors *= 2;
980 
981     return cmapP;
982 }
983 
984 
985 
986 static pixval
lut_maxval(ColorMap * const cmap,pixval const maxval)987 lut_maxval(ColorMap * const cmap,
988            pixval     const maxval) {
989 
990     pixval retval;
991 
992     if (maxval >= 255)
993         retval = maxval;
994     else {
995         if (!HAS_COLORLUT(cmap))
996             retval = maxval;
997         else {
998             unsigned int i;
999             unsigned char maxlut;
1000             maxlut = maxval;
1001             for( i = 0; i < maxval; i++ ) {
1002                 if( cmap->redlut   && cmap->redlut[i]   > maxlut )
1003                     maxlut = cmap->redlut[i];
1004                 if( cmap->greenlut && cmap->greenlut[i] > maxlut )
1005                     maxlut = cmap->greenlut[i];
1006                 if( cmap->bluelut  && cmap->bluelut[i]  > maxlut )
1007                     maxlut = cmap->bluelut[i];
1008             }
1009             pm_message("warning - "
1010                        "%d-bit index into 8-bit color lookup table, "
1011                        "table maxval=%d",
1012                        pm_maxvaltobits(maxval), maxlut);
1013             if( maxlut != maxval )
1014                 retval = 255;
1015             else
1016                 retval = maxval;
1017             pm_message("    assuming image maxval=%d", retval);
1018         }
1019     }
1020     return retval;
1021 }
1022 
1023 
1024 
1025 static void
get_color(ColorMap * const cmapP,unsigned int const idx,pixval * const redP,pixval * const greenP,pixval * const blueP)1026 get_color(ColorMap *   const cmapP,
1027           unsigned int const idx,
1028           pixval *     const redP,
1029           pixval *     const greenP,
1030           pixval *     const blueP) {
1031 
1032     if (HAS_COLORMAP(cmapP)) {
1033         if (idx >= cmapP->ncolors)
1034             pm_error("color index out of range: %u (max %u)",
1035                      idx, cmapP->ncolors);
1036         else {
1037             pixval const r = PPM_GETR(cmapP->color[idx]);
1038             pixval const g = PPM_GETG(cmapP->color[idx]);
1039             pixval const b = PPM_GETB(cmapP->color[idx]);
1040 
1041             *redP    = lookup_red   (cmapP, r);
1042             *greenP  = lookup_green (cmapP, g);
1043             *blueP   = lookup_blue  (cmapP, b);
1044         }
1045     } else
1046         *redP = *greenP = *blueP = lookup_mono(cmapP, idx);
1047 }
1048 
1049 
1050 
1051 /****************************************************************************
1052  Conversion functions
1053  ****************************************************************************/
1054 
1055 static void
1056 std_to_ppm(FILE *         const ifP,
1057            long           const chunksize,
1058            BitMapHeader * const bmhdP,
1059            ColorMap *     const cmap,
1060            long           const viewportmodes);
1061 
1062 
1063 
1064 static void
ham_to_ppm(FILE * const ifP,long const chunksize,BitMapHeader * const bmhdP,ColorMap * const cmap,long const viewportmodes)1065 ham_to_ppm(FILE *         const ifP,
1066            long           const chunksize,
1067            BitMapHeader * const bmhdP,
1068            ColorMap *     const cmap,
1069            long           const viewportmodes) {
1070 
1071     int cols, rows, hambits, hammask, hamshift, hammask2, col, row;
1072     pixval maxval;
1073     rawtype *rawrow;
1074     unsigned char hamlut[256];
1075 
1076     cols = bmhdP->w;
1077     rows = bmhdP->h;
1078     hambits = bmhdP->nPlanes - 2;
1079     hammask = (1 << hambits) - 1;
1080     hamshift = 8 - hambits;
1081     hammask2 = (1 << hamshift) - 1;
1082 
1083     if( hambits > 8 || hambits < 1 ) {
1084         int const assumed_viewportmodes = viewportmodes & ~(vmHAM);
1085 
1086         pm_message("%d-plane HAM?? - interpreting image as a normal ILBM",
1087                    bmhdP->nPlanes);
1088         std_to_ppm(ifP, chunksize, bmhdP, cmap, assumed_viewportmodes);
1089         return;
1090     } else {
1091         unsigned long remainingChunksize;
1092         pixel * transpColorP;
1093 
1094         pm_message("input is a %sHAM%d file",
1095                    HAS_MULTIPALETTE(cmap) ? "multipalette " : "",
1096                    bmhdP->nPlanes);
1097 
1098         if( HAS_COLORLUT(cmap) || HAS_MONOLUT(cmap) ) {
1099             pm_message("warning - color lookup tables ignored in HAM");
1100             if( cmap->redlut )      free(cmap->redlut);
1101             if( cmap->greenlut )    free(cmap->greenlut);
1102             if( cmap->bluelut )     free(cmap->bluelut);
1103             if( cmap->monolut )     free(cmap->monolut);
1104             cmap->redlut = cmap->greenlut = cmap->bluelut =
1105                 cmap->monolut = NULL;
1106         }
1107         if( !HAS_COLORMAP(cmap) ) {
1108             pm_message("no colormap - interpreting values as grayscale");
1109             maxval = pm_bitstomaxval(hambits);
1110             for( col = 0; col <= maxval; col++ )
1111                 hamlut[col] = col * MAXCOLVAL / maxval;
1112             cmap->monolut = hamlut;
1113         }
1114 
1115         transpColorP = transpColor(bmhdP, cmap, transpName, MAXCOLVAL);
1116 
1117         if( HAS_MULTIPALETTE(cmap) )
1118             multi_init(cmap, viewportmodes);
1119 
1120         rawrow = alloc_rawrow(cols);
1121 
1122         ppm_writeppminit(stdout, cols, rows, MAXCOLVAL, 0);
1123 
1124         remainingChunksize = chunksize;  /* initial value */
1125 
1126         for (row = 0; row < rows; ++row) {
1127             pixval r, g, b;
1128 
1129             if( HAS_MULTIPALETTE(cmap) )
1130                 multi_update(cmap, row);
1131 
1132             decode_row(ifP, &remainingChunksize, rawrow, bmhdP->nPlanes, bmhdP);
1133             decode_mask(ifP, &remainingChunksize, rawrow, bmhdP);
1134 
1135             r = g = b = 0;
1136             for( col = 0; col < cols; col++ ) {
1137                 int idx = rawrow[col] & hammask;
1138 
1139                 if( transpColorP && maskrow && maskrow[col] == PBM_WHITE )
1140                     pixelrow[col] = *transpColorP;
1141                 else {
1142                     switch((rawrow[col] >> hambits) & 0x03) {
1143                     case HAMCODE_CMAP:
1144                         get_color(cmap, idx, &r, &g, &b);
1145                         break;
1146                     case HAMCODE_BLUE:
1147                         b = ((b & hammask2) | (idx << hamshift));
1148                         /*b = hamlut[idx];*/
1149                         break;
1150                     case HAMCODE_RED:
1151                         r = ((r & hammask2) | (idx << hamshift));
1152                         /*r = hamlut[idx];*/
1153                         break;
1154                     case HAMCODE_GREEN:
1155                         g = ((g & hammask2) | (idx << hamshift));
1156                         /*g = hamlut[idx];*/
1157                         break;
1158                     default:
1159                         pm_error("ham_to_ppm(): "
1160                                  "impossible HAM code - can't happen");
1161                     }
1162                     PPM_ASSIGN(pixelrow[col], r, g, b);
1163                 }
1164             }
1165             ppm_writeppmrow(stdout, pixelrow, cols, MAXCOLVAL, 0);
1166         }
1167         chunk_end(ifP, ID_BODY, remainingChunksize);
1168     }
1169 }
1170 
1171 
1172 
1173 static void
std_to_ppm(FILE * const ifP,long const chunksize,BitMapHeader * const bmhdP,ColorMap * const cmap,long const viewportmodes)1174 std_to_ppm(FILE *         const ifP,
1175            long           const chunksize,
1176            BitMapHeader * const bmhdP,
1177            ColorMap *     const cmap,
1178            long           const viewportmodes) {
1179 
1180     if (viewportmodes & vmHAM) {
1181         ham_to_ppm(ifP, chunksize, bmhdP, cmap, viewportmodes);
1182     } else {
1183         unsigned int const cols = bmhdP->w;
1184         unsigned int const rows = bmhdP->h;
1185 
1186         rawtype *rawrow;
1187         unsigned int row, col;
1188         pixval maxval;
1189         unsigned long remainingChunksize;
1190         pixel * transpColorP;
1191 
1192         pm_message("input is a %d-plane %s%sILBM", bmhdP->nPlanes,
1193                    HAS_MULTIPALETTE(cmap) ? "multipalette " : "",
1194                    viewportmodes & vmEXTRA_HALFBRITE ? "EHB " : ""
1195             );
1196 
1197         if( bmhdP->nPlanes > MAXPLANES )
1198             pm_error("too many planes (max %d)", MAXPLANES);
1199 
1200         if( HAS_COLORMAP(cmap) ) {
1201             if( viewportmodes & vmEXTRA_HALFBRITE )
1202                 ehbcmap(cmap);  /* Modifies *cmap */
1203             maxval = MAXCOLVAL;
1204         }
1205         else {
1206             pm_message("no colormap - interpreting values as grayscale");
1207             maxval = lut_maxval(cmap, pm_bitstomaxval(bmhdP->nPlanes));
1208             if( maxval > PPM_OVERALLMAXVAL )
1209                 pm_error("nPlanes is too large");
1210         }
1211 
1212         transpColorP = transpColor(bmhdP, cmap, transpName, maxval);
1213 
1214         rawrow = alloc_rawrow(cols);
1215 
1216         if( HAS_MULTIPALETTE(cmap) )
1217             multi_init(cmap, viewportmodes);
1218 
1219         ppm_writeppminit( stdout, cols, rows, maxval, 0 );
1220 
1221         remainingChunksize = chunksize;  /* initial value */
1222 
1223         for (row = 0; row < rows; ++row) {
1224 
1225             if( HAS_MULTIPALETTE(cmap) )
1226                 multi_update(cmap, row);
1227 
1228             decode_row(ifP, &remainingChunksize, rawrow, bmhdP->nPlanes, bmhdP);
1229             decode_mask(ifP, &remainingChunksize, rawrow, bmhdP);
1230 
1231             for( col = 0; col < cols; col++ ) {
1232                 pixval r, g, b;
1233                 if( transpColorP && maskrow && maskrow[col] == PBM_WHITE )
1234                     pixelrow[col] = *transpColorP;
1235                 else {
1236                     get_color(cmap, rawrow[col], &r, &g, &b);
1237                     PPM_ASSIGN(pixelrow[col], r, g, b);
1238                 }
1239             }
1240             ppm_writeppmrow(stdout, pixelrow, cols, maxval, 0);
1241         }
1242         chunk_end(ifP, ID_BODY, remainingChunksize);
1243     }
1244 }
1245 
1246 
1247 
1248 static void
deep_to_ppm(FILE * const ifP,long const chunksize,BitMapHeader * const bmhdP,ColorMap * const cmap)1249 deep_to_ppm(FILE *         const ifP,
1250             long           const chunksize,
1251             BitMapHeader * const bmhdP,
1252             ColorMap *     const cmap) {
1253 
1254     unsigned int const cols = bmhdP->w;
1255     unsigned int const rows = bmhdP->h;
1256     unsigned int const planespercolor = bmhdP->nPlanes / 3;
1257 
1258     unsigned int col, row;
1259     rawtype *Rrow, *Grow, *Brow;
1260     pixval maxval;
1261     unsigned long remainingChunksize;
1262     pixel * transpColorP;
1263 
1264     pm_message("input is a deep (%d-bit) ILBM", bmhdP->nPlanes);
1265     if( planespercolor > MAXPLANES )
1266         pm_error("too many planes (max %d)", MAXPLANES * 3);
1267 
1268     if( bmhdP->masking == mskHasTransparentColor ||
1269         bmhdP->masking == mskLasso ) {
1270         pm_message("masking type '%s' in a deep ILBM?? - ignoring it",
1271                    mskNAME[bmhdP->masking]);
1272         bmhdP->masking = mskNone;
1273     }
1274 
1275     maxval = lut_maxval(cmap, pm_bitstomaxval(planespercolor));
1276     if( maxval > PPM_OVERALLMAXVAL )
1277         pm_error("nPlanes is too large");
1278 
1279     transpColorP = transpColor(bmhdP, cmap, transpName, maxval);
1280 
1281     Rrow = alloc_rawrow(cols);
1282     Grow = alloc_rawrow(cols);
1283     Brow = alloc_rawrow(cols);
1284 
1285     ppm_writeppminit(stdout, cols, rows, maxval, 0);
1286 
1287     remainingChunksize = chunksize;  /* initial value */
1288 
1289     for( row = 0; row < rows; row++ ) {
1290         decode_row(ifP, &remainingChunksize, Rrow, planespercolor, bmhdP);
1291         decode_row(ifP, &remainingChunksize, Grow, planespercolor, bmhdP);
1292         decode_row(ifP, &remainingChunksize, Brow, planespercolor, bmhdP);
1293         decode_mask(ifP, &remainingChunksize, NULL, bmhdP);
1294 
1295         for( col = 0; col < cols; col++ ) {
1296             if( transpColorP && maskrow && maskrow[col] == PBM_WHITE )
1297                 pixelrow[col] = *transpColorP;
1298             else
1299                 PPM_ASSIGN(pixelrow[col],   lookup_red(cmap, Rrow[col]),
1300                                             lookup_green(cmap, Grow[col]),
1301                                             lookup_blue(cmap, Brow[col]) );
1302         }
1303         ppm_writeppmrow(stdout, pixelrow, cols, maxval, 0);
1304     }
1305     chunk_end(ifP, ID_BODY, remainingChunksize);
1306 }
1307 
1308 
1309 
1310 static void
dcol_to_ppm(FILE * const ifP,long const chunksize,BitMapHeader * const bmhdP,ColorMap * const cmap,DirectColor * const dcol)1311 dcol_to_ppm(FILE *         const ifP,
1312             long           const chunksize,
1313             BitMapHeader * const bmhdP,
1314             ColorMap *     const cmap,
1315             DirectColor *  const dcol) {
1316 
1317     unsigned int const cols = bmhdP->w;
1318     unsigned int const rows = bmhdP->h;
1319     unsigned int const redplanes   = dcol->r;
1320     unsigned int const greenplanes = dcol->g;
1321     unsigned int const blueplanes  = dcol->b;
1322 
1323     int col, row;
1324     rawtype *Rrow, *Grow, *Brow;
1325     pixval maxval, redmaxval, greenmaxval, bluemaxval;
1326     pixval *redtable, *greentable, *bluetable;
1327     unsigned long remainingChunksize;
1328     pixel * transpColorP;
1329 
1330     pm_message("input is a %d:%d:%d direct color ILBM",
1331                 redplanes, greenplanes, blueplanes);
1332 
1333     if( redplanes > MAXPLANES ||
1334         blueplanes > MAXPLANES ||
1335         greenplanes > MAXPLANES )
1336         pm_error("too many planes (max %d per color component)", MAXPLANES);
1337 
1338     if( bmhdP->nPlanes != (redplanes + greenplanes + blueplanes) )
1339         pm_error("%s/%s plane number mismatch",
1340                  ID2string(ID_BMHD), ID2string(ID_DCOL));
1341 
1342     if( bmhdP->masking == mskHasTransparentColor ||
1343         bmhdP->masking == mskLasso ) {
1344         pm_message("masking type '%s' in a direct color ILBM?? - ignoring it",
1345                    mskNAME[bmhdP->masking]);
1346         bmhdP->masking = mskNone;
1347     }
1348 
1349     if( HAS_COLORLUT(cmap) ) {
1350         pm_error("This program does not know how to process a %s chunk "
1351                  "in direct color", ID2string(ID_CLUT));
1352         cmap->redlut = cmap->greenlut = cmap->bluelut = NULL;
1353     }
1354 
1355     redmaxval   = pm_bitstomaxval(redplanes);
1356     greenmaxval = pm_bitstomaxval(greenplanes);
1357     bluemaxval  = pm_bitstomaxval(blueplanes);
1358     maxval = MAX(redmaxval, MAX(greenmaxval, bluemaxval));
1359 
1360     if( maxval > PPM_OVERALLMAXVAL )
1361         pm_error("too many planes");
1362 
1363     if( redmaxval != maxval || greenmaxval != maxval || bluemaxval != maxval )
1364         pm_message("scaling colors to %d bits", pm_maxvaltobits(maxval));
1365 
1366     MALLOCARRAY_NOFAIL(redtable,   redmaxval   +1);
1367     MALLOCARRAY_NOFAIL(greentable, greenmaxval +1);
1368     MALLOCARRAY_NOFAIL(bluetable,  bluemaxval  +1);
1369 
1370     {
1371         unsigned int i;
1372         for (i = 0; i <= redmaxval; ++i)
1373             redtable[i] = ROUNDDIV(i * maxval, redmaxval);
1374         for (i = 0; i <= greenmaxval; ++i)
1375             greentable[i] = ROUNDDIV(i * maxval, greenmaxval);
1376         for (i = 0; i <= bluemaxval; ++i)
1377             bluetable[i] = ROUNDDIV(i * maxval, bluemaxval);
1378     }
1379     transpColorP = transpColor(bmhdP, cmap, transpName, maxval);
1380 
1381     Rrow = alloc_rawrow(cols);
1382     Grow = alloc_rawrow(cols);
1383     Brow = alloc_rawrow(cols);
1384 
1385     ppm_writeppminit(stdout, cols, rows, maxval, 0);
1386 
1387     remainingChunksize = chunksize;  /* initial value */
1388 
1389     for( row = 0; row < rows; row++ ) {
1390         decode_row(ifP, &remainingChunksize, Rrow, redplanes, bmhdP);
1391         decode_row(ifP, &remainingChunksize, Grow, greenplanes, bmhdP);
1392         decode_row(ifP, &remainingChunksize, Brow, blueplanes, bmhdP);
1393         decode_mask(ifP, &remainingChunksize, NULL, bmhdP);
1394 
1395         for( col = 0; col < cols; col++ ) {
1396             if( transpColorP && maskrow && maskrow[col] == PBM_WHITE )
1397                 pixelrow[col] = *transpColorP;
1398             else
1399                 PPM_ASSIGN( pixelrow[col],  redtable[Rrow[col]],
1400                                             greentable[Grow[col]],
1401                                             bluetable[Brow[col]]   );
1402         }
1403         ppm_writeppmrow(stdout, pixelrow, cols, maxval, 0);
1404     }
1405     chunk_end(ifP, ID_BODY, remainingChunksize);
1406 }
1407 
1408 
1409 
1410 static void
cmapToPpm(FILE * const ofP,ColorMap * const cmapP)1411 cmapToPpm(FILE *     const ofP,
1412           ColorMap * const cmapP) {
1413 
1414     ppm_colorrowtomapfile(ofP, cmapP->color, cmapP->ncolors, MAXCOLVAL);
1415 }
1416 
1417 
1418 
1419 static void
ipbm_to_ppm(FILE * const ifP,long const chunksize,BitMapHeader * const bmhdP,ColorMap * const cmap,long const viewportmodes)1420 ipbm_to_ppm(FILE *         const ifP,
1421             long           const chunksize,
1422             BitMapHeader * const bmhdP,
1423             ColorMap *     const cmap,
1424             long           const viewportmodes) {
1425 
1426     unsigned int const cols = bmhdP->w;
1427     unsigned int const rows = bmhdP->h;
1428 
1429     int col, row;
1430     pixval maxval;
1431     unsigned long remainingChunksize;
1432     pixel * transpColorP;
1433 
1434     pm_message("input is a %sPBM ",
1435                HAS_MULTIPALETTE(cmap) ? "multipalette " : "");
1436 
1437     if( bmhdP->nPlanes != 8 )
1438         pm_error("invalid number of planes for IFF-PBM: %d (must be 8)",
1439                  bmhdP->nPlanes);
1440 
1441     if( bmhdP->masking == mskHasMask )
1442         pm_error("Image has a maskplane, which is invalid in IFF-PBM");
1443 
1444     if( HAS_COLORMAP(cmap) )
1445         maxval = MAXCOLVAL;
1446     else {
1447         pm_message("no colormap - interpreting values as grayscale");
1448         maxval = lut_maxval(cmap, pm_bitstomaxval(bmhdP->nPlanes));
1449     }
1450 
1451     transpColorP = transpColor(bmhdP, cmap, transpName, maxval);
1452 
1453     if( HAS_MULTIPALETTE(cmap) )
1454         multi_init(cmap, viewportmodes);
1455 
1456     MALLOCARRAY_NOFAIL(ilbmrow, cols);
1457 
1458     ppm_writeppminit(stdout, cols, rows, maxval, 0);
1459 
1460     remainingChunksize = chunksize;  /* initial value */
1461 
1462     for( row = 0; row < rows; row++ ) {
1463         if( HAS_MULTIPALETTE(cmap) )
1464             multi_update(cmap, row);
1465 
1466         read_ilbm_plane(ifP, &remainingChunksize, cols, bmhdP->compression);
1467 
1468         for( col = 0; col < cols; col++ ) {
1469             pixval r, g, b;
1470             if( transpColorP && maskrow && maskrow[col] == PBM_WHITE )
1471                 pixelrow[col] = *transpColorP;
1472             else {
1473                 get_color(cmap, ilbmrow[col], &r, &g, &b);
1474                 PPM_ASSIGN(pixelrow[col], r, g, b);
1475             }
1476         }
1477         ppm_writeppmrow(stdout, pixelrow, cols, maxval, 0);
1478     }
1479     chunk_end(ifP, ID_BODY, remainingChunksize);
1480 }
1481 
1482 
1483 
1484 static void
rgbn_to_ppm(FILE * const ifP,long const chunksize,BitMapHeader * const bmhdP,ColorMap * const cmap)1485 rgbn_to_ppm(FILE *         const ifP,
1486             long           const chunksize,
1487             BitMapHeader * const bmhdP,
1488             ColorMap *     const cmap  /* for CLUTs */
1489     ) {
1490 
1491     unsigned int const rows = bmhdP->h;
1492     unsigned int const cols = bmhdP->w;
1493 
1494     unsigned int row;
1495     unsigned int count;
1496     pixval maxval;
1497     unsigned long remainingChunksize;
1498     pixel * transpColorP;
1499 
1500     pm_message("input is a %d-bit RGB image", (typeid == ID_RGB8 ? 8 : 4));
1501 
1502     if (bmhdP->compression != 4)
1503         pm_error("invalid compression mode for %s: %d (must be 4)",
1504                  ID2string(typeid), bmhdP->compression);
1505 
1506     switch (typeid) {
1507     case ID_RGBN:
1508         if (bmhdP->nPlanes != 13)
1509             pm_error("invalid number of planes for %s: %d (must be 13)",
1510                      ID2string(typeid), bmhdP->nPlanes);
1511         maxval = lut_maxval(cmap, 15);
1512         break;
1513     case ID_RGB8:
1514         if (bmhdP->nPlanes != 25)
1515             pm_error("invalid number of planes for %s: %d (must be 25)",
1516                      ID2string(typeid), bmhdP->nPlanes);
1517         maxval = 255;
1518         break;
1519     default:
1520         pm_error("rgbn_to_ppm(): invalid IFF ID %s - can't happen",
1521                  ID2string(typeid));
1522     }
1523 
1524     transpColorP = transpColor(bmhdP, cmap, transpName, maxval);
1525 
1526     ppm_writeppminit(stdout, cols, rows, maxval, 0);
1527 
1528     for (row = 0, count = 0, remainingChunksize = chunksize;
1529          row < rows;
1530          ++row) {
1531 
1532         unsigned int col;
1533 
1534         for (col = 0; col < cols; ++col) {
1535             unsigned int tries;
1536             unsigned int genlock;
1537             pixval r, g, b;
1538 
1539             tries = 0;
1540             while (count == 0) {
1541                 if (typeid == ID_RGB8) {
1542                     r = lookup_red(cmap,   get_byte(ifP, ID_BODY,
1543                                                     &remainingChunksize));
1544                     g = lookup_green(cmap, get_byte(ifP, ID_BODY,
1545                                                     &remainingChunksize));
1546                     b = lookup_blue(cmap,  get_byte(ifP, ID_BODY,
1547                                                     &remainingChunksize));
1548                     count = get_byte(ifP, ID_BODY, &remainingChunksize);
1549                     genlock = count & 0x80;
1550                     count &= 0x7f;
1551                 } else {
1552                     unsigned int const word =
1553                         get_big_short(ifP, ID_BODY, &remainingChunksize);
1554                     r = lookup_red(cmap, (word & 0xf000) >> 12);
1555                     g = lookup_green(cmap, (word & 0x0f00) >> 8);
1556                     b = lookup_blue(cmap, (word & 0x00f0) >> 4);
1557                     genlock = word & 0x0008;
1558                     count = word & 0x0007;
1559                 }
1560                 if (!count) {
1561                     count = get_byte(ifP, ID_BODY, &remainingChunksize);
1562                     if (count == 0)
1563                         count =
1564                             get_big_short(ifP, ID_BODY, &remainingChunksize);
1565                     if (count == 0)
1566                         ++tries;
1567                 }
1568             }
1569             if (tries > 0) {
1570                 pm_message("warning - repeat count 0 at col %u row %u: "
1571                            "skipped %u RGB entr%s",
1572                             col, row, tries, (tries == 1 ? "y" : "ies"));
1573             }
1574             if (maskfile) {
1575                 /* genlock bit set -> transparent */
1576                 if (genlock)
1577                     maskrow[col] = PBM_WHITE;
1578                 else
1579                     maskrow[col] = PBM_BLACK;
1580             }
1581             if (transpColorP && maskrow && maskrow[col] == PBM_WHITE)
1582                 pixelrow[col] = *transpColorP;
1583             else
1584                 PPM_ASSIGN(pixelrow[col], r, g, b);
1585             --count;
1586         }
1587         ppm_writeppmrow(stdout, pixelrow, cols, maxval, 0);
1588         if (maskfile) {
1589             pbm_writepbmrow(maskfile, maskrow, cols, 0);
1590             wrotemask = true;
1591         }
1592     }
1593     chunk_end(ifP, ID_BODY, remainingChunksize);
1594 }
1595 
1596 /****************************************************************************
1597  Multipalette chunk reader
1598 
1599     Currently there are three multipalette formats:
1600         SHAM - sliced HAM (obsolete)
1601         CTBL - dynamic HAM/Hires (obsolete)
1602         PCHG - palette change
1603     There is no official documentation available for SHAM and CTBL, so
1604    this is mostly guesswork from other sources and hexdumps of pictures...
1605 
1606     CTBL format (dynamic HAM/Hires):
1607         16 bigendian words per row (16 bit: 0000rrrrggggbbbb)
1608     This is simply an entire 4-bit colormap per row.
1609     I have no idea what the DYCP chunk is for.
1610 
1611     SHAM format (sliced HAM):
1612         1 word  - version info (?) - always 0
1613         16 bigendian words per row (16 bit: 0000rrrrggggbbbb)
1614     If the picture is laced, then there are only rows/2 changes, don't change
1615     palette on odd lines.
1616 
1617     PCHG format: A detailed description of this format is available
1618     from AmiNet as PCHGLib12.lha.
1619 
1620  ****************************************************************************/
1621 
1622 /* Turn big-endian 4-byte long and 2-byte short stored at x (unsigned char *)
1623  * into the native format of the CPU
1624  */
1625 #define BIG_LONG(x) (   ((unsigned long)((x)[0]) << 24) + \
1626                         ((unsigned long)((x)[1]) << 16) + \
1627                         ((unsigned long)((x)[2]) <<  8) + \
1628                         ((unsigned long)((x)[3]) <<  0) )
1629 #define BIG_WORD(x) (   ((unsigned short)((x)[0]) << 8) + \
1630                         ((unsigned short)((x)[1]) << 0) )
1631 
1632 
1633 
1634 static void
read_4bit_mp(FILE * const ifP,IFF_ID const iffid,long const chunksize,ColorMap * const cmap)1635 read_4bit_mp(FILE *     const ifP,
1636              IFF_ID     const iffid,
1637              long       const chunksize,
1638              ColorMap * const cmap) {
1639 
1640     int row, rows, i, type;
1641     short data;
1642     unsigned long remainingChunksize;
1643 
1644     type = (iffid == ID_SHAM ? MP_TYPE_SHAM : MP_TYPE_CTBL);
1645 
1646     if( cmap->mp_type >= type ) {
1647         skip_chunk(ifP, iffid, chunksize);
1648     } else {
1649         if( cmap->mp_type )
1650             multi_free(cmap);
1651         cmap->mp_type = type;
1652 
1653         remainingChunksize = chunksize;  /* initial value */
1654 
1655         if( type == MP_TYPE_SHAM ) {
1656             cmap->mp_flags = MP_FLAGS_SKIPLACED;
1657             get_big_short(ifP, iffid, &remainingChunksize); /* skip first wd */
1658         }
1659 
1660         cmap->mp_rows = rows = remainingChunksize/32; /* sizeof(word) * 16 */
1661         MALLOCARRAY_NOFAIL(cmap->mp_change, rows);
1662 
1663         for( row = 0; row < rows; row++ ) {
1664             MALLOCARRAY_NOFAIL(cmap->mp_change[row], 17);   /* 16 + sentinel */
1665             for( i = 0; i < 16; i++ ) {
1666                 data = get_big_short(ifP, iffid, &remainingChunksize);
1667                 cmap->mp_change[row][i].reg = i;
1668                 cmap->mp_change[row][i].r =
1669                     ((data & 0x0f00) >> 8) * FACTOR_4BIT;
1670                 cmap->mp_change[row][i].g =
1671                     ((data & 0x00f0) >> 4) * FACTOR_4BIT;
1672                 cmap->mp_change[row][i].b =
1673                     ((data & 0x000f) >> 0) * FACTOR_4BIT;
1674             }
1675             cmap->mp_change[row][16].reg = MP_REG_END;   /* sentinel */
1676         }
1677         chunk_end(ifP, iffid, remainingChunksize);
1678     }
1679 }
1680 
1681 
1682 
1683 static void
PCHG_DecompHuff(const unsigned char * const src,unsigned char * const dst,short * const tree,unsigned long const origsize)1684 PCHG_DecompHuff(const unsigned char * const src,
1685                 unsigned char *       const dst,
1686                 short *               const tree,
1687                 unsigned long         const origsize) {
1688 
1689     const unsigned char * srcCursor;
1690     unsigned char * dstCursor;
1691     unsigned long i;
1692     unsigned long bits;
1693     unsigned char thisbyte;
1694     short * p;
1695 
1696     srcCursor = &src[0];  /* initial value */
1697     dstCursor = &dst[0];  /* initial value */
1698     i = 0;     /* initial value */
1699     bits = 0;  /* initial value */
1700     p = tree;  /* initial value */
1701 
1702     while (i < origsize) {
1703         if (bits == 0)  {
1704             thisbyte = *srcCursor++;
1705             bits = 8;
1706         }
1707         if (thisbyte & (1 << 7)) {
1708             if (*p >= 0) {
1709                 *dstCursor++ = (unsigned char)*p;
1710                 ++i;
1711                 p = tree;
1712             } else
1713                 p += (*p / 2);
1714         } else {
1715             --p;
1716             if (*p > 0 && (*p & 0x100)) {
1717                 *dstCursor++ = (unsigned char )*p;
1718                 ++i;
1719                 p = tree;
1720             }
1721         }
1722         thisbyte <<= 1;
1723         --bits;
1724     }
1725 }
1726 
1727 
1728 
1729 static void
PCHG_Decompress(PCHGHeader * const PCHG,PCHGCompHeader * const CompHdr,unsigned char * const compdata,unsigned long const compsize,unsigned char * const comptree,unsigned char * const data)1730 PCHG_Decompress(PCHGHeader *     const PCHG,
1731                 PCHGCompHeader * const CompHdr,
1732                 unsigned char *  const compdata,
1733                 unsigned long    const compsize,
1734                 unsigned char *  const comptree,
1735                 unsigned char *  const data) {
1736 
1737     switch(PCHG->Compression) {
1738     case PCHG_COMP_HUFFMAN: {
1739         unsigned long const treesize = CompHdr->CompInfoSize;
1740         unsigned long const huffsize = treesize / 2;
1741         const bigend16 * const bigendComptree = (const void *)comptree;
1742 
1743         short * hufftree;
1744         unsigned long i;
1745 
1746         /* Convert big-endian 2-byte shorts to C shorts */
1747 
1748         MALLOCARRAY(hufftree, huffsize);
1749 
1750         if (!hufftree)
1751             pm_error("Couldn't get memory for %lu-byte Huffman tree",
1752                      huffsize);
1753 
1754         for (i = 0; i < huffsize; ++i)
1755             hufftree[i] = pm_uintFromBigend16(bigendComptree[i]);
1756 
1757         /* decompress the change structure data */
1758         PCHG_DecompHuff(compdata, data, &hufftree[huffsize-1],
1759                         CompHdr->OriginalDataSize);
1760 
1761         free(hufftree);
1762     } break;
1763         default:
1764             pm_error("unknown PCHG compression type %d", PCHG->Compression);
1765     }
1766 }
1767 
1768 
1769 static void
PCHG_ConvertSmall(PCHGHeader * const pchgP,ColorMap * const cmapP,unsigned char * const mask,unsigned long const dataSize)1770 PCHG_ConvertSmall(PCHGHeader *    const pchgP,
1771                   ColorMap *      const cmapP,
1772                   unsigned char * const mask,
1773                   unsigned long   const dataSize) {
1774 
1775     unsigned char *data;
1776     unsigned long remDataSize;
1777     unsigned char thismask;
1778     int bits, row, i, changes, masklen, reg;
1779     unsigned char ChangeCount16, ChangeCount32;
1780     unsigned short SmallChange;
1781     unsigned long totalchanges;
1782     int changedlines;
1783     unsigned char * maskCursor;
1784 
1785     totalchanges = 0;  /* initial value */
1786     changedlines = pchgP->ChangedLines;  /* initial value */
1787     masklen = 4 * MaskLongWords(pchgP->LineCount);
1788     maskCursor = mask;
1789     data = maskCursor + masklen; remDataSize = dataSize - masklen;
1790 
1791     bits = 0;
1792     for (row = pchgP->StartLine; changedlines && row < 0; ++row) {
1793         if (bits == 0) {
1794             if (masklen == 0) goto fail2;
1795             thismask = *maskCursor++;
1796             --masklen;
1797             bits = 8;
1798         }
1799         if (thismask & (1<<7)) {
1800             if (remDataSize < 2) goto fail;
1801             ChangeCount16 = *data++;
1802             ChangeCount32 = *data++;
1803             remDataSize -= 2;
1804 
1805             changes = ChangeCount16 + ChangeCount32;
1806             for (i = 0; i < changes; ++i) {
1807                 if (totalchanges >= pchgP->TotalChanges) goto fail;
1808                 if (remDataSize < 2) goto fail;
1809                 SmallChange = BIG_WORD(data); data += 2; remDataSize -= 2;
1810                 reg = ((SmallChange & 0xf000) >> 12) +
1811                     (i >= ChangeCount16 ? 16 : 0);
1812                 cmapP->mp_init[reg - pchgP->MinReg].reg = reg;
1813                 cmapP->mp_init[reg - pchgP->MinReg].r =
1814                     ((SmallChange & 0x0f00) >> 8) * FACTOR_4BIT;
1815                 cmapP->mp_init[reg - pchgP->MinReg].g =
1816                     ((SmallChange & 0x00f0) >> 4) * FACTOR_4BIT;
1817                 cmapP->mp_init[reg - pchgP->MinReg].b =
1818                     ((SmallChange & 0x000f) >> 0) * FACTOR_4BIT;
1819                 ++totalchanges;
1820             }
1821             --changedlines;
1822         }
1823         thismask <<= 1;
1824         --bits;
1825     }
1826 
1827     for (row = pchgP->StartLine; changedlines && row < cmapP->mp_rows; row++) {
1828         if (bits == 0) {
1829             if (masklen == 0) goto fail2;
1830             thismask = *maskCursor++;
1831             --masklen;
1832             bits = 8;
1833         }
1834         if(thismask & (1<<7)) {
1835             if (remDataSize < 2) goto fail;
1836             ChangeCount16 = *data++;
1837             ChangeCount32 = *data++;
1838             remDataSize -= 2;
1839 
1840             changes = ChangeCount16 + ChangeCount32;
1841             MALLOCARRAY_NOFAIL(cmapP->mp_change[row], changes + 1);
1842             for (i = 0; i < changes; ++i) {
1843                 if (totalchanges >= pchgP->TotalChanges) goto fail;
1844                 if (remDataSize < 2) goto fail;
1845                 SmallChange = BIG_WORD(data); data += 2; remDataSize -= 2;
1846                 reg = ((SmallChange & 0xf000) >> 12) +
1847                     (i >= ChangeCount16 ? 16 : 0);
1848                 cmapP->mp_change[row][i].reg = reg;
1849                 cmapP->mp_change[row][i].r =
1850                     ((SmallChange & 0x0f00) >> 8) * FACTOR_4BIT;
1851                 cmapP->mp_change[row][i].g =
1852                     ((SmallChange & 0x00f0) >> 4) * FACTOR_4BIT;
1853                 cmapP->mp_change[row][i].b =
1854                     ((SmallChange & 0x000f) >> 0) * FACTOR_4BIT;
1855                 ++totalchanges;
1856             }
1857             cmapP->mp_change[row][changes].reg = MP_REG_END;
1858             --changedlines;
1859         }
1860         thismask <<= 1;
1861         --bits;
1862     }
1863     if (totalchanges != pchgP->TotalChanges)
1864         pm_message("warning - got %ld change structures, "
1865                    "chunk header reports %ld",
1866                    totalchanges, pchgP->TotalChanges);
1867     return;
1868 fail:
1869     pm_error("insufficient data in SmallLineChanges structures");
1870 fail2:
1871     pm_error("insufficient data in line mask");
1872 }
1873 
1874 
1875 
1876 static void
PCHG_ConvertBig(PCHGHeader * const PCHG,ColorMap * const cmap,unsigned char * const maskStart,unsigned long const datasize)1877 PCHG_ConvertBig(PCHGHeader *    const PCHG,
1878                 ColorMap *      const cmap,
1879                 unsigned char * const maskStart,
1880                 unsigned long   const datasize) {
1881 
1882     unsigned char * data;
1883     unsigned char thismask;
1884     int bits;
1885     unsigned int row;
1886     int changes;
1887     int masklen;
1888     int reg;
1889     unsigned long totalchanges;
1890     int changedlines;
1891     unsigned long remDataSize;
1892     unsigned char * mask;
1893 
1894     mask = maskStart;  /* initial value */
1895     remDataSize = datasize;  /* initial value */
1896     changedlines = PCHG->ChangedLines;  /* initial value */
1897     totalchanges = 0;  /* initial value */
1898 
1899     masklen = 4 * MaskLongWords(PCHG->LineCount);
1900     data = mask + masklen; remDataSize -= masklen;
1901 
1902     for (row = PCHG->StartLine, bits = 0; changedlines && row < 0; ++row) {
1903         if (bits == 0) {
1904             if (masklen == 0)
1905                 pm_error("insufficient data in line mask");
1906             thismask = *mask++;
1907             --masklen;
1908             bits = 8;
1909         }
1910         if (thismask & (1<<7)) {
1911             unsigned int i;
1912 
1913             if (remDataSize < 2)
1914                 pm_error("insufficient data in BigLineChanges structures");
1915 
1916             changes = BIG_WORD(data); data += 2; remDataSize -= 2;
1917 
1918             for (i = 0; i < changes; ++i) {
1919                 if (totalchanges >= PCHG->TotalChanges)
1920                     pm_error("insufficient data in BigLineChanges structures");
1921 
1922                 if (remDataSize < 6)
1923                     pm_error("insufficient data in BigLineChanges structures");
1924 
1925                 reg = BIG_WORD(data); data += 2;
1926                 cmap->mp_init[reg - PCHG->MinReg].reg = reg;
1927                 ++data; /* skip alpha */
1928                 cmap->mp_init[reg - PCHG->MinReg].r = *data++;
1929                 cmap->mp_init[reg - PCHG->MinReg].b = *data++;  /* yes, RBG */
1930                 cmap->mp_init[reg - PCHG->MinReg].g = *data++;
1931                 remDataSize -= 6;
1932                 ++totalchanges;
1933             }
1934             --changedlines;
1935         }
1936         thismask <<= 1;
1937         --bits;
1938     }
1939 
1940     for (row = PCHG->StartLine; changedlines && row < cmap->mp_rows; ++row) {
1941         if (bits == 0) {
1942             if (masklen == 0)
1943                 pm_error("insufficient data in line mask");
1944 
1945             thismask = *mask++;
1946             --masklen;
1947             bits = 8;
1948         }
1949         if (thismask & (1<<7)) {
1950             unsigned int i;
1951 
1952             if (remDataSize < 2)
1953                 pm_error("insufficient data in BigLineChanges structures");
1954 
1955             changes = BIG_WORD(data); data += 2; remDataSize -= 2;
1956 
1957             MALLOCARRAY_NOFAIL(cmap->mp_change[row], changes + 1);
1958             for (i = 0; i < changes; ++i) {
1959                 if (totalchanges >= PCHG->TotalChanges)
1960                     pm_error("insufficient data in BigLineChanges structures");
1961 
1962                 if (remDataSize < 6)
1963                     pm_error("insufficient data in BigLineChanges structures");
1964 
1965                 reg = BIG_WORD(data); data += 2;
1966                 cmap->mp_change[row][i].reg = reg;
1967                 ++data; /* skip alpha */
1968                 cmap->mp_change[row][i].r = *data++;
1969                 cmap->mp_change[row][i].b = *data++;    /* yes, RBG */
1970                 cmap->mp_change[row][i].g = *data++;
1971                 remDataSize -= 6;
1972                 ++totalchanges;
1973             }
1974             cmap->mp_change[row][changes].reg = MP_REG_END;
1975             --changedlines;
1976         }
1977         thismask <<= 1;
1978         --bits;
1979     }
1980     if (totalchanges != PCHG->TotalChanges)
1981         pm_message("warning - got %ld change structures, "
1982                    "chunk header reports %ld",
1983                    totalchanges, PCHG->TotalChanges);
1984 }
1985 
1986 
1987 
1988 static void
read_pchg(FILE * const ifP,IFF_ID const iffid,long const chunksize,ColorMap * const cmap)1989 read_pchg(FILE *     const ifP,
1990           IFF_ID     const iffid,
1991           long       const chunksize,
1992           ColorMap * const cmap) {
1993 
1994     if( cmap->mp_type >= MP_TYPE_PCHG ) {
1995         skip_chunk(ifP, iffid, chunksize);
1996     } else {
1997         PCHGHeader      PCHG;
1998         unsigned char   *data;
1999         unsigned long   datasize;
2000         unsigned long   remainingChunksize;
2001         int i;
2002 
2003         if( cmap->mp_type )
2004             multi_free(cmap);
2005         cmap->mp_type = MP_TYPE_PCHG;
2006 
2007         remainingChunksize = chunksize;  /* initial value */
2008 
2009         PCHG.Compression = get_big_short(ifP, iffid, &remainingChunksize);
2010         PCHG.Flags       = get_big_short(ifP, iffid, &remainingChunksize);
2011         PCHG.StartLine   = get_big_short(ifP, iffid, &remainingChunksize);
2012         PCHG.LineCount   = get_big_short(ifP, iffid, &remainingChunksize);
2013         PCHG.ChangedLines= get_big_short(ifP, iffid, &remainingChunksize);
2014         PCHG.MinReg      = get_big_short(ifP, iffid, &remainingChunksize);
2015         PCHG.MaxReg      = get_big_short(ifP, iffid, &remainingChunksize);
2016         PCHG.MaxChanges  = get_big_short(ifP, iffid, &remainingChunksize);
2017         PCHG.TotalChanges= get_big_long(ifP, iffid, &remainingChunksize);
2018 
2019 #ifdef DEBUG
2020         pm_message("PCHG StartLine   : %d", PCHG.StartLine);
2021         pm_message("PCHG LineCount   : %d", PCHG.LineCount);
2022         pm_message("PCHG ChangedLines: %d", PCHG.ChangedLines);
2023         pm_message("PCHG TotalChanges: %d", PCHG.TotalChanges);
2024 #endif
2025 
2026         if( PCHG.Compression != PCHG_COMP_NONE ) {
2027             PCHGCompHeader  CompHdr;
2028             unsigned char *compdata, *comptree;
2029             long treesize, compsize;
2030 
2031             CompHdr.CompInfoSize     =
2032                 get_big_long(ifP, iffid, &remainingChunksize);
2033             CompHdr.OriginalDataSize =
2034                 get_big_long(ifP, iffid, &remainingChunksize);
2035 
2036             treesize = CompHdr.CompInfoSize;
2037             MALLOCARRAY_NOFAIL(comptree, treesize);
2038             read_bytes(ifP, treesize, comptree, iffid, &remainingChunksize);
2039 
2040             compsize = remainingChunksize;
2041             MALLOCARRAY_NOFAIL(compdata, compsize);
2042             read_bytes(ifP, compsize, compdata, iffid, &remainingChunksize);
2043 
2044             datasize = CompHdr.OriginalDataSize;
2045             MALLOCARRAY_NOFAIL(data, datasize);
2046             PCHG_Decompress(&PCHG, &CompHdr, compdata,
2047                             compsize, comptree, data);
2048 
2049             free(comptree);
2050             free(compdata);
2051         } else {
2052 #ifdef DEBUG
2053             pm_message("uncompressed PCHG");
2054 #endif
2055             datasize = remainingChunksize;
2056             MALLOCARRAY_NOFAIL(data, datasize);
2057             read_bytes(ifP, datasize, data, iffid, &remainingChunksize);
2058         }
2059 
2060         if( PCHG.Flags & PCHGF_USE_ALPHA )
2061             pm_message("warning - ignoring PCHG alpha channel because "
2062                        "this program doesn't know what to do with it");
2063 
2064         cmap->mp_rows = PCHG.StartLine + PCHG.LineCount;
2065         MALLOCARRAY_NOFAIL(cmap->mp_change, cmap->mp_rows);
2066         for( i = 0; i < cmap->mp_rows; i++ )
2067             cmap->mp_change[i] = NULL;
2068         if( PCHG.StartLine < 0 ) {
2069             int nch;
2070             nch = PCHG.MaxReg - PCHG.MinReg +1;
2071             MALLOCARRAY_NOFAIL(cmap->mp_init, nch + 1);
2072             for( i = 0; i < nch; i++ )
2073                 cmap->mp_init[i].reg = MP_REG_IGNORE;
2074             cmap->mp_init[nch].reg = MP_REG_END;
2075         }
2076 
2077         if( PCHG.Flags & PCHGF_12BIT ) {
2078 #ifdef DEBUG
2079             pm_message("SmallLineChanges");
2080 #endif
2081             PCHG_ConvertSmall(&PCHG, cmap, data, datasize);
2082         }
2083         else {
2084             if( PCHG.Flags & PCHGF_32BIT ) {
2085 #ifdef DEBUG
2086                 pm_message("BigLineChanges");
2087 #endif
2088                 PCHG_ConvertBig(&PCHG, cmap, data, datasize);
2089             }
2090             else
2091                 pm_error("unknown palette changes structure "
2092                          "format in %s chunk",
2093                          ID2string(iffid));
2094         }
2095         free(data);
2096         chunk_end(ifP, iffid, remainingChunksize);
2097     }
2098 }
2099 
2100 
2101 
2102 static bool
ignored_iffid(IFF_ID const iffid,IFF_ID const ignorelist[],unsigned int const ignorecount)2103 ignored_iffid(IFF_ID       const iffid,
2104               IFF_ID       const ignorelist[],
2105               unsigned int const ignorecount) {
2106 
2107     bool ignore;
2108 
2109     unsigned int i;
2110     ignore = FALSE;  /* initial assumption */
2111     for( i = 0; i < ignorecount && !ignore; i++ ) {
2112         if( iffid == ignorelist[i] )
2113             ignore = TRUE;
2114     }
2115     return ignore;
2116 }
2117 
2118 
2119 
2120 static void
process_body(FILE * const ifP,long const chunksize,BitMapHeader * const bmhdP,ColorMap * const cmap,FILE * const maskfile,int const fakeviewport,int const isdeepopt,DirectColor * const dcol,int * const viewportmodesP)2121 process_body( FILE *          const ifP,
2122               long            const chunksize,
2123               BitMapHeader *  const bmhdP,
2124               ColorMap *      const cmap,
2125               FILE *          const maskfile,
2126               int             const fakeviewport,
2127               int             const isdeepopt,
2128               DirectColor *   const dcol,
2129               int *           const viewportmodesP) {
2130 
2131     if (bmhdP == NULL)
2132         pm_error("%s chunk without %s chunk",
2133                  ID2string(ID_BODY), ID2string(ID_BMHD));
2134 
2135     prepareCmap(bmhdP, cmap);
2136 
2137     pixelrow = ppm_allocrow(bmhdP->w);
2138     if (maskfile) {
2139         maskrow = pbm_allocrow(bmhdP->w);
2140         pbm_writepbminit(maskfile, bmhdP->w, bmhdP->h, 0);
2141     }
2142 
2143     if (typeid == ID_ILBM) {
2144         int isdeep;
2145 
2146         MALLOCARRAY_NOFAIL(ilbmrow, RowBytes(bmhdP->w));
2147         *viewportmodesP |= fakeviewport;      /* -isham/-isehb */
2148 
2149         if( isdeepopt > 0 && (bmhdP->nPlanes % 3 != 0) ) {
2150             pm_message("cannot interpret %d-plane image as 'deep' "
2151                        "(# of planes must be divisible by 3)",
2152                        bmhdP->nPlanes);
2153             isdeep = 0;
2154         } else
2155             isdeep = isdeepopt;
2156 
2157         if (isdeep > 0)
2158             deep_to_ppm(ifP, chunksize, bmhdP, cmap);
2159         else if (dcol)
2160             dcol_to_ppm(ifP, chunksize, bmhdP, cmap, dcol);
2161         else if (bmhdP->nPlanes > 8) {
2162             if (bmhdP->nPlanes <= 16 && HAS_COLORMAP(cmap))
2163                 std_to_ppm(ifP, chunksize, bmhdP, cmap, *viewportmodesP);
2164             else if (isdeep >= 0 && (bmhdP->nPlanes % 3 == 0))
2165                 deep_to_ppm(ifP, chunksize, bmhdP, cmap);
2166             else if (bmhdP->nPlanes <= 16)
2167                 /* will be interpreted as grayscale */
2168                 std_to_ppm(ifP, chunksize, bmhdP, cmap, *viewportmodesP);
2169             else
2170                 pm_error("don't know how to interpret %d-plane image",
2171                          bmhdP->nPlanes);
2172         } else
2173             std_to_ppm(ifP, chunksize, bmhdP, cmap, *viewportmodesP);
2174     } else if( typeid == ID_PBM )
2175         ipbm_to_ppm(ifP, chunksize, bmhdP, cmap, *viewportmodesP);
2176     else   /* RGBN or RGB8 */
2177         rgbn_to_ppm(ifP, chunksize, bmhdP, cmap);
2178 }
2179 
2180 
2181 
2182 static void
processChunk(FILE * const ifP,long const formsize,IFF_ID const ignorelist[],unsigned int const ignorecount,int const fakeviewport,int const viewportmask,int const isdeepopt,bool const cmaponly,bool * const bodyChunkProcessedP,bool * const endchunkP,BitMapHeader ** const bmhdP,ColorMap * const cmap,DirectColor ** const dcolP,int * const viewportmodesP,long * const bytesReadP)2183 processChunk(FILE *          const ifP,
2184              long            const formsize,
2185              IFF_ID          const ignorelist[],
2186              unsigned int    const ignorecount,
2187              int             const fakeviewport,
2188              int             const viewportmask,
2189              int             const isdeepopt,
2190              bool            const cmaponly,
2191              bool *          const bodyChunkProcessedP,
2192              bool *          const endchunkP,
2193              BitMapHeader ** const bmhdP,
2194              ColorMap *      const cmap,
2195              DirectColor **  const dcolP,
2196              int *           const viewportmodesP,
2197              long *          const bytesReadP
2198     ) {
2199 
2200     IFF_ID iffid;
2201     long chunksize;
2202     long bytesread;
2203 
2204     bytesread = 0;
2205 
2206     iffid = get_big_long(ifP, ID_FORM, NULL);
2207     chunksize = get_big_long(ifP, iffid, NULL);
2208     bytesread += 8;
2209 
2210     if (debug)
2211         pm_message("reading %s chunk: %ld bytes", ID2string(iffid), chunksize);
2212 
2213     if (ignored_iffid(iffid, ignorelist, ignorecount)) {
2214         pm_message("ignoring %s chunk", ID2string(iffid));
2215         skip_chunk(ifP, iffid, chunksize);
2216     } else if (iffid == ID_END) {
2217         /* END chunks are not officially valid in IFF, but
2218            suggested as a future expansion for stream-writing,
2219            see Amiga RKM Devices, 3rd Ed, page 376
2220         */
2221         if (chunksize != 0 ) {
2222             pm_message("warning - non-0 %s chunk", ID2string(iffid));
2223             skip_chunk(ifP, iffid, chunksize);
2224         }
2225         if (formsize != 0xffffffff)
2226             pm_message("warning - %s chunk with FORM size 0x%08lx "
2227                        "(should be 0x%08x)",
2228                        ID2string(iffid), formsize, 0xffffffff);
2229         *endchunkP = 1;
2230     } else if (*bodyChunkProcessedP) {
2231         pm_message("%s chunk found after %s chunk - skipping",
2232                    ID2string(iffid), ID2string(ID_BODY));
2233         skip_chunk(ifP, iffid, chunksize);
2234     } else
2235         switch (iffid) {
2236         case ID_BMHD:
2237             *bmhdP = read_bmhd(ifP, iffid, chunksize);
2238             break;
2239         case ID_CMAP:
2240             read_cmap(ifP, iffid, chunksize, cmap);
2241             break;
2242         case ID_CMYK:
2243             read_cmyk(ifP, iffid, chunksize, cmap);
2244             break;
2245         case ID_CLUT:
2246             read_clut(ifP, iffid, chunksize, cmap);
2247             break;
2248         case ID_CAMG:
2249             if (chunksize != CAMGChunkSize)
2250                 pm_error("%s chunk size mismatch", ID2string(iffid));
2251             *viewportmodesP = get_big_long(ifP, ID_CAMG, NULL);
2252             *viewportmodesP &= viewportmask;      /* -isnotham/-isnotehb */
2253             break;
2254         case ID_PCHG:
2255             read_pchg(ifP, iffid, chunksize, cmap);
2256             break;
2257         case ID_CTBL:
2258         case ID_SHAM:
2259             read_4bit_mp(ifP, iffid, chunksize, cmap);
2260             break;
2261         case ID_DCOL:
2262             if (chunksize != DirectColorSize)
2263                 pm_error("%s chunk size mismatch", ID2string(iffid));
2264             MALLOCVAR_NOFAIL(*dcolP);
2265             (*dcolP)->r = get_byte(ifP, iffid, NULL);
2266             (*dcolP)->g = get_byte(ifP, iffid, NULL);
2267             (*dcolP)->b = get_byte(ifP, iffid, NULL);
2268             get_byte(ifP, iffid, NULL);       /* skip pad byte */
2269             break;
2270         case ID_BODY:
2271             if (cmaponly || (*bmhdP && (*bmhdP)->nPlanes == 0))
2272                 skip_chunk(ifP, ID_BODY,  chunksize);
2273             else {
2274                 process_body(ifP, chunksize, *bmhdP, cmap,
2275                              maskfile, fakeviewport, isdeepopt, *dcolP,
2276                              viewportmodesP);
2277 
2278                 *bodyChunkProcessedP = TRUE;
2279             } break;
2280         case ID_GRAB:   case ID_DEST:   case ID_SPRT:   case ID_CRNG:
2281         case ID_CCRT:   case ID_DYCP:   case ID_DPPV:   case ID_DRNG:
2282         case ID_EPSF:   case ID_JUNK:   case ID_CNAM:   case ID_PRVW:
2283         case ID_TINY:   case ID_DPPS:
2284             skip_chunk(ifP, iffid, chunksize);
2285             break;
2286         case ID_copy:   case ID_AUTH:   case ID_NAME:   case ID_ANNO:
2287         case ID_TEXT:   case ID_FVER:
2288             if (verbose)
2289                 display_chunk(ifP, iffid, chunksize);
2290             else
2291                 skip_chunk(ifP, iffid, chunksize);
2292             break;
2293         case ID_DPI: {
2294             int x, y;
2295 
2296             x = get_big_short(ifP, ID_DPI, NULL);
2297             y = get_big_short(ifP, ID_DPI, NULL);
2298             if (verbose)
2299                 pm_message("%s chunk:  dpi_x = %d    dpi_y = %d",
2300                            ID2string(ID_DPI), x, y);
2301         } break;
2302         default:
2303             pm_message("unknown chunk type %s - skipping",
2304                        ID2string(iffid));
2305             skip_chunk(ifP, iffid, chunksize);
2306             break;
2307         }
2308 
2309     bytesread += chunksize;
2310 
2311     if (ODD(chunksize)) {
2312         get_byte(ifP, iffid, NULL);
2313         bytesread += 1;
2314     }
2315     *bytesReadP = bytesread;
2316 }
2317 
2318 
2319 
2320 static void
maybeWriteColorMap(FILE * const ofP,const BitMapHeader * const bmhdP,ColorMap * const cmapP,bool const bodyChunkProcessed,bool const cmaponly)2321 maybeWriteColorMap(FILE *               const ofP,
2322                    const BitMapHeader * const bmhdP,
2323                    ColorMap *           const cmapP,
2324                    bool                 const bodyChunkProcessed,
2325                    bool                 const cmaponly) {
2326 /*----------------------------------------------------------------------------
2327    Write to file *ofP the color map *cmapP as a PPM, if appropriate.
2328 
2329    The logic (not just here -- in the program as a whole) for deciding whether
2330    to write the image or the colormap is quite twisted.  If I thought anyone
2331    was actually using this program, I would take the time to straighten it
2332    out.
2333 
2334    What's really sick about this subroutine is that in choosing to write
2335    a color map, it has to know that Caller hasn't already written
2336    the image.  Huge modularity violation.
2337 -----------------------------------------------------------------------------*/
2338     if (cmaponly) {
2339         if (HAS_COLORMAP(cmapP)) {
2340             prepareCmap(bmhdP, cmapP);
2341             cmapToPpm(ofP, cmapP);
2342         } else
2343             pm_error("You specified -cmaponly, but the ILBM "
2344                      "has no colormap");
2345     } else if (bmhdP && bmhdP->nPlanes == 0) {
2346         if (HAS_COLORMAP(cmapP)) {
2347             prepareCmap(bmhdP, cmapP);
2348             cmapToPpm(ofP, cmapP);
2349         } else
2350             pm_error("ILBM has neither a color map nor color planes");
2351     } else if (!bodyChunkProcessed) {
2352         if (HAS_COLORMAP(cmapP)) {
2353             pm_message("input is a colormap file");
2354             prepareCmap(bmhdP, cmapP);
2355             cmapToPpm(ofP, cmapP);
2356         } else
2357             pm_error("ILBM has neither %s or %s chunk",
2358                      ID2string(ID_BODY), ID2string(ID_CMAP));
2359     }
2360 }
2361 
2362 
2363 
2364 int
main(int argc,char * argv[])2365 main(int argc, char *argv[]) {
2366 
2367     FILE * ifP;
2368     int argn;
2369     short cmaponly = 0, isdeepopt = 0;
2370     bool endchunk;
2371     bool bodyChunkProcessed;
2372     long formsize, bytesread;
2373     int viewportmodes = 0, fakeviewport = 0, viewportmask;
2374     IFF_ID firstIffid;
2375     BitMapHeader *bmhdP = NULL;
2376     ColorMap * cmap;
2377     DirectColor *dcol = NULL;
2378 #define MAX_IGNORE  16
2379     IFF_ID ignorelist[MAX_IGNORE];
2380     int ignorecount = 0;
2381     char *maskname;
2382     const char * const usage =
2383         "[-verbose] [-ignore <chunkID> [-ignore <chunkID>] ...] "
2384         "[-isham|-isehb|-isdeep|-isnotham|-isnotehb|-isnotdeep] "
2385         "[-cmaponly] [-adjustcolors] "
2386         "[-transparent <color>] [-maskfile <filename>] [ilbmfile]";
2387     ppm_init(&argc, argv);
2388 
2389     viewportmask = 0xffffffff;
2390 
2391     argn = 1;
2392     while( argn < argc && argv[argn][0] == '-' && argv[argn][1] != '\0' ) {
2393         if( pm_keymatch(argv[argn], "-verbose", 2) )
2394             verbose = 1;
2395         else if( pm_keymatch(argv[argn], "-noverbose", 4) )
2396             verbose = 0;
2397         else if( pm_keymatch(argv[argn], "-isham", 4) )
2398             fakeviewport |= vmHAM;
2399         else if( pm_keymatch(argv[argn], "-isehb", 4) )
2400             fakeviewport |= vmEXTRA_HALFBRITE;
2401         else if( pm_keymatch(argv[argn], "-isdeep", 4) )
2402             isdeepopt = 1;
2403         else if( pm_keymatch(argv[argn], "-isnotham", 7) )
2404             viewportmask &= ~(vmHAM);
2405         else if( pm_keymatch(argv[argn], "-isnotehb", 7) )
2406             viewportmask &= ~(vmEXTRA_HALFBRITE);
2407         else if( pm_keymatch(argv[argn], "-isnotdeep", 7) )
2408             isdeepopt = -1;
2409         else if( pm_keymatch(argv[argn], "-cmaponly", 2) )
2410             cmaponly = 1;
2411         else if( pm_keymatch(argv[argn], "-adjustcolors", 2) )
2412             adjustcolors = 1;
2413         else if( pm_keymatch(argv[argn], "-noadjustcolors", 4) )
2414             adjustcolors = 0;
2415         else  if( pm_keymatch(argv[argn], "-transparent", 2) ) {
2416             if( ++argn >= argc )
2417                 pm_usage(usage);
2418             transpName = argv[argn];
2419         } else if( pm_keymatch(argv[argn], "-maskfile", 2) ) {
2420             if( ++argn >= argc )
2421                 pm_usage(usage);
2422             maskname = argv[argn];
2423             maskfile = pm_openw(maskname);
2424         } else if( pm_keymatch(argv[argn], "-ignore", 2) ) {
2425             if( ++argn >= argc )
2426                 pm_usage(usage);
2427             if( strlen(argv[argn]) != 4 )
2428                 pm_error("'-ignore' option needs a 4 byte chunk ID string "
2429                          "as argument");
2430             if( ignorecount >= MAX_IGNORE )
2431                 pm_error("max %d chunk IDs to ignore", MAX_IGNORE);
2432             ignorelist[ignorecount++] =
2433                 MAKE_ID(argv[argn][0], argv[argn][1], argv[argn][2],
2434                         argv[argn][3]);
2435         } else
2436             pm_usage(usage);
2437         ++argn;
2438     }
2439 
2440     if( argn < argc ) {
2441         ifP = pm_openr( argv[argn] );
2442         argn++;
2443     } else
2444         ifP = stdin;
2445 
2446     if( argn != argc )
2447         pm_usage(usage);
2448 
2449     wrotemask = false;  /* initial value */
2450 
2451     /* Read in the ILBM file. */
2452 
2453     firstIffid = get_big_long(ifP, ID_FORM, NULL);
2454     if (firstIffid != ID_FORM)
2455         pm_error("input is not a FORM type IFF file");
2456     formsize = get_big_long(ifP, ID_FORM, NULL);
2457     typeid = get_big_long(ifP, ID_FORM, NULL);
2458     if (typeid != ID_ILBM && typeid != ID_RGBN && typeid != ID_RGB8 &&
2459         typeid != ID_PBM)
2460         pm_error("input is not an ILBM, RGBN, RGB8 or PBM "
2461                  "type FORM IFF file");
2462     bytesread = 4;  /* FORM and formsize do not count */
2463 
2464     cmap = alloc_cmap();
2465 
2466     /* Main loop, parsing the IFF FORM. */
2467     bodyChunkProcessed = FALSE;
2468     endchunk = FALSE;
2469     while (!endchunk && formsize-bytesread >= 8) {
2470         long bytesReadForChunk;
2471 
2472         processChunk(ifP, formsize, ignorelist, ignorecount,
2473                      fakeviewport, viewportmask,
2474                      isdeepopt, cmaponly,
2475                      &bodyChunkProcessed,
2476                      &endchunk, &bmhdP, cmap, &dcol,
2477                      &viewportmodes, &bytesReadForChunk);
2478 
2479         bytesread += bytesReadForChunk;
2480     }
2481 
2482     if (maskfile) {
2483         pm_close(maskfile);
2484         if (!wrotemask)
2485             remove(maskname);
2486         pbm_freerow(maskrow);
2487     }
2488 
2489     maybeWriteColorMap(stdout, bmhdP, cmap, bodyChunkProcessed, cmaponly);
2490 
2491     {
2492         unsigned int skipped;
2493 
2494         for (skipped = 0; fgetc(ifP) != EOF; ++skipped)
2495             ++bytesread;
2496 
2497         if (skipped > 0)
2498             pm_message("skipped %u extraneous byte%s after last chunk",
2499                        skipped, (skipped == 1 ? "" : "s"));
2500     }
2501     pm_close(ifP);
2502 
2503     if (!endchunk && bytesread != formsize) {
2504         pm_message("warning - file length/FORM size field mismatch "
2505                    "(%ld != %ld+8)",
2506                    bytesread+8 /* FORM+size */, formsize);
2507     }
2508 
2509     return 0;
2510 }
2511 
2512 
2513 
2514