/***************************************************************** * fliff.c: FBM Release 1.0 25-Feb-90 Michael Mauldin * * Copyright (C) 1989,1990 by Michael Mauldin. Permission is granted * to use this file in whole or in part for any purpose, educational, * recreational or commercial, provided that this copyright notice * is retained unchanged. This software is available to all free of * charge by anonymous FTP and in the UUNET archives. * * fliff.c: * * CONTENTS * write_iff (image, stream) * read_iff (image, stream, mstr, mlen) * * EDITLOG * LastEditDate = Mon Jun 25 00:16:58 1990 - Michael Mauldin * LastFileName = /usr2/mlm/src/misc/fbm/fliff.c * * HISTORY * 25-Jun-90 Michael Mauldin (mlm@cs.cmu.edu) Carnegie Mellon * Package for Release 1.0 * * 06-Sep-89 Michael Mauldin (mlm) at Carnegie Mellon University * Beta release (version 0.96) mlm@cs.cmu.edu * * 04-Mar-89 Michael Mauldin (mlm) at Carnegie Mellon University * Read and Write support for Amiga IFF (except HAM mode) *****************************************************************/ # include # include # include # include "fbm.h" #ifndef lint static char *fbmid = "$FBM fliff.c <1.0> 25-Jun-90 (C) 1989,1990 by Michael Mauldin, source \ code available free from MLM@CS.CMU.EDU and from UUNET archives$"; #endif /**************************************************************** * from iff.h ****************************************************************/ # define BOOL int /* 1 bit value */ # define SBYTE char /* 8 bits signend */ # define UBYTE unsigned char /* 8 bits unsigned */ # define WORD short /* 16 bits signed */ # define UWORD unsigned short /* 16 bits unsigned */ # define LONG long /* 32 bits signed */ # define ID long # define MakeID(a,b,c,d) ( (LONG)(a)<<24L | (LONG)(b)<<16L | (c)<<8 | (d) ) # define FORM MakeID('F','O','R','M') # define PROP MakeID('P','R','O','P') # define LIST MakeID('L','I','S','T') # define CAT MakeID('C','A','T',' ') # define FILLER MakeID(' ',' ',' ',' ') # define NULL_CHUNK 0L /* No current chunk.*/ # define TRUE 1 # define FALSE 0 /* ---------- Chunk ----------------------------------------------------*/ /* All chunks start with a type ID and a count of the data bytes that follow--the chunk's "logical size" or "data size". If that number is odd, a 0 pad byte is written, too. */ typedef struct { ID ckID; LONG ckSize; } ChunkHeader; typedef struct { ID ckID; LONG ckSize; UBYTE ckData[ 1 /*REALLY: ckSize*/ ]; } Chunk; /*----------------------------------------------------------------------* * ILBM.H Definitions for InterLeaved BitMap raster image. 1/23/86 * * By Jerry Morrison and Steve Shaw, Electronic Arts. * This software is in the public domain. * * This version for the Commodore-Amiga computer. *----------------------------------------------------------------------*/ # define ID_ILBM MakeID('I','L','B','M') # define ID_BMHD MakeID('B','M','H','D') # define ID_CMAP MakeID('C','M','A','P') # define ID_GRAB MakeID('G','R','A','B') # define ID_DEST MakeID('D','E','S','T') # define ID_SPRT MakeID('S','P','R','T') # define ID_CAMG MakeID('C','A','M','G') # define ID_BODY MakeID('B','O','D','Y') /* ---------- BitMapHeader ---------------------------------------------*/ typedef UBYTE Masking; /* Choice of masking technique.*/ # define mskNone 0 # define mskHasMask 1 # define mskHasTransparentColor 2 # define mskLasso 3 typedef UBYTE Compression; /* Choice of compression algorithm applied to * each row of the source and mask planes. "cmpByteRun1" is the byte run * encoding generated by Mac's PackBits. See Packer.h . */ # define cmpNone 0 # define cmpByteRun1 1 /* Aspect ratios: The proper fraction xAspect/yAspect represents the pixel * aspect ratio pixel_width/pixel_height. * * For the 4 Amiga display modes: * 320 x 200: 10/11 (these pixels are taller than they are wide) * 320 x 400: 20/11 * 640 x 200: 5/11 * 640 x 400: 10/11 */ # define x320x200Aspect 10 # define y320x200Aspect 11 # define x320x400Aspect 20 # define y320x400Aspect 11 # define x640x200Aspect 5 # define y640x200Aspect 11 # define x640x400Aspect 10 # define y640x400Aspect 11 /* A BitMapHeader is stored in a BMHD chunk. */ typedef struct { UWORD w, h; /* raster width & height in pixels */ WORD x, y; /* position for this image */ UBYTE nPlanes; /* # source bitplanes */ Masking masking; /* masking technique */ Compression compression; /* compression algorithm */ UBYTE pad1; /* UNUSED. For consistency, put 0 here.*/ UWORD transparentColor; /* transparent "color number" */ UBYTE xAspect, yAspect; /* aspect ratio, a rational number x/y */ WORD pageWidth, pageHeight; /* source "page" size in pixels */ } BitMapHeader; /* RowBytes computes the number of bytes in a row, from the width in pixels.*/ # define RowBytes(w) (2 * (((w) + 15) / 16)) /* ---------- ColorRegister --------------------------------------------*/ /* A CMAP chunk is a packed array of ColorRegisters (3 bytes each). */ typedef struct { UBYTE red, green, blue; /* MUST be UBYTEs so ">> 4" won't sign extend.*/ } ColorRegister; /* Use this constant instead of sizeof(ColorRegister). */ # define sizeofColorRegister 3 typedef WORD Color4; /* Amiga RAM version of a color-register, * with 4 bits each RGB in low 12 bits.*/ # define swapword(X) ((((X) & 0xff) << 8) | (((X) & 0xff00) >> 8)) # define swaplong(X) (((unsigned) ((X) & 0xff000000) >> 24) | \ ((unsigned) ((X) & 0x00ff0000) >> 8) | \ ((unsigned) ((X) & 0x0000ff00) << 8) | \ ((unsigned) ((X) & 0x000000ff) << 24)) # define swapsize(X) ((machine_byte_order () == LITTLE) ? swaplong(X) : (X)) /* Maximum number of bitplanes in RAM. Current Amiga max w/dual playfield. */ # define MaxAmDepth 6 /* Chunks must be padded to align to even boundaries */ # define EVENALIGN(X) (((X) + 1) & ~1) /* ---------- Point2D --------------------------------------------------*/ /* A Point2D is stored in a GRAB chunk. */ typedef struct { WORD x, y; /* coordinates (pixels) */ } Point2D; /* ---------- DestMerge ------------------------------------------------*/ /* A DestMerge is stored in a DEST chunk. */ typedef struct { UBYTE depth; /* # bitplanes in the original source */ UBYTE pad1; /* UNUSED; for consistency store 0 here */ UWORD planePick; /* how to scatter source bitplanes into destination */ UWORD planeOnOff; /* default bitplane data for planePick */ UWORD planeMask; /* selects which bitplanes to store into */ } DestMerge; /* ---------- SpritePrecedence -----------------------------------------*/ /* A SpritePrecedence is stored in a SPRT chunk. */ typedef UWORD SpritePrecedence; /* ---------- Viewport Mode --------------------------------------------*/ /* A Commodore Amiga ViewPort->Modes is stored in a CAMG chunk. */ /* The chunk's content is declared as a LONG. */ /* ---------- CRange ---------------------------------------------------*/ /* A CRange is store in a CRNG chunk. */ typedef struct { WORD pad1; /* reserved for future use; store 0 here */ WORD rate; /* color cycling rate, 16384 = 60 steps/second */ WORD active; /* nonzero means color cycling is turned on */ UBYTE low, high; /* lower and upper color registers selected */ } CRange; /*----------------------------------------------------------------------* * PACKER.H typedefs for Data-Compresser. 1/22/86 * * This module implements the run compression algorithm "cmpByteRun1"; the * same encoding generated by Mac's PackBits. * * By Jerry Morrison and Steve Shaw, Electronic Arts. * This software is in the public domain. * * This version for the Commodore-Amiga computer. *----------------------------------------------------------------------*/ /* This macro computes the worst case packed size of a "row" of bytes. */ # define MaxPackedSize(rowSize) ( (rowSize) + ( ((rowSize)+127) >> 7 ) ) /*----------------------------------------------------------------------* * unpacker.c Convert data from "cmpByteRun1" run compression. 11/15/85 * * By Jerry Morrison and Steve Shaw, Electronic Arts. * This software is in the public domain. * * control bytes: * [0..127] : followed by n+1 bytes of data. * [-1..-127] : followed by byte to be repeated (-n)+1 times. * -128 : NOOP. * * This version for the Commodore-Amiga computer. *----------------------------------------------------------------------*/ /*----------- UnPackRow ------------------------------------------------*/ # define UGetByte() (*source++) # define UPutByte(c) (*dest++ = (c)) /* Given POINTERS to POINTER variables, unpacks one row, updating the source * and destination pointers until it produces dstBytes bytes. */ static UnPackRow(pSource, pDest, srcBytes0, dstBytes0) char **pSource, **pDest; int srcBytes0, dstBytes0; { register char *source = *pSource; register char *dest = *pDest; register int n; register char c; register int srcBytes = srcBytes0, dstBytes = dstBytes0; BOOL error = TRUE; /* assume error until we make it through the loop */ # ifdef DEBUG fprintf (stderr, "Unpack called, src %d, dst %d\n", srcBytes0, dstBytes0); # endif while( dstBytes > 0 ) { if ( (srcBytes -= 1) < 0 ) goto ErrorExit; n = UGetByte() & 0x0ff; if (n < 128) { # ifdef DEBUG fprintf (stderr, "Got %02x, copying %d bytes...\n", n, n+1); # endif n += 1; if ( (srcBytes -= n) < 0 ) goto ErrorExit; if ( (dstBytes -= n) < 0 ) goto ErrorExit; do { UPutByte(UGetByte()); } while (--n > 0); } else if (n != 128) { # ifdef DEBUG fprintf (stderr, "Got %02x, repeating byte %d times...\n", n, 257-n); # endif n = 257 - n; if ( (srcBytes -= 1) < 0 ) goto ErrorExit; if ( (dstBytes -= n) < 0 ) goto ErrorExit; c = UGetByte(); do { UPutByte(c); } while (--n > 0); } } error = FALSE; /* success! */ ErrorExit: *pSource = source; *pDest = dest; if (error) { fprintf (stderr, "error in unpack, src %d, dst %d\n", srcBytes, dstBytes); } return(error); } /**************************************************************** * read_iff: Read an Amiga format IFF Interleaved Bitmap ****************************************************************/ read_iff (image, rfile, mstr, mlen) FBM *image; FILE *rfile; char *mstr; int mlen; { char magic[8]; long formsize, buflen; Chunk *form; int result; /* First word is magic number */ magic[0] = NEXTMCH(rfile,mstr,mlen) & 0xff; magic[1] = NEXTMCH(rfile,mstr,mlen) & 0xff; magic[2] = NEXTMCH(rfile,mstr,mlen) & 0xff; magic[3] = NEXTMCH(rfile,mstr,mlen) & 0xff; magic[4] = '\0'; /* If magic number is not FORM, lose */ if (strcmp (magic, "FORM") != 0) { if (strncmp (magic, "FOR", 3) == 0 || strncmp (magic, "LIS", 3) == 0 || strncmp (magic, "CAT", 3) == 0) { fprintf (stderr, "Sorry, I only handle FORM type IFF files\n"); return (0); } fprintf (stderr, "read_iff: error, file not a FORM type IFF file, magic '%s'\n", magic); return (0); } /* Second longword is length of data chunk */ formsize = get_long (rfile, BIG); form = (Chunk *) malloc (formsize + 8); form->ckID = FORM; form->ckSize = formsize; /* Now read the rest of the chunk */ if ((buflen = fread (form->ckData, 1, formsize, stdin)) < formsize) { if (buflen < 0) { perror ("stdin"); } else { fprintf (stderr, "error: premature EOF in FORM after %d of %d bytes\n", buflen, formsize); } exit (1); } /* Recursively parse the FORM */ result = parse_form (image, form); /* Now we've read the image (or not) */ free (form); return (result); } /**************************************************************** * parse_form: Parse an IFF form chunk * * FORM ::= "FORM" #{ FormType (LocalChunk | FORM | LIST | CAT)* } * FormType ::= ID * LocalChunk ::= Property | Chunk ****************************************************************/ parse_form (image, chunk) FBM *image; Chunk *chunk; { register UBYTE *data, *tail; register int clrlen, colors; register BitMapHeader *bmh; register int i, bits; long formtype; int found_bmhd=0, found_cmap=0, found_body=0; Chunk *part; data = chunk->ckData; tail = data + chunk->ckSize; formtype = MakeID(data[0], data[1], data[2], data[3]); data += 4; if (formtype != ID_ILBM) { fprintf (stderr, "this FORM doesn't start with ILBM, but %4.4s, sorry.\n", &formtype); return (0); } while (data < tail) { part = (Chunk *) data; part->ckID = swapsize (part->ckID); part->ckSize = swapsize (part->ckSize); data += ( EVENALIGN (part->ckSize) + 8 ); # ifdef DEBUG fprintf (stderr, "Found %c%c%c%c, size %ld\n", (part->ckID & 0xff000000) >> 24, (part->ckID & 0x00ff0000) >> 16, (part->ckID & 0x0000ff00) >> 8, (part->ckID & 0x000000ff), part->ckSize); # endif if (part->ckID == ID_BMHD) { found_bmhd++; bmh = (BitMapHeader *) part->ckData; /* IFF uses BIG byte order, swap if necessary */ if (machine_byte_order () == LITTLE) { bmh->w = swapword (bmh->w); bmh->h = swapword (bmh->h); bmh->x = swapword (bmh->x); bmh->y = swapword (bmh->y); bmh->transparentColor = swapword (bmh->transparentColor); bmh->pageWidth = swapword (bmh->pageWidth); bmh->pageHeight = swapword (bmh->pageHeight); } image->hdr.rows = bmh->h; image->hdr.cols = bmh->w; image->hdr.planes = 1; image->hdr.bits = bmh->nPlanes; image->hdr.physbits = 8; image->hdr.rowlen = 2 * ((image->hdr.cols + 1) / 2); image->hdr.plnlen = image->hdr.rowlen * image->hdr.rows; image->hdr.clrlen = 0; image->hdr.aspect = (double) bmh->yAspect / bmh->xAspect; image->hdr.title[0] = '\0'; image->hdr.credits[0] = '\0'; } else if (part->ckID == ID_CMAP) { image->hdr.clrlen = part->ckSize; alloc_fbm (image); clrlen = image->hdr.clrlen; colors = clrlen / 3; for (i=0; ihdr.clrlen; i++) { image->cm[(colors * (i%3)) + i/3] = part->ckData[i]; } /* Compute number of bits in colormap */ for (i=colors, bits=1; i > 2; i /= 2, bits++) ; if (bits < image->hdr.bits) image->hdr.bits = bits; found_cmap++; } else if (part->ckID == ID_BODY) { if (!found_bmhd) { fprintf (stderr, "Error, BODY found with no BMHD header\n"); return (0); } if (found_cmap == 0) { alloc_fbm (image); } found_body++; /* Decode body */ fprintf (stderr, "Reading IFF [%dx%dx%d], %d physbits, %1.3lf aspect", image->hdr.cols, image->hdr.rows, image->hdr.bits, image->hdr.physbits, image->hdr.aspect); if (image->hdr.planes > 1) { fprintf (stderr, ", %d planes", image->hdr.planes); } if (image->hdr.clrlen > 1) { fprintf (stderr, ", %d colors", image->hdr.clrlen / 3); } if (bmh->compression) { fprintf (stderr, ", compressed"); } if (bmh->masking == mskHasMask) { fprintf (stderr, ", with mask"); } fprintf (stderr, "\n"); # ifdef DEBUG fprintf (stderr, "masking %d, compression %d, transparent %d, page [%dx%d]\n", bmh->masking, bmh->compression, bmh->transparentColor, bmh->pageWidth, bmh->pageHeight); for (i=0; i\n", i, image->cm[i], image->cm[i+colors], image->cm[i+colors*2]); } # endif return (read_iff_body (image, bmh, part)); } } return (0); } /**************************************************************** * read_iff_body: Read the bits in the ILBM body into the FBM image ****************************************************************/ read_iff_body (image, bmh, body) FBM *image; BitMapHeader *bmh; Chunk *body; { register int r, c, k, pmask, byte, bit; unsigned char *row, *bp, *buf, *obm, *tail; int bytlen = image->hdr.cols / 8; int planes = bmh->nPlanes + ((bmh->masking == mskHasMask) ? 1 : 0); buf = (unsigned char *) malloc (bytlen); bp = body->ckData; tail = bp + body->ckSize; # ifdef DEBUG fprintf (stderr, "Body length %d, planes %d: ", tail-bp, planes); for (c=0; c<20; c++) fprintf (stderr, "%02x", bp[c]); fprintf (stderr, "\n"); # endif /* Each iteration reads one scan line */ for (r=0; rhdr.rows; r++) { if (bp > tail) { fprintf (stderr, "Ran out of data in body after %d of %d rows\n", r, image->hdr.rows); return (0); } obm = &(image->bm[r * image->hdr.rowlen]); /* Clear the output row of pixels */ for (c=0; chdr.cols; c++) { obm[c] = 0; } /* Each loop reads one plane of this scan line */ for (k=0; kcompression == 0) { row = bp; bp += RowBytes (bmh->w); } else { row = buf; if (UnPackRow ((char **)&bp, (char **)&row, (int)(tail-bp), RowBytes (bmh->w)) != 0) { fprintf (stderr, "%s, row %d of %d, plane %d of %d, bytes per row %d\n", "Error in UnPackRow", r, image->hdr.rows, k, planes, RowBytes (bmh->w)); return (0); } row = buf; } /* Ignore extra planes (including the mask if any) */ if (k >= image->hdr.bits) continue; /* Now OR in these bits into the output pixels */ pmask = 1 << k; for (c=0; chdr.cols; c++) { byte = c >> 3; bit = 7 - (c & 7); bit = row[byte] & (1 << bit); obm[c] |= bit ? pmask : 0; } } } if (tail-bp > 1) { fprintf (stderr, "Warning, %d bytes of data unread\n", tail - bp); } return (1); } /**************************************************************** * write_iff: Write AMIGA IFF format ILBM file * * Writes FORM type ILBM * BMHD * CMAP (optional) * BODY (uncompressed) * ****************************************************************/ write_iff (image, wfile) FBM *image; FILE *wfile; { BitMapHeader bmhd; unsigned char *cmap, *body; int bodylen, cmaplen, bmhdlen, formlen, ilbmbits; if (image->hdr.planes > 1) { fprintf (stderr, "Error, write_iff cannot handle multi-plane images\n"); return (0); } /* Determine number of bits in output */ if (image->hdr.clrlen == 0) { ilbmbits = image->hdr.bits; } else { int colors = image->hdr.clrlen/3; for (ilbmbits=1; colors > 2; ilbmbits++, colors >>= 1) ; } fprintf (stderr, "Writing \"%s\" [%dx%d] as a %d bit IFF ILBM file\n", image->hdr.title[0] ? image->hdr.title : "", image->hdr.cols, image->hdr.rows, ilbmbits); if (ilbmbits > 5) { fprintf (stderr, "%s\n%s\n%s\n", "Warning: most IFF ILBM displays cannot handle more than", " 32 colors. You should probably run the image though", " 'gray2clr -u | fbquant -c32' first."); } /* Build BMHD, CMAP, and body chunks */ bmhdlen = build_bmhd (image, &bmhd, ilbmbits) ; cmaplen = build_cmap (image, &cmap, ilbmbits); bodylen = build_body (image, &body, ilbmbits); /* Length of FORM is length of subparts plus 8 for header + 4 for type */ formlen = bmhdlen + cmaplen + bodylen + 12; /*--------Write out FORM chunk header--------*/ fprintf (wfile, "FORM"); put_long (formlen-8, wfile, BIG); fprintf (wfile, "ILBM"); /*----Write out BMHD chunk----*/ fprintf (wfile, "BMHD"); put_long (bmhdlen-8, wfile, BIG); fwrite (&bmhd, bmhdlen-8, 1, wfile); /* No need to pad BMHD chunk, it must be even */ /*----Write out CMAP chunk----*/ if (cmaplen > 0) { fprintf (wfile, "CMAP"); put_long (cmaplen-8, wfile, BIG); fwrite (cmap, cmaplen-8, 1, wfile); /* Pad CMAP chunk if necessary */ if (cmaplen & 1) fputc (0, wfile); } /*----Write out BODY chunk----*/ fprintf (wfile, "BODY"); put_long (bodylen-8, wfile, BIG); fwrite (body, bodylen-8, 1, wfile); /* Pad BODY chunk if necessary */ if (bodylen & 1) fputc (0, wfile); /*--------Free memory and return--------*/ if (cmap) free (cmap); if (body) free (body); return (1); } /**************************************************************** * build_bmhd: Build a BitMapHeader, and byte swap it if necessary ****************************************************************/ build_bmhd (image, bmh, bits) FBM *image; BitMapHeader *bmh; int bits; { bmh->w = image->hdr.cols; bmh->h = image->hdr.rows; bmh->x = 0; bmh->y = 0; bmh->nPlanes = bits; bmh->masking = 0; bmh->compression = 0; bmh->pad1 = 0; bmh->transparentColor = 0; bmh->xAspect = 100; bmh->yAspect = (image->hdr.aspect * 100.0) + 0.5; bmh->pageWidth = bmh->w; bmh->pageHeight = bmh->h; /* IFF uses BIG byte order, swap if necessary */ if (machine_byte_order () == LITTLE) { bmh->w = swapword (bmh->w); bmh->h = swapword (bmh->h); bmh->x = swapword (bmh->x); bmh->y = swapword (bmh->y); bmh->transparentColor = swapword (bmh->transparentColor); bmh->pageWidth = swapword (bmh->pageWidth); bmh->pageHeight = swapword (bmh->pageHeight); } return (sizeof (*bmh) + 8); } /**************************************************************** * build_cmap: Convert an FBM format colormap to IFF format ****************************************************************/ build_cmap (image, cmap, bits) FBM *image; unsigned char **cmap; int bits; { register int len, i; register unsigned char *r, *g, *b, *c; int colors; colors = image->hdr.clrlen / 3; r = image->cm; g = r + colors; b = g + colors; len = 3*colors; *cmap = (unsigned char *) malloc (len); /* Now convert from three vectors to a vector of triples */ for (i=0, c= *cmap; ihdr.cols); size = bpr * image->hdr.rows * bits; *body = (unsigned char *) malloc (size); obm = *body; for (r=0; r < image->hdr.rows; r++) { for (k=0; kbm[r * image->hdr.rowlen]); # ifdef DEBUG if (r==23) { fprintf (stderr, "Row %d, plane %d, bytes: ", r, k); for (c=0; c<32; c++) fprintf (stderr, "%02x", bmp[c]); fprintf (stderr, "\n"); } # endif for (c=0, byte=0; chdr.cols; c++) { bit = (*bmp++ & mask) ? 1 : 0; # ifdef DEBUG if (r == 23 && c < 32) { fprintf (stderr, "%d", bit); } # endif byte = (byte << 1) | bit; if ((c&7) == 7) { *obm++ = byte; # ifdef DEBUG if (r == 23 && c <32) fprintf (stderr, " %d ", byte); # endif byte=0; } } # ifdef DEBUG if (r == 23) fprintf (stderr, "\n"); # endif if ((c & 7) != 0) { while ((c&7) != 0) { c++; byte <<= 1; } *obm++ = byte; } } } return (size + 8); }