diff tools/libgfx.c @ 2094:4276b8c0fef0

Revamp how the DMImage palette system and color formats work, as preparation for future work on supporting non-indexed/paletted images. It is still messy.
author Matti Hamalainen <ccr@tnsp.org>
date Tue, 05 Mar 2019 09:56:47 +0200
parents d17512dbb4ef
children 80786a28caf0
line wrap: on
line diff
--- a/tools/libgfx.c	Thu Feb 28 12:32:07 2019 +0200
+++ b/tools/libgfx.c	Tue Mar 05 09:56:47 2019 +0200
@@ -91,7 +91,7 @@
 }
 
 
-BOOL dmCompareColor(const DMColor *c1, const DMColor *c2, BOOL alpha)
+BOOL dmCompareColor(const DMColor *c1, const DMColor *c2, const BOOL alpha)
 {
     if (c1->r == c2->r &&
         c1->g == c2->g &&
@@ -133,7 +133,6 @@
     img->bpp     = (bpp <= 0) ? dmImageGetBitsPerPixel(format) : bpp;
     img->pitch   = (width * img->bpp) / 8;
     img->size    = img->pitch * img->height;
-    img->ctransp = -1;
     img->aspect  = -1;
 
     if ((img->data = dmMalloc(img->size)) == NULL)
@@ -151,63 +150,164 @@
     if (img != NULL)
     {
         if (!img->constpal)
-        {
-            dmFree(img->pal);
-        }
+            dmPaletteFree(img->pal);
+
         dmFree(img->data);
         dmFree(img);
     }
 }
 
 
-BOOL dmPaletteAlloc(DMColor **ppal, int ncolors, int ctransp)
+int dmPaletteAlloc(DMPalette **ppal, const int ncolors, const int ctransp)
 {
+    DMPalette *pal;
     if (ppal == NULL)
-        return FALSE;
+        return DMERR_NULLPTR;
+
+    *ppal = NULL;
+
+    // Allocate palette struct
+    if ((pal = dmMalloc0(sizeof(DMPalette))) == NULL)
+        return DMERR_MALLOC;
+
+    pal->ncolors = ncolors;
+    pal->ctransp = ctransp;
 
     // Allocate desired amount of palette
-    if ((*ppal = dmCalloc(ncolors, sizeof(DMColor))) == NULL)
-        return FALSE;
+    if ((pal->colors = dmCalloc(pal->ncolors, sizeof(DMColor))) == NULL)
+    {
+        dmFree(pal);
+        return DMERR_MALLOC;
+    }
 
     // Set alpha values to max, except for transparent color
-    for (int i = 0; i < ncolors; i++)
+    for (int i = 0; i < pal->ncolors; i++)
     {
-        (*ppal)[i].a = (i == ctransp) ? 0x00 : 0xff;
+        pal->colors[i].a = (i == pal->ctransp) ? 0x00 : 0xff;
     }
 
-    return TRUE;
+    *ppal = pal;
+
+    return DMERR_OK;
 }
 
 
-BOOL dmImagePaletteAlloc(DMImage *img, int ncolors, int ctransp)
+int dmPaletteResize(DMPalette **ppal, const int ncolors)
+{
+    DMPalette *pal;
+
+    if (ppal == NULL)
+        return DMERR_NULLPTR;
+
+    if (ncolors <= 0 || ncolors > 256)
+        return DMERR_INVALID_ARGS;
+
+    if (*ppal == NULL)
+        return dmPaletteAlloc(ppal, ncolors, -1);
+
+    pal = *ppal;
+
+    if (pal->ncolors == ncolors)
+        return DMERR_OK;
+
+    if ((pal->colors = dmRealloc(pal->colors, sizeof(DMColor) * ncolors)) == NULL)
+        return DMERR_MALLOC;
+
+    if (ncolors - pal->ncolors > 0)
+        memset(&(pal->colors[pal->ncolors]), 0, sizeof(DMColor) * (ncolors - pal->ncolors));
+
+    return DMERR_OK;
+}
+
+
+int dmPaletteCopy(DMPalette **pdst, const DMPalette *src)
 {
-    if (img == NULL)
-        return FALSE;
-
-    img->ncolors = ncolors;
-    img->ctransp = ctransp;
-    img->constpal = FALSE;
-
-    return dmPaletteAlloc(&(img->pal), ncolors, ctransp);
+    DMPalette *pal;
+    if (pdst == NULL)
+        return DMERR_NULLPTR;
+
+    *pdst = NULL;
+
+    // Allocate palette struct
+    if ((pal = dmMalloc(sizeof(DMPalette))) == NULL)
+        return DMERR_MALLOC;
+
+    memcpy(pal, src, sizeof(DMPalette));
+
+    // Allocate desired amount of palette
+    if ((pal->colors = dmCalloc(pal->ncolors, sizeof(DMColor))) == NULL)
+    {
+        dmFree(pal);
+        return DMERR_MALLOC;
+    }
+
+    memcpy(pal->colors, src->colors, sizeof(DMColor) * pal->ncolors);
+    *pdst = pal;
+
+    return DMERR_OK;
 }
 
 
-static BOOL dmPaletteReadData(DMResource *fp, DMColor *pal, int ncolors)
+void dmPaletteFree(DMPalette *pal)
 {
+    if (pal != NULL)
+    {
+        dmFree(pal->colors);
+        dmFree(pal);
+    }
+}
+
+
+static int dmPaletteReadData(DMResource *fp, DMPalette *pal, int ncolors)
+{
+    if (pal->ncolors < ncolors)
+        return DMERR_INVALID_ARGS;
+
     for (int i = 0; i < ncolors; i++)
     {
         Uint8 colR, colG, colB;
         if (!dmf_read_byte(fp, &colR) ||
             !dmf_read_byte(fp, &colG) ||
             !dmf_read_byte(fp, &colB))
-            return FALSE;
-
-        pal[i].r = colR;
-        pal[i].g = colG;
-        pal[i].b = colB;
+            return DMERR_FREAD;
+
+        pal->colors[i].r = colR;
+        pal->colors[i].g = colG;
+        pal->colors[i].b = colB;
     }
 
-    return TRUE;
+    return DMERR_OK;
+}
+
+
+static int dmPaletteWriteData(DMResource *fp, const DMPalette *pal, const int ncolors, const int npad)
+{
+    int i;
+
+    if (pal == NULL || fp == NULL)
+        return DMERR_NULLPTR;
+
+    if (pal->ncolors < ncolors)
+        return DMERR_INVALID_ARGS;
+
+    for (i = 0; i < ncolors; i++)
+    {
+        DMColor *col = &pal->colors[i];
+        if (!dmf_write_byte(fp, col->r) ||
+            !dmf_write_byte(fp, col->g) ||
+            !dmf_write_byte(fp, col->b))
+            return DMERR_FWRITE;
+    }
+
+    for (; i < npad; i++)
+    {
+        if (!dmf_write_byte(fp, 0) ||
+            !dmf_write_byte(fp, 0) ||
+            !dmf_write_byte(fp, 0))
+            return DMERR_FWRITE;
+    }
+
+    return DMERR_OK;
 }
 
 
@@ -236,71 +336,143 @@
                 *ptr3 = ptr2 + rowWidth,
                 *ptr4 = ptr3 + rowWidth;
 
-        for (x = 0; x < img->width; x++)
+        if (img->format == DM_COLFMT_PALETTE ||
+            img->format == DM_COLFMT_GRAYSCALE)
         {
-            Uint8 c = img->data[(y * img->pitch) + (x * img->bpp) / 8],
-                  qr, qg, qb, qa;
-
+            for (x = 0; x < img->width; x++)
+            {
+                Uint8 c = img->data[(y * img->pitch) + (x * img->bpp) / 8],
+                    qr, qg, qb, qa;
+
+                if (c >= img->pal->ncolors)
+                {
+                    res = DMERR_INVALID_DATA;
+                    goto done;
+                }
+
+                switch (spec->format)
+                {
+                    case DM_COLFMT_PALETTE:
+                    case DM_COLFMT_GRAYSCALE:
+                        for (xscale = 0; xscale < spec->scaleX; xscale++)
+                            *ptr1++ = c;
+                        break;
+
+                    case DM_COLFMT_RGBA:
+                        qr = img->pal->colors[c].r;
+                        qg = img->pal->colors[c].g;
+                        qb = img->pal->colors[c].b;
+                        qa = img->pal->colors[c].a;
+
+                        if (spec->planar)
+                        {
+                            for (xscale = 0; xscale < spec->scaleX; xscale++)
+                            {
+                                *ptr1++ = qr;
+                                *ptr2++ = qg;
+                                *ptr3++ = qb;
+                                *ptr4++ = qa;
+                            }
+                        }
+                        else
+                        {
+                            for (xscale = 0; xscale < spec->scaleX; xscale++)
+                            {
+                                *ptr1++ = qr;
+                                *ptr1++ = qg;
+                                *ptr1++ = qb;
+                                *ptr1++ = qa;
+                            }
+                        }
+                        break;
+
+                    case DM_COLFMT_RGB:
+                        qr = img->pal->colors[c].r;
+                        qg = img->pal->colors[c].g;
+                        qb = img->pal->colors[c].b;
+
+                        if (spec->planar)
+                        {
+                            for (xscale = 0; xscale < spec->scaleX; xscale++)
+                            {
+                                *ptr1++ = qr;
+                                *ptr2++ = qg;
+                                *ptr3++ = qb;
+                            }
+                        }
+                        else
+                        {
+                            for (xscale = 0; xscale < spec->scaleX; xscale++)
+                            {
+                                *ptr1++ = qr;
+                                *ptr1++ = qg;
+                                *ptr1++ = qb;
+                            }
+                        }
+                        break;
+
+                    default:
+                        res = DMERR_NOT_SUPPORTED;
+                        goto done;
+                }
+            }
+        }
+        else
+        if (img->format == DM_COLFMT_RGB ||
+            img->format == DM_COLFMT_RGBA)
+        {
+            Uint8 *sp = img->data + (y * img->pitch);
+
+            for (x = 0; x < img->width; x++, sp += img->bpp / 8)
             switch (spec->format)
             {
-                case DM_COLFMT_PALETTE:
-                case DM_COLFMT_GRAYSCALE:
-                    for (xscale = 0; xscale < spec->scaleX; xscale++)
-                        *ptr1++ = c;
-                    break;
-
-                case DM_COLFMT_RGBA:
-                    qr = img->pal[c].r;
-                    qg = img->pal[c].g;
-                    qb = img->pal[c].b;
-                    qa = img->pal[c].a;
-
+                 case DM_COLFMT_RGBA:
                     if (spec->planar)
                     {
                         for (xscale = 0; xscale < spec->scaleX; xscale++)
                         {
-                            *ptr1++ = qr;
-                            *ptr2++ = qg;
-                            *ptr3++ = qb;
-                            *ptr4++ = qa;
+                            *ptr1++ = sp[0];
+                            *ptr2++ = sp[1];
+                            *ptr3++ = sp[2];
+                            *ptr4++ = sp[3];
                         }
                     }
                     else
                     {
                         for (xscale = 0; xscale < spec->scaleX; xscale++)
                         {
-                            *ptr1++ = qr;
-                            *ptr1++ = qg;
-                            *ptr1++ = qb;
-                            *ptr1++ = qa;
+                            *ptr1++ = sp[0];
+                            *ptr1++ = sp[1];
+                            *ptr1++ = sp[2];
+                            *ptr1++ = sp[3];
                         }
                     }
                     break;
 
                 case DM_COLFMT_RGB:
-                    qr = img->pal[c].r;
-                    qg = img->pal[c].g;
-                    qb = img->pal[c].b;
-
                     if (spec->planar)
                     {
                         for (xscale = 0; xscale < spec->scaleX; xscale++)
                         {
-                            *ptr1++ = qr;
-                            *ptr2++ = qg;
-                            *ptr3++ = qb;
+                            *ptr1++ = sp[0];
+                            *ptr2++ = sp[1];
+                            *ptr3++ = sp[2];
                         }
                     }
                     else
                     {
                         for (xscale = 0; xscale < spec->scaleX; xscale++)
                         {
-                            *ptr1++ = qr;
-                            *ptr1++ = qg;
-                            *ptr1++ = qb;
+                            *ptr1++ = sp[0];
+                            *ptr1++ = sp[1];
+                            *ptr1++ = sp[2];
                         }
                     }
                     break;
+
+                default:
+                    res = DMERR_NOT_SUPPORTED;
+                    goto done;
             }
         }
 
@@ -337,18 +509,18 @@
         if (dmfprintf(fp,
             "%s_ncolors: dw.w %d\n"
             "%s_palette:\n",
-            prefix, img->ncolors,
+            prefix, img->pal->ncolors,
             prefix) < 0)
             return dmferror(fp);
 
         for (int i = 0; i < (1 << spec->nplanes); i++)
         {
             Uint32 color;
-            if (i < img->ncolors)
+            if (i < img->pal->ncolors)
             {
-                color = (DMCOL(img->pal[i].r) << 8) |
-                        (DMCOL(img->pal[i].g) << 4) |
-                        (DMCOL(img->pal[i].b));
+                color = (DMCOL(img->pal->colors[i].r) << 8) |
+                        (DMCOL(img->pal->colors[i].g) << 4) |
+                        (DMCOL(img->pal->colors[i].b));
             }
             else
                 color = 0;
@@ -584,7 +756,7 @@
         case '5': itype = DM_COLFMT_GRAYSCALE; break;
         default:
             res = dmError(DMERR_NOT_SUPPORTED,
-                "PPM: Unsupported PPM/PGM subtype.\n");
+                "PPM: Unsupported PPM/PNM/PGM subtype.\n");
             goto error;
     }
 
