[Contents] [Index] [Help] [Retrace] [Browse <] [Browse >]

/* ilbmr.c --- ILBM loading routines for use with iffparse */

/*----------------------------------------------------------------------*
 * ILBMR.C  Support routines for reading ILBM files.
 * (IFF is Interchange Format File.)
 *
 * Based on code by Jerry Morrison and Steve Shaw, Electronic Arts.
 * This software is in the public domain.
 * Modified for iffparse.library 05/90
 * This version for the Amiga computer.
 *----------------------------------------------------------------------*/

#include "iffp/ilbm.h"
#include "iffp/packer.h"
#include "iffp/ilbmapp.h"

#define movmem CopyMem

#define MaxSrcPlanes (25)

extern struct Library *GfxBase;

/*---------- loadbody ---------------------------------------------------*/

LONG loadbody(iff, bitmap, bmhd)
struct IFFHandle *iff;
struct BitMap *bitmap;
BitMapHeader *bmhd;
        {
        BYTE *buffer;
        ULONG bufsize;
        LONG error = 1;

        D(bug("In loadbody\n"));

        if(!(currentchunkis(iff,ID_ILBM,ID_BODY)))
            {
            message("ILBM has no BODY\n");    /* Maybe it's a palette */
            return(IFF_OKAY);
            }

        if((bitmap)&&(bmhd))
            {
            D(bug("Have bitmap and bmhd\n"));

            bufsize = MaxPackedSize(RowBytes(bmhd->w)) << 4;
            if(!(buffer = AllocMem(bufsize,0L)))
                {
                D(bug("Buffer alloc of %ld failed\n",bufsize));
                return(IFFERR_NOMEM);
                }
            error = loadbody2(iff, bitmap, NULL, bmhd, buffer, bufsize);
            D(bug("Returned from getbody, error = %ld\n",error));
            }
        FreeMem(buffer,bufsize);
        return(error);
        }


