diff --git a/include/gfx/convert.hpp b/include/gfx/convert.hpp index 0e7afc64..0dc42fb3 100644 --- a/include/gfx/convert.hpp +++ b/include/gfx/convert.hpp @@ -23,9 +23,14 @@ struct Rgba { Rgba(uint8_t r, uint8_t g, uint8_t b, uint8_t a) : red(r), green(g), blue(b), alpha(a) {} Rgba(uint32_t rgba) : red(rgba), green(rgba >> 8), blue(rgba >> 16), alpha(rgba >> 24) {} - operator uint32_t() const { + operator uint32_t() const { return toCSS(); } + /** + * Returns this RGBA as a 32-bit number that can be printed in hex (`%08x`) to yield its CSS + * representation + */ + uint32_t toCSS() const { auto shl = [](uint8_t val, unsigned shift) { return static_cast(val) << shift; }; - return shl(red, 0) | shl(green, 8) | shl(blue, 16) | shl(alpha, 24); + return shl(red, 24) | shl(green, 16) | shl(blue, 8) | shl(alpha, 0); } bool operator!=(Rgba const &other) const { return static_cast(*this) != static_cast(other); @@ -39,12 +44,15 @@ struct Rgba { */ static constexpr uint16_t transparent = 0b1'00000'00000'00000; - static constexpr uint8_t transparency_threshold = 5; // TODO: adjust this + /** + * All alpha values strictly below this will be considered transparent + */ + static constexpr uint8_t opacity_threshold = 0xF0; // TODO: adjust this /** * Computes the equivalent CGB color, respects the color curve depending on options */ uint16_t cgbColor() const { - if (alpha > 0xFF - transparency_threshold) + if (alpha < opacity_threshold) return transparent; if (options.useColorCurve) { assert(!"TODO"); diff --git a/include/gfx/main.hpp b/include/gfx/main.hpp index c7aa51e0..9a34cc34 100644 --- a/include/gfx/main.hpp +++ b/include/gfx/main.hpp @@ -11,6 +11,7 @@ #include #include +#include #include #include "helpers.h" @@ -27,7 +28,7 @@ struct Options { uint8_t nbPalettes = 8; // TODO uint8_t nbColorsPerPal = 0; // TODO; 0 means "auto" = 1 << bitDepth; std::array baseTileIDs{0, 0}; // TODO - std::array maxNbTiles{384, 0}; // TODO + std::array maxNbTiles{UINT16_MAX, 0}; // TODO std::filesystem::path tilemap{}; // -t, -T std::filesystem::path attrmap{}; // -a, -A std::filesystem::path palettes{}; // -p, -P @@ -35,6 +36,9 @@ struct Options { std::filesystem::path input{}; // positional arg format_(printf, 2, 3) void verbosePrint(char const *fmt, ...) const; + uint8_t maxPalSize() const { + return nbColorsPerPal; + } // TODO: minus 1 when transparency is active }; extern Options options; diff --git a/src/gfx/convert.cpp b/src/gfx/convert.cpp index 5185dcd5..2f6d12fb 100644 --- a/src/gfx/convert.cpp +++ b/src/gfx/convert.cpp @@ -44,7 +44,8 @@ public: if (!slot.has_value()) { slot.emplace(rgba); } else if (*slot != rgba) { - warning("Different colors melded together"); // TODO: indicate position + warning("Different colors melded together (#%08x into #%08x as %04x)", rgba.toCSS(), + slot->toCSS(), rgba.cgbColor()); // TODO: indicate position } } @@ -363,7 +364,7 @@ public: public: iterator begin() const { return {*this, _width, 0, 0}; } iterator end() const { - iterator it{*this, _width, _width - 8, _height - 8}; // Last valid one + iterator it{*this, _limit, _width - 8, _height - 8}; // Last valid one return ++it; // Now one-past-last } }; @@ -443,7 +444,9 @@ static void outputTileData(Png const &png, DefaultInitVec const &a } } output.sputc(row & 0xFF); - output.sputc(row >> 8); + if (options.bitDepth == 2) { + output.sputc(row >> 8); + } } ++iter; } @@ -505,6 +508,7 @@ public: mutable size_t tileID; TileData(Png::TilesVisitor::Tile const &tile, Palette const &palette) : _hash(0) { + size_t writeIndex = 0; for (uint32_t y = 0; y < 8; ++y) { uint16_t bitplanes = 0; for (uint32_t x = 0; x < 8; ++x) { @@ -517,8 +521,10 @@ public: bitplanes |= 0x100; } } - _data[y * 2] = bitplanes & 0xFF; - _data[y * 2 + 1] = bitplanes >> 8; + _data[writeIndex++] = bitplanes & 0xFF; + if (options.bitDepth == 2) { + _data[writeIndex++] = bitplanes >> 8; + } // Update the hash _hash ^= bitplanes; @@ -632,7 +638,7 @@ static void outputTileData(UniqueTiles const &tiles) { for (TileData const *tile : tiles) { assert(tile->tileID == tileID); ++tileID; - output.sputn(reinterpret_cast(tile->data().data()), tile->data().size()); + output.sputn(reinterpret_cast(tile->data().data()), options.bitDepth * 8); } } diff --git a/src/gfx/pal_packing.cpp b/src/gfx/pal_packing.cpp index 6d8ea962..c01edde8 100644 --- a/src/gfx/pal_packing.cpp +++ b/src/gfx/pal_packing.cpp @@ -203,10 +203,8 @@ public: size_t volume() const { return uniqueColors().size(); } bool canFit(ProtoPalette const &protoPal) const { auto &colors = uniqueColors(); - for (uint16_t color : protoPal) { - colors.insert(color); - } - return colors.size() <= 4; + colors.insert(protoPal.begin(), protoPal.end()); + return colors.size() <= options.maxPalSize(); } }; @@ -274,9 +272,9 @@ std::tuple, size_t> bestPal.assign(std::move(attrs)); // If this overloads the palette, get it back to normal (if possible) - while (bestPal.volume() > 4) { - options.verbosePrint("Palette %zu is overloaded! (%zu > 4)\n", bestPalIndex, - bestPal.volume()); + while (bestPal.volume() > options.maxPalSize()) { + options.verbosePrint("Palette %zu is overloaded! (%zu > %" PRIu8 ")\n", + bestPalIndex, bestPal.volume(), options.maxPalSize()); // Look for a proto-pal minimizing "efficiency" (size / rel_size) auto efficiency = [&bestPal](ProtoPalette const &pal) { @@ -309,7 +307,7 @@ std::tuple, size_t> // Deal with palettes still overloaded, by emptying them for (AssignedProtos &pal : assignments) { - if (pal.volume() > 4) { + if (pal.volume() > options.maxPalSize()) { for (ProtoPalAttrs &attrs : pal) { queue.emplace(std::move(attrs)); }