@@ -595,6 +767,9 @@
         goto error;
     }
 
+    dmMsg(2, "PPM: %d x %d, type=%d\n",
+        width, height, itype);
+
     if ((*pimg = img = dmImageAlloc(width, height, itype, -1)) == NULL)
     {
         res = dmError(DMERR_MALLOC,
@@ -602,7 +777,7 @@
         goto error;
     }
 
-    if (dmf_read_str(fp, img->data, img->size))
+    if (!dmf_read_str(fp, img->data, img->size))
     {
         res = dmError(DMERR_FREAD,
             "PPM: Could not read image data.\n");
@@ -755,14 +930,14 @@
 
         dmMemset(palette, 0, PNG_MAX_PALETTE_LENGTH * sizeof(png_color));
 
-        for (int i = 0; i < img->ncolors; i++)
+        for (int i = 0; i < img->pal->ncolors; i++)
         {
-            palette[i].red   = img->pal[i].r;
-            palette[i].green = img->pal[i].g;
-            palette[i].blue  = img->pal[i].b;
+            palette[i].red   = img->pal->colors[i].r;
+            palette[i].green = img->pal->colors[i].g;
+            palette[i].blue  = img->pal->colors[i].b;
         }
 
-        png_set_PLTE(png_ptr, info_ptr, palette, img->ncolors);
+        png_set_PLTE(png_ptr, info_ptr, palette, img->pal->ncolors);
         png_free(png_ptr, palette);
     }
 
@@ -802,8 +977,7 @@
     png_bytep *row_pointers = NULL;
     png_bytep trans = NULL;
     png_uint_32 width, height, res_x = 0, res_y = 0;
-    int i, itype, bit_depth, color_type, ncolors, ntrans, unit_type;
-    int res = DMERR_OK;
+    int res, itype, bit_depth, color_type, ncolors, ntrans, unit_type;
     DMImage *img;
 
     // Create PNG structures
@@ -911,7 +1085,7 @@
     if ((row_pointers = png_malloc(png_ptr, height * sizeof(png_bytep))) == NULL)
         goto error;
 
-    for (i = 0; i < img->height; i++)
+    for (int i = 0; i < img->height; i++)
         row_pointers[i] = img->data + (i * img->pitch);
 
     png_read_image(png_ptr, row_pointers);
@@ -927,17 +1101,14 @@
         {
             dmMsg(2, "PNG: Palette of %d colors found.\n", ncolors);
 
-            if (!dmImagePaletteAlloc(img, ncolors, -1))
-            {
-                res = DMERR_MALLOC;
+            if ((res = dmPaletteAlloc(&(img->pal), ncolors, -1)) != DMERR_OK)
                 goto error;
-            }
-
-            for (i = 0; i < img->ncolors; i++)
+
+            for (int i = 0; i < img->pal->ncolors; i++)
             {
-                img->pal[i].r = palette[i].red;
-                img->pal[i].g = palette[i].green;
-                img->pal[i].b = palette[i].blue;
+                img->pal->colors[i].r = palette[i].red;
+                img->pal->colors[i].g = palette[i].green;
+                img->pal->colors[i].b = palette[i].blue;
             }
         }
 
@@ -945,15 +1116,17 @@
         if (trans != NULL && ntrans > 0)
         {
             dmMsg(2, "PNG: %d transparent colors.\n", ntrans);
-            for (i = 0; i < img->ncolors && i < ntrans; i++)
+            for (int i = 0; i < img->pal->ncolors && i < ntrans; i++)
             {
-                img->pal[i].a = trans[i];
-                if (img->ctransp < 0 && trans[i] == 0)
-                    img->ctransp = i;
+                img->pal->colors[i].a = trans[i];
+                if (img->pal->ctransp < 0 && trans[i] == 0)
+                    img->pal->ctransp = i;
             }
         }
     }
 
