Skip to content

Commit

Permalink
Implement --background
Browse files Browse the repository at this point in the history
TODO: document the behaviour
  • Loading branch information
ISSOtm authored and Rangi42 committed Dec 11, 2024
1 parent c1c5b10 commit f3329b4
Show file tree
Hide file tree
Showing 7 changed files with 120 additions and 37 deletions.
1 change: 1 addition & 0 deletions contrib/bash_compl/_rgbgfx.bash
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ _rgbgfx_completions() {
[Z]="columns:normal"
[a]="attr-map:glob-*.attrmap"
[A]="auto-attr-map:normal"
[B]="background-color:unk"
[b]="base-tiles:unk"
[c]="colors:unk"
[d]="depth:unk"
Expand Down
1 change: 1 addition & 0 deletions contrib/zsh_compl/_rgbgfx
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ local args=(
'(-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'
'(-B --background-color)'{-B,--background-color}'+[Ignore tiles containing only specified color]:color:'
'(-b --base-tiles)'{-b,--base-tiles}'+[Base tile IDs for tile map output]:base tile IDs:'
'(-c --colors)'{-c,--colors}'+[Specify color palettes]:palette spec:'
'(-d --depth)'{-d,--depth}'+[Set bit depth]:bit depth:_depths'
Expand Down
29 changes: 28 additions & 1 deletion include/gfx/main.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@
#include <utility>
#include <vector>

#include "helpers.hpp"

#include "gfx/rgba.hpp"

struct Options {
Expand All @@ -20,7 +22,9 @@ struct Options {
bool columnMajor = false; // -Z
uint8_t verbosity = 0; // -v

std::string attrmap{}; // -a, -A
std::string attrmap{}; // -a, -A
std::optional<Rgba> bgColor{}; // -B
bool bgColorStrict; // If true, warns when the `bgColor` is ever not alone in a tile.
std::array<uint8_t, 2> baseTileIDs{0, 0}; // -b
enum {
NO_SPEC,
Expand Down Expand Up @@ -121,4 +125,27 @@ static constexpr auto flipTable = ([]() constexpr {
return table;
})();

// Parsing helpers.

constexpr uint8_t nibble(char c) {
if (c >= 'a') {
assume(c <= 'f');
return c - 'a' + 10;
} else if (c >= 'A') {
assume(c <= 'F');
return c - 'A' + 10;
} else {
assume(c >= '0' && c <= '9');
return c - '0';
}
}

constexpr uint8_t toHex(char c1, char c2) {
return nibble(c1) * 16 + nibble(c2);
}

constexpr uint8_t singleToHex(char c) {
return toHex(c, c);
}

#endif // RGBDS_GFX_MAIN_HPP
2 changes: 2 additions & 0 deletions man/rgbgfx.1
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,8 @@ and has the same size.
Same as
.Fl a Ar base_path Ns .attrmap
.Pq see Sx Automatic output paths .
.It Fl B Ar color , Fl \-background-color Ar color
TODO
.It Fl b Ar base_ids , Fl \-base-tiles Ar base_ids
Set the base IDs for tile map output.
.Ar base_ids
Expand Down
40 changes: 39 additions & 1 deletion src/gfx/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ void Options::verbosePrint(uint8_t level, char const *fmt, ...) const {
}

// Short options
static char const *optstring = "-Aa:b:Cc:d:i:L:mN:n:Oo:Pp:Qq:r:s:Tt:U:uVvXx:YZ";
static char const *optstring = "-Aa:B:b:Cc:d:i:L:mN:n:Oo:Pp:Qq:r:s:Tt:U:uVvXx:YZ";

/*
* Equivalent long options
Expand All @@ -123,6 +123,7 @@ static char const *optstring = "-Aa:b:Cc:d:i:L:mN:n:Oo:Pp:Qq:r:s:Tt:U:uVvXx:YZ";
static option const longopts[] = {
{"auto-attr-map", no_argument, nullptr, 'A'},
{"attr-map", required_argument, nullptr, 'a'},
{"background-color", required_argument, nullptr, 'B'},
{"base-tiles", required_argument, nullptr, 'b'},
{"color-curve", no_argument, nullptr, 'C'},
{"colors", required_argument, nullptr, 'c'},
Expand Down Expand Up @@ -367,6 +368,43 @@ static char *parseArgv(int argc, char *argv[]) {
warning("Overriding attrmap file %s", options.attrmap.c_str());
options.attrmap = musl_optarg;
break;
case 'B':
if (musl_optarg[0] != '#' || musl_optarg[1] == '\0') {
error("Background color specification must be either `#rgb` or `#rrggbb`");
} else {
size_t colorLen = strspn(&musl_optarg[1], "0123456789ABCDEFabcdef");
switch (colorLen) {
case 3:
options.bgColor = Rgba(
singleToHex(musl_optarg[1]),
singleToHex(musl_optarg[2]),
singleToHex(musl_optarg[3]),
0xFF
);
break;
case 6:
options.bgColor = Rgba(
toHex(musl_optarg[1], musl_optarg[2]),
toHex(musl_optarg[3], musl_optarg[4]),
toHex(musl_optarg[5], musl_optarg[6]),
0xFF
);
break;
default:
error("Unknown background color specification \"%s\"", musl_optarg);
}

options.bgColorStrict = true;
if (musl_optarg[colorLen + 1] == '!') {
options.bgColorStrict = false;
++colorLen;
}

if (musl_optarg[colorLen + 1] != '\0') {
error("Unexpected text \"%s\" after background color specification", &musl_optarg[colorLen + 1]);
}
}
break;
case 'b':
number = parseNumber(arg, "Bank 0 base tile ID", 0);
if (number >= 256) {
Expand Down
21 changes: 0 additions & 21 deletions src/gfx/pal_spec.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,27 +23,6 @@

using namespace std::string_view_literals;

constexpr uint8_t nibble(char c) {
if (c >= 'a') {
assume(c <= 'f');
return c - 'a' + 10;
} else if (c >= 'A') {
assume(c <= 'F');
return c - 'A' + 10;
} else {
assume(c >= '0' && c <= '9');
return c - '0';
}
}

constexpr uint8_t toHex(char c1, char c2) {
return nibble(c1) * 16 + nibble(c2);
}

constexpr uint8_t singleToHex(char c) {
return toHex(c, c);
}

template<typename Str> // Should be std::string or std::string_view
static void skipWhitespace(Str const &str, typename Str::size_type &pos) {
pos = std::min(str.find_first_not_of(" \t"sv, pos), str.length());
Expand Down
63 changes: 49 additions & 14 deletions src/gfx/process.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -528,9 +528,11 @@ struct AttrmapEntry {
bool xFlip;

static constexpr decltype(protoPaletteID) transparent = SIZE_MAX;
static constexpr decltype(protoPaletteID) background = transparent - 1;

bool isBackgroundTile() const { return protoPaletteID == background; }
size_t getPalID(DefaultInitVec<size_t> const &mappings) const {
return protoPaletteID == transparent ? 0 : mappings[protoPaletteID];
return isBackgroundTile() ? 0xFF : mappings[protoPaletteID == transparent ? 0 : protoPaletteID];
}
};

Expand Down Expand Up @@ -851,13 +853,16 @@ static void outputUnoptimizedTileData(
remainingTiles -= options.trim;

for (auto [tile, attr] : zip(png.visitAsTiles(), attrmap)) {
// If the tile is fully transparent, default to palette 0
Palette const &palette = palettes[attr.getPalID(mappings)];
for (uint32_t y = 0; y < 8; ++y) {
uint16_t bitplanes = TileData::rowBitplanes(tile, palette, y);
output->sputc(bitplanes & 0xFF);
if (options.bitDepth == 2) {
output->sputc(bitplanes >> 8);
// Do not emit fully-background tiles.
if (!attr.isBackgroundTile()) {
// If the tile is fully transparent, this defaults to palette 0.
Palette const &palette = palettes[attr.getPalID(mappings)];
for (uint32_t y = 0; y < 8; ++y) {
uint16_t bitplanes = TileData::rowBitplanes(tile, palette, y);
output->sputc(bitplanes & 0xFF);
if (options.bitDepth == 2) {
output->sputc(bitplanes >> 8);
}
}
}

Expand Down Expand Up @@ -897,14 +902,18 @@ static void outputUnoptimizedMaps(
if (tilemapOutput.has_value()) {
(*tilemapOutput)->sputc(tileID + options.baseTileIDs[bank]);
}
uint8_t palID = attr.getPalID(mappings);
if (attrmapOutput.has_value()) {
uint8_t palID = attr.getPalID(mappings) & 7;
(*attrmapOutput)->sputc(palID | bank << 3); // The other flags are all 0
(*attrmapOutput)->sputc((palID & 7) | bank << 3); // The other flags are all 0
}
if (palmapOutput.has_value()) {
(*palmapOutput)->sputc(attr.getPalID(mappings));
(*palmapOutput)->sputc(palID);
}

// Background tiles are skipped in the tile data, so they should be skipped in the maps too.
if (!attr.isBackgroundTile()) {
++tileID;
}
++tileID;
}
}

Expand Down Expand Up @@ -1000,7 +1009,7 @@ static UniqueTiles dedupTiles(
}

for (auto [tile, attr] : zip(png.visitAsTiles(), attrmap)) {
auto [tileID, matchType] = tiles.addTile({tile, palettes[mappings[attr.protoPaletteID]]});
auto [tileID, matchType] = attr.isBackgroundTile() ? std::tuple{uint16_t(0), TileData::EXACT} : tiles.addTile({tile, palettes[mappings[attr.protoPaletteID]]});

if (matchType == TileData::NOPE && options.output.empty()) {
error(
Expand Down Expand Up @@ -1121,6 +1130,10 @@ void process() {
// output (with the exception of an un-duplicated tilemap, but that's an acceptable loss.)
std::vector<ProtoPalette> protoPalettes;
DefaultInitVec<AttrmapEntry> attrmap{};
ProtoPalette bgPal;
if (options.bgColor.has_value()) {
bgPal.add(options.bgColor->cgbColor());
}

for (auto tile : png.visitAsTiles()) {
AttrmapEntry &attrs = attrmap.emplace_back();
Expand Down Expand Up @@ -1156,6 +1169,28 @@ void process() {
protoPalette.add(cgbColor);
}

if (options.bgColor.has_value()) {
switch (protoPalette.compare(bgPal)) {
case ProtoPalette::THEY_BIGGER: // Note that ties are resolved as `THEY_BIGGER`.
// The tile contains just the background color, skip it.
attrs.protoPaletteID = AttrmapEntry::background;
continue;
case ProtoPalette::WE_BIGGER:
if (options.bgColorStrict) {
warning(
"Tile (%" PRIu32 ", %" PRIu32 ") contains the background color (#%06" PRIx32
")!",
tile.x,
tile.y,
options.bgColor->toCSS() >> 8
);
}
break;
case ProtoPalette::NEITHER:
break;
}
}

// Insert the proto-palette, making sure to avoid overlaps
for (size_t n = 0; n < protoPalettes.size(); ++n) {
switch (protoPalette.compare(protoPalettes[n])) {
Expand Down Expand Up @@ -1188,7 +1223,7 @@ void process() {
}

attrs.protoPaletteID = protoPalettes.size();
if (protoPalettes.size() == AttrmapEntry::transparent) { // Check for overflow
if (protoPalettes.size() == AttrmapEntry::background) { // Check for overflow
fatal(
"Reached %zu proto-palettes... sorry, this image is too much for me to handle :(",
AttrmapEntry::transparent
Expand Down

0 comments on commit f3329b4

Please sign in to comment.