shithub: rgbds

Download patch

ref: 6bab2ea5c8f124a09ce5e62e3a87087905ca7eb8
parent: 35e57a55c92e5b4ac9708b09d0d9064b7a0f11e5
author: ISSOtm <[email protected]>
date: Sat Mar 12 08:03:08 EST 2022

Add different verbosity levels

And also some ASCII art, perhaps?

--- a/include/gfx/main.hpp
+++ b/include/gfx/main.hpp
@@ -24,8 +24,8 @@
 	bool fixInput = false; // -f
 	bool allowMirroring = false; // -m
 	bool allowDedup = false; // -u
-	bool beVerbose = false; // -v
 	bool columnMajor = false; // -Z, previously -h
+	uint8_t verbosity = 0; // -v
 
 	std::string attrmap{}; // -a, -A
 	std::array<uint8_t, 2> baseTileIDs{0, 0}; // -b
@@ -48,7 +48,14 @@
 
 	std::string input{}; // positional arg
 
-	format_(printf, 2, 3) void verbosePrint(char const *fmt, ...) const;
+	static constexpr uint8_t VERB_NONE = 0; // Normal, no extra output
+	static constexpr uint8_t VERB_CFG = 1; // Print configuration after parsing options
+	static constexpr uint8_t VERB_LOG_ACT = 2; // Log actions before doing them
+	static constexpr uint8_t VERB_INTERM = 3; // Print some intermediate results
+	static constexpr uint8_t VERB_DEBUG = 4; // Internals are logged
+	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
 	}
--- a/src/gfx/convert.cpp
+++ b/src/gfx/convert.cpp
@@ -121,7 +121,8 @@
 	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
-			options.verbosePrint("Too many colors for grayscale sorting (%zu > %" PRIu8 ")\n",
+			options.verbosePrint(Options::VERB_DEBUG,
+			                     "Too many colors for grayscale sorting (%zu > %" PRIu8 ")\n",
 			                     colors.size(), options.maxPalSize());
 			return false;
 		}
@@ -131,7 +132,8 @@
 				continue;
 			}
 			if (!color->isGray()) {
-				options.verbosePrint("Found non-gray color #%08x, not using grayscale sorting\n",
+				options.verbosePrint(Options::VERB_DEBUG,
+				                     "Found non-gray color #%08x, not using grayscale sorting\n",
 				                     color->toCSS());
 				return false;
 			}
@@ -138,6 +140,7 @@
 			uint8_t mask = 1 << color->grayIndex();
 			if (bins & mask) { // Two in the same bin!
 				options.verbosePrint(
+				    Options::VERB_DEBUG,
 				    "Color #%08x conflicts with another one, not using grayscale sorting\n",
 				    color->toCSS());
 				return false;
@@ -161,7 +164,7 @@
 			fatal("Failed to open input image (\"%s\"): %s", path.c_str(), strerror(errno));
 		}
 
-		options.verbosePrint("Opened input file\n");
+		options.verbosePrint(Options::VERB_LOG_ACT, "Opened input file\n");
 
 		std::array<unsigned char, 8> pngHeader;
 
@@ -171,7 +174,7 @@
 			fatal("Input file (\"%s\") is not a PNG image!", path.c_str());
 		}
 
-		options.verbosePrint("PNG header signature is OK\n");
+		options.verbosePrint(Options::VERB_INTERM, "PNG header signature is OK\n");
 
 		png = png_create_read_struct(PNG_LIBPNG_VER_STRING, (png_voidp)this, handleError,
 		                             handleWarning);
@@ -234,7 +237,8 @@
 				fatal("Unknown interlace type %d", interlaceType);
 			}
 		};
-		options.verbosePrint("Input image: %" PRIu32 "x%" PRIu32 " pixels, %dbpp %s, %s\n", height,
+		options.verbosePrint(Options::VERB_INTERM,
+		                     "Input image: %" PRIu32 "x%" PRIu32 " pixels, %dbpp %s, %s\n", height,
 		                     width, bitDepth, colorTypeName(), interlaceTypeName());
 
 		if (png_get_PLTE(png, info, &embeddedPal, &nbColors) != 0) {
@@ -243,15 +247,16 @@
 				assert(nbTransparentEntries == nbColors);
 			}
 
-			options.verbosePrint("Embedded palette has %d colors: [", nbColors);
+			options.verbosePrint(Options::VERB_INTERM, "Embedded palette has %d colors: [",
+			                     nbColors);
 			for (int i = 0; i < nbColors; ++i) {
 				auto const &color = embeddedPal[i];
-				options.verbosePrint("#%02x%02x%02x%02x%s", color.red, color.green, color.blue,
-				                     transparencyPal ? transparencyPal[i] : 0xFF,
-				                     i != nbColors - 1 ? ", " : "]\n");
+				options.verbosePrint(
+				    Options::VERB_INTERM, "#%02x%02x%02x%02x%s", color.red, color.green, color.blue,
+				    transparencyPal ? transparencyPal[i] : 0xFF, i != nbColors - 1 ? ", " : "]\n");
 			}
 		} else {
-			options.verbosePrint("No embedded palette\n");
+			options.verbosePrint(Options::VERB_INTERM, "No embedded palette\n");
 		}
 
 		// Set up transformations; to turn everything into RGBA888