+    res = DMERR_OK;
+
 error:
     if (png_ptr != NULL && info_ptr != NULL)
         png_destroy_read_struct(&png_ptr, &info_ptr, NULL);
@@ -1105,7 +1278,6 @@
             return DMERR_FWRITE;
     }
 
-
     return DMERR_OK;
 }
 
@@ -1145,12 +1317,12 @@
     if (spec.format == DM_COLFMT_PALETTE ||
         spec.format == DM_COLFMT_GRAYSCALE)
     {
-        const int ncolors = img->ncolors > DMPCX_PAL_COLORS ? DMPCX_PAL_COLORS : img->ncolors;
+        const int ncolors = img->pal->ncolors > DMPCX_PAL_COLORS ? DMPCX_PAL_COLORS : img->pal->ncolors;
         for (int i = 0; i < ncolors; i++)
         {
-            hdr.colorMap[i].r = img->pal[i].r;
-            hdr.colorMap[i].g = img->pal[i].g;
-            hdr.colorMap[i].b = img->pal[i].b;
+            hdr.colorMap[i].r = img->pal->colors[i].r;
+            hdr.colorMap[i].g = img->pal->colors[i].g;
+            hdr.colorMap[i].b = img->pal->colors[i].b;
         }
     }
     hdr.manufacturer = 10;