/* like the old GetBODY */
LONG loadbody2(iff, bitmap, mask, bmhd, buffer, bufsize)
struct IFFHandle *iff;
struct BitMap *bitmap;
BYTE *mask;
BitMapHeader *bmhd;
BYTE *buffer;
ULONG bufsize;
   {
   UBYTE srcPlaneCnt = bmhd->nPlanes;   /* Haven't counted for mask plane yet*/
   WORD srcRowBytes = RowBytes(bmhd->w);
   WORD destRowBytes = bitmap->BytesPerRow;
   LONG bufRowBytes = MaxPackedSize(srcRowBytes);
   int nRows = bmhd->h;
   WORD compression = bmhd->compression;
   register int iPlane, iRow, nEmpty;
   register WORD nFilled;
   BYTE *buf, *nullDest, *nullBuf, **pDest;
   BYTE *planes[MaxSrcPlanes]; /* array of ptrs to planes & mask */
   struct ContextNode *cn;

   D(bug("srcRowBytes = %ld\n",srcRowBytes));

   cn = CurrentChunk(iff);

   if (compression > cmpByteRun1)
      return(CLIENT_ERROR);

   D(bug("loadbody2: compression=%ld srcBytes=%ld bitmapBytes=%ld\n",
                compression, srcRowBytes, bitmap->BytesPerRow));
   D(bug("loadbody2: bufsize=%ld bufRowBytes=%ld, srcPlaneCnt=%ld\n",
                        bufsize, bufRowBytes, srcPlaneCnt));

   /* Complain if client asked for a conversion GetBODY doesn't handle.*/
   if ( srcRowBytes  >  bitmap->BytesPerRow  ||
         bufsize < bufRowBytes * 2  ||
         srcPlaneCnt > MaxSrcPlanes )
      return(CLIENT_ERROR);

   D(bug("loadbody2: past conversion checks\n"));

   if (nRows > bitmap->Rows)   nRows = bitmap->Rows;

   D(bug("loadbody2: srcRowBytes=%ld, srcRows=%ld, srcDepth=%ld, destDepth=%ld\n",
                srcRowBytes, nRows, bmhd->nPlanes, bitmap->Depth));

   /* Initialize array "planes" with bitmap ptrs; NULL in empty slots.*/
   for (iPlane = 0; iPlane < bitmap->Depth; iPlane++)
      planes[iPlane] = (BYTE *)bitmap->Planes[iPlane];
   for ( ;  iPlane < MaxSrcPlanes;  iPlane++)
      planes[iPlane] = NULL;

   /* Copy any mask plane ptr into corresponding "planes" slot.*/
   if (bmhd->masking == mskHasMask)
        {
        if (mask != NULL)
             planes[srcPlaneCnt] = mask;  /* If there are more srcPlanes than
               * dstPlanes, there will be NULL plane-pointers before this.*/
        else
             planes[srcPlaneCnt] = NULL;  /* In case more dstPlanes than src.*/
        srcPlaneCnt += 1;  /* Include mask plane in count.*/
        }

   /* Setup a sink for dummy destination of rows from unwanted planes.*/
   nullDest = buffer;
   buffer  += srcRowBytes;
   bufsize -= srcRowBytes;

   /* Read the BODY contents into client's bitmap.
    * De-interleave planes and decompress rows.
    * MODIFIES: Last iteration modifies bufsize.*/

   buf = buffer + bufsize;  /* Buffer is currently empty.*/
   for (iRow = nRows; iRow > 0; iRow--)
        {
        for (iPlane = 0; iPlane < srcPlaneCnt; iPlane++)
            {
            pDest = &planes[iPlane];

            /* Establish a sink for any unwanted plane.*/
            if (*pDest == NULL)
                {
                nullBuf = nullDest;
                pDest   = &nullBuf;
                }

            /* Read in at least enough bytes to uncompress next row.*/
            nEmpty  = buf - buffer;       /* size of empty part of buffer.*/
            nFilled = bufsize - nEmpty;   /* this part has data.*/
            if (nFilled < bufRowBytes)
                {
                /* Need to read more.*/

                /* Move the existing data to the front of the buffer.*/
                /* Now covers range buffer[0]..buffer[nFilled-1].*/
                movmem(buf, buffer, nFilled);  /* Could be moving 0 bytes.*/

                if(nEmpty > ChunkMoreBytes(cn))
                    {
                    /* There aren't enough bytes left to fill the buffer.*/
                    nEmpty = ChunkMoreBytes(cn);
                    bufsize = nFilled + nEmpty;  /* heh-heh */
                    }

                /* Append new data to the existing data.*/
                if(ReadChunkBytes(iff, &buffer[nFilled], nEmpty) < nEmpty)
                        return(CLIENT_ERROR);

                buf     = buffer;
                nFilled = bufsize;
                nEmpty  = 0;
                }

            /* Copy uncompressed row to destination plane.*/
            if(compression == cmpNone)
                {
                if(nFilled < srcRowBytes)  return(IFFERR_MANGLED);
                movmem(buf, *pDest, srcRowBytes);
                buf    += srcRowBytes;
                *pDest += destRowBytes;
                }
            else
                {
                /* Decompress row to destination plane.*/
                if ( unpackrow(&buf, pDest, nFilled,  srcRowBytes) )
                    /*  pSource, pDest, srcBytes, dstBytes  */
                        return(IFFERR_MANGLED);
                else *pDest += (destRowBytes - srcRowBytes);
                }
            }
        }
   return(IFF_OKAY);
   }


/* ----------- getcolors ------------- */

/* getcolors - allocates a ilbm->colortable for at least MAXAMCOLORREG
 *      and loads CMAP colors into it, setting ilbm->ncolors to number
 *      of colors actually loaded.
 */
LONG getcolors(struct ILBMInfo *ilbm)
        {
        struct IFFHandle        *iff;
        int error = 1;

        if(!(iff=ilbm->ParseInfo.iff))     return(CLIENT_ERROR);

        if(!(error = alloccolortable(ilbm)))
           error = loadcmap(iff, ilbm->colortable, &ilbm->ncolors);
        if(error) freecolors(ilbm);
        D(bug("getcolors: error = %ld\n",error));
        return(error);
        }


/* alloccolortable - allocates ilbm->colortable and sets ilbm->ncolors
 *      to the number of colors we have room for in the table.
 */

