changeset 447:0e27860ddcfe

Finish initial implementation of IFF ILBM loader. And whoa .. it seems to be working.
author Matti Hamalainen <ccr@tnsp.org>
date Sun, 04 Nov 2012 08:40:17 +0200
parents a6d0e101cd16
children f1aab48a76fe
files libgfx.c
diffstat 1 files changed, 172 insertions(+), 9 deletions(-) [+]
line wrap: on
line diff
--- a/libgfx.c	Sun Nov 04 08:38:25 2012 +0200
+++ b/libgfx.c	Sun Nov 04 08:40:17 2012 +0200
@@ -964,7 +964,6 @@
     DMIFFBMHD bmhd;
     int ncolors;
     DMColor *pal;
-    DMImage *img;
     BOOL paletted;
 } DMIFF;
 
@@ -994,7 +993,11 @@
 static BOOL dmSkipIFFChunkRest(FILE *fp, const DMIFFChunk *chunk, const Uint32 used)
 {
     if (chunk->size > used)
+    {
+        dmMsg(3, "ILBM: Skipping %d bytes (%d of %d consumed)\n",
+            chunk->size - used, used, chunk->size);
         return fseeko(fp, chunk->size - used, SEEK_CUR) == 0;
+    }
     else
         return TRUE;
 }
@@ -1018,9 +1021,144 @@
 }
 
 
-int dmDecodeILBMBody(FILE *fp, DMIFF *iff)
+static BOOL dmIFFDecodeByteRun1Row(FILE *fp, Uint8 *buf, const size_t bufLen)
+{
+    size_t offs = 0;
+    do
+    {
+        Sint8 dcount;
+        Uint8 data;
+
+        if (!dm_fread_byte(fp, (Uint8 *) &dcount))
+            return FALSE;
+        
+        if (dcount == -128)
+        {
+            if (!dm_fread_byte(fp, &data))
+                return FALSE;
+        }
+        else
+        if (dcount < 0)
+        {
+            int count = (-dcount) + 1;
+            if (!dm_fread_byte(fp, &data))
+                return FALSE;
+
+            while (count-- && offs < bufLen)
+                buf[offs++] = data;
+        }
+        else
+        {
+            int count = dcount + 1;
+            while (count-- && offs < bufLen)
+            {
+                if (!dm_fread_byte(fp, &data))
+                    return FALSE;
+
+                buf[offs++] = data;
+            }
+        }
+    } while (offs < bufLen);
+
+    return TRUE;
+}
+
+
+static BOOL dmIFFReadOneRow(FILE *fp, DMIFF *iff, Uint8 *buf, const size_t bufLen)
+{
+    if (iff->bmhd.compression == IFF_COMP_BYTERUN1)
+        return dmIFFDecodeByteRun1Row(fp, buf, bufLen);
+    else
+        return dm_fread_str(fp, buf, bufLen);
+}
+
+
+void dmDecodeBitPlane(Uint8 *dp, Uint8 *src, const int width, const int nplane)
+{
+    int xc;
+    for (xc = 0; xc < width; xc++)
+    {
+        const Uint8 data = (src[xc / 8] >> (7 - (xc & 7))) & 1;
+        dp[xc] |= (data << nplane);
+    }
+}
+
+
+int dmDecodeILBMBody(FILE *fp, DMIFF *iff, DMImage **pimg, Uint32 *read)
 {
-    return DMERR_OK;
+    DMImage *img;
+    Uint8 *buf;
+    size_t bufLen;
+    int yc, res = DMERR_OK;
+    
+    *read = 0;
+
+    // Allocate image
+    if ((*pimg = img = dmImageAlloc(iff->bmhd.w, iff->bmhd.h)) == NULL)
+        return DMERR_MALLOC;
+
+    // Allocate planar decoding buffer
+    bufLen = ((img->width + 15) / 16) * 2;
+    if ((buf = dmMalloc(bufLen)) == NULL)
+        return DMERR_MALLOC;
+
+    dmMsg(2, "ILBM: plane row size %d bytes.\n", bufLen);
+    
+    // Decode the chunk
+    for (yc = 0; yc < img->height; yc++)
+    {
+        int plane;
+        const int nplanes = iff->bmhd.nplanes;
+        Uint8 *dp = img->data + (yc * img->pitch);
+
+        memset(dp, 0, img->pitch);
+
+        for (plane = 0; plane < nplanes; plane++)
+        {
+            // Decompress or read data
+            if (!dmIFFReadOneRow(fp, iff, buf, bufLen))
+            {
+                dmError("ILBM: Error in reading image plane #%d.\n", plane);
+                res = DMERR_FREAD;
+                goto error;
+            }
+
+            // Decode bitplane
+            dmDecodeBitPlane(dp, buf, img->width, plane);
+
+            *read += bufLen;
+        }
+
+        // Read mask data
+        if (iff->bmhd.masking == IFF_MASK_HAS_MASK)
+        {
+            int xc;
+
+            // Decompress or read data
+            if (!dmIFFReadOneRow(fp, iff, buf, bufLen))
+            {
+                dmError("ILBM: Error in reading mask plane.\n");
+                res = DMERR_FREAD;
+                goto error;
+            }
+
+            // Decode mask
+            for (xc = 0; xc < img->width; xc++)
+            {
+                const Uint8 data = (buf[xc / 8] >> (7 - (xc & 7))) & 1;
+                
+                // Black out any pixels with mask bit 0
+                if (!data)
+                    dp[xc] = 0;
+            }
+
+            *read += bufLen;
+        }
+    }
+
+error:
+    dmFree(buf);
+    return res;
 }
 
 
