Skip to content

Commit

Permalink
Allow rgbgfx to generate a palette from a spec, without an image
Browse files Browse the repository at this point in the history
  • Loading branch information
Rangi42 committed Oct 31, 2023
1 parent 84f3cb4 commit 3df7992
Show file tree
Hide file tree
Showing 3 changed files with 74 additions and 58 deletions.
1 change: 1 addition & 0 deletions include/gfx/process.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#ifndef RGBDS_GFX_CONVERT_HPP
#define RGBDS_GFX_CONVERT_HPP

void processPalettes();
void process();

#endif // RGBDS_GFX_CONVERT_HPP
17 changes: 10 additions & 7 deletions src/gfx/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
114 changes: 63 additions & 51 deletions src/gfx/process.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<DefaultInitVec<size_t>, std::vector<Palette>>
generatePalettes(std::vector<ProtoPalette> const &protoPalettes, Png const &png) {
// Run a "pagination" problem solver
Expand Down Expand Up @@ -555,26 +574,7 @@ static std::tuple<DefaultInitVec<size_t>, std::vector<Palette>>
}

static std::tuple<DefaultInitVec<size_t>, std::vector<Palette>>
makePalsAsSpecified(std::vector<ProtoPalette> 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<ProtoPalette> const &protoPalettes) {
// Convert the palette spec to actual palettes
std::vector<Palette> palettes(options.palSpec.size());
for (auto [spec, pal] : zip(options.palSpec, palettes)) {
Expand Down Expand Up @@ -625,17 +625,38 @@ static std::tuple<DefaultInitVec<size_t>, std::vector<Palette>>
return {mappings, palettes};
}

static void outputPalettes(std::vector<Palette> 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<size_t> const &mappings, std::vector<Palette> 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);
}
}
}
}
Expand Down Expand Up @@ -955,6 +976,14 @@ static void outputPalmap(DefaultInitVec<AttrmapEntry> const &attrmap,

} // namespace optimized

void processPalettes() {
options.verbosePrint(Options::VERB_CFG, "Using libpng %s\n", png_get_libpng_ver(nullptr));

std::vector<ProtoPalette> 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));

Expand Down Expand Up @@ -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) {
Expand Down

0 comments on commit 3df7992

Please sign in to comment.