shithub: rgbds

Download patch

ref: 3c9d5b05d6984ba912a27552e9a5b34db77abc57
parent: e86eb9337a8e11b029327cef43861ee15e660285
author: ISSOtm <[email protected]>
date: Sun Mar 13 07:28:20 EDT 2022

Implement transparency handling

Though none of this has been tested so far...

--- a/include/gfx/main.hpp
+++ b/include/gfx/main.hpp
@@ -56,9 +56,9 @@
 	static constexpr uint8_t VERB_UNMAPPED = 5; // Unused so far
 	static constexpr uint8_t VERB_VVVVVV = 6; // What, can't I have a little fun?
 	format_(printf, 3, 4) void verbosePrint(uint8_t level, char const *fmt, ...) const;
-	uint8_t maxPalSize() const {
-		return nbColorsPerPal; // TODO: minus 1 when transparency is active
-	}
+
+	mutable bool hasTransparentPixels = false;
+	uint8_t maxOpaqueColors() const { return nbColorsPerPal - hasTransparentPixels; }
 };
 
 extern Options options;
--- a/src/gfx/convert.cpp
+++ b/src/gfx/convert.cpp
@@ -41,6 +41,10 @@
 	void registerColor(Rgba const &rgba) {
 		decltype(_colors)::value_type &slot = _colors[rgba.cgbColor()];
 
+		if (rgba.cgbColor() == Rgba::transparent) {
+			options.hasTransparentPixels = true;
+		}
+
 		if (!slot.has_value()) {
 			slot.emplace(rgba);
 		} else if (*slot != rgba) {
@@ -120,10 +124,10 @@
 
 	bool isSuitableForGrayscale() const {
 		// Check that all of the grays don't fall into the same "bin"
-		if (colors.size() > options.maxPalSize()) { // Apply the Pigeonhole Principle
+		if (colors.size() > options.maxOpaqueColors()) { // Apply the Pigeonhole Principle
 			options.verbosePrint(Options::VERB_DEBUG,
 			                     "Too many colors for grayscale sorting (%zu > %" PRIu8 ")\n",
-			                     colors.size(), options.maxPalSize());
+			                     colors.size(), options.maxOpaqueColors());
 			return false;
 		}
 		uint8_t bins = 0;
@@ -490,8 +494,8 @@
 		assert(options.palSpec.size() == 1);
 		// TODO: abort if ignored colors are being used; do it now for a friendlier error
 		// message
-		if (embPalSize > options.maxPalSize()) { // Ignore extraneous colors if they are unused
-			embPalSize = options.maxPalSize();
+		if (embPalSize > options.maxOpaqueColors()) { // Ignore extraneous colors if they are unused
+			embPalSize = options.maxOpaqueColors();
 		}
 		for (int i = 0; i < embPalSize; ++i) {
 			options.palSpec[0][i] = Rgba(embPalRGB[i].red, embPalRGB[i].green, embPalRGB[i].blue,
@@ -503,7 +507,7 @@
 	std::vector<Palette> palettes(options.palSpec.size());
 	auto palIter = palettes.begin(); // TODO: `zip`
 	for (auto const &spec : options.palSpec) {
-		for (size_t i = 0; i < options.maxPalSize(); ++i) {
+		for (size_t i = 0; i < options.nbColorsPerPal; ++i) {
 			(*palIter)[i] = spec[i].cgbColor();
 		}
 		++palIter;
@@ -532,7 +536,8 @@
 	output.open(options.palettes, std::ios_base::out | std::ios_base::binary);
 
 	for (Palette const &palette : palettes) {
-		for (uint16_t color : palette) {
+		for (uint8_t i = 0; i < options.nbColorsPerPal; ++i) {
+			uint16_t color = palette.colors[i]; // Will return `UINT16_MAX` for unused slots
 			output.sputc(color & 0xFF);
 			output.sputc(color >> 8);
 		}
--- a/src/gfx/main.cpp
+++ b/src/gfx/main.cpp
@@ -543,17 +543,19 @@
 }
 
 auto Palette::begin() -> decltype(colors)::iterator {
-	return colors.begin();
+	// Skip the first slot if reserved for transparency
+	return colors.begin() + options.hasTransparentPixels;
 }
 auto Palette::end() -> decltype(colors)::iterator {
-	return std::find(colors.begin(), colors.end(), UINT16_MAX);
+	return std::find(begin(), colors.end(), UINT16_MAX);
 }
 
 auto Palette::begin() const -> decltype(colors)::const_iterator {
-	return colors.begin();
+	// Skip the first slot if reserved for transparency
+	return colors.begin() + options.hasTransparentPixels;
 }
 auto Palette::end() const -> decltype(colors)::const_iterator {
-	return std::find(colors.begin(), colors.end(), UINT16_MAX);
+	return std::find(begin(), colors.end(), UINT16_MAX);
 }
 
 uint8_t Palette::size() const {
--- a/src/gfx/pal_packing.cpp
+++ b/src/gfx/pal_packing.cpp
@@ -201,7 +201,7 @@
 	bool canFit(ProtoPalette const &protoPal) const {
 		auto &colors = uniqueColors();
 		colors.insert(protoPal.begin(), protoPal.end());
-		return colors.size() <= options.maxPalSize();
+		return colors.size() <= options.maxOpaqueColors();
 	}
 
 	/**
@@ -275,7 +275,8 @@
 	// Decant on palettes
 	decantOn([&protoPalettes](AssignedProtos &to, AssignedProtos &from) {
 		// If the entire palettes can be merged, move all of `from`'s proto-palettes
-		if (to.combinedVolume(from.begin(), from.end(), protoPalettes) <= options.maxPalSize()) {
+		if (to.combinedVolume(from.begin(), from.end(), protoPalettes)
+		    <= options.maxOpaqueColors()) {
 			for (ProtoPalAttrs &attrs : from) {
 				to.assign(attrs.protoPalIndex);
 			}
@@ -321,7 +322,7 @@
 				++attrs;
 			} while (iter != processed.end());
 
-			if (to.combinedVolume(colors.begin(), colors.end()) <= options.maxPalSize()) {
+			if (to.combinedVolume(colors.begin(), colors.end()) <= options.maxOpaqueColors()) {
 				// Iterate through the component's proto-palettes, and transfer them
 				auto member = from.begin();
 				size_t curIndex = 0;
@@ -417,10 +418,10 @@
 			bestPal.assign(std::move(attrs));
 
 			// If this overloads the palette, get it back to normal (if possible)
-			while (bestPal.volume() > options.maxPalSize()) {
+			while (bestPal.volume() > options.maxOpaqueColors()) {
 				options.verbosePrint(Options::VERB_DEBUG,
 				                     "Palette %zu is overloaded! (%zu > %" PRIu8 ")\n",
-				                     bestPalIndex, bestPal.volume(), options.maxPalSize());
+				                     bestPalIndex, bestPal.volume(), options.maxOpaqueColors());
 
 				// Look for a proto-pal minimizing "efficiency" (size / rel_size)
 				auto efficiency = [&bestPal](ProtoPalette const &pal) {
@@ -453,7 +454,7 @@
 
 	// Deal with palettes still overloaded, by emptying them
 	for (AssignedProtos &pal : assignments) {
-		if (pal.volume() > options.maxPalSize()) {
+		if (pal.volume() > options.maxOpaqueColors()) {
 			for (ProtoPalAttrs &attrs : pal) {
 				queue.emplace(std::move(attrs));
 			}
--- a/src/gfx/pal_sorting.cpp
+++ b/src/gfx/pal_sorting.cpp
@@ -30,8 +30,8 @@
 		Palette &palette = palettes[0];
 		// Build our candidate array of colors
 		decltype(palette.colors) colors{UINT16_MAX, UINT16_MAX, UINT16_MAX, UINT16_MAX};
-		for (int i = 0; i < options.maxPalSize(); ++i) {
-			colors[i] = pngToRgb(i).cgbColor();
+		for (int i = 0; i < options.maxOpaqueColors(); ++i) {
+			colors[i + options.hasTransparentPixels] = pngToRgb(i).cgbColor();
 		}
 
 		// Check that the palette only uses those colors
@@ -38,7 +38,7 @@
 		if (std::all_of(palette.begin(), palette.end(), [&colors](uint16_t color) {
 			    return std::find(colors.begin(), colors.end(), color) != colors.end();
 		    })) {
-			if (palette.size() != options.maxPalSize()) {
+			if (palette.size() != options.maxOpaqueColors()) {
 				warning("Unused color in PNG embedded palette was re-added; please use `-c "
 				        "embedded` to get this in future versions");
 			}
--- a/src/gfx/rgba.cpp
+++ b/src/gfx/rgba.cpp
@@ -47,6 +47,6 @@
 
 uint8_t Rgba::grayIndex() const {
 	assert(isGray());
-	// Convert from [0; 256[ to [0; maxPalSize[
-	return static_cast<uint16_t>(255 - red) * options.maxPalSize() / 256;
+	// Convert from [0; 256[ to [0; maxOpaqueColors[
+	return static_cast<uint16_t>(255 - red) * options.maxOpaqueColors() / 256;
 }