ref: 05e36767b0d3dcf64e9a37bbd0a2cc93771dfbfd
parent: 531092f5bdc790c52827dce30dfc29c073ccaa15
author: ISSOtm <[email protected]>
date: Fri May 20 05:30:35 EDT 2022
Implement "palette map" output
--- a/include/gfx/main.hpp
+++ b/include/gfx/main.hpp
@@ -43,6 +43,7 @@
uint8_t nbPalettes = 8; // -n
std::string output{}; // -o
std::string palettes{}; // -p, -P
+ std::string palmap{}; // -q, -Q
uint8_t nbColorsPerPal = 0; // -s; 0 means "auto" = 1 << bitDepth;
std::string tilemap{}; // -t, -T
std::array<uint16_t, 2> unitSize{1, 1}; // -U (in tiles)
--- a/man/rgbgfx.1
+++ b/man/rgbgfx.1
@@ -26,6 +26,7 @@
.Op Fl n Ar nb_pals
.Op Fl o Ar out_file
.Op Fl p Ar pal_file | Fl P
+.Op Fl q Ar pal_map | Fl Q
.Op Fl s Ar nb_colors
.Op Fl t Ar tilemap | Fl T
.Op Fl U Ar unit_size
@@ -72,8 +73,6 @@
.Ql 0X2A ,
.Ql 0x2a .
.Pp
-TODO: add "palette map" output.
-.Pp
The following options are accepted:
.Bl -tag -width Ds
.It Fl a Ar attrmap , Fl Fl attr-map Ar attrmap
@@ -182,7 +181,10 @@
Abort if more than
.Ar nb_pals
palettes are generated.
-Note that attribute map output only has 3 bits for the palette ID, so a limit higher than 8 may yield incomplete data.
+This may not be more than 256.
+.Pp
+Note that attribute map output only has 3 bits for the palette ID, so a limit higher than 8 may yield incomplete data unless relying on a palette map
+.Pq see Fl q .
.It Fl o Ar out_file , Fl Fl output Ar out_file
Output the tile data in native 2bpp format or in 1bpp
.Pq depending on Fl d
@@ -196,6 +198,16 @@
.Ar path
is the input image's path with the extension set to
.Pa .pal .
+.It Fl q Ar pal_file , Fl Fl palette-map Ar pal_file
+Output the image's palette map to this file.
+This is useful if the input image contains more than 8 palettes, as the attribute map only contains the lower 3 bits of the palette indices.
+.It Fl Q , Fl Fl output-palette-map
+Same as
+.Fl q Ar path ,
+where
+.Ar path
+is the input image's path with the extension set to
+.Pa .palmap .
.It Fl s Ar nb_colors , Fl Fl palette-size Ar nb_colors
Specify how many colors each palette contains, including the transparent one if any.
.Ar nb_colors
--- a/src/gfx/main.cpp
+++ b/src/gfx/main.cpp
@@ -87,7 +87,7 @@
}
// Short options
-static char const *optstring = "-Aa:b:Cc:Dd:FfhL:mN:n:o:Pp:r:s:Tt:U:uVvx:Z";
+static char const *optstring = "-Aa:b:Cc:Dd:FfhL:mN:n:o:Pp:Qq:r:s:Tt:U:uVvx:Z";
/*
* Equivalent long options
@@ -100,40 +100,42 @@
* over short opt matching
*/
static struct option const longopts[] = {
- {"output-attr-map", no_argument, NULL, 'A'},
- {"attr-map", required_argument, NULL, 'a'},
- {"base-tiles", required_argument, NULL, 'b'},
- {"color-curve", no_argument, NULL, 'C'},
- {"colors", required_argument, NULL, 'c'},
- {"debug", no_argument, NULL, 'D'}, // Ignored
- {"depth", required_argument, NULL, 'd'},
- {"fix", no_argument, NULL, 'f'},
- {"fix-and-save", no_argument, NULL, 'F'}, // Deprecated
- {"horizontal", no_argument, NULL, 'h'}, // Deprecated
- {"slice", required_argument, NULL, 'L'},
- {"mirror-tiles", no_argument, NULL, 'm'},
- {"nb-tiles", required_argument, NULL, 'N'},
- {"nb-palettes", required_argument, NULL, 'n'},
- {"output", required_argument, NULL, 'o'},
- {"output-palette", no_argument, NULL, 'P'},
- {"palette", required_argument, NULL, 'p'},
- {"reverse", required_argument, NULL, 'r'},
- {"output-tilemap", no_argument, NULL, 'T'},
- {"tilemap", required_argument, NULL, 't'},
- {"unit-size", required_argument, NULL, 'U'},
- {"unique-tiles", no_argument, NULL, 'u'},
- {"version", no_argument, NULL, 'V'},
- {"verbose", no_argument, NULL, 'v'},
- {"trim-end", required_argument, NULL, 'x'},
- {"columns", no_argument, NULL, 'Z'},
- {NULL, no_argument, NULL, 0 }
+ {"output-attr-map", no_argument, NULL, 'A'},
+ {"attr-map", required_argument, NULL, 'a'},
+ {"base-tiles", required_argument, NULL, 'b'},
+ {"color-curve", no_argument, NULL, 'C'},
+ {"colors", required_argument, NULL, 'c'},
+ {"debug", no_argument, NULL, 'D'}, // Ignored
+ {"depth", required_argument, NULL, 'd'},
+ {"fix", no_argument, NULL, 'f'},
+ {"fix-and-save", no_argument, NULL, 'F'}, // Deprecated
+ {"horizontal", no_argument, NULL, 'h'}, // Deprecated
+ {"slice", required_argument, NULL, 'L'},
+ {"mirror-tiles", no_argument, NULL, 'm'},
+ {"nb-tiles", required_argument, NULL, 'N'},
+ {"nb-palettes", required_argument, NULL, 'n'},
+ {"output", required_argument, NULL, 'o'},
+ {"output-palette", no_argument, NULL, 'P'},
+ {"palette", required_argument, NULL, 'p'},
+ {"output-palette-map", no_argument, NULL, 'Q'},
+ {"palette-map", required_argument, NULL, 'q'},
+ {"reverse", required_argument, NULL, 'r'},
+ {"output-tilemap", no_argument, NULL, 'T'},
+ {"tilemap", required_argument, NULL, 't'},
+ {"unit-size", required_argument, NULL, 'U'},
+ {"unique-tiles", no_argument, NULL, 'u'},
+ {"version", no_argument, NULL, 'V'},
+ {"verbose", no_argument, NULL, 'v'},
+ {"trim-end", required_argument, NULL, 'x'},
+ {"columns", no_argument, NULL, 'Z'},
+ {NULL, no_argument, NULL, 0 }
};
static void printUsage(void) {
fputs("Usage: rgbgfx [-r stride] [-CmuVZ] [-v [-v ...]] [-a <attr_map> | -A]\n"
" [-b base_ids] [-c color_spec] [-d <depth>] [-L slice] [-N nb_tiles]\n"
- " [-n nb_pals] [-o <out_file>] [-p <pal_file> | -P] [-s nb_colors]\n"
- " [-t <tile_map> | -T] [-U unit_size] [-x <tiles>] <file>\n"
+ " [-n nb_pals] [-o <out_file>] [-p <pal_file> | -P] [-q <pal_mal> | -Q ]\n"
+ " [-s nb_colors] [-t <tile_map> | -T] [-U unit_size] [-x <tiles>] <file>\n"
"Useful options:\n"
" -m, --mirror-tiles optimize out mirrored tiles\n"
" -o, --output <path> set the output binary file\n"
@@ -319,7 +321,7 @@
* "at-file" path if one is encountered.
*/
static char *parseArgv(int argc, char **argv, bool &autoAttrmap, bool &autoTilemap,
- bool &autoPalettes) {
+ bool &autoPalettes, bool &autoPalmap) {
int opt;
while ((opt = musl_getopt_long_only(argc, argv, optstring, longopts, nullptr)) != -1) {
@@ -422,12 +424,12 @@
}
break;
case 'n':
- options.nbPalettes = parseNumber(arg, "Number of palettes", 8);
+ options.nbPalettes = parseNumber(arg, "Number of palettes", 256);
if (*arg != '\0') {
error("Number of palettes (-n) must be a valid number, not \"%s\"", musl_optarg);
}
- if (options.nbPalettes > 8) {
- error("Number of palettes (-n) must not exceed 8!");
+ if (options.nbPalettes > 256) {
+ error("Number of palettes (-n) must not exceed 256!");
} else if (options.nbPalettes == 0) {
error("Number of palettes (-n) may not be 0!");
}
@@ -442,6 +444,13 @@
autoPalettes = false;
options.palettes = musl_optarg;
break;
+ case 'Q':
+ autoPalmap = true;
+ break;
+ case 'q':
+ autoPalmap = false;
+ options.palmap = musl_optarg;
+ break;
case 'r':
options.reversedWidth = parseNumber(arg, "Reversed image stride");
if (*arg != '\0') {
@@ -512,7 +521,7 @@
}
int main(int argc, char *argv[]) {
- bool autoAttrmap = false, autoTilemap = false, autoPalettes = false;
+ bool autoAttrmap = false, autoTilemap = false, autoPalettes = false, autoPalmap = false;
struct AtFileStackEntry {
int parentInd; // Saved offset into parent argv
@@ -527,7 +536,8 @@
int curArgc = argc;
char **curArgv = argv;
for (;;) {
- char *atFileName = parseArgv(curArgc, curArgv, autoAttrmap, autoTilemap, autoPalettes);
+ char *atFileName =
+ parseArgv(curArgc, curArgv, autoAttrmap, autoTilemap, autoPalettes, autoPalmap);
if (atFileName) {
// Copy `argv[0]` for error reporting, and because option parsing skips it
AtFileStackEntry &stackEntry =
@@ -606,6 +616,7 @@
autoOutPath(autoAttrmap, options.attrmap, ".attrmap");
autoOutPath(autoTilemap, options.tilemap, ".tilemap");
autoOutPath(autoPalettes, options.palettes, ".pal");
+ autoOutPath(autoPalmap, options.palmap, ".palmap");
// Execute deferred external pal spec parsing, now that all other params are known
if (externalPalSpec) {
--- a/src/gfx/process.cpp
+++ b/src/gfx/process.cpp
@@ -739,7 +739,7 @@
static void outputMaps(Png const &png, DefaultInitVec<AttrmapEntry> const &attrmap,
DefaultInitVec<size_t> const &mappings) {
- std::optional<std::filebuf> tilemapOutput, attrmapOutput;
+ std::optional<std::filebuf> tilemapOutput, attrmapOutput, palmapOutput;
if (!options.tilemap.empty()) {
tilemapOutput.emplace();
tilemapOutput->open(options.tilemap, std::ios_base::out | std::ios_base::binary);
@@ -748,6 +748,10 @@
attrmapOutput.emplace();
attrmapOutput->open(options.attrmap, std::ios_base::out | std::ios_base::binary);
}
+ if (!options.palmap.empty()) {
+ palmapOutput.emplace();
+ palmapOutput->open(options.palmap, std::ios_base::out | std::ios_base::binary);
+ }
uint8_t tileID = 0;
uint8_t bank = 0;
@@ -765,9 +769,12 @@
if (attrmapOutput.has_value()) {
uint8_t palID = iter->getPalID(mappings) & 7;
attrmapOutput->sputc(palID | bank << 3); // The other flags are all 0
- ++iter;
}
+ if (palmapOutput.has_value()) {
+ palmapOutput->sputc(iter->getPalID(mappings));
+ }
++tileID;
+ ++iter;
}
assert(iter == attrmap.end());
}
@@ -874,11 +881,21 @@
for (AttrmapEntry const &entry : attrmap) {
uint8_t attr = entry.xFlip << 5 | entry.yFlip << 6;
attr |= entry.bank << 3;
- attr |= mappings[entry.protoPaletteID] & 7;
+ attr |= entry.getPalID(mappings) & 7;
output.sputc(attr);
}
}
+static void outputPalmap(DefaultInitVec<AttrmapEntry> const &attrmap,
+ DefaultInitVec<size_t> const &mappings) {
+ std::filebuf output;
+ output.open(options.attrmap, std::ios_base::out | std::ios_base::binary);
+
+ for (AttrmapEntry const &entry : attrmap) {
+ output.sputc(entry.getPalID(mappings));
+ }
+}
+
} // namespace optimized
void process() {
@@ -1023,9 +1040,10 @@
unoptimized::outputTileData(png, attrmap, palettes, mappings);
}
- if (!options.tilemap.empty() || !options.attrmap.empty()) {
- options.verbosePrint(Options::VERB_LOG_ACT,
- "Generating unoptimized tilemap and/or attrmap...\n");
+ if (!options.tilemap.empty() || !options.attrmap.empty() || !options.palmap.empty()) {
+ options.verbosePrint(
+ Options::VERB_LOG_ACT,
+ "Generating unoptimized tilemap and/or attrmap and/or palmap...\n");
unoptimized::outputMaps(png, attrmap, mappings);
}
} else {
@@ -1051,6 +1069,11 @@
if (!options.attrmap.empty()) {
options.verbosePrint(Options::VERB_LOG_ACT, "Generating optimized attrmap...\n");
optimized::outputAttrmap(attrmap, mappings);
+ }
+
+ if (!options.palmap.empty()) {
+ options.verbosePrint(Options::VERB_LOG_ACT, "Generating optimized palmap...\n");
+ optimized::outputPalmap(attrmap, mappings);
}
}
}
--- a/src/gfx/reverse.cpp
+++ b/src/gfx/reverse.cpp
@@ -178,9 +178,17 @@
// We do this now for two reasons:
// 1. Checking those during the main loop is harmful to optimization, and
// 2. It clutters the code more, and it's not in great shape to begin with
+ // TODO
}
- // TODO: palette map (overrides attributes)
+ std::optional<DefaultInitVec<uint8_t>> palmap;
+ if (!options.palmap.empty()) {
+ palmap = readInto(options.palmap);
+ if (palmap->size() != nbTileInstances) {
+ fatal("Palette map size (%zu tiles) doesn't match image's (%zu)", palmap->size(),
+ nbTileInstances);
+ }
+ }
options.verbosePrint(Options::VERB_LOG_ACT, "Writing image...\n");
std::filebuf pngFile;
@@ -246,6 +254,8 @@
(*tilemap)[index] - options.baseTileIDs[bank] + bank * options.maxNbTiles[0];
}
assert(tileID < nbTileInstances); // Should have been checked earlier
+ size_t palID = palmap ? (*palmap)[index] : attribute & 0b111;
+ assert(palID < palettes.size()); // Should be ensured on data read
// We do not have data for tiles trimmed with `-x`, so assume they are "blank"
static std::array<uint8_t, 16> const trimmedTile{
@@ -255,8 +265,7 @@
uint8_t const *tileData = tileID > nbTileInstances - options.trim
? trimmedTile.data()
: &tiles[tileID * tileSize];
- assert((attribute & 0b111) < palettes.size()); // Should be ensured on data read
- auto const &palette = palettes[attribute & 0b111];
+ auto const &palette = palettes[palID];
for (uint8_t y = 0; y < 8; ++y) {
// If vertically mirrored, fetch the bytes from the other end
uint8_t realY = attribute & 0x40 ? 7 - y : y;