diff --git a/include/gfx/proto_palette.hpp b/include/gfx/proto_palette.hpp index 1dd9c332..33bf4db8 100644 --- a/include/gfx/proto_palette.hpp +++ b/include/gfx/proto_palette.hpp @@ -34,6 +34,7 @@ public: ComparisonResult compare(ProtoPalette const &other) const; size_t size() const; + bool empty() const; decltype(_colorIndices)::const_iterator begin() const; decltype(_colorIndices)::const_iterator end() const; diff --git a/src/gfx/convert.cpp b/src/gfx/convert.cpp index 8042463c..aa0249a0 100644 --- a/src/gfx/convert.cpp +++ b/src/gfx/convert.cpp @@ -441,6 +441,8 @@ struct AttrmapEntry { bool bank; bool yFlip; bool xFlip; + + static constexpr decltype(protoPaletteID) transparent = SIZE_MAX; }; static std::tuple, std::vector> @@ -459,6 +461,13 @@ static std::tuple, std::vector> } std::vector 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 for (size_t protoPalID = 0; protoPalID < mappings.size(); ++protoPalID) { auto &pal = palettes[mappings[protoPalID]]; @@ -676,7 +685,9 @@ static void outputTileData(Png const &png, DefaultInitVec const &a auto iter = attrmap.begin(); 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) { uint16_t bitplanes = TileData::rowBitplanes(tile, palette, y); 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_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(); // Now, we have all the image's colors in `colors` @@ -851,13 +862,11 @@ void process() { if (options.verbosity >= Options::VERB_INTERM) { fputs("Image colors: [ ", stderr); - size_t i = 0; for (auto const &slot : colors) { if (!slot.has_value()) { continue; } - fprintf(stderr, "#%08x%s", slot->toCSS(), i != colors.size() - 1 ? ", " : ""); - ++i; + fprintf(stderr, "#%08x, ", slot->toCSS()); } fputs("]\n", stderr); } @@ -875,10 +884,19 @@ void process() { for (uint32_t y = 0; y < 8; ++y) { 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 for (size_t n = 0; n < protoPalettes.size(); ++n) { switch (tileColors.compare(protoPalettes[n])) { @@ -909,12 +927,24 @@ void process() { } } attrs.protoPaletteID = protoPalettes.size(); + if (protoPalettes.size() == AttrmapEntry::transparent) { + abort(); // TODO: nice error message + } protoPalettes.push_back(tileColors); contained:; } options.verbosePrint(Options::VERB_INTERM, "Image contains %zu proto-palette%s\n", 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 // We sort after all insertions to avoid moving items: https://stackoverflow.com/a/2710332 diff --git a/src/gfx/pal_packing.cpp b/src/gfx/pal_packing.cpp index ed8921d9..92c451ee 100644 --- a/src/gfx/pal_packing.cpp +++ b/src/gfx/pal_packing.cpp @@ -401,7 +401,7 @@ std::tuple, size_t> 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), protoPal.size()); if (assignments[i].relSizeOf(protoPal) < bestRelSize) { diff --git a/src/gfx/proto_palette.cpp b/src/gfx/proto_palette.cpp index 3dbcb10c..91ec8b92 100644 --- a/src/gfx/proto_palette.cpp +++ b/src/gfx/proto_palette.cpp @@ -66,6 +66,10 @@ size_t ProtoPalette::size() const { return std::distance(begin(), end()); } +bool ProtoPalette::empty() const { + return _colorIndices[0] == UINT16_MAX; +} + auto ProtoPalette::begin() const -> decltype(_colorIndices)::const_iterator { return _colorIndices.begin(); }