@@ -1246,34 +1418,15 @@
     if (spec.format == DM_COLFMT_PALETTE ||
         spec.format == DM_COLFMT_GRAYSCALE)
     {
-        int i;
-        dmMsg(2, "PCX: Writing palette of %d active entries.\n", img->ncolors);
+        dmMsg(2, "PCX: Writing palette of %d active entries.\n", img->pal->ncolors);
 
         dmf_write_byte(pcx.fp, 0x0C);
 
-        for (i = 0; i < img->ncolors; i++)
+        if ((res = dmPaletteWriteData(fp, img->pal, img->pal->ncolors, 256)) != DMERR_OK)
         {
-            if (!dmf_write_byte(pcx.fp, img->pal[i].r) ||
-                !dmf_write_byte(pcx.fp, img->pal[i].g) ||
-                !dmf_write_byte(pcx.fp, img->pal[i].b))
-            {
-                res = dmError(DMERR_FWRITE,
-                    "PCX: Could not write palette data.\n");
-                goto error;
-            }
-        }
-
-        // Pad the palette, if necessary
-        for (; i < 256; i++)
-        {
-            if (!dmf_write_byte(pcx.fp, 0) ||
-                !dmf_write_byte(pcx.fp, 0) ||
-                !dmf_write_byte(pcx.fp, 0))
-            {
-                res = dmError(DMERR_FWRITE,
-                    "PCX: Could not write palette data.\n");
-                goto error;
-            }
+            res = dmError(DMERR_FWRITE,
+                "PCX: Could not write palette data.\n");
+            goto error;
         }
     }
 
