Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement --background-color #1508

Draft
wants to merge 1 commit into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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`.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is quite surprising; what's ProtoPalette::NEITHER for then?

// 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
Loading