@@ -442,11 +447,11 @@
 	auto [mappings, nbPalettes] = packing::overloadAndRemove(protoPalettes);
 	assert(mappings.size() == protoPalettes.size());
 
-	if (options.beVerbose) {
-		options.verbosePrint("Proto-palette mappings: (%zu palette%s)\n", nbPalettes,
-		                     nbPalettes != 1 ? "s" : "");
+	if (options.verbosity >= Options::VERB_INTERM) {
+		fprintf(stderr, "Proto-palette mappings: (%zu palette%s)\n", nbPalettes,
+		        nbPalettes != 1 ? "s" : "");
 		for (size_t i = 0; i < mappings.size(); ++i) {
-			options.verbosePrint("%zu -> %zu\n", i, mappings[i]);
+			fprintf(stderr, "%zu -> %zu\n", i, mappings[i]);
 		}
 	}
 
@@ -835,9 +840,9 @@
 } // namespace optimized
 
 void process() {
-	options.verbosePrint("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("Reading tiles...\n");
+	options.verbosePrint(Options::VERB_LOG_ACT, "Reading tiles...\n");
 	Png png(options.input);
 	ImagePalette const &colors = png.getColors();
 
@@ -844,18 +849,18 @@
 	// Now, we have all the image's colors in `colors`
 	// The next step is to order the palette
 
-	if (options.beVerbose) {
-		options.verbosePrint("Image colors: [ ");
+	if (options.verbosity >= Options::VERB_INTERM) {
+		fputs("Image colors: [ ", stderr);
 		size_t i = 0;
 		for (auto const &slot : colors) {
 			if (!slot.has_value()) {
 				continue;
 			}
-			options.verbosePrint("#%02x%02x%02x%02x%s", slot->red, slot->green, slot->blue,
-			                     slot->alpha, i != colors.size() - 1 ? ", " : "");
+			fprintf(stderr, "#%02x%02x%02x%02x%s", slot->red, slot->green, slot->blue, slot->alpha,
+			        i != colors.size() - 1 ? ", " : "");
 			++i;
 		}
-		options.verbosePrint("]\n");
+		fputs("]\n", stderr);
 	}
 
 	// Now, iterate through the tiles, generating proto-palettes as we go
@@ -909,8 +914,8 @@
 contained:;
 	}
 
-	options.verbosePrint("Image contains %zu proto-palette%s\n", protoPalettes.size(),
-	                     protoPalettes.size() != 1 ? "s" : "");
+	options.verbosePrint(Options::VERB_INTERM, "Image contains %zu proto-palette%s\n",
+	                     protoPalettes.size(), protoPalettes.size() != 1 ? "s" : "");
 
 	// 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
@@ -922,13 +927,13 @@
 	                                ? generatePalettes(protoPalettes, png)
 	                                : makePalsAsSpecified(protoPalettes, png);
 
-	if (options.beVerbose) {
+	if (options.verbosity >= Options::VERB_INTERM) {
 		for (auto &&palette : palettes) {
-			options.verbosePrint("{ ");
+			fputs("{ ", stderr);
 			for (uint16_t colorIndex : palette) {
-				options.verbosePrint("%04" PRIx16 ", ", colorIndex);
+				fprintf(stderr, "%04" PRIx16 ", ", colorIndex);
 			}
-			options.verbosePrint("}\n");
+			fputs("}\n", stderr);
 		}
 	}
 
@@ -947,17 +952,18 @@
 		}
 
 		if (!options.output.empty()) {
-			options.verbosePrint("Generating unoptimized tile data...\n");
+			options.verbosePrint(Options::VERB_LOG_ACT, "Generating unoptimized tile data...\n");
 			unoptimized::outputTileData(png, attrmap, palettes, mappings);
 		}
 
 		if (!options.tilemap.empty() || !options.attrmap.empty()) {
-			options.verbosePrint("Generating unoptimized tilemap and/or attrmap...\n");
+			options.verbosePrint(Options::VERB_LOG_ACT,
+			                     "Generating unoptimized tilemap and/or attrmap...\n");
 			unoptimized::outputMaps(png, attrmap, mappings);
 		}
 	} else {
 		// All of these require the deduplication process to be performed to be output
-		options.verbosePrint("Deduplicating tiles...\n");
+		options.verbosePrint(Options::VERB_LOG_ACT, "Deduplicating tiles...\n");
 		optimized::UniqueTiles tiles = optimized::dedupTiles(png, attrmap, palettes, mappings);
 
 		if (tiles.size() > options.maxNbTiles[0] + options.maxNbTiles[1]) {
@@ -966,17 +972,17 @@
 		}
 
 		if (!options.output.empty()) {
-			options.verbosePrint("Generating optimized tile data...\n");
+			options.verbosePrint(Options::VERB_LOG_ACT, "Generating optimized tile data...\n");
 			optimized::outputTileData(tiles);
 		}
 
 		if (!options.tilemap.empty()) {
-			options.verbosePrint("Generating optimized tilemap...\n");
+			options.verbosePrint(Options::VERB_LOG_ACT, "Generating optimized tilemap...\n");
 			optimized::outputTilemap(attrmap);
 		}
 
 		if (!options.attrmap.empty()) {
-			options.verbosePrint("Generating optimized attrmap...\n");
+			options.verbosePrint(Options::VERB_LOG_ACT, "Generating optimized attrmap...\n");
 			optimized::outputAttrmap(attrmap, mappings);
 		}
 	}
--- a/src/gfx/main.cpp
+++ b/src/gfx/main.cpp
@@ -70,8 +70,8 @@
 	exit(1);
 }
 