LONG alloccolortable(struct ILBMInfo *ilbm)
        {
        struct IFFHandle        *iff;
        struct  StoredProperty  *sp;

        LONG    error = CLIENT_ERROR;
        ULONG   ctabsize;
        USHORT  ncolors;

        if(!(iff=ilbm->ParseInfo.iff))     return(CLIENT_ERROR);

        if(sp = FindProp (iff, ID_ILBM, ID_CMAP))
                {
                /*
                 * Compute the size table we need
                 */
                ncolors = sp->sp_Size / 3;         /* how many in CMAP */
                ncolors = MAX(ncolors, MAXAMCOLORREG);

                ctabsize = ncolors * sizeof(Color4);
                if(ilbm->colortable =
                   (Color4 *)AllocMem(ctabsize,MEMF_CLEAR|MEMF_PUBLIC))
                    {
                    ilbm->ncolors = ncolors;
                    ilbm->ctabsize = ctabsize;
                    error = 0L;
                    }
                else error = IFFERR_NOMEM;
                }
        D(bug("alloccolortable for %ld colors: error = %ld\n",ncolors,error));
        return(error);
        }


void freecolors(struct ILBMInfo *ilbm)
        {
        if(ilbm->colortable)
                {
                FreeMem(ilbm->colortable, ilbm->ctabsize);
                }
        ilbm->colortable = NULL;
        ilbm->ctabsize = 0;
        }



/* Passed IFFHandle, pointer to colortable array, and pointer to
 * a USHORT containing number of colors caller has space to hold,
 * loads the colors and sets pNcolors to the number actually read.
 *
 * NOTE !!! - Old GetCMAP passed a pointer to a UBYTE for pNcolors
 *            This one is passed a pointer to a USHORT
 */
LONG loadcmap(struct IFFHandle *iff, WORD *colortable,USHORT *pNcolors)
        {
        register struct StoredProperty  *sp;
        register LONG                   idx;
        register ULONG                  ncolors;
        register UBYTE                  *rgb;
        LONG                            r, g, b;

        if(!(colortable))
                {
                message("No colortable allocated\n");
                return(1);
                }

        if(!(sp = FindProp (iff, ID_ILBM, ID_CMAP)))    return(1);

        rgb = sp->sp_Data;
        ncolors = sp->sp_Size / sizeofColorRegister;
        if(*pNcolors < ncolors)    ncolors = *pNcolors;
        *pNcolors = ncolors;

        idx = 0;
        while (ncolors--)
                {
                r = (*rgb++ & 0xF0) << 4;
                g = *rgb++ & 0xF0;
                b = *rgb++ >> 4;
                colortable[idx] = r | g | b;
                idx++;
                }
        return(0);
        }

/*
 * Returns CAMG or computed mode for storage in ilbm->camg
 *
 * ilbm->Bmhd structure must be initialized prior to this call.
 */
ULONG getcamg(struct ILBMInfo *ilbm)
        {
        struct IFFHandle *iff;
        struct StoredProperty *sp;
        UWORD  wide,high,deep;
        ULONG modeid = 0L;

        if(!(iff=ilbm->ParseInfo.iff))     return(0L);

        wide = ilbm->Bmhd.pageWidth;
        high = ilbm->Bmhd.pageHeight;
        deep = ilbm->Bmhd.nPlanes;

        D(bug("Getting CAMG for w=%ld h=%ld d=%ld ILBM\n",wide,high,deep));

        /*
         * Grab CAMG's idea of the viewmodes.
         */
        if (sp = FindProp (iff, ID_ILBM, ID_CAMG))
                {
                modeid = (* (ULONG *) sp->sp_Data);

                /* knock bad bits out of old-style 16-bit viewmode CAMGs
                 */
                if((!(modeid & MONITOR_ID_MASK))||
                  ((modeid & EXTENDED_MODE)&&(!(modeid & 0xFFFF0000))))
                   modeid &=
                    (~(EXTENDED_MODE|SPRITES|GENLOCK_AUDIO|GENLOCK_VIDEO|VP_HIDE));

                /* check for bogus CAMG like DPaintII brushes
                 * with junk in upper word and extended bit
                 * not set in lower word.
                 */
                if((modeid & 0xFFFF0000)&&(!(modeid & 0x00001000))) sp=NULL;
                }

        if(!sp) {
                /*
                 * No CAMG (or bad CAMG) present; use computed modes.
                 */
                if (wide >= 640)        modeid = HIRES;
                if (high >= 400)        modeid |= LACE;
                if (deep == 6)
                        {
                        modeid |= ilbm->EHB ? EXTRA_HALFBRITE : HAM;
                        }
                D(bug("No CAMG found - using mode $%08lx\n",modeid));
                }

        D(bug("getcamg: modeid = $%08lx\n",modeid));
        return(modeid);
        }