changeset 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
files tools/64vw.c tools/gfxconv.c tools/lib64gfx.c tools/lib64gfx.h tools/libgfx.c tools/libgfx.h
diffstat 6 files changed, 399 insertions(+), 222 deletions(-) [+]
line wrap: on
line diff
--- a/tools/64vw.c	Thu Feb 28 12:32:07 2019 +0200
+++ b/tools/64vw.c	Tue Mar 05 09:56:47 2019 +0200
@@ -220,16 +220,18 @@
     bmap.pitch    = surf->pitch;
     bmap.width    = surf->w;
     bmap.height   = surf->h;
-    bmap.ncolors  = C64_NCOLORS;
-    bmap.constpal = TRUE;
-    bmap.pal      = dmDefaultC64Palette;
+
+    if ((ret = dmSetDefaultC64Palette(&bmap)) != DMERR_OK)
+        return ret;
 
     if (fmt->format->convertFrom != NULL)
         ret = fmt->format->convertFrom(&bmap, cimage, fmt, spec);
     else
         ret = dmC64ConvertGenericBMP2Image(&bmap, cimage, fmt, spec);
 
-    SDL_SetPaletteColors(surf->format->palette, (SDL_Color *) bmap.pal, 0, bmap.ncolors);
+    SDL_SetPaletteColors(surf->format->palette, (SDL_Color *) bmap.pal->colors, 0, bmap.pal->ncolors);
+
+    dmPaletteFree(bmap.pal);
 
     return ret;
 }