@@ -1472,9 +1625,7 @@
         // Decode bitplanes
         switch (hdr.bitsPerPlane)
         {
-            case 32:
             case 24:
-            case 16:
             case 8:
                 {
                     // Actually bytes and bits per plane per pixel ..
@@ -1533,9 +1684,9 @@
             ncolors = 256;
         }
 
-        if (!dmImagePaletteAlloc(img, ncolors, -1))
+        if ((res = dmPaletteAlloc(&(img->pal), ncolors, -1)) != DMERR_OK)
         {
-            res = dmError(DMERR_MALLOC,
+            res = dmError(res,
                 "PCX: Could not allocate palette data!\n");
             goto error;
         }
@@ -1544,23 +1695,25 @@
         {
             // Okay, attempt to read the palette data
             dmMsg(2, "PCX: Reading palette of %d colors\n", ncolors);
-            if (!dmPaletteReadData(fp, img->pal, ncolors))
+            if ((res = dmPaletteReadData(fp, img->pal, ncolors)) != DMERR_OK)
             {
-                res = dmError(DMERR_FREAD,
-                    "PCX: Error reading palette.\n");
+                dmErrorMsg("PCX: Error reading palette.\n");
                 goto error;
             }
         }
         else
         {
+            const int nmax = img->pal->ncolors > DMPCX_PAL_COLORS ? DMPCX_PAL_COLORS : img->pal->ncolors;
+
             // If the extra palette is not available, copy the colors from
             // the header palette to our internal palette structure.
-            dmMsg(2, "PCX: Initializing palette from header of %d colors\n", ncolors);
-            for (int i = 0; i < (img->ncolors > DMPCX_PAL_COLORS ? DMPCX_PAL_COLORS : img->ncolors); i++)
+            dmMsg(2, "PCX: Initializing palette from header of %d colors (%d)\n", ncolors, nmax);
+
+            for (int i = 0; i < nmax; i++)
             {
-                img->pal[i].r = hdr.colorMap[i].r;
-                img->pal[i].g = hdr.colorMap[i].g;
-                img->pal[i].b = hdr.colorMap[i].b;
+                img->pal->colors[i].r = hdr.colorMap[i].r;
+                img->pal->colors[i].g = hdr.colorMap[i].g;
+                img->pal->colors[i].b = hdr.colorMap[i].b;
             }
         }
     }
@@ -1626,7 +1779,7 @@
     DMIFFBMHD bmhd;
     Uint32 camg;
     int ncolors;
-    DMColor *pal;
+    DMPalette *pal;
     Uint32 idsig;
     char *idstr;
 } DMIFF;
