mirror of
https://github.com/gbdev/rgbds.git
synced 2025-11-20 18:22:07 +00:00
Fix transparency handling
Ensure that the color count is properly used, and that transparency is not counted as a color when packing palettes
This commit is contained in:
@@ -34,6 +34,7 @@ public:
|
|||||||
ComparisonResult compare(ProtoPalette const &other) const;
|
ComparisonResult compare(ProtoPalette const &other) const;
|
||||||
|
|
||||||
size_t size() const;
|
size_t size() const;
|
||||||
|
bool empty() const;
|
||||||
|
|
||||||
decltype(_colorIndices)::const_iterator begin() const;
|
decltype(_colorIndices)::const_iterator begin() const;
|
||||||
decltype(_colorIndices)::const_iterator end() const;
|
decltype(_colorIndices)::const_iterator end() const;
|
||||||
|
|||||||
@@ -441,6 +441,8 @@ struct AttrmapEntry {
|
|||||||
bool bank;
|
bool bank;
|
||||||
bool yFlip;
|
bool yFlip;
|
||||||
bool xFlip;
|
bool xFlip;
|
||||||
|
|
||||||
|
static constexpr decltype(protoPaletteID) transparent = SIZE_MAX;
|
||||||
};
|
};
|
||||||
|
|
||||||
static std::tuple<DefaultInitVec<size_t>, std::vector<Palette>>
|
static std::tuple<DefaultInitVec<size_t>, std::vector<Palette>>
|
||||||
@@ -459,6 +461,13 @@ static std::tuple<DefaultInitVec<size_t>, std::vector<Palette>>
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::vector<Palette> palettes(nbPalettes);
|
std::vector<Palette> palettes(nbPalettes);
|
||||||
|
// If the image contains at least one transparent pixel, force transparency in the first slot of
|
||||||
|
// all palettes
|
||||||
|
if (options.hasTransparentPixels) {
|
||||||
|
for (Palette &pal : palettes) {
|
||||||
|
pal.colors[0] = Rgba::transparent;
|
||||||
|
}
|
||||||
|
}
|
||||||
// Generate the actual palettes from the mappings
|
// Generate the actual palettes from the mappings
|
||||||
for (size_t protoPalID = 0; protoPalID < mappings.size(); ++protoPalID) {
|
for (size_t protoPalID = 0; protoPalID < mappings.size(); ++protoPalID) {
|
||||||
auto &pal = palettes[mappings[protoPalID]];
|
auto &pal = palettes[mappings[protoPalID]];
|
||||||
@@ -676,7 +685,9 @@ static void outputTileData(Png const &png, DefaultInitVec<AttrmapEntry> const &a
|
|||||||
|
|
||||||
auto iter = attrmap.begin();
|
auto iter = attrmap.begin();
|
||||||
for (auto tile : png.visitAsTiles(options.columnMajor)) {
|
for (auto tile : png.visitAsTiles(options.columnMajor)) {
|
||||||
Palette const &palette = palettes[mappings[iter->protoPaletteID]];
|
size_t protoPaletteID = iter->protoPaletteID;
|
||||||
|
// If the tile is fully transparent, default to palette 0
|
||||||
|
Palette const &palette = palettes[protoPaletteID != AttrmapEntry::transparent ? mappings[protoPaletteID] : 0];
|
||||||
for (uint32_t y = 0; y < 8; ++y) {
|
for (uint32_t y = 0; y < 8; ++y) {
|
||||||
uint16_t bitplanes = TileData::rowBitplanes(tile, palette, y);
|
uint16_t bitplanes = TileData::rowBitplanes(tile, palette, y);
|
||||||
output.sputc(bitplanes & 0xFF);
|
output.sputc(bitplanes & 0xFF);
|
||||||
@@ -843,7 +854,7 @@ void process() {
|
|||||||
options.verbosePrint(Options::VERB_CFG, "Using libpng %s\n", png_get_libpng_ver(nullptr));
|
options.verbosePrint(Options::VERB_CFG, "Using libpng %s\n", png_get_libpng_ver(nullptr));
|
||||||
|
|
||||||
options.verbosePrint(Options::VERB_LOG_ACT, "Reading tiles...\n");
|
options.verbosePrint(Options::VERB_LOG_ACT, "Reading tiles...\n");
|
||||||
Png png(options.input);
|
Png png(options.input); // This also sets `hasTransparentPixels` as a side effect
|
||||||
ImagePalette const &colors = png.getColors();
|
ImagePalette const &colors = png.getColors();
|
||||||
|
|
||||||
// Now, we have all the image's colors in `colors`
|
// Now, we have all the image's colors in `colors`
|
||||||
@@ -851,13 +862,11 @@ void process() {
|
|||||||
|
|
||||||
if (options.verbosity >= Options::VERB_INTERM) {
|
if (options.verbosity >= Options::VERB_INTERM) {
|
||||||
fputs("Image colors: [ ", stderr);
|
fputs("Image colors: [ ", stderr);
|
||||||
size_t i = 0;
|
|
||||||
for (auto const &slot : colors) {
|
for (auto const &slot : colors) {
|
||||||
if (!slot.has_value()) {
|
if (!slot.has_value()) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
fprintf(stderr, "#%08x%s", slot->toCSS(), i != colors.size() - 1 ? ", " : "");
|
fprintf(stderr, "#%08x, ", slot->toCSS());
|
||||||
++i;
|
|
||||||
}
|
}
|
||||||
fputs("]\n", stderr);
|
fputs("]\n", stderr);
|
||||||
}
|
}
|
||||||
@@ -875,9 +884,18 @@ void process() {
|
|||||||
|
|
||||||
for (uint32_t y = 0; y < 8; ++y) {
|
for (uint32_t y = 0; y < 8; ++y) {
|
||||||
for (uint32_t x = 0; x < 8; ++x) {
|
for (uint32_t x = 0; x < 8; ++x) {
|
||||||
tileColors.add(tile.pixel(x, y).cgbColor());
|
Rgba color = tile.pixel(x, y);
|
||||||
|
if (!color.isTransparent()) { // Do not count transparency in for packing
|
||||||
|
tileColors.add(color.cgbColor());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tileColors.empty()) {
|
||||||
|
// "Empty" proto-palettes screw with the packing process, so discard those
|
||||||
|
attrs.protoPaletteID = AttrmapEntry::transparent;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
// Insert the proto-palette, making sure to avoid overlaps
|
// Insert the proto-palette, making sure to avoid overlaps
|
||||||
for (size_t n = 0; n < protoPalettes.size(); ++n) {
|
for (size_t n = 0; n < protoPalettes.size(); ++n) {
|
||||||
@@ -909,12 +927,24 @@ void process() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
attrs.protoPaletteID = protoPalettes.size();
|
attrs.protoPaletteID = protoPalettes.size();
|
||||||
|
if (protoPalettes.size() == AttrmapEntry::transparent) {
|
||||||
|
abort(); // TODO: nice error message
|
||||||
|
}
|
||||||
protoPalettes.push_back(tileColors);
|
protoPalettes.push_back(tileColors);
|
||||||
contained:;
|
contained:;
|
||||||
}
|
}
|
||||||
|
|
||||||
options.verbosePrint(Options::VERB_INTERM, "Image contains %zu proto-palette%s\n",
|
options.verbosePrint(Options::VERB_INTERM, "Image contains %zu proto-palette%s\n",
|
||||||
protoPalettes.size(), protoPalettes.size() != 1 ? "s" : "");
|
protoPalettes.size(), protoPalettes.size() != 1 ? "s" : "");
|
||||||
|
if (options.verbosity >= Options::VERB_INTERM) {
|
||||||
|
for (auto const &protoPal : protoPalettes) {
|
||||||
|
fputs("[ ", stderr);
|
||||||
|
for (uint16_t color : protoPal) {
|
||||||
|
fprintf(stderr, "$%04x, ", color);
|
||||||
|
}
|
||||||
|
fputs("]\n", stderr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Sort the proto-palettes by size, which improves the packing algorithm's efficiency
|
// Sort the proto-palettes by size, which improves the packing algorithm's efficiency
|
||||||
// We sort after all insertions to avoid moving items: https://stackoverflow.com/a/2710332
|
// We sort after all insertions to avoid moving items: https://stackoverflow.com/a/2710332
|
||||||
|
|||||||
@@ -401,7 +401,7 @@ std::tuple<DefaultInitVec<size_t>, size_t>
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
options.verbosePrint(Options::VERB_DEBUG, "%zu/%zu: Rel size: %f (size = %zu)\n", i,
|
options.verbosePrint(Options::VERB_DEBUG, "%zu/%zu: Rel size: %f (size = %zu)\n", i + 1,
|
||||||
assignments.size(), assignments[i].relSizeOf(protoPal),
|
assignments.size(), assignments[i].relSizeOf(protoPal),
|
||||||
protoPal.size());
|
protoPal.size());
|
||||||
if (assignments[i].relSizeOf(protoPal) < bestRelSize) {
|
if (assignments[i].relSizeOf(protoPal) < bestRelSize) {
|
||||||
|
|||||||
@@ -66,6 +66,10 @@ size_t ProtoPalette::size() const {
|
|||||||
return std::distance(begin(), end());
|
return std::distance(begin(), end());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool ProtoPalette::empty() const {
|
||||||
|
return _colorIndices[0] == UINT16_MAX;
|
||||||
|
}
|
||||||
|
|
||||||
auto ProtoPalette::begin() const -> decltype(_colorIndices)::const_iterator {
|
auto ProtoPalette::begin() const -> decltype(_colorIndices)::const_iterator {
|
||||||
return _colorIndices.begin();
|
return _colorIndices.begin();
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user