Skip to content

Commit 9bdf65d

Browse files
ISSOtmRangi42
authored andcommitted
Implement --background
TODO: document the behaviour
1 parent 6982c8a commit 9bdf65d

File tree

7 files changed

+120
-37
lines changed

7 files changed

+120
-37
lines changed

contrib/bash_compl/_rgbgfx.bash

+1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ _rgbgfx_completions() {
1818
[Z]="columns:normal"
1919
[a]="attr-map:glob-*.attrmap"
2020
[A]="auto-attr-map:normal"
21+
[B]="background-color:unk"
2122
[b]="base-tiles:unk"
2223
[c]="colors:unk"
2324
[d]="depth:unk"

contrib/zsh_compl/_rgbgfx

+1
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ local args=(
2727
'(-Z --columns)'{-Z,--columns}'[Read the image in column-major order]'
2828

2929
'(-a --attr-map -A --auto-attr-map)'{-a,--attr-map}'+[Generate a map of tile attributes (mirroring)]:attrmap file:_files'
30+
'(-B --background-color)'{-B,--background-color}'+[Ignore tiles containing only specified color]:color:'
3031
'(-b --base-tiles)'{-b,--base-tiles}'+[Base tile IDs for tile map output]:base tile IDs:'
3132
'(-c --colors)'{-c,--colors}'+[Specify color palettes]:palette spec:'
3233
'(-d --depth)'{-d,--depth}'+[Set bit depth]:bit depth:_depths'

include/gfx/main.hpp

+28-1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010
#include <utility>
1111
#include <vector>
1212

13+
#include "helpers.hpp"
14+
1315
#include "gfx/rgba.hpp"
1416

1517
struct Options {
@@ -20,7 +22,9 @@ struct Options {
2022
bool columnMajor = false; // -Z
2123
uint8_t verbosity = 0; // -v
2224

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

128+
// Parsing helpers.
129+
130+
constexpr uint8_t nibble(char c) {
131+
if (c >= 'a') {
132+
assume(c <= 'f');
133+
return c - 'a' + 10;
134+
} else if (c >= 'A') {
135+
assume(c <= 'F');
136+
return c - 'A' + 10;
137+
} else {
138+
assume(c >= '0' && c <= '9');
139+
return c - '0';
140+
}
141+
}
142+
143+
constexpr uint8_t toHex(char c1, char c2) {
144+
return nibble(c1) * 16 + nibble(c2);
145+
}
146+
147+
constexpr uint8_t singleToHex(char c) {
148+
return toHex(c, c);
149+
}
150+
124151
#endif // RGBDS_GFX_MAIN_HPP

man/rgbgfx.1

+2
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,8 @@ and has the same size.
103103
Same as
104104
.Fl a Ar base_path Ns .attrmap
105105
.Pq see Sx Automatic output paths .
106+
.It Fl B Ar color , Fl \-background-color Ar color
107+
TODO
106108
.It Fl b Ar base_ids , Fl \-base-tiles Ar base_ids
107109
Set the base IDs for tile map output.
108110
.Ar base_ids

src/gfx/main.cpp

+39-1
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ void Options::verbosePrint(uint8_t level, char const *fmt, ...) const {
108108
}
109109

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

113113
/*
114114
* Equivalent long options
@@ -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";
123123
static option const longopts[] = {
124124
{"auto-attr-map", no_argument, nullptr, 'A'},
125125
{"attr-map", required_argument, nullptr, 'a'},
126+
{"background-color", required_argument, nullptr, 'B'},
126127
{"base-tiles", required_argument, nullptr, 'b'},
127128
{"color-curve", no_argument, nullptr, 'C'},
128129
{"colors", required_argument, nullptr, 'c'},
@@ -367,6 +368,43 @@ static char *parseArgv(int argc, char *argv[]) {
367368
warning("Overriding attrmap file %s", options.attrmap.c_str());
368369
options.attrmap = musl_optarg;
369370
break;
371+
case 'B':
372+
if (musl_optarg[0] != '#' || musl_optarg[1] == '\0') {
373+
error("Background color specification must be either `#rgb` or `#rrggbb`");
374+
} else {
375+
size_t colorLen = strspn(&musl_optarg[1], "0123456789ABCDEFabcdef");
376+
switch (colorLen) {
377+
case 3:
378+
options.bgColor = Rgba(
379+
singleToHex(musl_optarg[1]),
380+
singleToHex(musl_optarg[2]),
381+
singleToHex(musl_optarg[3]),
382+
0xFF
383+
);
384+
break;
385+
case 6:
386+
options.bgColor = Rgba(
387+
toHex(musl_optarg[1], musl_optarg[2]),
388+
toHex(musl_optarg[3], musl_optarg[4]),
389+
toHex(musl_optarg[5], musl_optarg[6]),
390+
0xFF
391+
);
392+
break;
393+
default:
394+
error("Unknown background color specification \"%s\"", musl_optarg);
395+
}
396+
397+
options.bgColorStrict = true;
398+
if (musl_optarg[colorLen + 1] == '!') {
399+
options.bgColorStrict = false;
400+
++colorLen;
401+
}
402+
403+
if (musl_optarg[colorLen + 1] != '\0') {
404+
error("Unexpected text \"%s\" after background color specification", &musl_optarg[colorLen + 1]);
405+
}
406+
}
407+
break;
370408
case 'b':
371409
number = parseNumber(arg, "Bank 0 base tile ID", 0);
372410
if (number >= 256) {

src/gfx/pal_spec.cpp

-21
Original file line numberDiff line numberDiff line change
@@ -23,27 +23,6 @@
2323

2424
using namespace std::string_view_literals;
2525

26-
constexpr uint8_t nibble(char c) {
27-
if (c >= 'a') {
28-
assume(c <= 'f');
29-
return c - 'a' + 10;
30-
} else if (c >= 'A') {
31-
assume(c <= 'F');
32-
return c - 'A' + 10;
33-
} else {
34-
assume(c >= '0' && c <= '9');
35-
return c - '0';
36-
}
37-
}
38-
39-
constexpr uint8_t toHex(char c1, char c2) {
40-
return nibble(c1) * 16 + nibble(c2);
41-
}
42-
43-
constexpr uint8_t singleToHex(char c) {
44-
return toHex(c, c);
45-
}
46-
4726
template<typename Str> // Should be std::string or std::string_view
4827
static void skipWhitespace(Str const &str, typename Str::size_type &pos) {
4928
pos = std::min(str.find_first_not_of(" \t"sv, pos), str.length());

src/gfx/process.cpp

+49-14
Original file line numberDiff line numberDiff line change
@@ -528,9 +528,11 @@ struct AttrmapEntry {
528528
bool xFlip;
529529

530530
static constexpr decltype(protoPaletteID) transparent = SIZE_MAX;
531+
static constexpr decltype(protoPaletteID) background = transparent - 1;
531532

533+
bool isBackgroundTile() const { return protoPaletteID == background; }
532534
size_t getPalID(DefaultInitVec<size_t> const &mappings) const {
533-
return protoPaletteID == transparent ? 0 : mappings[protoPaletteID];
535+
return isBackgroundTile() ? 0xFF : mappings[protoPaletteID == transparent ? 0 : protoPaletteID];
534536
}
535537
};
536538

@@ -851,13 +853,16 @@ static void outputUnoptimizedTileData(
851853
remainingTiles -= options.trim;
852854

853855
for (auto [tile, attr] : zip(png.visitAsTiles(), attrmap)) {
854-
// If the tile is fully transparent, default to palette 0
855-
Palette const &palette = palettes[attr.getPalID(mappings)];
856-
for (uint32_t y = 0; y < 8; ++y) {
857-
uint16_t bitplanes = TileData::rowBitplanes(tile, palette, y);
858-
output->sputc(bitplanes & 0xFF);
859-
if (options.bitDepth == 2) {
860-
output->sputc(bitplanes >> 8);
856+
// Do not emit fully-background tiles.
857+
if (!attr.isBackgroundTile()) {
858+
// If the tile is fully transparent, this defaults to palette 0.
859+
Palette const &palette = palettes[attr.getPalID(mappings)];
860+
for (uint32_t y = 0; y < 8; ++y) {
861+
uint16_t bitplanes = TileData::rowBitplanes(tile, palette, y);
862+
output->sputc(bitplanes & 0xFF);
863+
if (options.bitDepth == 2) {
864+
output->sputc(bitplanes >> 8);
865+
}
861866
}
862867
}
863868

@@ -897,14 +902,18 @@ static void outputUnoptimizedMaps(
897902
if (tilemapOutput.has_value()) {
898903
(*tilemapOutput)->sputc(tileID + options.baseTileIDs[bank]);
899904
}
905+
uint8_t palID = attr.getPalID(mappings);
900906
if (attrmapOutput.has_value()) {
901-
uint8_t palID = attr.getPalID(mappings) & 7;
902-
(*attrmapOutput)->sputc(palID | bank << 3); // The other flags are all 0
907+
(*attrmapOutput)->sputc((palID & 7) | bank << 3); // The other flags are all 0
903908
}
904909
if (palmapOutput.has_value()) {
905-
(*palmapOutput)->sputc(attr.getPalID(mappings));
910+
(*palmapOutput)->sputc(palID);
911+
}
912+
913+
// Background tiles are skipped in the tile data, so they should be skipped in the maps too.
914+
if (!attr.isBackgroundTile()) {
915+
++tileID;
906916
}
907-
++tileID;
908917
}
909918
}
910919

@@ -1000,7 +1009,7 @@ static UniqueTiles dedupTiles(
10001009
}
10011010

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

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

11251138
for (auto tile : png.visitAsTiles()) {
11261139
AttrmapEntry &attrs = attrmap.emplace_back();
@@ -1156,6 +1169,28 @@ void process() {
11561169
protoPalette.add(cgbColor);
11571170
}
11581171

1172+
if (options.bgColor.has_value()) {
1173+
switch (protoPalette.compare(bgPal)) {
1174+
case ProtoPalette::THEY_BIGGER: // Note that ties are resolved as `THEY_BIGGER`.
1175+
// The tile contains just the background color, skip it.
1176+
attrs.protoPaletteID = AttrmapEntry::background;
1177+
continue;
1178+
case ProtoPalette::WE_BIGGER:
1179+
if (options.bgColorStrict) {
1180+
warning(
1181+
"Tile (%" PRIu32 ", %" PRIu32 ") contains the background color (#%06" PRIx32
1182+
")!",
1183+
tile.x,
1184+
tile.y,
1185+
options.bgColor->toCSS() >> 8
1186+
);
1187+
}
1188+
break;
1189+
case ProtoPalette::NEITHER:
1190+
break;
1191+
}
1192+
}
1193+
11591194
// Insert the proto-palette, making sure to avoid overlaps
11601195
for (size_t n = 0; n < protoPalettes.size(); ++n) {
11611196
switch (protoPalette.compare(protoPalettes[n])) {
@@ -1188,7 +1223,7 @@ void process() {
11881223
}
11891224

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

0 commit comments

Comments
 (0)