@@ -1896,7 +2049,7 @@
 
                     // Black out any pixels with mask bit 0
                     if (!data)
-                        dp[xc] = img->ctransp < 0 ? 0 : img->ctransp;
+                        dp[xc] = img->pal->ctransp < 0 ? 0 : img->pal->ctransp;
                 }
             }
         }
@@ -2036,16 +2189,16 @@
                 // Read palette
                 if (iff.ncolors > 0)
                 {
-                    if (!dmPaletteAlloc(&iff.pal, iff.ncolors,
-                        (iff.bmhd.masking == IFF_MASK_TRANSP) ? iff.bmhd.transp : -1))
+                    if ((res = dmPaletteAlloc(&iff.pal, iff.ncolors,
+                        (iff.bmhd.masking == IFF_MASK_TRANSP) ? iff.bmhd.transp : -1)) != DMERR_OK)
                     {
-                        return dmError(DMERR_MALLOC,
-                            "IFF: Could not allocate palette data.\n");
+                        dmErrorMsg("IFF: Could not allocate palette data.\n");
+                        return res;
                     }
-                    if (!dmPaletteReadData(fp, iff.pal, iff.ncolors))
+                    if ((res = dmPaletteReadData(fp, iff.pal, iff.ncolors)) != DMERR_OK)
                     {
-                        return dmError(DMERR_FREAD,
-                            "IFF: Error reading CMAP.\n");
+                        dmErrorMsg("IFF: Error reading CMAP.\n");
+                        return res;
                     }
                 }
 
