diff --git a/include/gfx/process.hpp b/include/gfx/process.hpp index 1ac5931af5..4db5403c93 100644 --- a/include/gfx/process.hpp +++ b/include/gfx/process.hpp @@ -9,6 +9,7 @@ #ifndef RGBDS_GFX_CONVERT_HPP #define RGBDS_GFX_CONVERT_HPP +void processPalettes(); void process(); #endif // RGBDS_GFX_CONVERT_HPP diff --git a/src/gfx/main.cpp b/src/gfx/main.cpp index 377305277a..0fc4e58707 100644 --- a/src/gfx/main.cpp +++ b/src/gfx/main.cpp @@ -768,19 +768,22 @@ int main(int argc, char *argv[]) { fputs("Ready.\n", stderr); } - if (options.input.empty()) { - fatal("No input image specified"); - } - // Do not do anything if option parsing went wrong if (nbErrors) { giveUp(); } - if (options.reverse()) { - reverse(); + if (!options.input.empty()) { + if (options.reverse()) { + reverse(); + } else { + process(); + } + } else if (!options.palettes.empty() && options.palSpecType == Options::EXPLICIT + && !options.reverse()) { + processPalettes(); } else { - process(); + fatal("No input image specified"); } if (nbErrors) { diff --git a/src/gfx/process.cpp b/src/gfx/process.cpp index 4de48b4ab0..72cd3bcc60 100644 --- a/src/gfx/process.cpp +++ b/src/gfx/process.cpp @@ -511,6 +511,25 @@ struct AttrmapEntry { } }; +static void generatePalSpec(Png const &png) { + // Generate a palette spec from the first few colors in the embedded palette + auto [embPalSize, embPalRGB, embPalAlpha] = png.getEmbeddedPal(); + if (embPalRGB == nullptr) { + fatal("`-c embedded` was given, but the PNG does not have an embedded palette!"); + } + + // Fill in the palette spec + options.palSpec.emplace_back(); // A single palette, with `#00000000`s (transparent) + assert(options.palSpec.size() == 1); + if (embPalSize > options.maxOpaqueColors()) { // Ignore extraneous colors if they are unused + embPalSize = options.maxOpaqueColors(); + } + for (int i = 0; i < embPalSize; ++i) { + options.palSpec[0][i] = Rgba(embPalRGB[i].red, embPalRGB[i].green, embPalRGB[i].blue, + embPalAlpha ? embPalAlpha[i] : 0xFF); + } +} + static std::tuple, std::vector> generatePalettes(std::vector const &protoPalettes, Png const &png) { // Run a "pagination" problem solver @@ -555,26 +574,7 @@ static std::tuple, std::vector> } static std::tuple, std::vector> - makePalsAsSpecified(std::vector const &protoPalettes, Png const &png) { - if (options.palSpecType == Options::EMBEDDED) { - // Generate a palette spec from the first few colors in the embedded palette - auto [embPalSize, embPalRGB, embPalAlpha] = png.getEmbeddedPal(); - if (embPalRGB == nullptr) { - fatal("`-c embedded` was given, but the PNG does not have an embedded palette!"); - } - - // Fill in the palette spec - options.palSpec.emplace_back(); // A single palette, with `#00000000`s (transparent) - assert(options.palSpec.size() == 1); - if (embPalSize > options.maxOpaqueColors()) { // Ignore extraneous colors if they are unused - embPalSize = options.maxOpaqueColors(); - } - for (int i = 0; i < embPalSize; ++i) { - options.palSpec[0][i] = Rgba(embPalRGB[i].red, embPalRGB[i].green, embPalRGB[i].blue, - embPalAlpha ? embPalAlpha[i] : 0xFF); - } - } - + makePalsAsSpecified(std::vector const &protoPalettes) { // Convert the palette spec to actual palettes std::vector palettes(options.palSpec.size()); for (auto [spec, pal] : zip(options.palSpec, palettes)) { @@ -625,17 +625,38 @@ static std::tuple, std::vector> return {mappings, palettes}; } -static void outputPalettes(std::vector const &palettes) { - File output; - if (!output.open(options.palettes, std::ios_base::out | std::ios_base::binary)) { - fatal("Failed to open \"%s\": %s", output.c_str(options.palettes), strerror(errno)); +static void outputPalettes(DefaultInitVec const &mappings, std::vector const &palettes) { + if (options.verbosity >= Options::VERB_INTERM) { + for (auto &&palette : palettes) { + fputs("{ ", stderr); + for (uint16_t colorIndex : palette) { + fprintf(stderr, "%04" PRIx16 ", ", colorIndex); + } + fputs("}\n", stderr); + } } - for (Palette const &palette : palettes) { - for (uint8_t i = 0; i < options.nbColorsPerPal; ++i) { - uint16_t color = palette.colors[i]; // Will return `UINT16_MAX` for unused slots - output->sputc(color & 0xFF); - output->sputc(color >> 8); + if (palettes.size() > options.nbPalettes) { + // If the palette generation is wrong, other (dependee) operations are likely to be + // nonsensical, so fatal-error outright + fatal("Generated %zu palettes, over the maximum of %" PRIu8, palettes.size(), + options.nbPalettes); + } + + if (!options.palettes.empty()) { + File output; + if (!output.open(options.palettes, std::ios_base::out | std::ios_base::binary)) { + fatal("Failed to open \"%s\": %s", output.c_str(options.palettes), + strerror(errno)); + } + + for (Palette const &palette : palettes) { + for (uint8_t i = 0; i < options.nbColorsPerPal; ++i) { + // Will output `UINT16_MAX` for unused slots + uint16_t color = palette.colors[i]; + output->sputc(color & 0xFF); + output->sputc(color >> 8); + } } } } @@ -955,6 +976,14 @@ static void outputPalmap(DefaultInitVec const &attrmap, } // namespace optimized +void processPalettes() { + options.verbosePrint(Options::VERB_CFG, "Using libpng %s\n", png_get_libpng_ver(nullptr)); + + std::vector protoPalettes; + auto [mappings, palettes] = makePalsAsSpecified(protoPalettes); + outputPalettes(mappings, palettes); +} + void process() { options.verbosePrint(Options::VERB_CFG, "Using libpng %s\n", png_get_libpng_ver(nullptr)); @@ -1063,30 +1092,13 @@ contained:; } } + if (options.palSpecType == Options::EMBEDDED) { + generatePalSpec(png); + } auto [mappings, palettes] = options.palSpecType == Options::NO_SPEC ? generatePalettes(protoPalettes, png) - : makePalsAsSpecified(protoPalettes, png); - - if (options.verbosity >= Options::VERB_INTERM) { - for (auto &&palette : palettes) { - fputs("{ ", stderr); - for (uint16_t colorIndex : palette) { - fprintf(stderr, "%04" PRIx16 ", ", colorIndex); - } - fputs("}\n", stderr); - } - } - - if (palettes.size() > options.nbPalettes) { - // If the palette generation is wrong, other (dependee) operations are likely to be - // nonsensical, so fatal-error outright - fatal("Generated %zu palettes, over the maximum of %" PRIu8, palettes.size(), - options.nbPalettes); - } - - if (!options.palettes.empty()) { - outputPalettes(palettes); - } + : makePalsAsSpecified(protoPalettes); + outputPalettes(mappings, palettes); // If deduplication is not happening, we just need to output the tile data and/or maps as-is if (!options.allowDedup) {