Mercurial > hg > dmlib
comparison tools/gfxconv.c @ 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 | fce2ed3f681f |
children | 87c8be2bc8ba |
comparison
equal
deleted
inserted
replaced
2486:6d12670e0248 | 2487:14a4f8d78fe6 |
---|---|
145 optRemapMatchAlpha = FALSE, | 145 optRemapMatchAlpha = FALSE, |
146 optUsePalette = FALSE; | 146 optUsePalette = FALSE; |
147 int optRemapMode = REMAP_NONE; | 147 int optRemapMode = REMAP_NONE; |
148 int optNRemapTable = 0, | 148 int optNRemapTable = 0, |
149 optScaleMode = SCALE_AUTO; | 149 optScaleMode = SCALE_AUTO; |
150 float optRemapMaxDist = 0; | 150 float optRemapMaxDist = -1; |
151 int optRemapFail = 0; | 151 int optRemapNoMatchColor = -1; |
152 DMMapValue optRemapTable[DM_MAX_COLORS]; | 152 DMMapValue optRemapTable[DM_MAX_COLORS]; |
153 int optColorMap[D64_NCOLORS]; | 153 int optColorMap[D64_NCOLORS]; |
154 char *optCharROMFilename = NULL; | 154 char *optCharROMFilename = NULL; |
155 DMC64Palette *optC64Palette = NULL; | 155 DMC64Palette *optC64Palette = NULL; |
156 char *optPaletteFile = NULL; | 156 char *optPaletteFile = NULL; |
1137 else | 1137 else |
1138 return FALSE; | 1138 return FALSE; |
1139 } | 1139 } |
1140 | 1140 |
1141 | 1141 |
1142 // XXX TODO: we need to evaluate the color vector itself, not just the distance | |
1142 float dmGetColorDist(const DMColor *c1, const DMColor *c2, const BOOL alpha) | 1143 float dmGetColorDist(const DMColor *c1, const DMColor *c2, const BOOL alpha) |
1143 { | 1144 { |
1144 const float | 1145 const float |
1145 dr = (c1->r - c2->r) / 255.0, | 1146 dr = (c1->r - c2->r) / 255.0, |
1146 dg = (c1->g - c2->g) / 255.0, | 1147 dg = (c1->g - c2->g) / 255.0, |
1240 } | 1241 } |
1241 | 1242 |
1242 | 1243 |
1243 int dmRemapImageColors(DMImage **pdst, const DMImage *src, | 1244 int dmRemapImageColors(DMImage **pdst, const DMImage *src, |
1244 const DMPalette *dpal, const BOOL alpha, | 1245 const DMPalette *dpal, const BOOL alpha, |
1245 const float maxDist, const int noMatch, | 1246 const float maxDist, const int noMatchColor, |
1246 const BOOL removeUnused) | 1247 const BOOL removeUnused) |
1247 { | 1248 { |
1248 DMPalette *tpal = NULL; | 1249 DMPalette *tpal = NULL; |
1249 const DMPalette *ppal; | 1250 const DMPalette *ppal; |
1250 BOOL *mapped = NULL, *used = NULL; | 1251 BOOL *used = NULL; |
1251 int *mapping = NULL; | 1252 int *mapping = NULL, *mapped = NULL; |
1252 int res = DMERR_OK; | 1253 int res = DMERR_OK; |
1254 BOOL fail = FALSE; | |
1253 | 1255 |
1254 if (pdst == NULL || src == NULL || dpal == NULL) | 1256 if (pdst == NULL || src == NULL || dpal == NULL) |
1255 return DMERR_NULLPTR; | 1257 return DMERR_NULLPTR; |
1256 | 1258 |
1257 if (src->pal == NULL || src->pixfmt != DM_PIXFMT_PALETTE) | 1259 if (src->pal == NULL || src->pixfmt != DM_PIXFMT_PALETTE) |
1259 res = dmError(DMERR_INVALID_DATA, | 1261 res = dmError(DMERR_INVALID_DATA, |
1260 "Source image is not paletted.\n"); | 1262 "Source image is not paletted.\n"); |
1261 goto out; | 1263 goto out; |
1262 } | 1264 } |
1263 | 1265 |
1264 dmMsg(1, "Remapping image from %d to %d colors @ tolerance=%1.2f, %s%s.\n", | 1266 dmMsg(1, "Remapping image from %d to %d colors @ maxDist=", |
1265 src->pal->ncolors, | 1267 src->pal->ncolors, |
1266 dpal->ncolors, | 1268 dpal->ncolors); |
1267 maxDist < 0 ? 0 : maxDist, | 1269 |
1268 alpha ? "match alpha" : "disregard alpha", | 1270 if (maxDist < 0) |
1269 noMatch < 0 ? ", fail on 'no match'" : ""); | 1271 dmPrint(1, "auto"); |
1272 else | |
1273 dmPrint(1, "%1.3f", maxDist); | |
1274 | |
1275 dmPrint(1, ", %s, ", | |
1276 alpha ? "match alpha" : "ignore alpha"); | |
1277 | |
1278 if (noMatchColor < 0) | |
1279 dmPrint(1, "fail on 'no match'\n"); | |
1280 else | |
1281 dmPrint(1, "use color #%d if no match\n", noMatchColor); | |
1270 | 1282 |
1271 // Allocate remapping tables | 1283 // Allocate remapping tables |
1272 if ((mapped = dmMalloc0(src->pal->ncolors * sizeof(*mapped))) == NULL || | 1284 if ((mapped = dmMalloc0(src->pal->ncolors * sizeof(*mapped))) == NULL || |
1273 (used = dmMalloc0(src->pal->ncolors * sizeof(*used))) == NULL || | 1285 (used = dmMalloc0(src->pal->ncolors * sizeof(*used))) == NULL || |
1274 (mapping = dmMalloc(src->pal->ncolors * sizeof(*mapping))) == NULL) | 1286 (mapping = dmMalloc(src->pal->ncolors * sizeof(*mapping))) == NULL) |
1284 // Populate remap table | 1296 // Populate remap table |
1285 for (int sc = 0; sc < src->pal->ncolors; sc++) | 1297 for (int sc = 0; sc < src->pal->ncolors; sc++) |
1286 { | 1298 { |
1287 // Check if we can find a match in destination palette dpal | 1299 // Check if we can find a match in destination palette dpal |
1288 float closestDist = 1000000, dist = 0; | 1300 float closestDist = 1000000, dist = 0; |
1301 int closestDC = -1; | |
1289 | 1302 |
1290 for (int dc = 0; dc < dpal->ncolors; dc++) | 1303 for (int dc = 0; dc < dpal->ncolors; dc++) |
1291 { | 1304 { |
1292 if (maxDist < 0) | 1305 dist = dmGetColorDist(&src->pal->colors[sc], &dpal->colors[dc], alpha); |
1293 { | 1306 if (dist < closestDist) |
1294 if (dmExactCompareColor(&src->pal->colors[sc], &dpal->colors[dc], alpha)) | 1307 { |
1295 { | 1308 closestDist = dist; |
1296 mapping[sc] = dc; | 1309 closestDC = dc; |
1297 break; | 1310 } |
1298 } | 1311 } |
1312 | |
1313 // Did we find a close-enough match? | |
1314 if (maxDist >= 0 && closestDist > maxDist) | |
1315 { | |
1316 // No, either error out or use noMatchColor color index | |
1317 if (noMatchColor < 0) | |
1318 { | |
1319 DMColor *dcol = &dpal->colors[closestDC]; | |
1320 | |
1321 dmPrint(0, | |
1322 "No match for source color #%d. Closest: #%d (%02x %02x %02x) [dist=%1.3f > %1.3f]\n", | |
1323 sc, closestDC, dcol->r, dcol->g, dcol->b, | |
1324 closestDist, maxDist); | |
1325 fail = TRUE; | |
1299 } | 1326 } |
1300 else | 1327 else |
1301 { | 1328 { |
1302 dist = dmGetColorDist(&src->pal->colors[sc], &dpal->colors[dc], alpha); | 1329 closestDC = noMatchColor; |
1303 if (dist < maxDist && dist < closestDist) | |
1304 { | |
1305 closestDist = dist; | |
1306 mapping[sc] = dc; | |
1307 } | |
1308 } | |
1309 } | |
1310 | |
1311 // Did we find a match? | |
1312 if (mapping[sc] < 0) | |
1313 { | |
1314 // No, either error out or use noMatch color index | |
1315 if (noMatch < 0) | |
1316 { | |
1317 res = dmError(DMERR_INVALID_DATA, | |
1318 "Could not remap source color #%d, no matches found.\n", | |
1319 sc); | |
1320 goto out; | |
1321 } | |
1322 else | |
1323 { | |
1324 mapping[sc] = noMatch; | |
1325 } | 1330 } |
1326 } | 1331 } |
1327 else | 1332 else |
1328 { | 1333 { |
1329 DMColor *cs = &src->pal->colors[sc], | 1334 DMColor *scol = &src->pal->colors[sc], |
1330 *cd = &dpal->colors[mapping[sc]]; | 1335 *dcol = &dpal->colors[closestDC]; |
1331 | 1336 |
1332 dmPrint(3, "Palette match #%d (%02x %02x %02x) -> #%d (%02x %02x %02x) [dist=%1.2f]\n", | 1337 dmPrint(3, "Palette match #%d (%02x %02x %02x) -> #%d (%02x %02x %02x) [dist=%1.3f]\n", |
1333 sc, cs->r, cs->g, cs->b, | 1338 sc, scol->r, scol->g, scol->b, |
1334 mapping[sc], cd->r, cd->g, cd->b, | 1339 closestDC, dcol->r, dcol->g, dcol->b, |
1335 closestDist); | 1340 closestDist); |
1336 } | 1341 } |
1342 | |
1343 mapping[sc] = closestDC; | |
1344 } | |
1345 | |
1346 if (fail) | |
1347 { | |
1348 res = DMERR_INVALID_DATA; | |
1349 goto out; | |
1337 } | 1350 } |
1338 | 1351 |
1339 // Remove unused colors if requested | 1352 // Remove unused colors if requested |
1340 if (removeUnused) | 1353 if (removeUnused) |
1341 { | 1354 { |
1342 // Get the actually used colors | 1355 // Get the actually used colors |
1343 int nused, nc; | 1356 int nused; |
1344 | 1357 |
1345 if ((res = dmScanUsedColors(src, TRUE, used, &nused)) != DMERR_OK) | 1358 if ((res = dmScanUsedColors(src, TRUE, used, &nused)) != DMERR_OK) |
1346 goto out; | 1359 goto out; |
1347 | 1360 |
1348 dmMsg(2, "Found %d used colors.\n", nused); | 1361 dmMsg(2, "Found %d used color indices.\n", nused); |
1362 | |
1363 // Remove duplicates from the mapped colour indices | |
1364 for (int index = 0; index < src->pal->ncolors; index++) | |
1365 { | |
1366 for (int n = 0; n < src->pal->ncolors; n++) | |
1367 if (n != index && | |
1368 mapping[index] == mapping[n] && | |
1369 used[n] && used[index]) | |
1370 { | |
1371 used[n] = FALSE; | |
1372 } | |
1373 } | |
1374 | |
1375 // Re-count number of actually used indices | |
1376 nused = 0; | |
1377 for (int index = 0; index < src->pal->ncolors; index++) | |
1378 if (used[index]) | |
1379 nused++; | |
1380 | |
1381 dmMsg(2, "After mapped dupe removal, %d color indices used.\n", nused); | |
1349 | 1382 |
1350 if ((res = dmPaletteAlloc(&tpal, nused, -1)) != DMERR_OK) | 1383 if ((res = dmPaletteAlloc(&tpal, nused, -1)) != DMERR_OK) |
1351 { | 1384 { |
1352 dmErrorMsg("Could not allocate memory for remap palette.\n"); | 1385 dmErrorMsg("Could not allocate memory for remap palette.\n"); |
1353 goto out; | 1386 goto out; |
1354 } | 1387 } |
1355 | 1388 |
1356 // Now, copy colors from dpal to tpal, re-mapping palette data | 1389 // Copy colors from dpal to tpal, also mapping the reordered indices |
1357 // and reorder the mapping indices to match it. | 1390 nused = 0; |
1358 nc = 0; | |
1359 for (int index = 0; index < src->pal->ncolors; index++) | 1391 for (int index = 0; index < src->pal->ncolors; index++) |
1360 if (used[index]) | 1392 if (used[index]) |
1361 { | 1393 { |
1362 memcpy(&tpal->colors[nc], &dpal->colors[mapping[index]], sizeof(DMColor)); | 1394 // Copy the color to tpal |
1363 mapping[index] = nc; | 1395 memcpy(&tpal->colors[nused], &dpal->colors[mapping[index]], sizeof(DMColor)); |
1364 nc++; | 1396 |
1397 // Save current mapping to mapped[] | |
1398 mapped[nused] = mapping[index]; | |
1399 | |
1400 // Reorder the mapping | |
1401 mapping[index] = nused; | |
1402 nused++; | |
1403 } | |
1404 else | |
1405 { | |
1406 // "Unused" color, find matching mapping from mapped[] | |
1407 for (int n = 0; n < nused; n++) | |
1408 if (mapping[index] == mapped[n]) | |
1409 { | |
1410 mapping[index] = n; | |
1411 break; | |
1412 } | |
1365 } | 1413 } |
1366 | 1414 |
1367 ppal = tpal; | 1415 ppal = tpal; |
1368 } | 1416 } |
1369 else | 1417 else |
1809 goto out; | 1857 goto out; |
1810 } | 1858 } |
1811 | 1859 |
1812 if ((res = dmRemapImageColors(&image, pimage, | 1860 if ((res = dmRemapImageColors(&image, pimage, |
1813 optPaletteData, optRemapMatchAlpha, | 1861 optPaletteData, optRemapMatchAlpha, |
1814 //optRemapMaxDist, optRemapFail, | 1862 optRemapMaxDist, optRemapNoMatchColor, |
1815 0.5, -1, | |
1816 optRemapRemove)) != DMERR_OK) | 1863 optRemapRemove)) != DMERR_OK) |
1817 goto out; | 1864 goto out; |
1818 | 1865 |
1819 allocated = TRUE; | 1866 allocated = TRUE; |
1820 break; | 1867 break; |