@@ -2129,7 +2282,7 @@
     if (*pimg != NULL && (*pimg)->format == DM_COLFMT_PALETTE)
     {
         // Check that we DO have a palette ..
-        if (iff.pal == NULL || iff.ncolors == 0)
+        if (iff.pal == NULL || iff.pal->ncolors == 0)
         {
             return dmError(DMERR_INVALID_DATA,
                 "IFF: A paletted/indexed color image with no CMAP. Bailing out.\n");
@@ -2138,7 +2291,7 @@
         // If halfbrite is used, duplicate the palette
         if (iff.camg & IFF_CAMG_HALFBRITE)
         {
-            void *ptmp;
+            int ncolors = iff.ncolors;
             if (iff.idsig != IFF_ID_ILBM)
             {
                 dmErrorMsg("IFF: Non-planar PBM file with Halfbrite enabled! This might not work.\n");
@@ -2150,25 +2303,21 @@
                     "IFF: Halfbrite enabled, but ncolors > 128.\n");
             }
 
-            if ((ptmp = dmRealloc(iff.pal, sizeof(DMColor) * iff.ncolors * 2)) == NULL)
+            if ((res = dmPaletteResize(&iff.pal, ncolors * 2)) != DMERR_OK)
             {
-                dmFree(iff.pal);
-                iff.pal = NULL;
-                return DMERR_MALLOC;
+                dmPaletteFree(iff.pal);
+                return res;
             }
-            else
-                iff.pal = ptmp;
-
-            for (int i = 0; i < iff.ncolors; i++)
+
+            for (int i = 0; i < ncolors; i++)
             {
-                int i2 = iff.ncolors + i;
-                iff.pal[i2].r = iff.pal[i].r / 2;
-                iff.pal[i2].g = iff.pal[i].g / 2;
-                iff.pal[i2].b = iff.pal[i].b / 2;
+                int i2 = ncolors + i;
+                iff.pal->colors[i2].r = iff.pal->colors[i].r / 2;
+                iff.pal->colors[i2].g = iff.pal->colors[i].g / 2;
+                iff.pal->colors[i2].b = iff.pal->colors[i].b / 2;
             }
         }
 
-        (*pimg)->ncolors = iff.ncolors;
         (*pimg)->pal = iff.pal;
     }
 
@@ -2384,8 +2533,8 @@
     iff.bmhd.yasp        = 1;
 
     iff.camg             = 0; // XXX TODO: when/if HAM support
-    iff.bmhd.masking     = (img->ctransp < 0) ? IFF_MASK_NONE : spec->mask;
-    iff.bmhd.transp      = (img->ctransp >= 0 && spec->mask == IFF_MASK_TRANSP) ? img->ctransp : 0xffff;
+    iff.bmhd.masking     = (img->pal && img->pal->ctransp < 0) ? IFF_MASK_NONE : spec->mask;
+    iff.bmhd.transp      = (img->pal && img->pal->ctransp >= 0 && spec->mask == IFF_MASK_TRANSP) ? img->pal->ctransp : 0xffff;
     iff.bmhd.nplanes     = (iff.idsig == IFF_ID_PBM && spec->nplanes < 8) ? 8 : spec->nplanes;
 
     // Apparently ACBM can't/should not use compression .. even though
@@ -2436,29 +2585,23 @@
     //
     // CMAP
     //
-    if (img->ncolors > 0 && spec->format == DM_COLFMT_PALETTE)
+    if (img->pal->ncolors > 0 && spec->format == DM_COLFMT_PALETTE)
     {
         if ((res = dmWriteIFFChunkHdr(fp, &iff.chCMAP, IFF_ID_CMAP)) != DMERR_OK)
             goto out;
 
-        for (int i = 0; i < img->ncolors; i++)
+        if ((res = dmPaletteWriteData(fp, img->pal, img->pal->ncolors, -1)) != DMERR_OK)
         {
-            DMColor *col = &img->pal[i];
-            if (!dmf_write_byte(fp, col->r) ||
-                !dmf_write_byte(fp, col->g) ||
-                !dmf_write_byte(fp, col->b))
-            {
-                res = dmError(DMERR_FWRITE,
-                    "IFF: Could not write CMAP palette entry #%d.\n", i);
-                goto out;
-            }
+            res = dmError(DMERR_FWRITE,
+                "IFF: Could not write CMAP palette.\n");
+            goto out;
         }
 
         if ((res = dmWriteIFFChunkFinish(fp, &iff.chCMAP)) != DMERR_OK)
             goto out;
 
         dmMsg(2, "IFF: CMAP %d entries (%d bytes)\n",
-            img->ncolors, iff.chCMAP.size);
+            img->pal->ncolors, iff.chCMAP.size);
     }
 
     //
@@ -2556,7 +2699,7 @@
                     dmMemset(buf, 0, bufLen);
 
                     for (int xc = 0; xc < img->width * spec->scaleX; xc++)
-                        buf[xc / 8] |= (sp[xc / spec->scaleX] == img->ctransp) << (7 - (xc & 7));
+                        buf[xc / 8] |= (sp[xc / spec->scaleX] == img->pal->ctransp) << (7 - (xc & 7));
 
                     if (!dmIFFWriteOneRow(fp, &iff, buf, bufLen))
                     {