changeset 2487:14a4f8d78fe6

More work on image palette remapping functionality.
author Matti Hamalainen <ccr@tnsp.org>
date Tue, 28 Apr 2020 18:09:07 +0300
parents 6d12670e0248
children 87c8be2bc8ba
files tools/gfxconv.c
diffstat 1 files changed, 102 insertions(+), 55 deletions(-) [+]
line wrap: on
line diff
--- a/tools/gfxconv.c	Tue Apr 28 14:45:44 2020 +0300
+++ b/tools/gfxconv.c	Tue Apr 28 18:09:07 2020 +0300
@@ -147,8 +147,8 @@
 int     optRemapMode = REMAP_NONE;
 int     optNRemapTable = 0,
         optScaleMode = SCALE_AUTO;
-float   optRemapMaxDist = 0;
-int     optRemapFail = 0;
+float   optRemapMaxDist = -1;
+int     optRemapNoMatchColor = -1;
 DMMapValue optRemapTable[DM_MAX_COLORS];
 int     optColorMap[D64_NCOLORS];
 char    *optCharROMFilename = NULL;
@@ -1139,6 +1139,7 @@
 }
 
 
+// XXX TODO: we need to evaluate the color vector itself, not just the distance
 float dmGetColorDist(const DMColor *c1, const DMColor *c2, const BOOL alpha)
 {
     const float
@@ -1242,14 +1243,15 @@
 
 int dmRemapImageColors(DMImage **pdst, const DMImage *src,
     const DMPalette *dpal, const BOOL alpha,
-    const float maxDist, const int noMatch,
+    const float maxDist, const int noMatchColor,
     const BOOL removeUnused)
 {
     DMPalette *tpal = NULL;
     const DMPalette *ppal;
-    BOOL *mapped = NULL, *used = NULL;
-    int *mapping = NULL;
+    BOOL *used = NULL;
+    int *mapping = NULL, *mapped = NULL;
     int res = DMERR_OK;
+    BOOL fail = FALSE;
 
     if (pdst == NULL || src == NULL || dpal == NULL)
         return DMERR_NULLPTR;
@@ -1261,12 +1263,22 @@
         goto out;
     }
 
-    dmMsg(1, "Remapping image from %d to %d colors @ tolerance=%1.2f, %s%s.\n",
+    dmMsg(1, "Remapping image from %d to %d colors @ maxDist=",
         src->pal->ncolors,
-        dpal->ncolors,
-        maxDist < 0 ? 0 : maxDist,
-        alpha ? "match alpha" : "disregard alpha",
-        noMatch < 0 ? ", fail on 'no match'" : "");
+        dpal->ncolors);
+
+    if (maxDist < 0)
+        dmPrint(1, "auto");
+    else
+        dmPrint(1, "%1.3f", maxDist);
+
+    dmPrint(1, ", %s, ",
+        alpha ? "match alpha" : "ignore alpha");
+
+    if (noMatchColor < 0)
+        dmPrint(1, "fail on 'no match'\n");
+    else
+        dmPrint(1, "use color #%d if no match\n", noMatchColor);
 
     // Allocate remapping tables
     if ((mapped  = dmMalloc0(src->pal->ncolors * sizeof(*mapped))) == NULL ||
@@ -1286,66 +1298,87 @@
     {
         // Check if we can find a match in destination palette dpal
         float closestDist = 1000000, dist = 0;
+        int closestDC = -1;
 
         for (int dc = 0; dc < dpal->ncolors; dc++)
         {
-            if (maxDist < 0)
+            dist = dmGetColorDist(&src->pal->colors[sc], &dpal->colors[dc], alpha);
+            if (dist < closestDist)
             {
-                if (dmExactCompareColor(&src->pal->colors[sc], &dpal->colors[dc], alpha))
-                {
-                    mapping[sc] = dc;
-                    break;
-                }
+                closestDist = dist;
+                closestDC = dc;
+            }
+        }
+
+        // Did we find a close-enough match?
+        if (maxDist >= 0 && closestDist > maxDist)
+        {
+            // No, either error out or use noMatchColor color index
+            if (noMatchColor < 0)
+            {
+                DMColor *dcol = &dpal->colors[closestDC];
+
+                dmPrint(0,
+                    "No match for source color #%d. Closest: #%d (%02x %02x %02x) [dist=%1.3f > %1.3f]\n",
+                    sc, closestDC, dcol->r, dcol->g, dcol->b,
+                    closestDist, maxDist);
+                fail = TRUE;
             }
             else
             {
-                dist = dmGetColorDist(&src->pal->colors[sc], &dpal->colors[dc], alpha);
-                if (dist < maxDist && dist < closestDist)
-                {
-                    closestDist = dist;
-                    mapping[sc] = dc;
-                }
-            }
-        }
-
-        // Did we find a match?
-        if (mapping[sc] < 0)
-        {
-            // No, either error out or use noMatch color index
-            if (noMatch < 0)
-            {
-                res = dmError(DMERR_INVALID_DATA,
-                    "Could not remap source color #%d, no matches found.\n",
-                    sc);
-                goto out;
-            }
-            else
-            {
-                mapping[sc] = noMatch;
+                closestDC = noMatchColor;
             }
         }
         else
         {
-            DMColor *cs = &src->pal->colors[sc],
-                    *cd = &dpal->colors[mapping[sc]];
-
-            dmPrint(3, "Palette match #%d (%02x %02x %02x) -> #%d (%02x %02x %02x) [dist=%1.2f]\n",
-                sc, cs->r, cs->g, cs->b,
-                mapping[sc], cd->r, cd->g, cd->b,
+            DMColor *scol = &src->pal->colors[sc],
+                    *dcol = &dpal->colors[closestDC];
+
+            dmPrint(3, "Palette match #%d (%02x %02x %02x) -> #%d (%02x %02x %02x) [dist=%1.3f]\n",
+                sc, scol->r, scol->g, scol->b,
+                closestDC, dcol->r, dcol->g, dcol->b,
                 closestDist);
         }
+
+        mapping[sc] = closestDC;
+    }
+
+    if (fail)
+    {
+        res = DMERR_INVALID_DATA;
+        goto out;
     }
 
     // Remove unused colors if requested
     if (removeUnused)
     {
         // Get the actually used colors
-        int nused, nc;
+        int nused;
 
         if ((res = dmScanUsedColors(src, TRUE, used, &nused)) != DMERR_OK)
             goto out;
 
-        dmMsg(2, "Found %d used colors.\n", nused);
+        dmMsg(2, "Found %d used color indices.\n", nused);
+
+        // Remove duplicates from the mapped colour indices
+        for (int index = 0; index < src->pal->ncolors; index++)
+        {
+            for (int n = 0; n < src->pal->ncolors; n++)
+            if (n != index &&
+                mapping[index] == mapping[n] &&
+                used[n] && used[index])
+            {
+                used[n] = FALSE;
+            }
+        }
+
+        // Re-count number of actually used indices
+        nused = 0;
+        for (int index = 0; index < src->pal->ncolors; index++)
+        if (used[index])
+            nused++;
+
+        dmMsg(2, "After mapped dupe removal, %d color indices used.\n", nused);
 
         if ((res = dmPaletteAlloc(&tpal, nused, -1)) != DMERR_OK)
         {
@@ -1353,15 +1386,30 @@
             goto out;
         }
 
-        // Now, copy colors from dpal to tpal, re-mapping palette data
-        // and reorder the mapping indices to match it.
-        nc = 0;
+        // Copy colors from dpal to tpal, also mapping the reordered indices
+        nused = 0;
         for (int index = 0; index < src->pal->ncolors; index++)
         if (used[index])
         {
-            memcpy(&tpal->colors[nc], &dpal->colors[mapping[index]], sizeof(DMColor));
-            mapping[index] = nc;
-            nc++;
+            // Copy the color to tpal
+            memcpy(&tpal->colors[nused], &dpal->colors[mapping[index]], sizeof(DMColor));
+
+            // Save current mapping to mapped[]
+            mapped[nused] = mapping[index];
+
+            // Reorder the mapping
+            mapping[index] = nused;
+            nused++;
+        }
+        else
+        {
+            // "Unused" color, find matching mapping from mapped[]
+            for (int n = 0; n < nused; n++)
+            if (mapping[index] == mapped[n])
+            {
+                mapping[index] = n;
+                break;
+            }
         }
 
         ppal = tpal;
@@ -1811,8 +1859,7 @@
 
                 if ((res = dmRemapImageColors(&image, pimage,
                     optPaletteData, optRemapMatchAlpha,
-                    //optRemapMaxDist, optRemapFail,
-                    0.5, -1,
+                    optRemapMaxDist, optRemapNoMatchColor,
                     optRemapRemove)) != DMERR_OK)
                     goto out;