-void Options::verbosePrint(char const *fmt, ...) const {
-	if (beVerbose) {
+void Options::verbosePrint(uint8_t level, char const *fmt, ...) const {
+	if (verbosity >= level) {
 		va_list ap;
 
 		va_start(ap, fmt);
@@ -308,7 +308,9 @@
 			printf("rgbgfx %s\n", get_package_version_string());
 			exit(0);
 		case 'v':
-			options.beVerbose = true;
+			if (options.verbosity < Options::VERB_VVVVVV) {
+				++options.verbosity;
+			}
 			break;
 		case 'x':
 			options.trim = parseNumber(arg, "Number of tiles to trim");
@@ -372,8 +374,36 @@
 	autoOutPath(autoTilemap, options.tilemap, ".tilemap");
 	autoOutPath(autoPalettes, options.palettes, ".pal");
 
-	if (options.beVerbose) {
+	if (options.verbosity >= Options::VERB_CFG) {
 		fprintf(stderr, "rgbgfx %s\n", get_package_version_string());
+
+		if (options.verbosity >= Options::VERB_VVVVVV) {
+			fputc('\n', stderr);
+			static std::array<uint16_t, 21> gfx{
+			    0x1FE, 0x3FF, 0x399, 0x399, 0x3FF, 0x3FF, 0x381, 0x3C3, 0x1FE, 0x078, 0x1FE,
+			    0x3FF, 0x3FF, 0x3FF, 0x37B, 0x37B, 0x0FC, 0x0CC, 0x1CE, 0x1CE, 0x1CE,
+			};
+			static std::array<char const *, 3> textbox{
+			    "  ,----------------------------------------.",
+			    "  | Augh, dimensional interference again?! |",
+			    "  `----------------------------------------'"};
+			for (size_t i = 0; i < gfx.size(); ++i) {
+				uint16_t row = gfx[i];
+				for (uint8_t _ = 0; _ < 10; ++_) {
+					unsigned char c = row & 1 ? '0' : ' ';
+					fputc(c, stderr);
+					// Double the pixel horizontally, otherwise the aspect ratio looks wrong
+					fputc(c, stderr);
+					row >>= 1;
+				}
+				if (i < textbox.size()) {
+					fputs(textbox[i], stderr);
+				}
+				fputc('\n', stderr);
+			}
+			fputc('\n', stderr);
+		}
+
 		fputs("Options:\n", stderr);
 		if (options.fixInput)
 			fputs("\tConvert input to indexed\n", stderr);
--- a/src/gfx/pal_packing.cpp
+++ b/src/gfx/pal_packing.cpp
@@ -342,7 +342,8 @@
 
 std::tuple<DefaultInitVec<size_t>, size_t>
     overloadAndRemove(std::vector<ProtoPalette> const &protoPalettes) {
-	options.verbosePrint("Paginating palettes using \"overload-and-remove\" strategy...\n");
+	options.verbosePrint(Options::VERB_LOG_ACT,
+	                     "Paginating palettes using \"overload-and-remove\" strategy...\n");
 
 	struct Iota {
 		using value_type = size_t;
@@ -388,8 +389,9 @@
 				continue;
 			}
 
-			options.verbosePrint("%zu/%zu: Rel size: %f (size = %zu)\n", i, assignments.size(),
-			                     assignments[i].relSizeOf(protoPal), protoPal.size());
+			options.verbosePrint(Options::VERB_DEBUG, "%zu/%zu: Rel size: %f (size = %zu)\n", i,
+			                     assignments.size(), assignments[i].relSizeOf(protoPal),
+			                     protoPal.size());
 			if (assignments[i].relSizeOf(protoPal) < bestRelSize) {
 				bestPalIndex = i;
 			}
@@ -405,7 +407,8 @@
 
 			// If this overloads the palette, get it back to normal (if possible)
 			while (bestPal.volume() > options.maxPalSize()) {
-				options.verbosePrint("Palette %zu is overloaded! (%zu > %" PRIu8 ")\n",
+				options.verbosePrint(Options::VERB_DEBUG,
+				                     "Palette %zu is overloaded! (%zu > %" PRIu8 ")\n",
 				                     bestPalIndex, bestPal.volume(), options.maxPalSize());
 
 				// Look for a proto-pal minimizing "efficiency" (size / rel_size)
@@ -454,10 +457,11 @@
 		    std::find_if(assignments.begin(), assignments.end(),
 		                 [&protoPal](AssignedProtos const &pal) { return pal.canFit(protoPal); });
 		if (iter == assignments.end()) { // No such page, create a new one
-			options.verbosePrint("Adding new palette for overflow\n");
+			options.verbosePrint(Options::VERB_DEBUG, "Adding new palette for overflow\n");
 			assignments.emplace_back(protoPalettes, std::move(attrs));
 		} else {
-			options.verbosePrint("Assigning overflow to palette %zu\n", iter - assignments.begin());
+			options.verbosePrint(Options::VERB_DEBUG, "Assigning overflow to palette %zu\n",
+			                     iter - assignments.begin());
 			iter->assign(std::move(attrs));
 		}
 		queue.pop();
@@ -467,15 +471,15 @@
 	decant(assignments, protoPalettes);
 	// Note that the result does not contain any empty palettes
 
-	if (options.beVerbose) {
+	if (options.verbosity >= Options::VERB_INTERM) {
 		for (auto &&assignment : assignments) {
-			options.verbosePrint("{ ");
+			fprintf(stderr, "{ ");
 			for (auto &&attrs : assignment) {
 				for (auto &&colorIndex : protoPalettes[attrs.palIndex]) {
-					options.verbosePrint("%04" PRIx16 ", ", colorIndex);
+					fprintf(stderr, "%04" PRIx16 ", ", colorIndex);
 				}
 			}
-			options.verbosePrint("} (volume = %zu)\n", assignment.volume());
+			fprintf(stderr, "} (volume = %zu)\n", assignment.volume());
 		}
 	}
 
--- a/src/gfx/pal_sorting.cpp
+++ b/src/gfx/pal_sorting.cpp
@@ -16,7 +16,7 @@
 
 void indexed(std::vector<Palette> &palettes, int palSize, png_color const *palRGB,
              png_byte *palAlpha) {
-	options.verbosePrint("Sorting palettes using embedded palette...\n");
+	options.verbosePrint(Options::VERB_LOG_ACT, "Sorting palettes using embedded palette...\n");
 
 	auto pngToRgb = [&palRGB, &palAlpha](int index) {
 		auto const &c = palRGB[index];
@@ -71,7 +71,7 @@
 
 void grayscale(std::vector<Palette> &palettes,
                std::array<std::optional<Rgba>, 0x8001> const &colors) {
-	options.verbosePrint("Sorting grayscale-only palette...\n");
+	options.verbosePrint(Options::VERB_LOG_ACT, "Sorting grayscale-only palette...\n");
 
 	// This method is only applicable if there are at most as many colors as colors per palette, so
 	// we should only have a single palette.
@@ -95,7 +95,7 @@
 }
 
 void rgb(std::vector<Palette> &palettes) {
-	options.verbosePrint("Sorting palettes by \"\"\"luminance\"\"\"...\n");
+	options.verbosePrint(Options::VERB_LOG_ACT, "Sorting palettes by \"\"\"luminance\"\"\"...\n");
 
 	for (Palette &pal : palettes) {
 		std::sort(pal.begin(), pal.end(), [](uint16_t lhs, uint16_t rhs) {