/*****************************************************************
 * 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 <stdio.h>
# include <math.h>
# include <ctype.h>
# 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; i<image->hdr.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<colors; i++)
	{ fprintf (stderr, "    color %3d:  <%3d, %3d, %3d>\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; r<image->hdr.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; c<image->hdr.cols; c++)
    { obm[c] = 0; }

    /* Each loop reads one plane of this scan line */    
    for (k=0; k<planes; k++)
    {
      /* First get pointer to data packed 8 bits per byte */
      if (bmh->compression == 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; c<image->hdr.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; i<colors; i++)
  { *c++ = *r++;
    *c++ = *g++;
    *c++ = *b++;
  }
  
  /* Return length of chunk, just length of map plus 8 bytes chunk header */
  return (len + 8);
}

/****************************************************************
 * build_body: Interleave the bits for the byte plane
 ****************************************************************/

build_body (image, body, bits)
register FBM *image;
unsigned char **body;
int bits;
{ int bpr, size;
  register unsigned char *obm, *bmp;
  register int r, c, k, mask, byte, bit;

  bpr = RowBytes (image->hdr.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; k<bits; k++)
    { mask = 1 << k;
      bmp = &(image->bm[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; c<image->hdr.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);
  
}
