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