@@ -1029,6 +1167,7 @@
     Uint32 idILBM;
     DMIFFChunk chunk;
     DMIFF iff;
+    Uint32 read;
     BOOL parsed = FALSE;
     int i, res = DMERR_OK;
 
@@ -1062,9 +1201,11 @@
         switch (chunk.id)
         {
             case IFF_ID_BMHD:
+                // Check for multiple occurences of BMHD
                 if (!dmCheckIFFChunk(&iff.chBMHD, &chunk, FALSE, sizeof(iff.bmhd)))
                     return DMERR_FREAD;
 
+                // Read BMHD data
                 if (!dm_fread_be16(fp, &iff.bmhd.w) ||
                     !dm_fread_be16(fp, &iff.bmhd.h) ||
                     !dm_fread_be16(fp, (Uint16 *) &iff.bmhd.x) ||
@@ -1083,8 +1224,20 @@
                     return DMERR_FREAD;
                 }
                 dmMsg(2, "ILBM: BMHD %d x %d @ %d, %d : nplanes=%d, comp=%d, mask=%d\n",
-                    iff.bmhd.w, iff.bmhd.h, iff.bmhd.x, iff.bmhd.y, iff.bmhd.nplanes, iff.bmhd.compression, iff.bmhd.masking);
+                    iff.bmhd.w, iff.bmhd.h, iff.bmhd.x, iff.bmhd.y,
+                    iff.bmhd.nplanes, iff.bmhd.compression, iff.bmhd.masking);
                 
+                // Sanity check
+                if (iff.bmhd.nplanes < 1 || iff.bmhd.nplanes > 8 ||
+                    (iff.bmhd.compression != IFF_COMP_NONE &&
+                    iff.bmhd.compression != IFF_COMP_BYTERUN1) ||
+                    (iff.bmhd.masking != IFF_MASK_NONE &&
+                    iff.bmhd.masking != IFF_MASK_HAS_MASK))
+                {
+                    dmError("ILBM: Unsupported features, refusing to load.\n");
+                    return DMERR_INVALID_DATA;
+                }
+
                 if (!dmSkipIFFChunkRest(fp, &chunk, sizeof(iff.bmhd)))
                     return DMERR_FREAD;
                 break;
@@ -1138,9 +1291,11 @@
                 break;
             
             case IFF_ID_BODY:
+                // Check for multiple occurences of CMAP
                 if (!dmCheckIFFChunk(&iff.chBODY, &chunk, FALSE, 1))
                     return DMERR_FREAD;
 
+                // Check for sanity
                 if (!iff.chBMHD.count)
                 {
                     dmError("ILBM: BODY chunk before BMHD?\n");
@@ -1148,13 +1303,14 @@
                 }
 
                 dmMsg(2, "ILBM: BODY chunk size %d bytes\n", chunk.size);
+                
+                // Decode the body
+                if ((res = dmDecodeILBMBody(fp, &iff, pimg, &read)) != DMERR_OK)
+                    return res;
 
-                if (fseeko(fp, chunk.size, SEEK_CUR) != 0)
-                {
-                    dmError("ILBM: Error skipping in file.");
+                if (!dmSkipIFFChunkRest(fp, &chunk, read))
                     return DMERR_FREAD;
-                }
-                 
+
                 if (iff.chCMAP.count)
                     parsed = TRUE;
                 break;
@@ -1177,6 +1333,13 @@
             fgetc(fp);
     }
 
+    // Set colormap after finishing
+    if (iff.pal != NULL && iff.ncolors > 0 && *pimg != NULL)
+    {
+        (*pimg)->ncolors = iff.ncolors;
+        (*pimg)->pal = iff.pal;
+    }
+
     return res;
 }