From 1fd361a1ea06e44286c213ca1f814f49306fdc43 Mon Sep 17 00:00:00 2001 From: giraffedata Date: Sat, 19 Aug 2006 03:12:28 +0000 Subject: Create Subversion repository git-svn-id: http://svn.code.sf.net/p/netpbm/code/trunk@1 9d0c8265-081b-0410-96cb-a4ca84ce46f8 --- converter/ppm/ilbmtoppm.c | 2381 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 2381 insertions(+) create mode 100644 converter/ppm/ilbmtoppm.c (limited to 'converter/ppm/ilbmtoppm.c') diff --git a/converter/ppm/ilbmtoppm.c b/converter/ppm/ilbmtoppm.c new file mode 100644 index 00000000..f9f9bac3 --- /dev/null +++ b/converter/ppm/ilbmtoppm.c @@ -0,0 +1,2381 @@ +/* ilbmtoppm.c - read an IFF ILBM file and produce a portable pixmap +** +** Copyright (C) 1989 by Jef Poskanzer. +** +** Permission to use, copy, modify, and distribute this software and its +** documentation for any purpose and without fee is hereby granted, provided +** that the above copyright notice appear in all copies and that both that +** copyright notice and this permission notice appear in supporting +** documentation. This software is provided "as is" without express or +** implied warranty. +** +** Modified by Mark Thompson on 10/4/90 to accomodate 24-bit IFF files +** as used by ASDG, NewTek, etc. +** +** Modified by Ingo Wilken (Ingo.Wilken@informatik.uni-oldenburg.de) +** 20/Jun/93: +** - row-by-row operation +** - better de-interleave algorithm +** - colormap files +** - direct color +** 04/Oct/93: +** - multipalette capability (PCHG chunk) +** - options -ignore, -isham, -isehb and -adjustcolors +** 22/May/94: +** - minor change: check first for 24 planes, then for HAM +** 21/Sep/94: +** - write mask plane to a file if -maskfile option used +** - write colormap file +** - added sliced HAM/dynamic HAM/dynamic Hires multipalette formats (SHAM, CTBL chunk) +** - added color lookup tables (CLUT chunk) +** - major rework of colormap/multipalette handling +** - now uses numeric IFF IDs +** 24/Oct/94: +** - transparentColor capability +** - added RGBN/RGB8 image types +** - 24-bit & direct color modified to n-bit deep ILBM +** 22/Feb/95: +** - direct color (DCOL) reimplemented +** 29/Mar/95 +** - added IFF-PBM format +*/ + +#include + +#include "pm_c_util.h" +#include "ppm.h" +#include "ilbm.h" +#include "mallocvar.h" + +/*#define DEBUG*/ + +typedef struct { + int reg; /* color register to change */ + pixval r, g, b; /* new colors for register */ +} PaletteChange; + +typedef struct { + pixel *color; + int ncolors; + /* lookup tables */ + unsigned char *redlut; + unsigned char *greenlut; + unsigned char *bluelut; + unsigned char *monolut; + /* multipalette stuff */ + PaletteChange *mp_init; + PaletteChange **mp_change; + int mp_rows; /* # of rows in change array */ + int mp_type; /* see below, higher types preferred */ + int mp_flags; + IFF_ID mp_id; +} ColorMap; + +#define HAS_COLORMAP(cmap) ((cmap) && (cmap)->color) +#define HAS_COLORLUT(cmap) ((cmap) && ((cmap)->redlut || (cmap)->greenlut || (cmap)->bluelut)) +#define HAS_MONOLUT(cmap) ((cmap) && (cmap)->monolut) +#define HAS_MULTIPALETTE(cmap) (HAS_COLORMAP(cmap) && (cmap)->mp_type) +#define MP_TYPE_SHAM 1 +#define MP_TYPE_CTBL 2 +#define MP_TYPE_PCHG 3 +#define MP_REG_IGNORE -1 +#define MP_REG_END -2 +#define MP_FLAGS_SKIPLACED (1<<0) + +#define FACTOR_4BIT 17 /* scale factor maxval 15 -> maxval 255 */ + +static short verbose = 0; +static short adjustcolors = 0; +static unsigned char *ilbmrow; +static pixel *pixelrow; +static FILE *maskfile = NULL; +static bit *maskrow = NULL; +static short wrotemask = 0; +static IFF_ID typeid; /* ID_ILBM, ID_RGBN, ID_RGB8 */ + +static pixel *transpColor = NULL; /* transparent color */ +static short transpIndex = -1; +static char *transpName = NULL; + +static bool debug = FALSE; + + + +static char * +ID2string(id) + IFF_ID id; +{ + static char str[] = "abcd"; + + str[0] = (char)(id >> 24 & 0xff); + str[1] = (char)(id >> 16 & 0xff); + str[2] = (char)(id >> 8 & 0xff); + str[3] = (char)(id >> 0 & 0xff); + + return str; +} + + +/**************************************************************************** + Memory allocation + ****************************************************************************/ +static ColorMap * +alloc_cmap(void) { + + ColorMap * cmap; + + MALLOCVAR_NOFAIL(cmap); + + cmap->color = NULL; + cmap->ncolors = 0; + cmap->monolut = NULL; + cmap->redlut = NULL; + cmap->greenlut = NULL; + cmap->bluelut = NULL; + cmap->mp_init = NULL; + cmap->mp_change = NULL; + cmap->mp_rows = 0; + cmap->mp_type = 0; + cmap->mp_flags = 0; + + return cmap; +} + + + +static rawtype * +alloc_rawrow(cols) + int cols; +{ + rawtype *r; + int i; + + MALLOCARRAY_NOFAIL(r, cols); + + for( i = 0; i < cols; i++ ) + r[i] = 0; + + return r; +} + + +/**************************************************************************** + Basic I/O functions + ****************************************************************************/ + +static void +readerr(f, iffid) + FILE *f; + IFF_ID iffid; +{ + if( ferror(f) ) + pm_error("read error"); + else + pm_error("premature EOF in %s chunk", ID2string(iffid)); +} + + +static void +read_bytes(FILE * const ifP, + int const bytes, + unsigned char * const buffer, + IFF_ID const iffid, + unsigned long * const counterP) { + + if (counterP) { + if (*counterP < bytes) + pm_error("insufficient data in %s chunk", ID2string(iffid)); + *counterP -= bytes; + } + if (fread(buffer, 1, bytes, ifP) != bytes) + readerr(ifP, iffid); +} + + + +static unsigned char +get_byte(ifp, iffid, counter) + FILE* ifp; + IFF_ID iffid; + long *counter; +{ + int i; + + if( counter ) { + if( *counter == 0 ) + pm_error("insufficient data in %s chunk", ID2string(iffid)); + --(*counter); + } + i = getc(ifp); + if( i == EOF ) + readerr(ifp, iffid); + + return (unsigned char) i; +} + +static long +get_big_long(FILE * const ifP, + IFF_ID const iffid, + unsigned long * const counterP) { + + long l; + + if (counterP) { + if (*counterP < 4) + pm_error("insufficient data in %s chunk", ID2string(iffid)); + *counterP -= 4; + } + if (pm_readbiglong(ifP, &l) == -1) + readerr(ifP, iffid); + + return l; +} + + + +static short +get_big_short(FILE * const ifP, + IFF_ID const iffid, + unsigned long * const counterP) { + + short s; + + if (counterP) { + if (*counterP < 2) + pm_error("insufficient data in %s chunk", ID2string(iffid)); + *counterP -= 2; + } + if (pm_readbigshort(ifP, &s) == -1) + readerr(ifP, iffid); + + return s; +} + + + +/**************************************************************************** + Chunk reader + ****************************************************************************/ + +static void +chunk_end(FILE * const ifP, + IFF_ID const iffid, + unsigned long const chunksize) { + + if (chunksize > 0) { + unsigned long remainingChunksize; + pm_message("warning - %ld extraneous byte%s in %s chunk", + chunksize, (chunksize == 1 ? "" : "s"), ID2string(iffid)); + remainingChunksize = chunksize; /* initial value */ + while (remainingChunksize > 0) + get_byte(ifP, iffid, &remainingChunksize); + } +} + + + +static void +skip_chunk(FILE * const ifP, + IFF_ID const iffid, + unsigned long const chunksize) { + unsigned long remainingChunksize; + + remainingChunksize = chunksize; /* initial value */ + + while (remainingChunksize > 0) + get_byte(ifP, iffid, &remainingChunksize); +} + + + +static void +display_chunk(FILE * const ifP, + IFF_ID const iffid, + unsigned long const chunksize) { + + int byte; + unsigned long remainingChunksize; + + pm_message("contents of %s chunk:", ID2string(iffid)); + + remainingChunksize = chunksize; /* initial value */ + byte = '\0'; + + while (remainingChunksize > 0) { + byte = get_byte(ifP, iffid, &remainingChunksize); + if (fputc(byte, stderr) == EOF) + pm_error("write error"); + } + if (byte != '\n') + if (fputc('\n', stderr) == EOF) + pm_error("write error"); +} + + +static void +read_cmap(FILE * const ifp, + IFF_ID const iffid, + long const chunksize, + ColorMap * const cmap) { + + long colors; + + colors = chunksize / 3; + if( colors == 0 ) { + pm_error("warning - empty %s colormap", ID2string(iffid)); + skip_chunk(ifp, iffid, chunksize); + } else { + unsigned int i; + if( cmap->color ) /* prefer CMAP-chunk over CMYK-chunk */ + ppm_freerow(cmap->color); + cmap->color = ppm_allocrow(colors); + cmap->ncolors = colors; + + for( i = 0; i < colors; ++i ) { + int r, g, b; + r = get_byte(ifp, iffid, &chunksize); + g = get_byte(ifp, iffid, &chunksize); + b = get_byte(ifp, iffid, &chunksize); + PPM_ASSIGN(cmap->color[i], r, g, b); + } + chunk_end(ifp, iffid, chunksize); + } +} + + + +static void +read_cmyk(FILE * const ifp, + IFF_ID const iffid, + long const chunksize, + ColorMap * const cmap) { + + if( HAS_COLORMAP(cmap) ) { /* prefer RGB color map */ + skip_chunk(ifp, iffid, chunksize); + } else { + long const colors = chunksize/4; + if( colors == 0 ) { + pm_error("warning - empty %s colormap", ID2string(iffid)); + skip_chunk(ifp, iffid, chunksize); + } else { + unsigned int i; + cmap->color = ppm_allocrow(colors); + cmap->ncolors = colors; + + for( i = 0; i < colors; ++i ) { + int c, m, y, k; + c = get_byte(ifp, iffid, &chunksize); + m = get_byte(ifp, iffid, &chunksize); + y = get_byte(ifp, iffid, &chunksize); + k = get_byte(ifp, iffid, &chunksize); + + { + pixval const red = + MAXCOLVAL - MIN(MAXCOLVAL, + c*(MAXCOLVAL-k)/MAXCOLVAL+k); + pixval const green = + MAXCOLVAL - MIN(MAXCOLVAL, + m*(MAXCOLVAL-k)/MAXCOLVAL+k); + pixval const blue = + MAXCOLVAL - MIN(MAXCOLVAL, + y*(MAXCOLVAL-k)/MAXCOLVAL+k); + + PPM_ASSIGN(cmap->color[i], red, green, blue); + } + } + chunk_end(ifp, iffid, chunksize); + } + } +} + + + +static void +read_clut(FILE * const ifp, + IFF_ID const iffid, + unsigned long const chunksize, + ColorMap * const cmap) { + + if (chunksize != CLUTSize) { + pm_message("invalid size for %s chunk - skipping it", + ID2string(iffid)); + skip_chunk(ifp, iffid, chunksize); + } else { + long type; + unsigned char * lut; + unsigned long remainingChunksize; + unsigned int i; + + type = get_big_long(ifp, iffid, &remainingChunksize); + get_big_long(ifp, iffid, &remainingChunksize); /* skip reserved fld */ + + MALLOCARRAY_NOFAIL(lut, 256); + for( i = 0; i < 256; ++i ) + lut[i] = get_byte(ifp, iffid, &remainingChunksize); + + switch( type ) { + case CLUT_MONO: + cmap->monolut = lut; + break; + case CLUT_RED: + cmap->redlut = lut; + break; + case CLUT_GREEN: + cmap->greenlut = lut; + break; + case CLUT_BLUE: + cmap->bluelut = lut; + break; + default: + pm_message("warning - %s type %ld not recognized", + ID2string(iffid), type); + free(lut); + } + } +} + + + +static BitMapHeader * +read_bmhd(FILE * const ifP, + IFF_ID const iffid, + unsigned long const chunksize) { + + BitMapHeader * bmhdP; + + if( chunksize != BitMapHeaderSize ) { + pm_message("invalid size for %s chunk - skipping it", + ID2string(iffid)); + skip_chunk(ifP, iffid, chunksize); + bmhdP = NULL; + } else { + unsigned long remainingChunksize; + + MALLOCVAR_NOFAIL(bmhdP); + + remainingChunksize = chunksize; /* initial value */ + + bmhdP->w = get_big_short(ifP, iffid, &remainingChunksize); + bmhdP->h = get_big_short(ifP, iffid, &remainingChunksize); + bmhdP->x = get_big_short(ifP, iffid, &remainingChunksize); + bmhdP->y = get_big_short(ifP, iffid, &remainingChunksize); + bmhdP->nPlanes = get_byte(ifP, iffid, &remainingChunksize); + bmhdP->masking = get_byte(ifP, iffid, &remainingChunksize); + bmhdP->compression = get_byte(ifP, iffid, &remainingChunksize); + bmhdP->flags = get_byte(ifP, iffid, &remainingChunksize); + bmhdP->transparentColor = + get_big_short(ifP, iffid, &remainingChunksize); + bmhdP->xAspect = get_byte(ifP, iffid, &remainingChunksize); + bmhdP->yAspect = get_byte(ifP, iffid, &remainingChunksize); + bmhdP->pageWidth = get_big_short(ifP, iffid, &remainingChunksize); + bmhdP->pageHeight = get_big_short(ifP, iffid, &remainingChunksize); + + if( verbose ) { + if( typeid == ID_ILBM ) + pm_message("dimensions: %dx%d, %d planes", + bmhdP->w, bmhdP->h, bmhdP->nPlanes); + else + pm_message("dimensions: %dx%d", bmhdP->w, bmhdP->h); + + if( typeid == ID_ILBM || typeid == ID_PBM ) { + pm_message("compression: %s", + bmhdP->compression <= cmpMAXKNOWN ? + cmpNAME[bmhdP->compression] : "unknown"); + + switch( bmhdP->masking ) { + case mskNone: + break; + case mskHasMask: + case mskHasTransparentColor: + if( !maskfile ) + pm_message("use '-maskfile ' " + "to generate a PBM mask file from %s", + mskNAME[bmhdP->masking]); + break; + case mskLasso: + pm_message("warning - masking type '%s' not recognized", + mskNAME[bmhdP->masking]); + break; + default: + pm_error("unknown masking type %d", bmhdP->masking); + } + } + else /* RGBN/RGB8 */ + if( !maskfile ) + pm_message("use '-maskfile ' " + "to generate a PBM mask file " + "from genlock bits"); + } + + /* fix aspect ratio */ + if( bmhdP->xAspect == 0 || bmhdP->yAspect == 0 ) { + pm_message("warning - illegal aspect ratio %d:%d, using 1:1", + bmhdP->xAspect, bmhdP->yAspect); + bmhdP->xAspect = bmhdP->yAspect = 1; + } + + if( bmhdP->xAspect != bmhdP->yAspect ) { + pm_message("warning - non-square pixels; " + "to fix do a 'pnmscale -%cscale %g'", + bmhdP->xAspect > bmhdP->yAspect ? 'x' : 'y', + bmhdP->xAspect > bmhdP->yAspect ? + (float)(bmhdP->xAspect)/bmhdP->yAspect : + (float)(bmhdP->yAspect)/bmhdP->xAspect); + } + } + return bmhdP; +} + + +/**************************************************************************** + ILBM functions + ****************************************************************************/ + + +static void +read_ilbm_plane(FILE * const ifP, + unsigned long * const remainingChunksizeP, + int const bytes, + int const compression) { + + unsigned char *ubp; + int j, byte; + int bytesRemaining; + + bytesRemaining = bytes; /* initial value */ + + switch(compression) { + case cmpNone: + read_bytes(ifP, bytesRemaining, ilbmrow, + ID_BODY, remainingChunksizeP); + break; + case cmpByteRun1: + ubp = ilbmrow; + do { + byte = (int)get_byte(ifP, ID_BODY, remainingChunksizeP); + if( byte <= 127 ) { + j = byte; + bytesRemaining -= (j+1); + if( bytesRemaining < 0 ) + pm_error("error doing ByteRun1 decompression"); + for( ; j >= 0; j-- ) + *ubp++ = get_byte(ifP, ID_BODY, remainingChunksizeP); + } + else + if ( byte != 128 ) { + j = 256 - byte; + bytesRemaining -= (j+1); + if( bytesRemaining < 0 ) + pm_error("error doing ByteRun1 decompression"); + byte = (int)get_byte(ifP, ID_BODY, remainingChunksizeP); + for( ; j >= 0; j-- ) + *ubp++ = (unsigned char)byte; + } + /* 128 is a NOP */ + } + while( bytesRemaining > 0 ); + break; + default: + pm_error("unknown compression type %d", compression); + } +} + + +static const unsigned char bit_mask[] = {1, 2, 4, 8, 16, 32, 64, 128}; + +static void +decode_row(FILE * const ifP, + unsigned long * const remainingChunksizeP, + rawtype * const chunkyrow, + int const nPlanes, + BitMapHeader * const bmhdP) { + + int plane, col, cols, cbit, bytes; + unsigned char *ilp; + rawtype *chp; + + cols = bmhdP->w; + bytes = RowBytes(cols); + for( plane = 0; plane < nPlanes; plane++ ) { + int mask; + + mask = 1 << plane; + read_ilbm_plane(ifP, remainingChunksizeP, bytes, bmhdP->compression); + + ilp = ilbmrow; + chp = chunkyrow; + + cbit = 7; + for( col = 0; col < cols; col++, cbit--, chp++ ) { + if( cbit < 0 ) { + cbit = 7; + ilp++; + } + if( *ilp & bit_mask[cbit] ) + *chp |= mask; + else + *chp &= ~mask; + } + } +} + + +static void +decode_mask(FILE * const ifP, + unsigned long * const remainingChunksizeP, + rawtype * const chunkyrow, + BitMapHeader * const bmhdP) { + + int col, cols, cbit; + unsigned char *ilp; + + cols = bmhdP->w; + switch( bmhdP->masking ) { + case mskNone: + break; + case mskHasMask: /* mask plane */ + read_ilbm_plane(ifP, remainingChunksizeP, RowBytes(cols), + bmhdP->compression); + if( maskfile ) { + ilp = ilbmrow; + cbit = 7; + for( col = 0; col < cols; col++, cbit-- ) { + if( cbit < 0 ) { + cbit = 7; + ilp++; + } + if( *ilp & bit_mask[cbit] ) + maskrow[col] = PBM_BLACK; + else + maskrow[col] = PBM_WHITE; + } + pbm_writepbmrow(maskfile, maskrow, cols, 0); + wrotemask = 1; + } + break; + case mskHasTransparentColor: + if( !chunkyrow ) + pm_error("decode_mask(): chunkyrow == NULL - can't happen"); + + if( maskfile ) { + for( col = 0; col < cols; col++ ) { + if( chunkyrow[col] == bmhdP->transparentColor ) + maskrow[col] = PBM_WHITE; + else + maskrow[col] = PBM_BLACK; + } + pbm_writepbmrow(maskfile, maskrow, cols, 0); + wrotemask = 1; + } + break; + case mskLasso: + pm_error("This program does not know how to process Lasso masking"); + break; + default: + pm_error("decode_mask(): unknown masking type %d - can't happen", + bmhdP->masking); + } +} + + +/**************************************************************************** + Multipalette handling + ****************************************************************************/ + + +static void +multi_adjust(cmap, row, palchange) + ColorMap *cmap; + int row; + PaletteChange *palchange; +{ + int i, reg; + + for( i = 0; palchange[i].reg != MP_REG_END; i++ ) { + reg = palchange[i].reg; + if( reg >= cmap->ncolors ) { + pm_message("warning - palette change register out of range"); + pm_message(" row %d change structure %d reg=%d (max %d)", + row, i, reg, cmap->ncolors-1); + pm_message(" ignoring it... " + "colors might get messed up from here"); + } + else + if( reg != MP_REG_IGNORE ) { + PPM_ASSIGN(cmap->color[reg], + palchange[i].r, palchange[i].g, palchange[i].b); + } + } +} + +static void +multi_init(cmap, viewportmodes) + ColorMap *cmap; + long viewportmodes; +{ + if( cmap->mp_init ) + multi_adjust(cmap, -1, cmap->mp_init); + if( !(viewportmodes & vmLACE) ) + cmap->mp_flags &= ~(MP_FLAGS_SKIPLACED); +} + +static void +multi_update(cmap, row) + ColorMap *cmap; + int row; +{ + if( cmap->mp_flags & MP_FLAGS_SKIPLACED ) { + if( ODD(row) ) + return; + if( row/2 < cmap->mp_rows && cmap->mp_change[row/2] ) + multi_adjust(cmap, row, cmap->mp_change[row/2]); + } + else { + if( row < cmap->mp_rows && cmap->mp_change[row] ) + multi_adjust(cmap, row, cmap->mp_change[row]); + } +} + +static void +multi_free(cmap) + ColorMap *cmap; +{ + int i; + + if( cmap->mp_init ) { + free(cmap->mp_init); + cmap->mp_init = NULL; + } + if( cmap->mp_change ) { + for( i = 0; i < cmap->mp_rows; i++ ) { + if( cmap->mp_change[i] ) + free(cmap->mp_change[i]); + } + free(cmap->mp_change); + cmap->mp_change = NULL; + } + cmap->mp_rows = 0; + cmap->mp_type = 0; + cmap->mp_flags = 0; +} + + +/**************************************************************************** + Colormap handling + ****************************************************************************/ + +static void +check_cmap(bmhd, cmap) + BitMapHeader *bmhd; + ColorMap *cmap; +{ + pixval colmaxval = 0; + int shifted = 1; + int i, r, g, b; + + if( bmhd ) { + if( bmhd->masking == mskHasTransparentColor || + bmhd->masking == mskLasso ) { + transpIndex = bmhd->transparentColor; + if( !transpName ) { + MALLOCVAR_NOFAIL(transpColor); + if( transpIndex >= cmap->ncolors ) { + pm_message("using default transparent color (black)"); + PPM_ASSIGN(*transpColor, 0, 0, 0); + } + else + *transpColor = cmap->color[transpIndex]; + } + } + + if( bmhd->flags & BMHD_FLAGS_CMAPOK ) + return; + } + + if( !HAS_COLORMAP(cmap) ) + return; + + for( i = 0; i < cmap->ncolors; i++ ) { + r = PPM_GETR(cmap->color[i]); + if( r > colmaxval ) colmaxval = r; + if( r & 0x0f ) shifted = 0; + + g = PPM_GETG(cmap->color[i]); + if( g > colmaxval ) colmaxval = g; + if( g & 0x0f ) shifted = 0; + + b = PPM_GETB(cmap->color[i]); + if( b > colmaxval ) colmaxval = b; + if( b & 0x0f ) shifted = 0; + } + +#ifdef DEBUG + pm_message("colormap maxval is %d", colmaxval); +#endif + if( colmaxval == 0 ) + pm_message("warning - black colormap"); + else + if( shifted || colmaxval <= 15 ) { + if( !adjustcolors ) { + pm_message("warning - probably %s4-bit colormap", + shifted ? "shifted " : ""); + pm_message(" use '-adjustcolors' to scale colormap to 8 bits"); + } + else { + pm_message("scaling colormap to 8 bits"); + for( i = 0; i < cmap->ncolors; i++ ) { + r = PPM_GETR(cmap->color[i]); + g = PPM_GETG(cmap->color[i]); + b = PPM_GETB(cmap->color[i]); + if( shifted ) { + r >>= 4; + g >>= 4; + b >>= 4; + } + r *= FACTOR_4BIT; + g *= FACTOR_4BIT; + b *= FACTOR_4BIT; + PPM_ASSIGN(cmap->color[i], r, g, b); + } + } + } +} + + +static pixval +lookup_red(cmap, oldval) + ColorMap *cmap; + int oldval; +{ + if( cmap && cmap->redlut && oldval < 256 ) + return cmap->redlut[oldval]; + else + return oldval; +} + +static pixval +lookup_green(cmap, oldval) + ColorMap *cmap; + int oldval; +{ + if( cmap && cmap->greenlut && oldval < 256 ) + return cmap->greenlut[oldval]; + else + return oldval; +} + +static pixval +lookup_blue(cmap, oldval) + ColorMap *cmap; + int oldval; +{ + if( cmap && cmap->bluelut && oldval < 256 ) + return cmap->bluelut[oldval]; + else + return oldval; +} + +static pixval +lookup_mono(cmap, oldval) + ColorMap *cmap; + int oldval; +{ + if( cmap && cmap->monolut && oldval < 256 ) + return cmap->monolut[oldval]; + else + return oldval; +} + +static ColorMap * +ehbcmap(cmap) + ColorMap *cmap; +{ + pixel *tempcolor = NULL; + int i, col; + + col = cmap->ncolors; + + tempcolor = ppm_allocrow(col * 2); + for( i = 0; i < col; i++ ) { + tempcolor[i] = cmap->color[i]; + PPM_ASSIGN(tempcolor[col + i], PPM_GETR(cmap->color[i]) / 2, + PPM_GETG(cmap->color[i]) / 2, + PPM_GETB(cmap->color[i]) / 2 ); + } + ppm_freerow(cmap->color); + cmap->color = tempcolor; + cmap->ncolors *= 2; + + return cmap; +} + + + +static pixval +lut_maxval(ColorMap * const cmap, + pixval const maxval) { + + pixval retval; + + if (maxval >= 255) + retval = maxval; + else { + if (!HAS_COLORLUT(cmap)) + retval = maxval; + else { + unsigned int i; + unsigned char maxlut; + maxlut = maxval; + for( i = 0; i < maxval; i++ ) { + if( cmap->redlut && cmap->redlut[i] > maxlut ) + maxlut = cmap->redlut[i]; + if( cmap->greenlut && cmap->greenlut[i] > maxlut ) + maxlut = cmap->greenlut[i]; + if( cmap->bluelut && cmap->bluelut[i] > maxlut ) + maxlut = cmap->bluelut[i]; + } + pm_message("warning - " + "%d-bit index into 8-bit color lookup table, " + "table maxval=%d", + pm_maxvaltobits(maxval), maxlut); + if( maxlut != maxval ) + retval = 255; + else + retval = maxval; + pm_message(" assuming image maxval=%d", retval); + } + } + return retval; +} + + + +static void +get_color(cmap, idx, red, green, blue) + ColorMap *cmap; + int idx; + pixval *red, *green, *blue; +{ + if( HAS_COLORMAP(cmap) ) { + pixval r, g, b; + + if( idx >= cmap->ncolors ) + pm_error("color index out of range: %d (max %d)", + idx, cmap->ncolors); + r = PPM_GETR(cmap->color[idx]); + g = PPM_GETG(cmap->color[idx]); + b = PPM_GETB(cmap->color[idx]); + + *red = lookup_red(cmap, r); + *green = lookup_green(cmap, g); + *blue = lookup_blue(cmap, b); + } + else { + *red = *green = *blue = lookup_mono(cmap, idx); + } +} + + +/**************************************************************************** + Conversion functions + ****************************************************************************/ + +static void +std_to_ppm(FILE * const ifp, + long const chunksize, + BitMapHeader * const bmhd, + ColorMap * const cmap, + long const viewportmodes); + +static void +ham_to_ppm(FILE * const ifp, + long const chunksize, + BitMapHeader * const bmhd, + ColorMap * const cmap, + long const viewportmodes) { + + int cols, rows, hambits, hammask, hamshift, hammask2, col, row; + pixval maxval; + rawtype *rawrow; + unsigned char hamlut[256]; + + cols = bmhd->w; + rows = bmhd->h; + hambits = bmhd->nPlanes - 2; + hammask = (1 << hambits) - 1; + hamshift = 8 - hambits; + hammask2 = (1 << hamshift) - 1; + + if( hambits > 8 || hambits < 1 ) { + int const assumed_viewportmodes = viewportmodes & ~(vmHAM); + + pm_message("%d-plane HAM?? - interpreting image as a normal ILBM", + bmhd->nPlanes); + std_to_ppm(ifp, chunksize, bmhd, cmap, assumed_viewportmodes); + return; + } else { + unsigned long remainingChunksize; + + pm_message("input is a %sHAM%d file", + HAS_MULTIPALETTE(cmap) ? "multipalette " : "", + bmhd->nPlanes); + + if( HAS_COLORLUT(cmap) || HAS_MONOLUT(cmap) ) { + pm_message("warning - color lookup tables ignored in HAM"); + if( cmap->redlut ) free(cmap->redlut); + if( cmap->greenlut ) free(cmap->greenlut); + if( cmap->bluelut ) free(cmap->bluelut); + if( cmap->monolut ) free(cmap->monolut); + cmap->redlut = cmap->greenlut = cmap->bluelut = + cmap->monolut = NULL; + } + if( !HAS_COLORMAP(cmap) ) { + pm_message("no colormap - interpreting values as grayscale"); + maxval = pm_bitstomaxval(hambits); + for( col = 0; col <= maxval; col++ ) + hamlut[col] = col * MAXCOLVAL / maxval; + cmap->monolut = hamlut; + } + + if( transpName ) { + MALLOCVAR_NOFAIL(transpColor); + *transpColor = ppm_parsecolor(transpName, MAXCOLVAL); + } + + if( HAS_MULTIPALETTE(cmap) ) + multi_init(cmap, viewportmodes); + + rawrow = alloc_rawrow(cols); + + ppm_writeppminit(stdout, cols, rows, MAXCOLVAL, 0); + + remainingChunksize = chunksize; /* initial value */ + + for (row = 0; row < rows; ++row) { + pixval r, g, b; + + if( HAS_MULTIPALETTE(cmap) ) + multi_update(cmap, row); + + decode_row(ifp, &remainingChunksize, rawrow, bmhd->nPlanes, bmhd); + decode_mask(ifp, &remainingChunksize, rawrow, bmhd); + + r = g = b = 0; + for( col = 0; col < cols; col++ ) { + int idx = rawrow[col] & hammask; + + if( transpColor && maskrow && maskrow[col] == PBM_WHITE ) + pixelrow[col] = *transpColor; + else { + switch((rawrow[col] >> hambits) & 0x03) { + case HAMCODE_CMAP: + get_color(cmap, idx, &r, &g, &b); + break; + case HAMCODE_BLUE: + b = ((b & hammask2) | (idx << hamshift)); + /*b = hamlut[idx];*/ + break; + case HAMCODE_RED: + r = ((r & hammask2) | (idx << hamshift)); + /*r = hamlut[idx];*/ + break; + case HAMCODE_GREEN: + g = ((g & hammask2) | (idx << hamshift)); + /*g = hamlut[idx];*/ + break; + default: + pm_error("ham_to_ppm(): " + "impossible HAM code - can't happen"); + } + PPM_ASSIGN(pixelrow[col], r, g, b); + } + } + ppm_writeppmrow(stdout, pixelrow, cols, MAXCOLVAL, 0); + } + chunk_end(ifp, ID_BODY, remainingChunksize); + } +} + + + +static void +std_to_ppm(FILE * const ifP, + long const chunksize, + BitMapHeader * const bmhd, + ColorMap * const cmap, + long const viewportmodes) { + + if (viewportmodes & vmHAM) { + ham_to_ppm(ifP, chunksize, bmhd, cmap, viewportmodes); + } else { + unsigned int const cols = bmhd->w; + unsigned int const rows = bmhd->h; + + rawtype *rawrow; + unsigned int row, col; + pixval maxval; + unsigned long remainingChunksize; + + pm_message("input is a %d-plane %s%sILBM", bmhd->nPlanes, + HAS_MULTIPALETTE(cmap) ? "multipalette " : "", + viewportmodes & vmEXTRA_HALFBRITE ? "EHB " : "" + ); + + if( bmhd->nPlanes > MAXPLANES ) + pm_error("too many planes (max %d)", MAXPLANES); + + if( HAS_COLORMAP(cmap) ) { + if( viewportmodes & vmEXTRA_HALFBRITE ) + ehbcmap(cmap); /* Modifies *cmap */ + maxval = MAXCOLVAL; + } + else { + pm_message("no colormap - interpreting values as grayscale"); + maxval = lut_maxval(cmap, pm_bitstomaxval(bmhd->nPlanes)); + if( maxval > PPM_OVERALLMAXVAL ) + pm_error("nPlanes is too large"); + } + + if( transpName ) { + MALLOCVAR_NOFAIL(transpColor); + *transpColor = ppm_parsecolor(transpName, maxval); + } + + rawrow = alloc_rawrow(cols); + + if( HAS_MULTIPALETTE(cmap) ) + multi_init(cmap, viewportmodes); + + ppm_writeppminit( stdout, cols, rows, maxval, 0 ); + + remainingChunksize = chunksize; /* initial value */ + + for (row = 0; row < rows; ++row) { + + if( HAS_MULTIPALETTE(cmap) ) + multi_update(cmap, row); + + decode_row(ifP, &remainingChunksize, rawrow, bmhd->nPlanes, bmhd); + decode_mask(ifP, &remainingChunksize, rawrow, bmhd); + + for( col = 0; col < cols; col++ ) { + pixval r, g, b; + if( transpColor && maskrow && maskrow[col] == PBM_WHITE ) + pixelrow[col] = *transpColor; + else { + get_color(cmap, rawrow[col], &r, &g, &b); + PPM_ASSIGN(pixelrow[col], r, g, b); + } + } + ppm_writeppmrow(stdout, pixelrow, cols, maxval, 0); + } + chunk_end(ifP, ID_BODY, remainingChunksize); + } +} + + + +static void +deep_to_ppm(FILE * const ifP, + long const chunksize, + BitMapHeader * const bmhdP, + ColorMap * const cmap) { + + unsigned int const cols = bmhdP->w; + unsigned int const rows = bmhdP->h; + unsigned int const planespercolor = bmhdP->nPlanes / 3; + + unsigned int col, row; + rawtype *Rrow, *Grow, *Brow; + pixval maxval; + unsigned long remainingChunksize; + + pm_message("input is a deep (%d-bit) ILBM", bmhdP->nPlanes); + if( planespercolor > MAXPLANES ) + pm_error("too many planes (max %d)", MAXPLANES * 3); + + if( bmhdP->masking == mskHasTransparentColor || + bmhdP->masking == mskLasso ) { + pm_message("masking type '%s' in a deep ILBM?? - ignoring it", + mskNAME[bmhdP->masking]); + bmhdP->masking = mskNone; + } + + maxval = lut_maxval(cmap, pm_bitstomaxval(planespercolor)); + if( maxval > PPM_OVERALLMAXVAL ) + pm_error("nPlanes is too large"); + + if( transpName ) { + MALLOCVAR_NOFAIL(transpColor); + *transpColor = ppm_parsecolor(transpName, maxval); + } + + Rrow = alloc_rawrow(cols); + Grow = alloc_rawrow(cols); + Brow = alloc_rawrow(cols); + + ppm_writeppminit(stdout, cols, rows, maxval, 0); + + remainingChunksize = chunksize; /* initial value */ + + for( row = 0; row < rows; row++ ) { + decode_row(ifP, &remainingChunksize, Rrow, planespercolor, bmhdP); + decode_row(ifP, &remainingChunksize, Grow, planespercolor, bmhdP); + decode_row(ifP, &remainingChunksize, Brow, planespercolor, bmhdP); + decode_mask(ifP, &remainingChunksize, NULL, bmhdP); + + for( col = 0; col < cols; col++ ) { + if( transpColor && maskrow && maskrow[col] == PBM_WHITE ) + pixelrow[col] = *transpColor; + else + PPM_ASSIGN(pixelrow[col], lookup_red(cmap, Rrow[col]), + lookup_green(cmap, Grow[col]), + lookup_blue(cmap, Brow[col]) ); + } + ppm_writeppmrow(stdout, pixelrow, cols, maxval, 0); + } + chunk_end(ifP, ID_BODY, remainingChunksize); +} + + + +static void +dcol_to_ppm(FILE * const ifP, + long const chunksize, + BitMapHeader * const bmhdP, + ColorMap * const cmap, + DirectColor * const dcol) { + + unsigned int const cols = bmhdP->w; + unsigned int const rows = bmhdP->h; + unsigned int const redplanes = dcol->r; + unsigned int const greenplanes = dcol->g; + unsigned int const blueplanes = dcol->b; + + int col, row, i; + rawtype *Rrow, *Grow, *Brow; + pixval maxval, redmaxval, greenmaxval, bluemaxval; + pixval *redtable, *greentable, *bluetable; + unsigned long remainingChunksize; + + pm_message("input is a %d:%d:%d direct color ILBM", + redplanes, greenplanes, blueplanes); + + if( redplanes > MAXPLANES || + blueplanes > MAXPLANES || + greenplanes > MAXPLANES ) + pm_error("too many planes (max %d per color component)", MAXPLANES); + + if( bmhdP->nPlanes != (redplanes + greenplanes + blueplanes) ) + pm_error("%s/%s plane number mismatch", + ID2string(ID_BMHD), ID2string(ID_DCOL)); + + if( bmhdP->masking == mskHasTransparentColor || + bmhdP->masking == mskLasso ) { + pm_message("masking type '%s' in a direct color ILBM?? - ignoring it", + mskNAME[bmhdP->masking]); + bmhdP->masking = mskNone; + } + + if( HAS_COLORLUT(cmap) ) { + pm_error("This program does not know how to process a %s chunk " + "in direct color", ID2string(ID_CLUT)); + cmap->redlut = cmap->greenlut = cmap->bluelut = NULL; + } + + redmaxval = pm_bitstomaxval(redplanes); + greenmaxval = pm_bitstomaxval(greenplanes); + bluemaxval = pm_bitstomaxval(blueplanes); + maxval = MAX(redmaxval, MAX(greenmaxval, bluemaxval)); + + if( maxval > PPM_OVERALLMAXVAL ) + pm_error("too many planes"); + + if( redmaxval != maxval || greenmaxval != maxval || bluemaxval != maxval ) + pm_message("scaling colors to %d bits", pm_maxvaltobits(maxval)); + + MALLOCARRAY_NOFAIL(redtable, redmaxval +1); + MALLOCARRAY_NOFAIL(greentable, greenmaxval +1); + MALLOCARRAY_NOFAIL(bluetable, bluemaxval +1); + + for( i = 0; i <= redmaxval; i++ ) + redtable[i] = (i * maxval + redmaxval/2)/redmaxval; + for( i = 0; i <= greenmaxval; i++ ) + greentable[i] = (i * maxval + greenmaxval/2)/greenmaxval; + for( i = 0; i <= bluemaxval; i++ ) + bluetable[i] = (i * maxval + bluemaxval/2)/bluemaxval; + + if( transpName ) { + MALLOCVAR_NOFAIL(transpColor); + *transpColor = ppm_parsecolor(transpName, maxval); + } + + Rrow = alloc_rawrow(cols); + Grow = alloc_rawrow(cols); + Brow = alloc_rawrow(cols); + + ppm_writeppminit(stdout, cols, rows, maxval, 0); + + remainingChunksize = chunksize; /* initial value */ + + for( row = 0; row < rows; row++ ) { + decode_row(ifP, &remainingChunksize, Rrow, redplanes, bmhdP); + decode_row(ifP, &remainingChunksize, Grow, greenplanes, bmhdP); + decode_row(ifP, &remainingChunksize, Brow, blueplanes, bmhdP); + decode_mask(ifP, &remainingChunksize, NULL, bmhdP); + + for( col = 0; col < cols; col++ ) { + if( transpColor && maskrow && maskrow[col] == PBM_WHITE ) + pixelrow[col] = *transpColor; + else + PPM_ASSIGN( pixelrow[col], redtable[Rrow[col]], + greentable[Grow[col]], + bluetable[Brow[col]] ); + } + ppm_writeppmrow(stdout, pixelrow, cols, maxval, 0); + } + chunk_end(ifP, ID_BODY, remainingChunksize); +} + + + +static void +cmap_to_ppm(cmap) + ColorMap *cmap; +{ + ppm_colorrowtomapfile(stdout, cmap->color, cmap->ncolors, MAXCOLVAL); +#if 0 + int i; + ppm_writeppminit(stdout, cmap->ncolors, 1, MAXCOLVAL, 1); + for( i = 0; i < cmap->ncolors; i++ ) + ppm_writeppmrow(stdout, &(cmap->color[i]), 1, MAXCOLVAL, 1); +#endif +} + + + +static void +ipbm_to_ppm(FILE * const ifP, + long const chunksize, + BitMapHeader * const bmhdP, + ColorMap * const cmap, + long const viewportmodes) { + + unsigned int const cols = bmhdP->w; + unsigned int const rows = bmhdP->h; + + int col, row; + pixval maxval; + unsigned long remainingChunksize; + + pm_message("input is a %sPBM ", + HAS_MULTIPALETTE(cmap) ? "multipalette " : ""); + + if( bmhdP->nPlanes != 8 ) + pm_error("invalid number of planes for IFF-PBM: %d (must be 8)", + bmhdP->nPlanes); + + if( bmhdP->masking == mskHasMask ) + pm_error("Image has a maskplane, which is invalid in IFF-PBM"); + + if( HAS_COLORMAP(cmap) ) + maxval = MAXCOLVAL; + else { + pm_message("no colormap - interpreting values as grayscale"); + maxval = lut_maxval(cmap, pm_bitstomaxval(bmhdP->nPlanes)); + } + + if( transpName ) { + MALLOCVAR_NOFAIL(transpColor); + *transpColor = ppm_parsecolor(transpName, maxval); + } + + if( HAS_MULTIPALETTE(cmap) ) + multi_init(cmap, viewportmodes); + + MALLOCARRAY_NOFAIL(ilbmrow, cols); + + ppm_writeppminit(stdout, cols, rows, maxval, 0); + + remainingChunksize = chunksize; /* initial value */ + + for( row = 0; row < rows; row++ ) { + if( HAS_MULTIPALETTE(cmap) ) + multi_update(cmap, row); + + read_ilbm_plane(ifP, &remainingChunksize, cols, bmhdP->compression); + + for( col = 0; col < cols; col++ ) { + pixval r, g, b; + if( transpColor && maskrow && maskrow[col] == PBM_WHITE ) + pixelrow[col] = *transpColor; + else { + get_color(cmap, ilbmrow[col], &r, &g, &b); + PPM_ASSIGN(pixelrow[col], r, g, b); + } + } + ppm_writeppmrow(stdout, pixelrow, cols, maxval, 0); + } + chunk_end(ifP, ID_BODY, remainingChunksize); +} + + + +static void +rgbn_to_ppm(FILE * const ifP, + long const chunksize, + BitMapHeader * const bmhdP, + ColorMap * const cmap /* for CLUTs */ + ) { + + unsigned int const rows = bmhdP->h; + unsigned int const cols = bmhdP->w; + + int row, col, count, genlock, tries; + pixval r, g, b, maxval; + unsigned long remainingChunksize; + + pm_message("input is a %d-bit RGB image", (typeid == ID_RGB8 ? 8 : 4)); + + if( bmhdP->compression != 4 ) + pm_error("invalid compression mode for %s: %d (must be 4)", + ID2string(typeid), bmhdP->compression); + + switch( typeid ) { + case ID_RGBN: + if( bmhdP->nPlanes != 13 ) + pm_error("invalid number of planes for %s: %d (must be 13)", + ID2string(typeid), bmhdP->nPlanes); + maxval = lut_maxval(cmap, 15); + break; + case ID_RGB8: + if( bmhdP->nPlanes != 25 ) + pm_error("invalid number of planes for %s: %d (must be 25)", + ID2string(typeid), bmhdP->nPlanes); + maxval = 255; + break; + default: + pm_error("rgbn_to_ppm(): invalid IFF ID %s - can't happen", + ID2string(typeid)); + } + + if( transpName ) { + MALLOCVAR_NOFAIL(transpColor); + *transpColor = ppm_parsecolor(transpName, maxval); + } + + ppm_writeppminit(stdout, cols, rows, maxval, 0); + + remainingChunksize = chunksize; /* initial value */ + count = 0; + for( row = 0; row < rows; row++ ) { + for( col = 0; col < cols; col++ ) { + tries = 0; + while( !count ) { + if( typeid == ID_RGB8 ) { + r = lookup_red(cmap, get_byte(ifP, ID_BODY, + &remainingChunksize)); + g = lookup_green(cmap, get_byte(ifP, ID_BODY, + &remainingChunksize)); + b = lookup_blue(cmap, get_byte(ifP, ID_BODY, + &remainingChunksize)); + count = get_byte(ifP, ID_BODY, &remainingChunksize); + genlock = count & 0x80; + count &= 0x7f; + } + else { + int word; + word = get_big_short(ifP, ID_BODY, &remainingChunksize); + r = lookup_red(cmap, (word & 0xf000) >> 12); + g = lookup_green(cmap, (word & 0x0f00) >> 8); + b = lookup_blue(cmap, (word & 0x00f0) >> 4); + genlock = word & 0x0008; + count = word & 0x0007; + } + if( !count ) { + count = get_byte(ifP, ID_BODY, &remainingChunksize); + if( !count ) + count = + get_big_short(ifP, ID_BODY, &remainingChunksize); + if( !count ) + ++tries; + } + } + if( tries ) { + pm_message("warning - repeat count 0 at col %d row %d: " + "skipped %d RGB entr%s", + col, row, tries, (tries == 1 ? "y" : "ies")); + } + if( maskfile ) { + /* genlock bit set -> transparent */ + if( genlock ) + maskrow[col] = PBM_WHITE; + else + maskrow[col] = PBM_BLACK; + } + if( transpColor && maskrow && maskrow[col] == PBM_WHITE ) + pixelrow[col] = *transpColor; + else + PPM_ASSIGN(pixelrow[col], r, g, b); + --count; + } + ppm_writeppmrow(stdout, pixelrow, cols, maxval, 0); + if( maskfile ) { + pbm_writepbmrow(maskfile, maskrow, cols, 0); + wrotemask = 1; + } + } + chunk_end(ifP, ID_BODY, remainingChunksize); +} + +/**************************************************************************** + Multipalette chunk reader + + Currently there are three multipalette formats: + SHAM - sliced HAM (obselete) + CTBL - dynamic HAM/Hires (obselete) + PCHG - palette change + There is no official documentation available for SHAM and CTBL, so + this is mostly guesswork from other sources and hexdumps of pictures... + + CTBL format (dynamic HAM/Hires): + 16 bigendian words per row (16 bit: 0000rrrrggggbbbb) + This is simply an entire 4-bit colormap per row. + I have no idea what the DYCP chunk is for. + + SHAM format (sliced HAM): + 1 word - version info (?) - always 0 + 16 bigendian words per row (16 bit: 0000rrrrggggbbbb) + If the picture is laced, then there are only rows/2 changes, don't change + palette on odd lines. + + PCHG format: A detailed description of this format is available + from AmiNet as PCHGLib12.lha. + + ****************************************************************************/ + +/* Turn big-endian 4-byte long and 2-byte short stored at x (unsigned char *) + * into the native format of the CPU + */ +#define BIG_LONG(x) ( ((unsigned long)((x)[0]) << 24) + \ + ((unsigned long)((x)[1]) << 16) + \ + ((unsigned long)((x)[2]) << 8) + \ + ((unsigned long)((x)[3]) << 0) ) +#define BIG_WORD(x) ( ((unsigned short)((x)[0]) << 8) + \ + ((unsigned short)((x)[1]) << 0) ) + + + +static void +read_4bit_mp(FILE * const ifP, + IFF_ID const iffid, + long const chunksize, + ColorMap * const cmap) { + + int row, rows, i, type; + short data; + unsigned long remainingChunksize; + + type = (iffid == ID_SHAM ? MP_TYPE_SHAM : MP_TYPE_CTBL); + + if( cmap->mp_type >= type ) { + skip_chunk(ifP, iffid, chunksize); + } else { + if( cmap->mp_type ) + multi_free(cmap); + cmap->mp_type = type; + + remainingChunksize = chunksize; /* initial value */ + + if( type == MP_TYPE_SHAM ) { + cmap->mp_flags = MP_FLAGS_SKIPLACED; + get_big_short(ifP, iffid, &remainingChunksize); /* skip first wd */ + } + + cmap->mp_rows = rows = remainingChunksize/32; /* sizeof(word) * 16 */ + MALLOCARRAY_NOFAIL(cmap->mp_change, rows); + + for( row = 0; row < rows; row++ ) { + MALLOCARRAY_NOFAIL(cmap->mp_change[row], 17); /* 16 + sentinel */ + for( i = 0; i < 16; i++ ) { + data = get_big_short(ifP, iffid, &remainingChunksize); + cmap->mp_change[row][i].reg = i; + cmap->mp_change[row][i].r = + ((data & 0x0f00) >> 8) * FACTOR_4BIT; + cmap->mp_change[row][i].g = + ((data & 0x00f0) >> 4) * FACTOR_4BIT; + cmap->mp_change[row][i].b = + ((data & 0x000f) >> 0) * FACTOR_4BIT; + } + cmap->mp_change[row][16].reg = MP_REG_END; /* sentinel */ + } + chunk_end(ifP, iffid, remainingChunksize); + } +} + + + +static void +PCHG_DecompHuff(src, dest, tree, origsize) + unsigned char *src, *dest; + short *tree; + unsigned long origsize; +{ + unsigned long i = 0, bits = 0; + unsigned char thisbyte; + short *p; + + p = tree; + while( i < origsize ) { + if( bits == 0 ) { + thisbyte = *src++; + bits = 8; + } + if( thisbyte & (1 << 7) ) { + if( *p >= 0 ) { + *dest++ = (unsigned char)*p; + i++; + p = tree; + } + else + p += (*p / 2); + } + else { + p--; + if( *p > 0 && (*p & 0x100) ) { + *dest++ = (unsigned char )*p; + i++; + p = tree; + } + } + thisbyte <<= 1; + bits--; + } +} + + +static void +PCHG_Decompress(PCHG, CompHdr, compdata, compsize, comptree, data) + PCHGHeader *PCHG; + PCHGCompHeader *CompHdr; + unsigned char *compdata; + unsigned long compsize; + unsigned char *comptree; + unsigned char *data; +{ + short *hufftree; + unsigned long huffsize, i; + unsigned long treesize = CompHdr->CompInfoSize; + + switch( PCHG->Compression ) { + case PCHG_COMP_HUFFMAN: + +#ifdef DEBUG + pm_message("PCHG Huffman compression"); +#endif + /* turn big-endian 2-byte shorts into native format */ + huffsize = treesize/2; + MALLOCVAR_NOFAIL(hufftree); + for( i = 0; i < huffsize; i++ ) { + hufftree[i] = (short)BIG_WORD(comptree); + comptree += 2; + } + + /* decompress the change structure data */ + PCHG_DecompHuff(compdata, data, &hufftree[huffsize-1], + CompHdr->OriginalDataSize); + + free(hufftree); + break; + default: + pm_error("unknown PCHG compression type %d", PCHG->Compression); + } +} + + +static void +PCHG_ConvertSmall(PCHG, cmap, mask, datasize) + PCHGHeader *PCHG; + ColorMap *cmap; + unsigned char *mask; + unsigned long datasize; +{ + unsigned char *data; + unsigned char thismask; + int bits, row, i, changes, masklen, reg; + unsigned char ChangeCount16, ChangeCount32; + unsigned short SmallChange; + unsigned long totalchanges = 0; + int changedlines = PCHG->ChangedLines; + + masklen = 4 * MaskLongWords(PCHG->LineCount); + data = mask + masklen; datasize -= masklen; + + bits = 0; + for( row = PCHG->StartLine; changedlines && row < 0; row++ ) { + if( bits == 0 ) { + if( masklen == 0 ) goto fail2; + thismask = *mask++; + --masklen; + bits = 8; + } + if( thismask & (1<<7) ) { + if( datasize < 2 ) goto fail; + ChangeCount16 = *data++; + ChangeCount32 = *data++; + datasize -= 2; + + changes = ChangeCount16 + ChangeCount32; + for( i = 0; i < changes; i++ ) { + if( totalchanges >= PCHG->TotalChanges ) goto fail; + if( datasize < 2 ) goto fail; + SmallChange = BIG_WORD(data); data += 2; datasize -= 2; + reg = ((SmallChange & 0xf000) >> 12) + + (i >= ChangeCount16 ? 16 : 0); + cmap->mp_init[reg - PCHG->MinReg].reg = reg; + cmap->mp_init[reg - PCHG->MinReg].r = + ((SmallChange & 0x0f00) >> 8) * FACTOR_4BIT; + cmap->mp_init[reg - PCHG->MinReg].g = + ((SmallChange & 0x00f0) >> 4) * FACTOR_4BIT; + cmap->mp_init[reg - PCHG->MinReg].b = + ((SmallChange & 0x000f) >> 0) * FACTOR_4BIT; + ++totalchanges; + } + --changedlines; + } + thismask <<= 1; + bits--; + } + + for( row = PCHG->StartLine; changedlines && row < cmap->mp_rows; row++ ) { + if( bits == 0 ) { + if( masklen == 0 ) goto fail2; + thismask = *mask++; + --masklen; + bits = 8; + } + if( thismask & (1<<7) ) { + if( datasize < 2 ) goto fail; + ChangeCount16 = *data++; + ChangeCount32 = *data++; + datasize -= 2; + + changes = ChangeCount16 + ChangeCount32; + MALLOCARRAY_NOFAIL(cmap->mp_change[row], changes + 1); + for( i = 0; i < changes; i++ ) { + if( totalchanges >= PCHG->TotalChanges ) goto fail; + if( datasize < 2 ) goto fail; + SmallChange = BIG_WORD(data); data += 2; datasize -= 2; + reg = ((SmallChange & 0xf000) >> 12) + + (i >= ChangeCount16 ? 16 : 0); + cmap->mp_change[row][i].reg = reg; + cmap->mp_change[row][i].r = + ((SmallChange & 0x0f00) >> 8) * FACTOR_4BIT; + cmap->mp_change[row][i].g = + ((SmallChange & 0x00f0) >> 4) * FACTOR_4BIT; + cmap->mp_change[row][i].b = + ((SmallChange & 0x000f) >> 0) * FACTOR_4BIT; + ++totalchanges; + } + cmap->mp_change[row][changes].reg = MP_REG_END; + --changedlines; + } + thismask <<= 1; + bits--; + } + if( totalchanges != PCHG->TotalChanges ) + pm_message("warning - got %ld change structures, " + "chunk header reports %ld", + totalchanges, PCHG->TotalChanges); + return; +fail: + pm_error("insufficient data in SmallLineChanges structures"); +fail2: + pm_error("insufficient data in line mask"); +} + + +static void +PCHG_ConvertBig(PCHG, cmap, mask, datasize) + PCHGHeader *PCHG; + ColorMap *cmap; + unsigned char *mask; + unsigned long datasize; +{ + unsigned char *data; + unsigned char thismask; + int bits, row, i, changes, masklen, reg; + unsigned long totalchanges = 0; + int changedlines = PCHG->ChangedLines; + + masklen = 4 * MaskLongWords(PCHG->LineCount); + data = mask + masklen; datasize -= masklen; + + bits = 0; + for( row = PCHG->StartLine; changedlines && row < 0; row++ ) { + if( bits == 0 ) { + if( masklen == 0 ) goto fail2; + thismask = *mask++; + --masklen; + bits = 8; + } + if( thismask & (1<<7) ) { + if( datasize < 2 ) goto fail; + changes = BIG_WORD(data); data += 2; datasize -= 2; + + for( i = 0; i < changes; i++ ) { + if( totalchanges >= PCHG->TotalChanges ) goto fail; + if( datasize < 6 ) goto fail; + reg = BIG_WORD(data); data += 2; + cmap->mp_init[reg - PCHG->MinReg].reg = reg; + ++data; /* skip alpha */ + cmap->mp_init[reg - PCHG->MinReg].r = *data++; + cmap->mp_init[reg - PCHG->MinReg].b = *data++; /* yes, RBG */ + cmap->mp_init[reg - PCHG->MinReg].g = *data++; + datasize -= 6; + ++totalchanges; + } + --changedlines; + } + thismask <<= 1; + bits--; + } + + for( row = PCHG->StartLine; changedlines && row < cmap->mp_rows; row++ ) { + if( bits == 0 ) { + if( masklen == 0 ) goto fail2; + thismask = *mask++; + --masklen; + bits = 8; + } + if( thismask & (1<<7) ) { + if( datasize < 2 ) goto fail; + changes = BIG_WORD(data); data += 2; datasize -= 2; + + MALLOCARRAY_NOFAIL(cmap->mp_change[row], changes + 1); + for( i = 0; i < changes; i++ ) { + if( totalchanges >= PCHG->TotalChanges ) goto fail; + if( datasize < 6 ) goto fail; + reg = BIG_WORD(data); data += 2; + cmap->mp_change[row][i].reg = reg; + ++data; /* skip alpha */ + cmap->mp_change[row][i].r = *data++; + cmap->mp_change[row][i].b = *data++; /* yes, RBG */ + cmap->mp_change[row][i].g = *data++; + datasize -= 6; + ++totalchanges; + } + cmap->mp_change[row][changes].reg = MP_REG_END; + --changedlines; + } + thismask <<= 1; + bits--; + } + if( totalchanges != PCHG->TotalChanges ) + pm_message("warning - got %ld change structures, " + "chunk header reports %ld", + totalchanges, PCHG->TotalChanges); + return; +fail: + pm_error("insufficient data in BigLineChanges structures"); +fail2: + pm_error("insufficient data in line mask"); +} + + +static void +read_pchg(FILE * const ifp, + IFF_ID const iffid, + long const chunksize, + ColorMap * const cmap) { + + if( cmap->mp_type >= MP_TYPE_PCHG ) { + skip_chunk(ifp, iffid, chunksize); + } else { + PCHGHeader PCHG; + unsigned char *data; + unsigned long datasize; + unsigned long remainingChunksize; + int i; + + if( cmap->mp_type ) + multi_free(cmap); + cmap->mp_type = MP_TYPE_PCHG; + + remainingChunksize = chunksize; /* initial value */ + + PCHG.Compression = get_big_short(ifp, iffid, &remainingChunksize); + PCHG.Flags = get_big_short(ifp, iffid, &remainingChunksize); + PCHG.StartLine = get_big_short(ifp, iffid, &remainingChunksize); + PCHG.LineCount = get_big_short(ifp, iffid, &remainingChunksize); + PCHG.ChangedLines= get_big_short(ifp, iffid, &remainingChunksize); + PCHG.MinReg = get_big_short(ifp, iffid, &remainingChunksize); + PCHG.MaxReg = get_big_short(ifp, iffid, &remainingChunksize); + PCHG.MaxChanges = get_big_short(ifp, iffid, &remainingChunksize); + PCHG.TotalChanges= get_big_long(ifp, iffid, &remainingChunksize); + +#ifdef DEBUG + pm_message("PCHG StartLine : %d", PCHG.StartLine); + pm_message("PCHG LineCount : %d", PCHG.LineCount); + pm_message("PCHG ChangedLines: %d", PCHG.ChangedLines); + pm_message("PCHG TotalChanges: %d", PCHG.TotalChanges); +#endif + + if( PCHG.Compression != PCHG_COMP_NONE ) { + PCHGCompHeader CompHdr; + unsigned char *compdata, *comptree; + long treesize, compsize; + + CompHdr.CompInfoSize = + get_big_long(ifp, iffid, &remainingChunksize); + CompHdr.OriginalDataSize = + get_big_long(ifp, iffid, &remainingChunksize); + + treesize = CompHdr.CompInfoSize; + MALLOCVAR_NOFAIL(comptree); + read_bytes(ifp, treesize, comptree, iffid, &remainingChunksize); + + compsize = remainingChunksize; + MALLOCARRAY_NOFAIL(compdata, remainingChunksize); + read_bytes(ifp, compsize, compdata, iffid, &remainingChunksize); + + datasize = CompHdr.OriginalDataSize; + MALLOCARRAY_NOFAIL(data, datasize); + PCHG_Decompress(&PCHG, &CompHdr, compdata, + compsize, comptree, data); + + free(comptree); + free(compdata); + } else { +#ifdef DEBUG + pm_message("uncompressed PCHG"); +#endif + datasize = remainingChunksize; + MALLOCARRAY_NOFAIL(data, datasize); + read_bytes(ifp, datasize, data, iffid, &remainingChunksize); + } + + if( PCHG.Flags & PCHGF_USE_ALPHA ) + pm_message("warning - ignoring PCHG alpha channel because " + "this program doesn't know what to do with it"); + + cmap->mp_rows = PCHG.StartLine + PCHG.LineCount; + MALLOCARRAY_NOFAIL(cmap->mp_change, cmap->mp_rows); + for( i = 0; i < cmap->mp_rows; i++ ) + cmap->mp_change[i] = NULL; + if( PCHG.StartLine < 0 ) { + int nch; + nch = PCHG.MaxReg - PCHG.MinReg +1; + MALLOCARRAY_NOFAIL(cmap->mp_init, nch + 1); + for( i = 0; i < nch; i++ ) + cmap->mp_init[i].reg = MP_REG_IGNORE; + cmap->mp_init[nch].reg = MP_REG_END; + } + + if( PCHG.Flags & PCHGF_12BIT ) { +#ifdef DEBUG + pm_message("SmallLineChanges"); +#endif + PCHG_ConvertSmall(&PCHG, cmap, data, datasize); + } + else { + if( PCHG.Flags & PCHGF_32BIT ) { +#ifdef DEBUG + pm_message("BigLineChanges"); +#endif + PCHG_ConvertBig(&PCHG, cmap, data, datasize); + } + else + pm_error("unknown palette changes structure " + "format in %s chunk", + ID2string(iffid)); + } + free(data); + chunk_end(ifp, iffid, remainingChunksize); + } +} + + + +static bool +ignored_iffid(IFF_ID const iffid, + IFF_ID const ignorelist[], + unsigned int const ignorecount) { + + bool ignore; + + unsigned int i; + ignore = FALSE; /* initial assumption */ + for( i = 0; i < ignorecount && !ignore; i++ ) { + if( iffid == ignorelist[i] ) + ignore = TRUE; + } + return ignore; +} + + + +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) { + + if( bmhdP == NULL ) + pm_error("%s chunk without %s chunk", + ID2string(ID_BODY), ID2string(ID_BMHD)); + + check_cmap(bmhdP, cmap); + + pixelrow = ppm_allocrow(bmhdP->w); + if( maskfile ) { + maskrow = pbm_allocrow(bmhdP->w); + pbm_writepbminit(maskfile, bmhdP->w, bmhdP->h, 0); + } + + if( typeid == ID_ILBM ) { + int isdeep; + + MALLOCARRAY_NOFAIL(ilbmrow, RowBytes(bmhdP->w)); + *viewportmodesP |= fakeviewport; /* -isham/-isehb */ + + if( isdeepopt > 0 && (bmhdP->nPlanes % 3 != 0) ) { + pm_message("cannot interpret %d-plane image as 'deep' " + "(# of planes must be divisible by 3)", + bmhdP->nPlanes); + isdeep = 0; + } else + isdeep = isdeepopt; + + if( isdeep > 0 ) + deep_to_ppm(ifp, chunksize, bmhdP, cmap); + else if( dcol ) + dcol_to_ppm(ifp, chunksize, bmhdP, cmap, dcol); + else if( bmhdP->nPlanes > 8 ) { + if( bmhdP->nPlanes <= 16 && HAS_COLORMAP(cmap) ) + std_to_ppm(ifp, chunksize, bmhdP, cmap, *viewportmodesP); + else if( isdeep >= 0 && (bmhdP->nPlanes % 3 == 0) ) + deep_to_ppm(ifp, chunksize, bmhdP, cmap); + else if( bmhdP->nPlanes <= 16 ) + /* will be interpreted as grayscale */ + std_to_ppm(ifp, chunksize, bmhdP, cmap, *viewportmodesP); + else + pm_error("don't know how to interpret %d-plane image", + bmhdP->nPlanes); + } else + std_to_ppm(ifp, chunksize, bmhdP, cmap, *viewportmodesP); + } else if( typeid == ID_PBM ) + ipbm_to_ppm(ifp, chunksize, bmhdP, cmap, *viewportmodesP); + else /* RGBN or RGB8 */ + rgbn_to_ppm(ifp, chunksize, bmhdP, cmap); +} + + + +static void +process_chunk(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 + ) { + + IFF_ID iffid; + long chunksize; + long bytesread; + + bytesread = 0; + + iffid = get_big_long(ifp, ID_FORM, NULL); + chunksize = get_big_long(ifp, iffid, NULL); + bytesread += 8; + + if (debug) + pm_message("reading %s chunk: %ld bytes", + ID2string(iffid), chunksize); + + if( ignored_iffid(iffid, ignorelist, ignorecount) ) { + pm_message("ignoring %s chunk", ID2string(iffid)); + skip_chunk(ifp, iffid, chunksize); + } else if( iffid == ID_END ) { + /* END chunks are not officially valid in IFF, but + suggested as a future expansion for stream-writing, + see Amiga RKM Devices, 3rd Ed, page 376 + */ + if( chunksize != 0 ) { + pm_message("warning - non-0 %s chunk", ID2string(iffid)); + skip_chunk(ifp, iffid, chunksize); + } + if( formsize != 0xffffffff ) + pm_message("warning - %s chunk with FORM size 0x%08lx " + "(should be 0x%08x)", + ID2string(iffid), formsize, 0xffffffff); + *endchunkP = 1; + } else if( *bodyChunkProcessedP ) { + pm_message("%s chunk found after %s chunk - skipping", + ID2string(iffid), ID2string(ID_BODY)); + skip_chunk(ifp, iffid, chunksize); + } else + switch( iffid ) { + case ID_BMHD: + *bmhdP = read_bmhd(ifp, iffid, chunksize); + break; + case ID_CMAP: + read_cmap(ifp, iffid, chunksize, cmap); + break; + case ID_CMYK: + read_cmyk(ifp, iffid, chunksize, cmap); + break; + case ID_CLUT: + read_clut(ifp, iffid, chunksize, cmap); + break; + case ID_CAMG: + if( chunksize != CAMGChunkSize ) + pm_error("%s chunk size mismatch", ID2string(iffid)); + *viewportmodesP = get_big_long(ifp, ID_CAMG, NULL); + *viewportmodesP &= viewportmask; /* -isnotham/-isnotehb */ + break; + case ID_PCHG: + read_pchg(ifp, iffid, chunksize, cmap); + break; + case ID_CTBL: + case ID_SHAM: + read_4bit_mp(ifp, iffid, chunksize, cmap); + break; + case ID_DCOL: + if( chunksize != DirectColorSize ) + pm_error("%s chunk size mismatch", ID2string(iffid)); + MALLOCVAR_NOFAIL(*dcolP); + (*dcolP)->r = get_byte(ifp, iffid, NULL); + (*dcolP)->g = get_byte(ifp, iffid, NULL); + (*dcolP)->b = get_byte(ifp, iffid, NULL); + (void)get_byte(ifp, iffid, NULL); /* skip pad byte */ + break; + case ID_BODY: + if( cmaponly || (*bmhdP && (*bmhdP)->nPlanes == 0 )) { + skip_chunk(ifp, ID_BODY, chunksize); + return; + } + + process_body(ifp, chunksize, *bmhdP, cmap, + maskfile, fakeviewport, isdeepopt, *dcolP, + viewportmodesP); + + *bodyChunkProcessedP = TRUE; + + break; + case ID_GRAB: case ID_DEST: case ID_SPRT: case ID_CRNG: + case ID_CCRT: case ID_DYCP: case ID_DPPV: case ID_DRNG: + case ID_EPSF: case ID_JUNK: case ID_CNAM: case ID_PRVW: + case ID_TINY: case ID_DPPS: + skip_chunk(ifp, iffid, chunksize); + break; + case ID_copy: case ID_AUTH: case ID_NAME: case ID_ANNO: + case ID_TEXT: case ID_FVER: + if( verbose ) + display_chunk(ifp, iffid, chunksize); + else + skip_chunk(ifp, iffid, chunksize); + break; + case ID_DPI: + { + int x, y; + + x = get_big_short(ifp, ID_DPI, NULL); + y = get_big_short(ifp, ID_DPI, NULL); + if( verbose ) + pm_message("%s chunk: dpi_x = %d dpi_y = %d", + ID2string(ID_DPI), x, y); + } + break; + default: + pm_message("unknown chunk type %s - skipping", + ID2string(iffid)); + skip_chunk(ifp, iffid, chunksize); + break; + } + + bytesread += chunksize; + + if( ODD(chunksize) ) { + (void) get_byte(ifp, iffid, NULL); + bytesread += 1; + } + *bytesReadP = bytesread; +} + + +int +main(int argc, char *argv[]) { + + FILE *ifp; + int argn; + short cmaponly = 0, isdeepopt = 0; + bool endchunk; + bool bodyChunkProcessed; + long formsize, bytesread; + int viewportmodes = 0, fakeviewport = 0, viewportmask; + IFF_ID firstIffid; + BitMapHeader *bmhdP = NULL; + ColorMap * cmap; + DirectColor *dcol = NULL; +#define MAX_IGNORE 16 + IFF_ID ignorelist[MAX_IGNORE]; + int ignorecount = 0; + char *maskname; + const char * const usage = + "[-verbose] [-ignore [-ignore ] ...] " + "[-isham|-isehb|-isdeep|-isnotham|-isnotehb|-isnotdeep] " + "[-cmaponly] [-adjustcolors] " + "[-transparent ] [-maskfile ] [ilbmfile]"; + ppm_init(&argc, argv); + + viewportmask = 0xffffffff; + + argn = 1; + while( argn < argc && argv[argn][0] == '-' && argv[argn][1] != '\0' ) { + if( pm_keymatch(argv[argn], "-verbose", 2) ) + verbose = 1; + else if( pm_keymatch(argv[argn], "-noverbose", 4) ) + verbose = 0; + else if( pm_keymatch(argv[argn], "-isham", 4) ) + fakeviewport |= vmHAM; + else if( pm_keymatch(argv[argn], "-isehb", 4) ) + fakeviewport |= vmEXTRA_HALFBRITE; + else if( pm_keymatch(argv[argn], "-isdeep", 4) ) + isdeepopt = 1; + else if( pm_keymatch(argv[argn], "-isnotham", 7) ) + viewportmask &= ~(vmHAM); + else if( pm_keymatch(argv[argn], "-isnotehb", 7) ) + viewportmask &= ~(vmEXTRA_HALFBRITE); + else if( pm_keymatch(argv[argn], "-isnotdeep", 7) ) + isdeepopt = -1; + else if( pm_keymatch(argv[argn], "-cmaponly", 2) ) + cmaponly = 1; + else if( pm_keymatch(argv[argn], "-adjustcolors", 2) ) + adjustcolors = 1; + else if( pm_keymatch(argv[argn], "-noadjustcolors", 4) ) + adjustcolors = 0; + else if( pm_keymatch(argv[argn], "-transparent", 2) ) { + if( ++argn >= argc ) + pm_usage(usage); + transpName = argv[argn]; + } else if( pm_keymatch(argv[argn], "-maskfile", 2) ) { + if( ++argn >= argc ) + pm_usage(usage); + maskname = argv[argn]; + maskfile = pm_openw(maskname); + } else if( pm_keymatch(argv[argn], "-ignore", 2) ) { + if( ++argn >= argc ) + pm_usage(usage); + if( strlen(argv[argn]) != 4 ) + pm_error("'-ignore' option needs a 4 byte chunk ID string " + "as argument"); + if( ignorecount >= MAX_IGNORE ) + pm_error("max %d chunk IDs to ignore", MAX_IGNORE); + ignorelist[ignorecount++] = + MAKE_ID(argv[argn][0], argv[argn][1], argv[argn][2], + argv[argn][3]); + } else + pm_usage(usage); + ++argn; + } + + if( argn < argc ) { + ifp = pm_openr( argv[argn] ); + argn++; + } else + ifp = stdin; + + if( argn != argc ) + pm_usage(usage); + + /* Read in the ILBM file. */ + + firstIffid = get_big_long(ifp, ID_FORM, NULL); + if( firstIffid != ID_FORM ) + pm_error("input is not a FORM type IFF file"); + formsize = get_big_long(ifp, ID_FORM, NULL); + typeid = get_big_long(ifp, ID_FORM, NULL); + if( typeid != ID_ILBM && typeid != ID_RGBN && typeid != ID_RGB8 && + typeid != ID_PBM ) + pm_error( "input is not an ILBM, RGBN, RGB8 or PBM " + "type FORM IFF file" ); + bytesread = 4; /* FORM and formsize do not count */ + + cmap = alloc_cmap(); + + /* Main loop, parsing the IFF FORM. */ + bodyChunkProcessed = FALSE; + endchunk = FALSE; + while( !endchunk && formsize-bytesread >= 8 ) { + long bytes_read_for_chunk; + + process_chunk(ifp, formsize, ignorelist, ignorecount, + fakeviewport, viewportmask, + isdeepopt, cmaponly, + &bodyChunkProcessed, + &endchunk, &bmhdP, cmap, &dcol, + &viewportmodes, &bytes_read_for_chunk); + + bytesread += bytes_read_for_chunk; + } + + if( maskfile ) { + pm_close(maskfile); + if( !wrotemask ) + remove(maskname); + pbm_freerow(maskrow); + } + + if( cmaponly || (bmhdP && bmhdP->nPlanes == 0 )) { + if( HAS_COLORMAP(cmap) ) { + check_cmap(bmhdP, cmap); + cmap_to_ppm(cmap); + } else + pm_error("no colormap"); + } else if( bodyChunkProcessed ) { + if( HAS_COLORMAP(cmap) ) { + pm_message("input is a colormap file"); + check_cmap(bmhdP, cmap); + cmap_to_ppm(cmap); + } else + pm_error("no %s or %s chunk found", + ID2string(ID_BODY), ID2string(ID_CMAP)); + } + + { + unsigned int skipped; + + for( skipped = 0; fgetc(ifp) != EOF; ++skipped ) + bytesread++; + + if( skipped > 0 ) + pm_message("skipped %u extraneous byte%s after last chunk", + skipped, (skipped == 1 ? "" : "s")); + } + pm_close(ifp); + + if( !endchunk && bytesread != formsize ) { + pm_message("warning - file length/FORM size field mismatch " + "(%ld != %ld+8)", + bytesread+8 /* FORM+size */, formsize); + } + + return 0; +} -- cgit 1.4.1