--- a/tools/gfxconv.c	Thu Feb 28 12:32:07 2019 +0200
+++ b/tools/gfxconv.c	Tue Mar 05 09:56:47 2019 +0200
@@ -1046,28 +1046,38 @@
 
 int dmRemapImageColors(DMImage **pdst, const DMImage *src)
 {
-    DMColor *npal = dmCalloc(src->ncolors, sizeof(DMColor));
-    int  *mapping = dmMalloc(src->ncolors * sizeof(int));
-    BOOL *mapped  = dmMalloc(src->ncolors * sizeof(BOOL));
-    BOOL *used    = dmMalloc(src->ncolors * sizeof(BOOL));
-    int n, index, xc, yc, ncolors;
+    DMPalette *npal = NULL;
+    int  *mapping = dmMalloc(src->pal->ncolors * sizeof(int));
+    BOOL *mapped  = dmMalloc(src->pal->ncolors * sizeof(BOOL));
+    BOOL *used    = dmMalloc(src->pal->ncolors * sizeof(BOOL));
+    int n, index, xc, yc, ncolors, res = DMERR_OK;
     DMImage *dst;
 
+    if ((res = dmPaletteAlloc(&npal, src->pal->ncolors, -1)) != DMERR_OK)
+    {
+        dmErrorMsg("Could not allocate memory for remap palette.\n");
+        goto error;
+    }
+
     if ((dst = *pdst = dmImageAlloc(src->width, src->height, src->format, src->bpp)) == NULL)
     {
-        return dmError(DMERR_MALLOC,
+        res = dmError(DMERR_MALLOC,
             "Could not allocate memory for remapped image.\n");
+        goto error;
     }
 
-    dmMsg(1, "Remapping %d output image colors of %d colors.\n", optNRemapTable, src->ncolors);
 
-    if (npal == NULL || mapping == NULL || mapped == NULL || used == NULL)
+    if (mapping == NULL || mapped == NULL || used == NULL)
     {
-        return dmError(DMERR_MALLOC,
+        res = dmError(DMERR_MALLOC,
             "Could not allocate memory for reused palette.\n");
+        goto error;
     }
 
-    for (index = 0; index < src->ncolors; index++)
+    dmMsg(1, "Remapping %d output image colors of %d colors.\n",
+            optNRemapTable, src->pal->ncolors);
+
+    for (index = 0; index < src->pal->ncolors; index++)
     {
         mapping[index] = -1;
         mapped[index] = used[index] = FALSE;
@@ -1081,7 +1091,7 @@
         for (xc = 0; xc < src->width; xc++)
         {
             Uint8 col = dp[xc];
-            if (col < src->ncolors && !used[col])
+            if (col < src->pal->ncolors && !used[col])
             {
                 used[col] = TRUE;
                 ncolors++;
@@ -1097,9 +1107,9 @@
         if (map->triplet)
         {
             BOOL found = FALSE;
-            for (n = 0; n < src->ncolors; n++)
+            for (n = 0; n < src->pal->ncolors; n++)
             {
-                if (dmCompareColor(&(src->pal[n]), &(map->color), map->alpha))
+                if (dmCompareColor(&(src->pal->colors[n]), &(map->color), map->alpha))
                 {
                     dmMsg(3, "RGBA match #%02x%02x%02x%02x: %d -> %d\n",
                         map->color.r, map->color.g, map->color.b, map->color.a,
@@ -1133,11 +1143,11 @@
     if (optRemapRemove)
         dmMsg(2, "Removing unused colors.\n");
 
-    for (index = 0; index < src->ncolors; index++)
+    for (index = 0; index < src->pal->ncolors; index++)
     if (mapping[index] < 0 &&
         (!optRemapRemove || (optRemapRemove && used[index])))
     {
-        for (n = 0; n < src->ncolors; n++)
+        for (n = 0; n < src->pal->ncolors; n++)
         if (!mapped[n])
         {
             mapping[index] = n;
@@ -1148,14 +1158,14 @@
 
     // Calculate final number of palette colors
     ncolors = 0;
-    for (index = 0; index < src->ncolors; index++)
+    for (index = 0; index < src->pal->ncolors; index++)
     {
         if (mapping[index] + 1 > ncolors)
             ncolors = mapping[index] + 1;
     }
 
     // Copy palette entries
-    for (index = 0; index < src->ncolors; index++)
+    for (index = 0; index < src->pal->ncolors; index++)
     {
         if (mapping[index] >= 0)
         {
@@ -1172,7 +1182,9 @@
         for (xc = 0; xc < src->width; xc++)
         {
             Uint8 col = sp[xc];
-            if (col < src->ncolors && mapping[col] >= 0 && mapping[col] < src->ncolors)
+            if (col < src->pal->ncolors &&
+                mapping[col] >= 0 &&
+                mapping[col] < src->pal->ncolors)
                 dp[xc] = mapping[col];
             else
                 dp[xc] = 0;
@@ -1180,13 +1192,18 @@
     }
 
     // Set new palette, free memory
-    dst->pal = npal;
-    dst->ncolors = ncolors;
+    if ((res = dmPaletteCopy(&dst->pal, npal)) != DMERR_OK)
+    {
+        res = dmError(DMERR_MALLOC,
+            "Could not allocate memory for final remapped palette.\n");
+        goto error;
+    }
 
+error:
     dmFree(mapping);
     dmFree(mapped);
     dmFree(used);
-    return DMERR_OK;
+    return res;
 }
 
 
@@ -1397,7 +1414,7 @@
     // Perform color remapping
     DMImage *image = pimage;
     BOOL allocated = FALSE;
-    if (optRemapColors)
+    if (optRemapColors && pimage->format == DM_COLFMT_PALETTE)
     {
         if ((res = dmRemapImageColors(&image, pimage)) != DMERR_OK)
             return res;
@@ -1411,7 +1428,7 @@
         spec->nplanes = 0;
         for (int n = 8; n >= 0;)
         {
-            if ((image->ncolors - 1) & (1 << n))
+            if ((image->pal->ncolors - 1) & (1 << n))
             {
                 spec->nplanes = n + 1;
                 break;
@@ -1787,7 +1804,11 @@
             outImage = dmImageAlloc(outWidthPX * outIWidth, outIHeight * outHeight, DM_COLFMT_PALETTE, -1);
         }
 
-        dmSetDefaultC64Palette(outImage);
+        if ((err = dmSetDefaultC64Palette(outImage)) != DMERR_OK)
+        {
+            dmErrorMsg("Could not allocate C64 palette for output image: %d\n", err);
+            goto error;
+        }
 
         while (offs + outSize < dataSize && (optItemCount < 0 || itemCount < optItemCount))
         {
@@ -1795,7 +1816,7 @@
                 dataBuf + offs, outWidth, outHeight, optInMulticolor, optColorMap)) != DMERR_OK)
             {
                 dmErrorMsg("Internal error in conversion of raw data to bitmap: %d.\n", err);
-                break;
+                goto error;
             }
 
             if (optSequential)
--- a/tools/lib64gfx.c	Thu Feb 28 12:32:07 2019 +0200
+++ b/tools/lib64gfx.c	Tue Mar 05 09:56:47 2019 +0200
@@ -108,19 +108,23 @@
 }
 
 
-void dmSetDefaultC64Palette(DMImage *img)
+int dmSetDefaultC64Palette(DMImage *img)
 {
-    img->constpal = TRUE;
-    img->pal      = dmDefaultC64Palette;
-    img->ncolors  = C64_NCOLORS;
-    img->ctransp  = 255;
+    int res;
+    if ((res = dmPaletteAlloc(&(img->pal), C64_NCOLORS, 255)) != DMERR_OK)
+        return res;
+
+    memcpy(img->pal->colors, dmDefaultC64Palette, img->pal->ncolors * sizeof(DMColor));
+
+    return DMERR_OK;
 }
 
 
-BOOL dmSetMixedColorC64Palette(DMImage *img)
+int dmSetMixedColorC64Palette(DMImage *img)
 {
-    if (!dmImagePaletteAlloc(img, C64_NCOLORS * C64_NCOLORS, -1))
-        return FALSE;
+    int res;
+    if ((res = dmPaletteAlloc(&(img->pal), C64_NCOLORS * C64_NCOLORS, -1)) != DMERR_OK)
+        return res;
 
     int n = 0;
     for (int n1 = 0; n1 < C64_NCOLORS; n1++)
@@ -129,14 +133,14 @@
         for (int n2 = 0; n2 < C64_NCOLORS; n2++)
         {
             const DMColor *col2 = &dmDefaultC64Palette[n2];
-            img->pal[n].r = (col1->r + col2->r) / 2;
-            img->pal[n].g = (col1->g + col2->g) / 2;
-            img->pal[n].b = (col1->b + col2->b) / 2;
+            img->pal->colors[n].r = (col1->r + col2->r) / 2;
+            img->pal->colors[n].g = (col1->g + col2->g) / 2;
+            img->pal->colors[n].b = (col1->b + col2->b) / 2;
             n++;
         }
     }
 
-    return TRUE;
+    return DMERR_OK;
 }
 
 
@@ -1401,9 +1405,8 @@
         return DMERR_MALLOC;
 
     // Set partial palette information
-    dst->ncolors  = C64_NCOLORS;
-    dst->constpal = TRUE;
-    dst->pal      = dmDefaultC64Palette;
+    if ((res = dmSetDefaultC64Palette(dst)) != DMERR_OK)
+        return res;
 
     // Convert
     if (fmt->format->convertFrom != NULL)
--- a/tools/lib64gfx.h	Thu Feb 28 12:32:07 2019 +0200
+++ b/tools/lib64gfx.h	Tue Mar 05 09:56:47 2019 +0200
@@ -306,8 +306,8 @@
 char *    dmC64GetImageTypeString(char *buf, const size_t len, const int type, const BOOL lng);
 void      dmC64ImageDump(FILE *fh, const DMC64Image *img, const DMC64ImageFormat *fmt, const char *indent);
 
-void      dmSetDefaultC64Palette(DMImage *img);
-BOOL      dmSetMixedColorC64Palette(DMImage *img);
+int       dmSetDefaultC64Palette(DMImage *img);
+int       dmSetMixedColorC64Palette(DMImage *img);
 
 BOOL      dmCompareAddr16(const DMGrowBuf *buf, const size_t offs, const Uint16 addr);
 int       dmC64ImageGetNumBanks(const DMC64ImageFormat *fmt);
--- 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))
                     {
--- a/tools/libgfx.h	Thu Feb 28 12:32:07 2019 +0200
+++ b/tools/libgfx.h	Tue Mar 05 09:56:47 2019 +0200
@@ -70,6 +70,14 @@
 };
 
 
+typedef struct
+{
+    int ncolors;    // number of colors in palette, if any
+    int ctransp;    // transparency color index, -1 if none
+    DMColor *colors;// colors
+} DMPalette;
+
+
 // Bitmapped image struct
 typedef struct
 {
@@ -80,10 +88,8 @@
 
     float aspect;   // aspect ratio (vert / horiz), <= 0 if not set
 
-    int ncolors;    // number of colors in palette, if any
-    int ctransp;    // transparency color index
     BOOL constpal;  // is the palette a const?
-    DMColor *pal;   // pointer to palette struct, NULL if no palette
+    DMPalette *pal; // pointer to palette struct, NULL if no palette
 
     size_t size;
     Uint8 *data;
@@ -124,9 +130,11 @@
 int       dmImageGetBitsPerPixel(const int format);
 int       dmImageProbeGeneric(const Uint8 *buf, const size_t len, const DMImageFormat **fmt, int *index);
 
-BOOL dmCompareColor(const DMColor *c1, const DMColor *c2, BOOL alpha);
-BOOL dmPaletteAlloc(DMColor **ppal, int ncolors, int ctransp);
-BOOL dmImagePaletteAlloc(DMImage *img, int ncolors, int ctransp);
+BOOL dmCompareColor(const DMColor *c1, const DMColor *c2, const BOOL alpha);
+int  dmPaletteAlloc(DMPalette **ppal, const int ncolors, const int ctransp);
+int  dmPaletteResize(DMPalette **ppal, const int ncolors);
+void dmPaletteFree(DMPalette *pal);
+int  dmPaletteCopy(DMPalette **pdst, const DMPalette *src);
 
 
 int dmWriteImageData(const DMImage *img, void *cbdata,