# HG changeset patch # User Matti Hamalainen # Date 1588094915 -10800 # Node ID ec036e88a0c2f8033c3aced0296629db6a78cf2c # Parent 1bd7387984ed405ba6fcdadd2bd87367ec513e31 More improvements to palette remapping etc. diff -r 1bd7387984ed -r ec036e88a0c2 tools/gfxconv.c --- a/tools/gfxconv.c Tue Apr 28 19:26:04 2020 +0300 +++ b/tools/gfxconv.c Tue Apr 28 20:28:35 2020 +0300 @@ -174,6 +174,7 @@ static const DMOptArg optList[] = { { 0, '?', "help" , "Show this help", OPT_NONE }, + { 3, 0, "longhelp" , "Show a longer help", OPT_NONE }, { 1, 0, "license" , "Print out this program's license agreement", OPT_NONE }, { 2, 'v', "verbose" , "Be more verbose", OPT_NONE }, @@ -183,18 +184,18 @@ { 16, 'f', "format" , "Set output format (see --formats)", OPT_ARGREQ }, { 18, 'F', "formats" , "List supported input/output formats", OPT_NONE }, { 20, 'q', "sequential" , "Output sequential files (image output only)", OPT_NONE }, - { 22, 'm', "colormap" , "Set color index mapping (see below for information)", OPT_ARGREQ }, + { 22, 'm', "colormap" , "Set color index mapping (see '-m help')", OPT_ARGREQ }, { 24, 'n', "numitems" , "How many 'items' to output (default: all)", OPT_ARGREQ }, { 26, 'w', "width" , "Item width (number of items per row, min 1)", OPT_ARGREQ }, - { 28, 'S', "scale" , "Scale output image by specified value(s) (see below)", OPT_ARGREQ }, + { 28, 'S', "scale" , "Scale output image by specified value(s) (see '-S help')", OPT_ARGREQ }, { 30, 'P', "paletted" , "Use indexed/paletted output IF possible.", OPT_NONE }, { 32, 'N', "nplanes" , "# of bitplanes (some output formats)", OPT_ARGREQ }, { 34, 'B', "bpp" , "Bits per plane (some output formats)", OPT_ARGREQ }, { 36, 'I', "interleave" , "Interleaved/planar output (some output formats)", OPT_NONE }, { 38, 'C', "compress" , "Use compression -C <0-9>, 0 = disable, default is 9", OPT_ARGREQ }, - { 42, 'R', "remap" , "Remap output image colors (-R <(#RRGGBB|index):index>[,<..>][+remove] | -R auto[+remove] | -R @map.txt[+remove])", OPT_ARGREQ }, + { 42, 'R', "remap" , "Remap output image colors (see '-R help')", OPT_ARGREQ }, { 44, 0, "char-rom" , "Set character ROM file to be used.", OPT_ARGREQ }, - { 46, 'p', "palette" , "Set palette to be used (see list with -p help). " + { 46, 'p', "palette" , "Set palette to be used (see '-p help'). " "For paletted image file input, this will replace the " "image's original palette. Color remapping will not be " "done unless -R option is also specified.", OPT_ARGREQ }, @@ -232,60 +233,110 @@ } -void argShowHelp() +void argShowHelp(void) { dmPrintBanner(stdout, dmProgName, "[options] []"); dmArgsPrintHelp(stdout, optList, optListN, 0, 80 - 2); fprintf(stdout, "\n" - "Output image scaling (-S)\n" - "-------------------------\n" - "Scaling option '-S ', '-S :', '-S :*' can be used to set\n" - "the direct or relative scale integer factor(s). '-S ' sets both height\n" - "and width scale factor to . '-S :*' scales width by X*n and\n" - "height Y*n. Certain input formats set their default aspect/scale factors.\n" - "By prepending -S parameters with asterisk ('*') you can scale relative to\n" - "those values. (e.g. '-S *2' for example.) NOTE! Only integer scale factors\n" - "can be used at the moment.\n" - "\n" - "Palette remapping (-R)\n" - "----------------------\n" - "Indexed palette color remapping can be performed via the -R option, either\n" - "specifying single colors or filename of file containing remap definitions.\n" - "Colors to be remapped can be specified either by their palette index or by\n" - "their RGB values as a hex triplet (#rrggbb). Example of a remap definition:\n" - "-R #000000:0,#ffffff:1 would remap black and white to indices 0 and 1.\n" - "\n" - "Remap file can be specified as \"-R @filename\", and it is a text file with\n" - "one remap definition per line in same format as above. All empty lines and\n" - "lines starting with a semicolor (;) will be ignored as comments. Any extra\n" - "whitespace separating items will be ignored as well.\n" - "\n" - "Optional +remove can be specified (-R <...>+remove), which will remove all\n" - "unused colors from the palette. This is not always desirable, for example\n" - "when converting multiple images to same palette. You can also specify the\n" - "+remove option by itself to remove all unused colors: -R +remove\n" - "\n" - "Color index mapping (-m)\n" - "------------------------\n" - "Color index map definitions are used for sprite/char data input (and ANSI\n" - "output), to set what colors of the C64 palette are used for each single\n" - "color/multi color bit-combination.\n" - "For example, if the input is multi color sprite or char, you can define\n" - "colors like: -m 0,8,3,15 .. for hires/single color: -m 0,1\n" - "The numbers are palette indexes, and the order is for bit(pair)-values\n" - "00, 01, 10, 11 (multi color) and 0, 1 (single color). NOTICE! 255 is the\n" - "special transparency color index; -m 255,2 would use transparency for\n" - "'0' bits and and C64 color 2 for '1' bits.\n" - "\n" - "Default character ROM file for this build is:\n" - "%s\n", + "Default C64 character ROM file for this build is:\n" + "%s\n" + "\n", DM_DEF_CHARGEN ); } +const char * argGetHelpTopic(const int opt) +{ + switch (opt) + { + case 28: + return + "Output image scaling (-S)\n" + "-------------------------\n" + "Scaling option '-S ', '-S :', '-S :*' can be used to set\n" + "the direct or relative scale integer factor(s). '-S ' sets both height\n" + "and width scale factor to . '-S :*' scales width by X*n and\n" + "height Y*n. Certain input formats set their default aspect/scale factors.\n" + "By prepending -S parameters with asterisk ('*') you can scale relative to\n" + "those values. (e.g. '-S *2' for example.) NOTE! Only integer scale factors\n" + "can be used at the moment.\n"; + + case 22: + return + "Color index mapping (-m)\n" + "------------------------\n" + "Color index map definitions are used for sprite/char data input (and ANSI\n" + "output), to set what colors of the C64 palette are used for each single\n" + "color/multi color bit-combination.\n" + "For example, if the input is multi color sprite or char, you can define\n" + "colors like: -m 0,8,3,15 .. for hires/single color: -m 0,1\n" + "The numbers are palette indexes, and the order is for bit(pair)-values\n" + "00, 01, 10, 11 (multi color) and 0, 1 (single color). NOTICE! 255 is the\n" + "special transparency color index; -m 255,2 would use transparency for\n" + "'0' bits and and C64 color 2 for '1' bits.\n"; + + case 42: + return + "Palette remapping (-R)\n" + "----------------------\n" + "Indexed palette color remapping can be performed via the '-R' option in\n" + "several different ways:\n" + "\n" + " 1) '-R auto'\n" + " will remap input image to a destination palette specified with the\n" + " '-p' option (which may be a supported palette file, another paletted\n" + " image file, or one of the internal gfxconv palettes, see '-p help')\n" + " Modifiers: +alpha, +remove, +max=, +nomatch=\n" + "\n" + " Example: '-R auto+remove -p pepto' would remap the input image to\n" + " the internal Pepto's C64 palette.\n" + "\n" + " 2) '-R <(#RRGGBBaa|index):index>[,<#RRGGBBaa|index>:index]'\n" + " can be used to specify single RGB(A) color quadruplets/triplets or\n" + " palette indices to be remapped to destination palette indices. Any\n" + " Unspecified indices will be automatically mapped. Specifying alpha\n" + " channel is optional, and will require +alpha flag to be enabled for\n" + " comparisions.\n" + " Modifiers: +alpha, +remove, +max=, +nomatch=\n" + "\n" + " Example: '-R #000000:0,#ffffff:1' would map black and white to indices 0 and 1.\n" + "\n" + " 3) '-R @'\n" + " can be used to specify a mapping file, which is a text file with one\n" + " remap definition per line in same format as above. All empty lines and\n" + " lines starting with a semicolor (;) will be ignored as comments. Also\n" + " any extra whitespace separating items will be ignored as well.\n" + " Modifiers: +remove\n" + "\n" + "Optional modifier flags can be specified as well:\n" + "\n" + " +remove Remove all unused colors from the resulting palette.\n" + "\n" + " +alpha Enable alpha value matching in color comparisions.\n" + " NOTE! This may result in unexpected behaviour.\n" + "\n" + " +max= Set the maximum color distance/delta acceptable for\n" + " matching colors. Default is -1, meaning closest possible\n" + " that can be found even if the match is poor. Any value \n" + " above 0 will be considered strict limit, see 'nomatch'\n" + " modifier below.\n" + "\n" + " +nomatch=\n" + " If no acceptable match is found (see +max modifier)\n" + " then use this () color index. This may have unexpected\n" + " results with +remove modifier. The default value of 'n'\n" + " is -1, which will result in error if no match is found.\n" + ""; + + default: + return NULL; + } +} + + // // Replace filename extension based on format pattern. // Usage: res = dm_strdup_fext(orig_filename, "foo_%s.cmp"); @@ -698,12 +749,6 @@ } -BOOL dmParseValTok(const int ch) -{ - return ch != '+'; -} - - BOOL argHandleOpt(const int optN, char *optArg, char *currArg) { unsigned int tmpUInt; @@ -716,6 +761,20 @@ exit(0); break; + case 3: + argShowHelp(); + argShowFormats(); + argShowC64PaletteHelp(stdout); + + for (int n = 0; n < optListN; n++) + { + const char *str = argGetHelpTopic(optList[n].id); + if (str != NULL) + fprintf(stdout, "\n%s\n", str); + } + exit(0); + break; + case 1: dmPrintLicense(stdout); exit(0); @@ -1199,17 +1258,6 @@ } -BOOL dmExactCompareColor(const DMColor *c1, const DMColor *c2, const BOOL alpha) -{ - if (c1->r == c2->r && - c1->g == c2->g && - c1->b == c2->b) - return alpha ? (c1->a == c2->a) : TRUE; - else - return FALSE; -} - - // 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) { @@ -1313,9 +1361,9 @@ int dmRemapImageColors(DMImage **pdst, const DMImage *src, - const DMPalette *dpal, const BOOL alpha, + const DMPalette *dpal, const float maxDist, const int noMatchColor, - const BOOL removeUnused) + const BOOL alpha, const BOOL removeUnused) { DMPalette *tpal = NULL; const DMPalette *ppal; @@ -1510,12 +1558,14 @@ int dmMapImageColors(DMImage **pdst, const DMImage *src, const DMMapValue *mapTable, const int nmapTable, - const BOOL removeUnused) + const float maxDist, const int noMatchColor, + const BOOL alpha, const BOOL removeUnused) { DMPalette *tpal = NULL; BOOL *mapped = NULL, *used = NULL; int *mapping = NULL; int nused, res = DMERR_OK; + BOOL fail = FALSE; if (pdst == NULL || src == NULL || mapTable == NULL) return DMERR_NULLPTR; @@ -1549,8 +1599,14 @@ goto out; } - dmMsg(1, "Remapping %d input image of %d colors.\n", - optNRemapTable, src->pal->ncolors); + dmMsg(1, "Remapping %d input image of %d colors, %s, ", + optNRemapTable, src->pal->ncolors, + 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); // Match and mark mapped colors for (int index = 0; index < nmapTable; index++) @@ -1558,27 +1614,58 @@ const DMMapValue *map = &mapTable[index]; if (map->triplet) { - BOOL found = FALSE; + float closestDist = 1000000, dist = 0; + int closestDC = -1; + for (int n = 0; n < src->pal->ncolors; n++) { - if (dmExactCompareColor(&src->pal->colors[n], &map->color, map->alpha)) + dist = dmGetColorDist(&src->pal->colors[n], &map->color, map->alpha && alpha); + if (dist < closestDist) { - dmMsg(3, "RGBA match #%02x%02x%02x%02x: %d -> %d\n", - map->color.r, map->color.g, map->color.b, map->color.a, - n, - map->to); - - mapping[n] = map->to; - mapped[map->to] = TRUE; - found = TRUE; + closestDist = dist; + closestDC = n; } } - if (!found) + // Did we find a close-enough match? + if (maxDist >= 0 && closestDist > maxDist) { - dmMsg(3, "No RGBA match found for map index %d, #%02x%02x%02x%02x\n", - index, - map->color.r, map->color.g, map->color.b, map->color.a); + // No, either error out or use noMatchColor color index + if (noMatchColor < 0) + { + DMColor *dcol = &src->pal->colors[closestDC]; + + dmMsg(3, "No RGBA match found for map index %d, #%02x%02x%02x%02x. Closest: #%d (#%02x%02x%02x%02x) [dist=%1.3f > %1.3f]\n", + index, map->color.r, map->color.g, map->color.b, map->color.a, + closestDC, dcol->r, dcol->g, dcol->b, dcol->a, closestDist, maxDist); + + fail = TRUE; + } + else + { + DMColor *dcol = &src->pal->colors[noMatchColor]; + closestDC = noMatchColor; + + dmMsg(3, "RGBA noMatch #%02x%02x%02x%02x: #%d -> #%d #%02x%02x%02x%02x [dist=%1.3f]\n", + map->color.r, map->color.g, map->color.b, map->color.a, + map->to, + closestDC, dcol->r, dcol->g, dcol->b, dcol->a, closestDist); + + mapping[closestDC] = map->to; + mapped[map->to] = TRUE; + } + } + else + { + DMColor *dcol = &src->pal->colors[closestDC]; + + dmMsg(3, "RGBA match #%02x%02x%02x%02x: #%d -> #%d #%02x%02x%02x%02x [dist=%1.3f]\n", + map->color.r, map->color.g, map->color.b, map->color.a, + map->to, + closestDC, dcol->r, dcol->g, dcol->b, dcol->a, closestDist); + + mapping[closestDC] = map->to; + mapped[map->to] = TRUE; } } else @@ -1591,6 +1678,12 @@ } } + if (fail) + { + res = DMERR_INVALID_DATA; + goto out; + } + // Fill the unmapped colors if (removeUnused) {