Allow mirroring only the X or Y axes (#1468)

This commit is contained in:
Sylvie
2024-08-18 21:47:39 -04:00
committed by GitHub
parent b7290366cb
commit 356367bfd3
5 changed files with 51 additions and 23 deletions

View File

@@ -13,6 +13,8 @@ _rgbgfx_completions() {
[O]="group-outputs:normal" [O]="group-outputs:normal"
[u]="unique-tiles:normal" [u]="unique-tiles:normal"
[v]="verbose:normal" [v]="verbose:normal"
[X]="mirror-x:normal"
[Y]="mirror-y:normal"
[Z]="columns:normal" [Z]="columns:normal"
[a]="attr-map:glob-*.attrmap" [a]="attr-map:glob-*.attrmap"
[A]="auto-attr-map:normal" [A]="auto-attr-map:normal"

View File

@@ -22,6 +22,8 @@ local args=(
'(-t --tilemap -T --auto-tilemap)'{-T,--auto-tilemap}'[Shortcut for -t <file>.tilemap]' '(-t --tilemap -T --auto-tilemap)'{-T,--auto-tilemap}'[Shortcut for -t <file>.tilemap]'
'(-u --unique-tiles)'{-u,--unique-tiles}'[Eliminate redundant tiles]' '(-u --unique-tiles)'{-u,--unique-tiles}'[Eliminate redundant tiles]'
{-v,--verbose}'[Enable verbose output]' {-v,--verbose}'[Enable verbose output]'
'(-X --mirror-x)'{-X,--mirror-x}'[Eliminate horizontally mirrored tiles from output]'
'(-Y --mirror-y)'{-Y,--mirror-y}'[Eliminate vertically mirrored tiles from output]'
'(-Z --columns)'{-Z,--columns}'[Read the image in column-major order]' '(-Z --columns)'{-Z,--columns}'[Read the image in column-major order]'
'(-a --attr-map -A --auto-attr-map)'{-a,--attr-map}'+[Generate a map of tile attributes (mirroring)]:attrmap file:_files' '(-a --attr-map -A --auto-attr-map)'{-a,--attr-map}'+[Generate a map of tile attributes (mirroring)]:attrmap file:_files'

View File

@@ -14,9 +14,10 @@
struct Options { struct Options {
bool useColorCurve = false; // -C bool useColorCurve = false; // -C
bool allowMirroring = false; // -m
bool allowDedup = false; // -u bool allowDedup = false; // -u
bool columnMajor = false; // -Z, previously -h bool allowMirroringX = false; // -X, -m
bool allowMirroringY = false; // -Y, -m
bool columnMajor = false; // -Z
uint8_t verbosity = 0; // -v uint8_t verbosity = 0; // -v
std::string attrmap{}; // -a, -A std::string attrmap{}; // -a, -A

View File

@@ -144,14 +144,16 @@ static option const longopts[] = {
{"unique-tiles", no_argument, nullptr, 'u'}, {"unique-tiles", no_argument, nullptr, 'u'},
{"version", no_argument, nullptr, 'V'}, {"version", no_argument, nullptr, 'V'},
{"verbose", no_argument, nullptr, 'v'}, {"verbose", no_argument, nullptr, 'v'},
{"mirror-x", no_argument, nullptr, 'X'},
{"trim-end", required_argument, nullptr, 'x'}, {"trim-end", required_argument, nullptr, 'x'},
{"mirror-y", no_argument, nullptr, 'Y'},
{"columns", no_argument, nullptr, 'Z'}, {"columns", no_argument, nullptr, 'Z'},
{nullptr, no_argument, nullptr, 0 } {nullptr, no_argument, nullptr, 0 }
}; };
static void printUsage() { static void printUsage() {
fputs( fputs(
"Usage: rgbgfx [-r stride] [-CmOuVZ] [-v [-v ...]] [-a <attr_map> | -A]\n" "Usage: rgbgfx [-r stride] [-CmOuVXYZ] [-v [-v ...]] [-a <attr_map> | -A]\n"
" [-b <base_ids>] [-c <colors>] [-d <depth>] [-L <slice>] [-N <nb_tiles>]\n" " [-b <base_ids>] [-c <colors>] [-d <depth>] [-L <slice>] [-N <nb_tiles>]\n"
" [-n <nb_pals>] [-o <out_file>] [-p <pal_file> | -P] [-q <pal_map> | -Q]\n" " [-n <nb_pals>] [-o <out_file>] [-p <pal_file> | -P] [-q <pal_map> | -Q]\n"
" [-s <nb_colors>] [-t <tile_map> | -T] [-x <nb_tiles>] <file>\n" " [-s <nb_colors>] [-t <tile_map> | -T] [-x <nb_tiles>] <file>\n"
@@ -466,7 +468,8 @@ static char *parseArgv(int argc, char *argv[]) {
} }
break; break;
case 'm': case 'm':
options.allowMirroring = true; options.allowMirroringX = true; // Imply `-X`
options.allowMirroringY = true; // Imply `-Y`
[[fallthrough]]; // Imply `-u` [[fallthrough]]; // Imply `-u`
case 'u': case 'u':
options.allowDedup = true; options.allowDedup = true;
@@ -582,6 +585,14 @@ static char *parseArgv(int argc, char *argv[]) {
error("Tile trim (-x) argument must be a valid number, not \"%s\"", musl_optarg); error("Tile trim (-x) argument must be a valid number, not \"%s\"", musl_optarg);
} }
break; break;
case 'X':
options.allowMirroringX = true;
options.allowDedup = true; // Imply `-u`
break;
case 'Y':
options.allowMirroringY = true;
options.allowDedup = true; // Imply `-u`
break;
case 'Z': case 'Z':
options.columnMajor = true; options.columnMajor = true;
break; break;
@@ -757,10 +768,12 @@ int main(int argc, char *argv[]) {
fputs("Options:\n", stderr); fputs("Options:\n", stderr);
if (options.columnMajor) if (options.columnMajor)
fputs("\tVisit image in column-major order\n", stderr); fputs("\tVisit image in column-major order\n", stderr);
if (options.allowMirroring)
fputs("\tAllow mirroring tiles\n", stderr);
if (options.allowDedup) if (options.allowDedup)
fputs("\tAllow deduplicating tiles\n", stderr); fputs("\tAllow deduplicating tiles\n", stderr);
if (options.allowMirroringX)
fputs("\tAllow deduplicating horizontally mirrored tiles\n", stderr);
if (options.allowMirroringY)
fputs("\tAllow deduplicating vertically mirrored tiles\n", stderr);
if (options.useColorCurve) if (options.useColorCurve)
fputs("\tUse color curve\n", stderr); fputs("\tUse color curve\n", stderr);
fprintf(stderr, "\tBit depth: %" PRIu8 "bpp\n", options.bitDepth); fprintf(stderr, "\tBit depth: %" PRIu8 "bpp\n", options.bitDepth);

View File

@@ -747,10 +747,10 @@ public:
// Update the hash // Update the hash
_hash ^= bitplanes; _hash ^= bitplanes;
if (options.allowMirroring) { if (options.allowMirroringX) {
// Count the line itself as mirrorred; vertical mirroring is // Count the line itself as mirrorred horizontally; vertical mirroring is already
// already taken care of because the symmetric line will be XOR'd // taken care of because the symmetric line will be XOR'd the same way.
// the same way. (...which is a problem, but probably benign.) // (...this reduces the hash's efficiency, but seems benign with most real-world data.)
_hash ^= flipTable[bitplanes >> 8] << 8 | flipTable[bitplanes & 0xFF]; _hash ^= flipTable[bitplanes >> 8] << 8 | flipTable[bitplanes & 0xFF];
} }
} }
@@ -773,17 +773,19 @@ public:
return MatchType::EXACT; return MatchType::EXACT;
} }
if (!options.allowMirroring) {
return MatchType::NOPE;
}
// Check if we have horizontal mirroring, which scans the array forward again // Check if we have horizontal mirroring, which scans the array forward again
if (std::equal(RANGE(_data), other._data.begin(), [](uint8_t lhs, uint8_t rhs) { if (options.allowMirroringX
&& std::equal(RANGE(_data), other._data.begin(), [](uint8_t lhs, uint8_t rhs) {
return lhs == flipTable[rhs]; return lhs == flipTable[rhs];
})) { })) {
return MatchType::HFLIP; return MatchType::HFLIP;
} }
// The remaining possibilities for matching all require vertical mirroring
if (!options.allowMirroringY) {
return MatchType::NOPE;
}
// Check if we have vertical or vertical+horizontal mirroring, for which we have to read // Check if we have vertical or vertical+horizontal mirroring, for which we have to read
// bitplane *pairs* backwards // bitplane *pairs* backwards
bool hasVFlip = true, hasVHFlip = true; bool hasVFlip = true, hasVHFlip = true;
@@ -803,8 +805,16 @@ public:
} }
// If we have both (i.e. we have symmetry), default to vflip only // If we have both (i.e. we have symmetry), default to vflip only
assume(hasVFlip || hasVHFlip); if (hasVFlip) {
return hasVFlip ? MatchType::VFLIP : MatchType::VHFLIP; return MatchType::VFLIP;
}
// If we allow both and have both, then use both
if (options.allowMirroringX && hasVHFlip) {
return MatchType::VHFLIP;
}
return MatchType::NOPE;
} }
friend bool operator==(TileData const &lhs, TileData const &rhs) { friend bool operator==(TileData const &lhs, TileData const &rhs) {
return lhs.tryMatching(rhs) != MatchType::NOPE; return lhs.tryMatching(rhs) != MatchType::NOPE;