mirror of
https://github.com/gbdev/rgbds.git
synced 2025-11-20 18:22:07 +00:00
Implement rgbgfx -O (#1240)
This commit is contained in:
@@ -10,6 +10,7 @@ _rgbgfx_completions() {
|
|||||||
[V]="version:normal"
|
[V]="version:normal"
|
||||||
[C]="color-curve:normal"
|
[C]="color-curve:normal"
|
||||||
[m]="mirror-tiles:normal"
|
[m]="mirror-tiles:normal"
|
||||||
|
[O]="group-outputs:normal"
|
||||||
[u]="unique-tiles:normal"
|
[u]="unique-tiles:normal"
|
||||||
[v]="verbose:normal"
|
[v]="verbose:normal"
|
||||||
[Z]="columns:normal"
|
[Z]="columns:normal"
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ local args=(
|
|||||||
'(-a --attr-map -A --output-attr-map)'{-A,--output-attr-map}'[Shortcut for -a <file>.attrmap]'
|
'(-a --attr-map -A --output-attr-map)'{-A,--output-attr-map}'[Shortcut for -a <file>.attrmap]'
|
||||||
'(-C --color-curve)'{-C,--color-curve}'[Generate palettes using GBC color curve]'
|
'(-C --color-curve)'{-C,--color-curve}'[Generate palettes using GBC color curve]'
|
||||||
'(-m --mirror-tiles)'{-m,--mirror-tiles}'[Eliminate mirrored tiles from output]'
|
'(-m --mirror-tiles)'{-m,--mirror-tiles}'[Eliminate mirrored tiles from output]'
|
||||||
|
'(-O --group-outputs)'{-O,--group-outputs}'[Base "shortcut" options on the output path, not input]'
|
||||||
'(-p --palette -P --output-palette)'{-P,--output-palette}'[Shortcut for -p <file>.pal]'
|
'(-p --palette -P --output-palette)'{-P,--output-palette}'[Shortcut for -p <file>.pal]'
|
||||||
'(-q --palette-map -Q --output-palette-map)'{-Q,--output-palette-map}'[Shortcut for -p <file>.palmap]'
|
'(-q --palette-map -Q --output-palette-map)'{-Q,--output-palette-map}'[Shortcut for -p <file>.palmap]'
|
||||||
'(-t --tilemap -T --output-tilemap)'{-T,--output-tilemap}'[Shortcut for -t <file>.tilemap]'
|
'(-t --tilemap -T --output-tilemap)'{-T,--output-tilemap}'[Shortcut for -t <file>.tilemap]'
|
||||||
|
|||||||
59
man/rgbgfx.1
59
man/rgbgfx.1
@@ -10,7 +10,7 @@
|
|||||||
.Nd Game Boy graphics converter
|
.Nd Game Boy graphics converter
|
||||||
.Sh SYNOPSIS
|
.Sh SYNOPSIS
|
||||||
.Nm
|
.Nm
|
||||||
.Op Fl CmuVZ
|
.Op Fl CmOuVZ
|
||||||
.Op Fl v Op Fl v No ...
|
.Op Fl v Op Fl v No ...
|
||||||
.Op Fl a Ar attrmap | Fl A
|
.Op Fl a Ar attrmap | Fl A
|
||||||
.Op Fl b Ar base_ids
|
.Op Fl b Ar base_ids
|
||||||
@@ -100,11 +100,8 @@ follows the same order
|
|||||||
and has the same size.
|
and has the same size.
|
||||||
.It Fl A , Fl Fl output-attr-map
|
.It Fl A , Fl Fl output-attr-map
|
||||||
Same as
|
Same as
|
||||||
.Fl a Ar path ,
|
.Fl a Ar base_path Ns .attrmap
|
||||||
where
|
.Pq see Sx Automatic output paths .
|
||||||
.Ar path
|
|
||||||
is the input image's path with the extension set to
|
|
||||||
.Pa .attrmap .
|
|
||||||
.It Fl b Ar base_ids , Fl Fl base-tiles Ar base_ids
|
.It Fl b Ar base_ids , Fl Fl base-tiles Ar base_ids
|
||||||
Set the base IDs for tile map output.
|
Set the base IDs for tile map output.
|
||||||
.Ar base_ids
|
.Ar base_ids
|
||||||
@@ -207,6 +204,13 @@ This may not be more than 256.
|
|||||||
.Pp
|
.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
|
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 .
|
.Pq see Fl q .
|
||||||
|
.It Fl O , Fl Fl group-outputs
|
||||||
|
Sets the
|
||||||
|
.Sq base path
|
||||||
|
to be the output tile data path from
|
||||||
|
.Fl o
|
||||||
|
instead of the input image path
|
||||||
|
.Pq see Sx Automatic output paths .
|
||||||
.It Fl o Ar out_file , Fl Fl output Ar out_file
|
.It Fl o Ar out_file , Fl Fl output Ar out_file
|
||||||
Output the tile data in native 2bpp format or in 1bpp
|
Output the tile data in native 2bpp format or in 1bpp
|
||||||
.Pq depending on Fl d
|
.Pq depending on Fl d
|
||||||
@@ -215,21 +219,15 @@ to this file.
|
|||||||
Output the image's palette set to this file.
|
Output the image's palette set to this file.
|
||||||
.It Fl P , Fl Fl output-palette
|
.It Fl P , Fl Fl output-palette
|
||||||
Same as
|
Same as
|
||||||
.Fl p Ar path ,
|
.Fl p Ar base_path Ns .pal
|
||||||
where
|
.Pq see Sx Automatic output paths .
|
||||||
.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
|
.It Fl q Ar pal_file , Fl Fl palette-map Ar pal_file
|
||||||
Output the image's palette map to this 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.
|
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
|
.It Fl Q , Fl Fl output-palette-map
|
||||||
Same as
|
Same as
|
||||||
.Fl q Ar path ,
|
.Fl q Ar base_path Ns .palmap
|
||||||
where
|
.Pq see Sx Automatic output paths .
|
||||||
.Ar path
|
|
||||||
is the input image's path with the extension set to
|
|
||||||
.Pa .palmap .
|
|
||||||
.It Fl r Ar width , Fl Fl reverse Ar width
|
.It Fl r Ar width , Fl Fl reverse Ar width
|
||||||
Switches
|
Switches
|
||||||
.Nm
|
.Nm
|
||||||
@@ -264,11 +262,8 @@ and/or
|
|||||||
to keep track of duplicate tiles.
|
to keep track of duplicate tiles.
|
||||||
.It Fl T , Fl Fl output-tilemap
|
.It Fl T , Fl Fl output-tilemap
|
||||||
Same as
|
Same as
|
||||||
.Fl t Ar path ,
|
.Fl t Ar base_path Ns .tilemap
|
||||||
where
|
.Pq see Sx Automatic output paths .
|
||||||
.Ar path
|
|
||||||
is the input image's path with the extension set to
|
|
||||||
.Pa .tilemap .
|
|
||||||
.It Fl u , Fl Fl unique-tiles
|
.It Fl u , Fl Fl unique-tiles
|
||||||
Deduplicate identical tiles. Only one of each unique tile will be saved in the tile data file.
|
Deduplicate identical tiles. Only one of each unique tile will be saved in the tile data file.
|
||||||
Useful with a tile map
|
Useful with a tile map
|
||||||
@@ -533,6 +528,28 @@ The contents of individual bytes follows the GBC's native format:
|
|||||||
.El
|
.El
|
||||||
.Pp
|
.Pp
|
||||||
Note that if more than 8 palettes are used, only the lowest 3 bits of the palette ID are output.
|
Note that if more than 8 palettes are used, only the lowest 3 bits of the palette ID are output.
|
||||||
|
.Ss Automatic output paths
|
||||||
|
For convenience,
|
||||||
|
.Nm
|
||||||
|
provides shortcuts to generate all files in the same directory.
|
||||||
|
This is done by using the uppercase version of a flag
|
||||||
|
.Pq for example, Fl A No instead of Fl a .
|
||||||
|
The
|
||||||
|
.Ar base_path
|
||||||
|
is the input image path
|
||||||
|
.Pq or the output tile data path from Fl o , No if Fl O No was given
|
||||||
|
with its extension, if any, removed.
|
||||||
|
.Pp
|
||||||
|
For example, these two are equivalent:
|
||||||
|
.Bd -literal -indent Ds
|
||||||
|
$ rgbgfx img/player.png -o build/player.2bpp -P
|
||||||
|
$ rgbgfx img/player.png -o build/player.2bpp -p img/player.pal
|
||||||
|
.Pp
|
||||||
|
And so are these two:
|
||||||
|
.Bd -literal -indent Ds
|
||||||
|
$ rgbgfx img/player.png -o build/player.2bpp -O -P
|
||||||
|
$ rgbgfx img/player.png -o build/player.2bpp -p build/player.pal
|
||||||
|
.El
|
||||||
.Sh REVERSE MODE
|
.Sh REVERSE MODE
|
||||||
.Nm
|
.Nm
|
||||||
can produce a PNG image from valid data.
|
can produce a PNG image from valid data.
|
||||||
|
|||||||
@@ -34,7 +34,16 @@
|
|||||||
using namespace std::literals::string_view_literals;
|
using namespace std::literals::string_view_literals;
|
||||||
|
|
||||||
Options options;
|
Options options;
|
||||||
char const *externalPalSpec = nullptr;
|
|
||||||
|
static struct LocalOptions {
|
||||||
|
char const *externalPalSpec;
|
||||||
|
bool autoAttrmap;
|
||||||
|
bool autoTilemap;
|
||||||
|
bool autoPalettes;
|
||||||
|
bool autoPalmap;
|
||||||
|
bool groupOutputs;
|
||||||
|
} localOptions;
|
||||||
|
|
||||||
static uintmax_t nbErrors;
|
static uintmax_t nbErrors;
|
||||||
|
|
||||||
[[noreturn]] void giveUp() {
|
[[noreturn]] void giveUp() {
|
||||||
@@ -91,7 +100,7 @@ void Options::verbosePrint(uint8_t level, char const *fmt, ...) const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Short options
|
// Short options
|
||||||
static char const *optstring = "-Aa:b:Cc:Dd:FfhL:mN:n:o:Pp:Qq:r:s:Tt:U:uVvx:Z";
|
static char const *optstring = "-Aa:b:Cc:Dd:FfhL:mN:n:Oo:Pp:Qq:r:s:Tt:U:uVvx:Z";
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Equivalent long options
|
* Equivalent long options
|
||||||
@@ -118,6 +127,7 @@ static struct option const longopts[] = {
|
|||||||
{"mirror-tiles", no_argument, NULL, 'm'},
|
{"mirror-tiles", no_argument, NULL, 'm'},
|
||||||
{"nb-tiles", required_argument, NULL, 'N'},
|
{"nb-tiles", required_argument, NULL, 'N'},
|
||||||
{"nb-palettes", required_argument, NULL, 'n'},
|
{"nb-palettes", required_argument, NULL, 'n'},
|
||||||
|
{"group-outputs", no_argument, NULL, 'O'},
|
||||||
{"output", required_argument, NULL, 'o'},
|
{"output", required_argument, NULL, 'o'},
|
||||||
{"output-palette", no_argument, NULL, 'P'},
|
{"output-palette", no_argument, NULL, 'P'},
|
||||||
{"palette", required_argument, NULL, 'p'},
|
{"palette", required_argument, NULL, 'p'},
|
||||||
@@ -136,7 +146,7 @@ static struct option const longopts[] = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
static void printUsage(void) {
|
static void printUsage(void) {
|
||||||
fputs("Usage: rgbgfx [-r stride] [-CmuVZ] [-v [-v ...]] [-a <attr_map> | -A]\n"
|
fputs("Usage: rgbgfx [-r stride] [-CmOuVZ] [-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"
|
||||||
@@ -321,23 +331,21 @@ static std::vector<size_t> readAtFile(std::filesystem::path const &path,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Parses an arg vector, modifying `options` as options are read.
|
* Parses an arg vector, modifying `options` and `localOptions` as options are read.
|
||||||
* The three booleans are for the "auto path" flags, since their processing must be deferred to the
|
* The `localOptions` struct is for flags which must be processed after the option parsing finishes.
|
||||||
* end of option parsing.
|
|
||||||
*
|
*
|
||||||
* Returns NULL if the vector was fully parsed, or a pointer (which is part of the arg vector) to an
|
* Returns NULL if the vector was fully parsed, or a pointer (which is part of the arg vector) to an
|
||||||
* "at-file" path if one is encountered.
|
* "at-file" path if one is encountered.
|
||||||
*/
|
*/
|
||||||
static char *parseArgv(int argc, char **argv, bool &autoAttrmap, bool &autoTilemap,
|
static char *parseArgv(int argc, char **argv) {
|
||||||
bool &autoPalettes, bool &autoPalmap) {
|
|
||||||
for (int ch; (ch = musl_getopt_long_only(argc, argv, optstring, longopts, nullptr)) != -1;) {
|
for (int ch; (ch = musl_getopt_long_only(argc, argv, optstring, longopts, nullptr)) != -1;) {
|
||||||
char *arg = musl_optarg; // Make a copy for scanning
|
char *arg = musl_optarg; // Make a copy for scanning
|
||||||
switch (ch) {
|
switch (ch) {
|
||||||
case 'A':
|
case 'A':
|
||||||
autoAttrmap = true;
|
localOptions.autoAttrmap = true;
|
||||||
break;
|
break;
|
||||||
case 'a':
|
case 'a':
|
||||||
autoAttrmap = false;
|
localOptions.autoAttrmap = false;
|
||||||
if (options.attrmap.has_value())
|
if (options.attrmap.has_value())
|
||||||
warning("Overriding attrmap file %s", options.attrmap->c_str());
|
warning("Overriding attrmap file %s", options.attrmap->c_str());
|
||||||
options.attrmap = musl_optarg;
|
options.attrmap = musl_optarg;
|
||||||
@@ -385,7 +393,7 @@ static char *parseArgv(int argc, char **argv, bool &autoAttrmap, bool &autoTilem
|
|||||||
// size to be split; thus, we defer that
|
// size to be split; thus, we defer that
|
||||||
// TODO: this does not validate the `fmt` part of any external spec but the last
|
// TODO: this does not validate the `fmt` part of any external spec but the last
|
||||||
// one, but I guess that's okay
|
// one, but I guess that's okay
|
||||||
externalPalSpec = musl_optarg;
|
localOptions.externalPalSpec = musl_optarg;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'd':
|
case 'd':
|
||||||
@@ -481,25 +489,28 @@ static char *parseArgv(int argc, char **argv, bool &autoAttrmap, bool &autoTilem
|
|||||||
error("Number of palettes (-n) may not be 0!");
|
error("Number of palettes (-n) may not be 0!");
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case 'O':
|
||||||
|
localOptions.groupOutputs = true;
|
||||||
|
break;
|
||||||
case 'o':
|
case 'o':
|
||||||
if (options.output.has_value())
|
if (options.output.has_value())
|
||||||
warning("Overriding tile data file %s", options.output->c_str());
|
warning("Overriding tile data file %s", options.output->c_str());
|
||||||
options.output = musl_optarg;
|
options.output = musl_optarg;
|
||||||
break;
|
break;
|
||||||
case 'P':
|
case 'P':
|
||||||
autoPalettes = true;
|
localOptions.autoPalettes = true;
|
||||||
break;
|
break;
|
||||||
case 'p':
|
case 'p':
|
||||||
autoPalettes = false;
|
localOptions.autoPalettes = false;
|
||||||
if (options.palettes.has_value())
|
if (options.palettes.has_value())
|
||||||
warning("Overriding palettes file %s", options.palettes->c_str());
|
warning("Overriding palettes file %s", options.palettes->c_str());
|
||||||
options.palettes = musl_optarg;
|
options.palettes = musl_optarg;
|
||||||
break;
|
break;
|
||||||
case 'Q':
|
case 'Q':
|
||||||
autoPalmap = true;
|
localOptions.autoPalmap = true;
|
||||||
break;
|
break;
|
||||||
case 'q':
|
case 'q':
|
||||||
autoPalmap = false;
|
localOptions.autoPalmap = false;
|
||||||
if (options.palmap.has_value())
|
if (options.palmap.has_value())
|
||||||
warning("Overriding palette map file %s", options.palmap->c_str());
|
warning("Overriding palette map file %s", options.palmap->c_str());
|
||||||
options.palmap = musl_optarg;
|
options.palmap = musl_optarg;
|
||||||
@@ -525,10 +536,10 @@ static char *parseArgv(int argc, char **argv, bool &autoAttrmap, bool &autoTilem
|
|||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case 'T':
|
case 'T':
|
||||||
autoTilemap = true;
|
localOptions.autoTilemap = true;
|
||||||
break;
|
break;
|
||||||
case 't':
|
case 't':
|
||||||
autoTilemap = false;
|
localOptions.autoTilemap = false;
|
||||||
if (options.tilemap.has_value())
|
if (options.tilemap.has_value())
|
||||||
warning("Overriding tilemap file %s", options.tilemap->c_str());
|
warning("Overriding tilemap file %s", options.tilemap->c_str());
|
||||||
options.tilemap = musl_optarg;
|
options.tilemap = musl_optarg;
|
||||||
@@ -577,8 +588,6 @@ static char *parseArgv(int argc, char **argv, bool &autoAttrmap, bool &autoTilem
|
|||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char *argv[]) {
|
int main(int argc, char *argv[]) {
|
||||||
bool autoAttrmap = false, autoTilemap = false, autoPalettes = false, autoPalmap = false;
|
|
||||||
|
|
||||||
struct AtFileStackEntry {
|
struct AtFileStackEntry {
|
||||||
int parentInd; // Saved offset into parent argv
|
int parentInd; // Saved offset into parent argv
|
||||||
std::vector<char *> argv; // This context's arg pointer vec
|
std::vector<char *> argv; // This context's arg pointer vec
|
||||||
@@ -592,8 +601,7 @@ int main(int argc, char *argv[]) {
|
|||||||
int curArgc = argc;
|
int curArgc = argc;
|
||||||
char **curArgv = argv;
|
char **curArgv = argv;
|
||||||
for (;;) {
|
for (;;) {
|
||||||
char *atFileName =
|
char *atFileName = parseArgv(curArgc, curArgv);
|
||||||
parseArgv(curArgc, curArgv, autoAttrmap, autoTilemap, autoPalettes, autoPalmap);
|
|
||||||
if (atFileName) {
|
if (atFileName) {
|
||||||
// Copy `argv[0]` for error reporting, and because option parsing skips it
|
// Copy `argv[0]` for error reporting, and because option parsing skips it
|
||||||
AtFileStackEntry &stackEntry =
|
AtFileStackEntry &stackEntry =
|
||||||
@@ -649,22 +657,24 @@ int main(int argc, char *argv[]) {
|
|||||||
auto autoOutPath = [](bool autoOptEnabled, std::optional<std::filesystem::path> &path,
|
auto autoOutPath = [](bool autoOptEnabled, std::optional<std::filesystem::path> &path,
|
||||||
char const *extension) {
|
char const *extension) {
|
||||||
if (autoOptEnabled) {
|
if (autoOptEnabled) {
|
||||||
if (!options.input.has_value()) {
|
auto image = localOptions.groupOutputs ? options.output : options.input;
|
||||||
fputs("FATAL: No input image specified\n", stderr);
|
if (!image.has_value()) {
|
||||||
|
fprintf(stderr, "FATAL: No %s specified\n", localOptions.groupOutputs
|
||||||
|
? "output tile data file" : "input image");
|
||||||
printUsage();
|
printUsage();
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
path.emplace(*options.input).replace_extension(extension);
|
path.emplace(*image).replace_extension(extension);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
autoOutPath(autoAttrmap, options.attrmap, ".attrmap");
|
autoOutPath(localOptions.autoAttrmap, options.attrmap, ".attrmap");
|
||||||
autoOutPath(autoTilemap, options.tilemap, ".tilemap");
|
autoOutPath(localOptions.autoTilemap, options.tilemap, ".tilemap");
|
||||||
autoOutPath(autoPalettes, options.palettes, ".pal");
|
autoOutPath(localOptions.autoPalettes, options.palettes, ".pal");
|
||||||
autoOutPath(autoPalmap, options.palmap, ".palmap");
|
autoOutPath(localOptions.autoPalmap, options.palmap, ".palmap");
|
||||||
|
|
||||||
// Execute deferred external pal spec parsing, now that all other params are known
|
// Execute deferred external pal spec parsing, now that all other params are known
|
||||||
if (externalPalSpec) {
|
if (localOptions.externalPalSpec) {
|
||||||
parseExternalPalSpec(externalPalSpec);
|
parseExternalPalSpec(localOptions.externalPalSpec);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (options.verbosity >= Options::VERB_CFG) {
|
if (options.verbosity >= Options::VERB_CFG) {
|
||||||
|
|||||||
Reference in New Issue
Block a user