home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / unix / volume19 / fbm / part07 / fliff.c < prev   
Encoding:
C/C++ Source or Header  |  1989-06-08  |  22.9 KB  |  798 lines

  1. /*****************************************************************
  2.  * fliff.c: FBM Library 0.9 (Beta test) 07-Mar-89  Michael Mauldin
  3.  *
  4.  * Copyright (C) 1989 by Michael Mauldin.  Permission is granted to
  5.  * use this file in whole or in part provided that you do not sell it
  6.  * for profit and that this copyright notice is retained unchanged.
  7.  *
  8.  * fliff.c: 
  9.  *
  10.  * CONTENTS
  11.  *    write_iff (image, stream)
  12.  *    read_iff (image, stream, mstr, mlen)
  13.  *
  14.  * EDITLOG
  15.  *    LastEditDate = Mon Mar 20 18:54:08 1989 - Michael Mauldin
  16.  *    LastFileName = /usr2/mlm/src/misc/fbm/fliff.c
  17.  *
  18.  * HISTORY
  19.  * 07-Mar-89  Michael Mauldin (mlm) at Carnegie Mellon University
  20.  *    Beta release (version 0.9) mlm@cs.cmu.edu
  21.  *
  22.  * 04-Mar-89  Michael Mauldin (mlm) at Carnegie Mellon University
  23.  *    Read and Write support for Amiga IFF (except HAM mode)
  24.  *****************************************************************/
  25.  
  26. # include <stdio.h>
  27. # include <math.h>
  28. # include <ctype.h>
  29. # include "fbm.h"
  30.  
  31. #ifndef lint
  32. static char *fbmid =
  33.     "$FBM fliff.c <0.9> 07-Mar-89  (C) 1989 by Michael Mauldin$";
  34. #endif
  35.  
  36. /****************************************************************
  37.  * from iff.h
  38.  ****************************************************************/
  39.  
  40. # define BOOL    int        /*  1 bit value            */
  41. # define SBYTE    char        /*  8 bits signend        */
  42. # define UBYTE    unsigned char    /*  8 bits unsigned        */
  43. # define WORD    short        /* 16 bits signed        */
  44. # define UWORD    unsigned short    /* 16 bits unsigned        */
  45. # define LONG    long        /* 32 bits signed        */
  46. # define ID    long
  47. # define MakeID(a,b,c,d)  ( (LONG)(a)<<24L | (LONG)(b)<<16L | (c)<<8 | (d) )
  48. # define FORM MakeID('F','O','R','M')
  49. # define PROP MakeID('P','R','O','P')
  50. # define LIST MakeID('L','I','S','T')
  51. # define CAT  MakeID('C','A','T',' ')
  52. # define FILLER MakeID(' ',' ',' ',' ')
  53. # define NULL_CHUNK 0L           /* No current chunk.*/
  54. # define TRUE    1
  55. # define FALSE    0
  56.  
  57.  
  58. /* ---------- Chunk ----------------------------------------------------*/
  59.  
  60. /* All chunks start with a type ID and a count of the data bytes that 
  61.    follow--the chunk's "logical size" or "data size". If that number is odd,
  62.    a 0 pad byte is written, too. */
  63.  
  64. typedef struct {
  65.     ID      ckID;
  66.     LONG  ckSize;
  67.     } ChunkHeader;
  68.  
  69. typedef struct {
  70.     ID      ckID;
  71.     LONG  ckSize;
  72.     UBYTE ckData[ 1 /*REALLY: ckSize*/ ];
  73.     } Chunk;
  74.  
  75. /*----------------------------------------------------------------------*
  76.  * ILBM.H  Definitions for InterLeaved BitMap raster image.     1/23/86
  77.  *
  78.  * By Jerry Morrison and Steve Shaw, Electronic Arts.
  79.  * This software is in the public domain.
  80.  *
  81.  * This version for the Commodore-Amiga computer.
  82.  *----------------------------------------------------------------------*/
  83.  
  84. # define ID_ILBM MakeID('I','L','B','M')
  85. # define ID_BMHD MakeID('B','M','H','D')
  86. # define ID_CMAP MakeID('C','M','A','P')
  87. # define ID_GRAB MakeID('G','R','A','B')
  88. # define ID_DEST MakeID('D','E','S','T')
  89. # define ID_SPRT MakeID('S','P','R','T')
  90. # define ID_CAMG MakeID('C','A','M','G')
  91. # define ID_BODY MakeID('B','O','D','Y')
  92.  
  93. /* ---------- BitMapHeader ---------------------------------------------*/
  94.  
  95. typedef UBYTE Masking;        /* Choice of masking technique.*/
  96. # define mskNone                 0
  97. # define mskHasMask              1
  98. # define mskHasTransparentColor  2
  99. # define mskLasso                3
  100.  
  101. typedef UBYTE Compression;    /* Choice of compression algorithm applied to
  102.      * each row of the source and mask planes. "cmpByteRun1" is the byte run
  103.      * encoding generated by Mac's PackBits. See Packer.h . */
  104. # define cmpNone      0
  105. # define cmpByteRun1  1
  106.  
  107. /* Aspect ratios: The proper fraction xAspect/yAspect represents the pixel
  108.  * aspect ratio pixel_width/pixel_height.
  109.  *
  110.  * For the 4 Amiga display modes:
  111.  *   320 x 200: 10/11  (these pixels are taller than they are wide)
  112.  *   320 x 400: 20/11
  113.  *   640 x 200:  5/11
  114.  *   640 x 400: 10/11        */
  115. # define x320x200Aspect 10
  116. # define y320x200Aspect 11
  117. # define x320x400Aspect 20
  118. # define y320x400Aspect 11
  119. # define x640x200Aspect  5
  120. # define y640x200Aspect 11
  121. # define x640x400Aspect 10
  122. # define y640x400Aspect 11
  123.  
  124. /* A BitMapHeader is stored in a BMHD chunk. */
  125. typedef struct {
  126.     UWORD w, h;            /* raster width & height in pixels */
  127.     WORD  x, y;            /* position for this image */
  128.     UBYTE nPlanes;        /* # source bitplanes */
  129.     Masking masking;        /* masking technique */
  130.     Compression compression;    /* compression algorithm */
  131.     UBYTE pad1;            /* UNUSED.  For consistency, put 0 here.*/
  132.     UWORD transparentColor;    /* transparent "color number" */
  133.     UBYTE xAspect, yAspect;    /* aspect ratio, a rational number x/y */
  134.     WORD  pageWidth, pageHeight;  /* source "page" size in pixels */
  135.     } BitMapHeader;
  136.  
  137. /* RowBytes computes the number of bytes in a row, from the width in pixels.*/
  138. # define RowBytes(w)   (2 * (((w) + 15) / 16))
  139.  
  140.  
  141. /* ---------- ColorRegister --------------------------------------------*/
  142. /* A CMAP chunk is a packed array of ColorRegisters (3 bytes each). */
  143. typedef struct {
  144.     UBYTE red, green, blue;   /* MUST be UBYTEs so ">> 4" won't sign extend.*/
  145.     } ColorRegister;
  146.  
  147. /* Use this constant instead of sizeof(ColorRegister). */
  148. # define sizeofColorRegister  3
  149.  
  150. typedef WORD Color4;    /* Amiga RAM version of a color-register,
  151.              * with 4 bits each RGB in low 12 bits.*/
  152.  
  153. # define swapword(X)    ((((X) & 0xff) << 8) | (((X) & 0xff00) >> 8))
  154. # define swaplong(X)    (((unsigned) ((X) & 0xff000000) >> 24) |    \
  155.              ((unsigned) ((X) & 0x00ff0000) >> 8) |    \
  156.              ((unsigned) ((X) & 0x0000ff00) << 8) |    \
  157.              ((unsigned) ((X) & 0x000000ff) << 24))
  158.  
  159. # define swapsize(X) ((machine_byte_order () == LITTLE) ? swaplong(X) : (X))
  160.  
  161. /* Maximum number of bitplanes in RAM. Current Amiga max w/dual playfield. */
  162. # define MaxAmDepth 6
  163.  
  164. /* Chunks must be padded to align to even boundaries */
  165. # define EVENALIGN(X) (((X) + 1) & ~1)
  166.  
  167. /* ---------- Point2D --------------------------------------------------*/
  168. /* A Point2D is stored in a GRAB chunk. */
  169. typedef struct {
  170.     WORD x, y;        /* coordinates (pixels) */
  171.     } Point2D;
  172.  
  173. /* ---------- DestMerge ------------------------------------------------*/
  174. /* A DestMerge is stored in a DEST chunk. */
  175. typedef struct {
  176.     UBYTE depth;    /* # bitplanes in the original source */
  177.     UBYTE pad1;        /* UNUSED; for consistency store 0 here */
  178.     UWORD planePick;    /* how to scatter source bitplanes into destination */
  179.     UWORD planeOnOff;    /* default bitplane data for planePick */
  180.     UWORD planeMask;    /* selects which bitplanes to store into */
  181.     } DestMerge;
  182.  
  183. /* ---------- SpritePrecedence -----------------------------------------*/
  184. /* A SpritePrecedence is stored in a SPRT chunk. */
  185. typedef UWORD SpritePrecedence;
  186.  
  187. /* ---------- Viewport Mode --------------------------------------------*/
  188. /* A Commodore Amiga ViewPort->Modes is stored in a CAMG chunk. */
  189. /* The chunk's content is declared as a LONG. */
  190.  
  191. /* ---------- CRange ---------------------------------------------------*/
  192. /* A CRange is store in a CRNG chunk. */
  193. typedef struct {
  194.     WORD  pad1;        /* reserved for future use; store 0 here */
  195.     WORD  rate;        /* color cycling rate, 16384 = 60 steps/second */
  196.     WORD  active;    /* nonzero means color cycling is turned on */
  197.     UBYTE low, high;    /* lower and upper color registers selected */
  198.     } CRange;
  199.  
  200. /*----------------------------------------------------------------------*
  201.  * PACKER.H  typedefs for Data-Compresser.                  1/22/86
  202.  *
  203.  * This module implements the run compression algorithm "cmpByteRun1"; the
  204.  * same encoding generated by Mac's PackBits.
  205.  *
  206.  * By Jerry Morrison and Steve Shaw, Electronic Arts.
  207.  * This software is in the public domain.
  208.  *
  209.  * This version for the Commodore-Amiga computer.
  210.  *----------------------------------------------------------------------*/
  211.  
  212. /* This macro computes the worst case packed size of a "row" of bytes. */
  213. # define MaxPackedSize(rowSize)  ( (rowSize) + ( ((rowSize)+127) >> 7 ) )
  214.  
  215. /*----------------------------------------------------------------------*
  216.  * unpacker.c Convert data from "cmpByteRun1" run compression. 11/15/85
  217.  *
  218.  * By Jerry Morrison and Steve Shaw, Electronic Arts.
  219.  * This software is in the public domain.
  220.  *
  221.  *    control bytes:
  222.  *     [0..127]   : followed by n+1 bytes of data.
  223.  *     [-1..-127] : followed by byte to be repeated (-n)+1 times.
  224.  *     -128       : NOOP.
  225.  *
  226.  * This version for the Commodore-Amiga computer.
  227.  *----------------------------------------------------------------------*/
  228.  
  229. /*----------- UnPackRow ------------------------------------------------*/
  230.  
  231. # define UGetByte()    (*source++)
  232. # define UPutByte(c)    (*dest++ = (c))
  233.  
  234. /* Given POINTERS to POINTER variables, unpacks one row, updating the source
  235.  * and destination pointers until it produces dstBytes bytes. */
  236. static UnPackRow(pSource, pDest, srcBytes0, dstBytes0)
  237.     char **pSource, **pDest;  int srcBytes0, dstBytes0; {
  238.     register char *source = *pSource;
  239.     register char *dest   = *pDest;
  240.     register int n;
  241.     register char c;
  242.     register int srcBytes = srcBytes0, dstBytes = dstBytes0;
  243.     BOOL error = TRUE;    /* assume error until we make it through the loop */
  244.  
  245.  
  246. # ifdef DEBUG
  247.     fprintf (stderr, "Unpack called, src %d, dst %d\n",
  248.              srcBytes0, dstBytes0);
  249. # endif
  250.  
  251.     while( dstBytes > 0 )  {
  252.     if ( (srcBytes -= 1) < 0 )  goto ErrorExit;
  253.         n = UGetByte() & 0x0ff;
  254.  
  255.         if (n < 128) {
  256. # ifdef DEBUG
  257.             fprintf (stderr, "Got %02x, copying %d bytes...\n", n, n+1);
  258. # endif
  259.  
  260.         n += 1;
  261.         
  262.         if ( (srcBytes -= n) < 0 )  goto ErrorExit;
  263.         if ( (dstBytes -= n) < 0 )  goto ErrorExit;
  264.         do {  UPutByte(UGetByte());  } while (--n > 0);
  265.         }
  266.  
  267.         else if (n != 128) {
  268. # ifdef DEBUG
  269.             fprintf (stderr, "Got %02x, repeating byte %d times...\n",
  270.             n, 257-n);
  271. # endif
  272.  
  273.         n = 257 - n;
  274.         
  275.         if ( (srcBytes -= 1) < 0 )  goto ErrorExit;
  276.         if ( (dstBytes -= n) < 0 )  goto ErrorExit;
  277.         c = UGetByte();
  278.         do {  UPutByte(c);  } while (--n > 0);
  279.         }
  280.     }
  281.     error = FALSE;    /* success! */
  282.  
  283.   ErrorExit:
  284.     *pSource = source;  *pDest = dest;
  285.     
  286.     if (error)
  287.     { fprintf (stderr, "error in unpack, src %d, dst %d\n", 
  288.            srcBytes, dstBytes);
  289.     }
  290.     
  291.     return(error);
  292.     }
  293.  
  294. /****************************************************************
  295.  * read_iff: Read an Amiga format IFF Interleaved Bitmap
  296.  ****************************************************************/
  297.  
  298. read_iff (image, rfile, mstr, mlen)
  299. FBM *image;
  300. FILE *rfile;
  301. char *mstr;
  302. int mlen;
  303. { char magic[8];
  304.   long formsize, buflen;
  305.   Chunk *form;
  306.   int result;
  307.  
  308.   /* First word is magic number */
  309.   magic[0] = NEXTMCH(rfile,mstr,mlen) & 0xff;
  310.   magic[1] = NEXTMCH(rfile,mstr,mlen) & 0xff;
  311.   magic[2] = NEXTMCH(rfile,mstr,mlen) & 0xff;
  312.   magic[3] = NEXTMCH(rfile,mstr,mlen) & 0xff;
  313.   magic[4] = '\0';
  314.  
  315.   /* If magic number is not FORM, lose */
  316.   if (strcmp (magic, "FORM") != 0)
  317.   { if (strncmp (magic, "FOR", 3) == 0 ||
  318.     strncmp (magic, "LIS", 3) == 0 ||
  319.     strncmp (magic, "CAT", 3) == 0)
  320.     { fprintf (stderr, "Sorry, I only handle FORM type IFF files\n");
  321.       return (0);
  322.     }
  323.     
  324.     fprintf (stderr,
  325.          "read_iff: error, file not a FORM type IFF file, magic '%s'\n",
  326.           magic);
  327.     return (0);
  328.   }
  329.  
  330.   /* Second longword is length of data chunk */
  331.   formsize = get_long (rfile, BIG);  
  332.   
  333.   form = (Chunk *) malloc (formsize + 8);
  334.   form->ckID = FORM;
  335.   form->ckSize = formsize;
  336.  
  337.   /* Now read the rest of the chunk */
  338.   if ((buflen = fread (form->ckData, 1, formsize, stdin)) < formsize)
  339.   { if (buflen < 0)
  340.     { perror ("stdin"); }
  341.     else
  342.     { fprintf (stderr, "error: premature EOF in FORM after %d of %d bytes\n",
  343.            buflen, formsize);
  344.     }
  345.  
  346.     exit (1);
  347.   }
  348.  
  349.   /* Recursively parse the FORM */  
  350.   result = parse_form (image, form);
  351.  
  352.   /* Now we've read the image (or not) */  
  353.   free (form);
  354.  
  355.   return (result);
  356. }
  357.  
  358. /****************************************************************
  359.  * parse_form: Parse an IFF form chunk
  360.  *
  361.  *    FORM       ::= "FORM" #{ FormType (LocalChunk | FORM | LIST | CAT)* }
  362.  *    FormType   ::= ID
  363.  *    LocalChunk ::= Property | Chunk
  364.  ****************************************************************/
  365.  
  366. parse_form (image, chunk)
  367. FBM *image;
  368. Chunk *chunk;
  369. { register UBYTE *data, *tail;
  370.   register int clrlen, colors;
  371.   register BitMapHeader *bmh;
  372.   register int i, bits;
  373.   long formtype;
  374.   int found_bmhd=0, found_cmap=0, found_body=0;
  375.   Chunk *part;
  376.  
  377.   data = chunk->ckData;
  378.   tail = data + chunk->ckSize;  
  379.   
  380.   formtype = MakeID(data[0], data[1], data[2], data[3]);
  381.   data += 4;
  382.   
  383.   if (formtype != ID_ILBM)
  384.   { fprintf (stderr, "this FORM doesn't start with ILBM, but %4.4s, sorry.\n",
  385.          &formtype);
  386.     return (0);
  387.   }
  388.   
  389.   while (data < tail)
  390.   { part = (Chunk *) data;
  391.     part->ckID   = swapsize (part->ckID);
  392.     part->ckSize = swapsize (part->ckSize);
  393.     data += ( EVENALIGN (part->ckSize) + 8 );
  394.     
  395. # ifdef DEBUG
  396.     fprintf (stderr, "Found %c%c%c%c, size %ld\n",
  397.         (part->ckID & 0xff000000) >> 24,
  398.         (part->ckID & 0x00ff0000) >> 16,
  399.         (part->ckID & 0x0000ff00) >>  8,
  400.         (part->ckID & 0x000000ff),
  401.         part->ckSize);
  402. # endif
  403.  
  404.     if (part->ckID == ID_BMHD)
  405.     { found_bmhd++;
  406.       bmh = (BitMapHeader *) part->ckData;
  407.       
  408.       /* IFF uses BIG byte order, swap if necessary */
  409.       if (machine_byte_order () == LITTLE)
  410.       { bmh->w = swapword (bmh->w);
  411.         bmh->h = swapword (bmh->h);
  412.     bmh->x = swapword (bmh->x);
  413.     bmh->y = swapword (bmh->y);
  414.     bmh->transparentColor = swapword (bmh->transparentColor);
  415.     bmh->pageWidth = swapword (bmh->pageWidth);
  416.     bmh->pageHeight = swapword (bmh->pageHeight);
  417.       }
  418.       
  419.       image->hdr.rows = bmh->h;
  420.       image->hdr.cols = bmh->w;
  421.       image->hdr.planes = 1;
  422.       image->hdr.bits = bmh->nPlanes;
  423.       image->hdr.physbits = 8;
  424.       image->hdr.rowlen = 16 * ((image->hdr.cols + 15) / 16);
  425.       image->hdr.plnlen = image->hdr.rowlen * image->hdr.rows;
  426.       image->hdr.clrlen = 0;
  427.       image->hdr.aspect = (double) bmh->yAspect / bmh->xAspect;
  428.       image->hdr.title[0] = '\0';
  429.       image->hdr.credits[0] = '\0';
  430.     }
  431.     else if (part->ckID == ID_CMAP)
  432.     { image->hdr.clrlen = part->ckSize;
  433.  
  434.       alloc_fbm (image);
  435.  
  436.       clrlen = image->hdr.clrlen;
  437.       colors = clrlen / 3;
  438.  
  439.       for (i=0; i<image->hdr.clrlen; i++)
  440.       { image->cm[(colors * (i%3)) + i/3] = part->ckData[i]; }
  441.  
  442.       /* Compute number of bits in colormap */
  443.       for (i=colors, bits=1; i > 2; i /= 2, bits++) ;
  444.       
  445.       if (bits < image->hdr.bits) image->hdr.bits = bits;
  446.  
  447.       found_cmap++;
  448.     }
  449.     else if (part->ckID == ID_BODY)
  450.     { if (!found_bmhd)
  451.       { fprintf (stderr, "Error, BODY found with no BMHD header\n");
  452.         return (0);
  453.       }
  454.       
  455.       if (found_cmap == 0)
  456.       { alloc_fbm (image); }
  457.       
  458.       found_body++;
  459.       
  460.       /* Decode body */
  461.       fprintf (stderr, "Reading IFF [%dx%dx%d], %d physbits, %1.3lf aspect",
  462.         image->hdr.cols, image->hdr.rows,
  463.         image->hdr.bits, image->hdr.physbits,
  464.         image->hdr.aspect);
  465.       if (image->hdr.planes > 1)
  466.       { fprintf (stderr, ", %d planes", image->hdr.planes); }
  467.       if (image->hdr.clrlen > 1)
  468.       { fprintf (stderr, ", %d colors", image->hdr.clrlen / 3); }
  469.       if (bmh->compression)
  470.       { fprintf (stderr, ", compressed"); }
  471.       if (bmh->masking == mskHasMask)
  472.       { fprintf (stderr, ", with mask"); }
  473.       fprintf (stderr, "\n");
  474.  
  475. # ifdef DEBUG
  476.       fprintf (stderr,
  477.            "masking %d, compression %d, transparent %d, page [%dx%d]\n",
  478.            bmh->masking, bmh->compression, bmh->transparentColor,
  479.            bmh->pageWidth, bmh->pageHeight);
  480.  
  481.     for (i=0; i<colors; i++)
  482.     { fprintf (stderr, "    color %3d:  <%3d, %3d, %3d>\n", i,
  483.            image->cm[i], image->cm[i+colors], image->cm[i+colors*2]);
  484.     }
  485. # endif
  486.       
  487.       return (read_iff_body (image, bmh, part));
  488.     }
  489.   }
  490.   
  491.   return (0);
  492. }
  493.  
  494. /****************************************************************
  495.  * read_iff_body: Read the bits in the ILBM body into the FBM image
  496.  ****************************************************************/
  497.  
  498. read_iff_body (image, bmh, body)
  499. FBM *image;
  500. BitMapHeader *bmh;
  501. Chunk *body;
  502. { register int r, c, k, pmask, byte, bit;
  503.   unsigned char *row, *bp, *buf, *obm, *tail;
  504.   int bytlen = image->hdr.cols / 8;
  505.   int planes =  bmh->nPlanes + ((bmh->masking == mskHasMask) ? 1 : 0);
  506.   
  507.   buf = (unsigned char *) malloc (bytlen);
  508.   
  509.   bp = body->ckData;
  510.   tail = bp + body->ckSize;
  511.  
  512. # ifdef DEBUG
  513.   fprintf (stderr, "Body length %d, planes %d: ", tail-bp, planes);
  514.   for (c=0; c<20; c++) fprintf (stderr, "%02x", bp[c]);
  515.   fprintf (stderr, "\n");
  516. # endif
  517.  
  518.   /* Each iteration reads one scan line */
  519.   for (r=0; r<image->hdr.rows; r++)
  520.   {
  521.     if (bp > tail)
  522.     { fprintf (stderr, "Ran out of data in body after %d of %d rows\n",
  523.         r, image->hdr.rows);
  524.       return (0);
  525.     }
  526.  
  527.     obm = &(image->bm[r * image->hdr.rowlen]);
  528.  
  529.     /* Clear the output row of pixels */
  530.     for (c=0; c<image->hdr.cols; c++)
  531.     { obm[c] = 0; }
  532.  
  533.     /* Each loop reads one plane of this scan line */    
  534.     for (k=0; k<planes; k++)
  535.     {
  536.       /* First get pointer to data packed 8 bits per byte */
  537.       if (bmh->compression == 0)
  538.       { row = bp; bp += RowBytes (bmh->w); }
  539.       else
  540.       { row = buf;
  541.         if (UnPackRow (&bp, &row, (int) (tail-bp), RowBytes (bmh->w)) != 0)
  542.         { fprintf (stderr,
  543.            "%s, row %d of %d, plane %d of %d, bytes per row %d\n",
  544.            "Error in UnPackRow",
  545.            r, image->hdr.rows, k, planes, RowBytes (bmh->w));
  546.       return (0);
  547.     }
  548.     row = buf;
  549.       }
  550.  
  551.       /* Ignore extra planes (including the mask if any) */
  552.       if (k >= image->hdr.bits)
  553.         continue;
  554.  
  555.       /* Now OR in these bits into the output pixels */
  556.       pmask = 1 << k;
  557.  
  558.       for (c=0; c<image->hdr.cols; c++)
  559.       { byte = c >> 3;
  560.         bit = 7 - (c & 7);
  561.     bit = row[byte] & (1 << bit);
  562.  
  563.     obm[c] |= bit ? pmask : 0;
  564.       }
  565.     }
  566.   }
  567.  
  568.   if (tail-bp > 1)
  569.   { fprintf (stderr, "Warning, %d bytes of data unread\n", tail - bp); }
  570.   
  571.   return (1);
  572. }
  573.  
  574. /****************************************************************
  575.  * write_iff: Write AMIGA IFF format ILBM file
  576.  *
  577.  * Writes        FORM type ILBM
  578.  *                BMHD
  579.  *                CMAP (optional)
  580.  *                BODY (uncompressed)
  581.  *
  582.  ****************************************************************/
  583.  
  584. write_iff (image, wfile)
  585. FBM *image;
  586. FILE *wfile;
  587. { BitMapHeader bmhd;
  588.   unsigned char *cmap, *body;
  589.   int bodylen, cmaplen, bmhdlen, formlen, ilbmbits;
  590.  
  591.   if (image->hdr.planes > 1)
  592.   { fprintf (stderr, "Error, write_iff cannot handle multi-plane images\n");
  593.     return (0);
  594.   }
  595.  
  596.   /* Determine number of bits in output */
  597.   if (image->hdr.clrlen == 0)
  598.   { ilbmbits = image->hdr.bits; }
  599.   else
  600.   { int colors = image->hdr.clrlen/3;
  601.     for (ilbmbits=1; colors > 2; ilbmbits++, colors >>= 1) ;
  602.   }
  603.   
  604.   fprintf (stderr, "Writing \"%s\" [%dx%d] as a %d bit IFF ILBM file\n",
  605.        image->hdr.title[0] ? image->hdr.title : "",
  606.        image->hdr.cols, image->hdr.rows, ilbmbits);
  607.  
  608.   if (ilbmbits > 5)
  609.   { fprintf (stderr, "%s\n%s\n%s\n",
  610.     "Warning: most IFF ILBM displays cannot handle more than",
  611.     "     32 colors. You should probably run the image though",
  612.     "     'gray2clr -u | fbquant -c32' first.");
  613.   }
  614.  
  615.   /* Build BMHD, CMAP, and body chunks */
  616.   bmhdlen = build_bmhd (image, &bmhd, ilbmbits) ;
  617.   cmaplen = build_cmap (image, &cmap, ilbmbits);
  618.   bodylen = build_body (image, &body, ilbmbits);
  619.   
  620.   /* Length of FORM is length of subparts plus 8 for header + 4 for type */
  621.   formlen = bmhdlen + cmaplen + bodylen + 12;
  622.  
  623.   /*--------Write out FORM chunk header--------*/
  624.   fprintf (wfile, "FORM");
  625.   put_long (formlen-8, wfile, BIG);
  626.   fprintf (wfile, "ILBM");
  627.  
  628.   /*----Write out BMHD chunk----*/
  629.   fprintf (wfile, "BMHD");
  630.   put_long (bmhdlen-8, wfile, BIG);
  631.   fwrite (&bmhd, bmhdlen-8, 1, wfile);
  632.  
  633.   /* No need to pad BMHD chunk, it must be even */
  634.       
  635.   /*----Write out CMAP chunk----*/
  636.   if (cmaplen > 0)
  637.   { fprintf (wfile, "CMAP");
  638.     put_long (cmaplen-8, wfile, BIG);
  639.     fwrite (cmap, cmaplen-8, 1, wfile);
  640.   
  641.     /* Pad CMAP chunk if necessary */
  642.     if (cmaplen & 1) fputc (0, wfile);
  643.   }
  644.       
  645.   /*----Write out BODY chunk----*/
  646.   fprintf (wfile, "BODY");
  647.   put_long (bodylen-8, wfile, BIG);
  648.   fwrite (body, bodylen-8, 1, wfile);
  649.  
  650.   /* Pad BODY chunk if necessary */
  651.   if (bodylen & 1) fputc (0, wfile);
  652.       
  653.   /*--------Free memory and return--------*/
  654.   if (cmap)    free (cmap);
  655.   if (body)    free (body);
  656.  
  657.   return (1);
  658. }
  659.  
  660. /****************************************************************
  661.  * build_bmhd: Build a BitMapHeader, and byte swap it if necessary
  662.  ****************************************************************/
  663.  
  664. build_bmhd (image, bmh, bits)
  665. FBM *image;
  666. BitMapHeader *bmh;
  667. int bits;
  668. {
  669.   bmh->w = image->hdr.cols;
  670.   bmh->h = image->hdr.rows;
  671.   bmh->x = 0;
  672.   bmh->y = 0;
  673.   bmh->nPlanes = bits;
  674.   bmh->masking = 0;  
  675.   bmh->compression = 0;
  676.   bmh->pad1 = 0;
  677.   bmh->transparentColor = 0;
  678.   bmh->xAspect = 100;
  679.   bmh->yAspect = (image->hdr.aspect * 100.0) + 0.5;
  680.   bmh->pageWidth = bmh->w;
  681.   bmh->pageHeight = bmh->h;
  682.  
  683.   /* IFF uses BIG byte order, swap if necessary */
  684.   if (machine_byte_order () == LITTLE)
  685.   { bmh->w = swapword (bmh->w);
  686.     bmh->h = swapword (bmh->h);
  687.     bmh->x = swapword (bmh->x);
  688.     bmh->y = swapword (bmh->y);
  689.     bmh->transparentColor = swapword (bmh->transparentColor);
  690.     bmh->pageWidth = swapword (bmh->pageWidth);
  691.     bmh->pageHeight = swapword (bmh->pageHeight);
  692.   }
  693.  
  694.   return (sizeof (*bmh) + 8);
  695. }
  696.  
  697. /****************************************************************
  698.  * build_cmap: Convert an FBM format colormap to IFF format
  699.  ****************************************************************/
  700.  
  701. build_cmap (image, cmap, bits)
  702. FBM *image;
  703. unsigned char **cmap;
  704. int bits;
  705. { register int len, i;
  706.   register unsigned char *r, *g, *b, *c;
  707.   int colors;
  708.  
  709.   colors = image->hdr.clrlen / 3;
  710.   
  711.   r = image->cm;
  712.   g = r + colors;
  713.   b = g + colors;
  714.   
  715.   len = 3*colors;
  716.   *cmap = (unsigned char *) malloc (len);
  717.  
  718.   /* Now convert from three vectors to a vector of triples */
  719.   for (i=0, c= *cmap; i<colors; i++)
  720.   { *c++ = *r++;
  721.     *c++ = *g++;
  722.     *c++ = *b++;
  723.   }
  724.   
  725.   /* Return length of chunk, just length of map plus 8 bytes chunk header */
  726.   return (len + 8);
  727. }
  728.  
  729. /****************************************************************
  730.  * build_body: Interleave the bits for the byte plane
  731.  ****************************************************************/
  732.  
  733. build_body (image, body, bits)
  734. register FBM *image;
  735. unsigned char **body;
  736. int bits;
  737. { int bpr, size;
  738.   register unsigned char *obm, *bmp;
  739.   register int r, c, k, mask, byte, bit;
  740.  
  741.   bpr = RowBytes (image->hdr.cols);
  742.   
  743.   size = bpr * image->hdr.rows * bits;
  744.   
  745.   *body = (unsigned char *) malloc (size);
  746.  
  747.   obm = *body;  
  748.  
  749.   for (r=0; r < image->hdr.rows; r++)
  750.   { for (k=0; k<bits; k++)
  751.     { mask = 1 << k;
  752.       bmp = &(image->bm[r * image->hdr.rowlen]);
  753.  
  754. # ifdef DEBUG      
  755.       if (r==23)
  756.       { fprintf (stderr, "Row %d, plane %d, bytes: ", r, k);
  757.         for (c=0; c<32; c++) fprintf (stderr, "%02x", bmp[c]);
  758.     fprintf (stderr, "\n");
  759.       }
  760. # endif
  761.       
  762.       for (c=0, byte=0; c<image->hdr.cols; c++)
  763.       { bit = (*bmp++ & mask) ? 1 : 0;
  764.  
  765.  
  766. # ifdef DEBUG
  767.         if (r == 23 && c < 32)
  768.     { fprintf (stderr, "%d", bit); }
  769. # endif
  770.  
  771.     byte = (byte << 1) | bit;
  772.         if ((c&7) == 7)
  773.     { *obm++ = byte;
  774.  
  775. # ifdef DEBUG
  776.       if (r == 23 && c <32) fprintf (stderr, " %d ", byte);
  777. # endif
  778.  
  779.       byte=0;
  780.         }
  781.       }
  782.       
  783. # ifdef DEBUG
  784.       if (r == 23) fprintf (stderr, "\n");
  785. # endif
  786.       
  787.       if ((c & 7) != 0)
  788.       { while ((c&7) != 0)
  789.         { c++; byte <<= 1; }
  790.     *obm++ = byte;
  791.       }
  792.     }
  793.   }
  794.   
  795.   return (size + 8);
  796.   
  797. }
  798.