shithub: rgbds

Download patch

ref: 969412af246eb7ca8fe1f5799281eb0df67a6102
parent: c10345f26d2aa370bc35d73ac5c81ea85beb788e
author: Rangi <[email protected]>
date: Sat Oct 1 08:45:00 EDT 2022

Parse HEX palettes (#1081)

Addresses one item of #1065

--- a/man/rgbgfx.1
+++ b/man/rgbgfx.1
@@ -384,6 +384,10 @@
 Useful to force several images to share the same palette.
 .It Cm gpl
 .Lk https://docs.gimp.org/2.10/en/gimp-concepts-palettes.html GIMP palette .
+.It Cm hex
+Plaintext lines of hexadecimal colors in
+.Ql rrggbb
+format.
 .It Cm psp
 .Lk https://www.selapa.net/swatches/colors/fileformats.php#psp_pal Paint Shop Pro palette .
 .El
--- a/src/gfx/pal_spec.cpp
+++ b/src/gfx/pal_spec.cpp
@@ -55,7 +55,7 @@
 
 template<typename Str> // Should be std::string or std::string_view
 static void skipWhitespace(Str const &str, typename Str::size_type &pos) {
-	pos = std::min(str.find_first_not_of(" \t", pos), str.length());
+	pos = std::min(str.find_first_not_of(" \t"sv, pos), str.length());
 }
 
 void parseInlinePalSpec(char const * const rawArg) {
@@ -233,7 +233,8 @@
 	std::string::size_type start = n;
 
 	uintmax_t value = 0; // Use a larger type to handle overflow more easily
-	for (auto end = std::min(str.length(), str.find_first_not_of("0123456789", n)); n < end; ++n) {
+	for (auto end = std::min(str.length(), str.find_first_not_of("0123456789"sv, n)); n < end;
+	     ++n) {
 		value = std::min(value * 10 + (str[n] - '0'), (uintmax_t)std::numeric_limits<U>::max);
 	}
 
@@ -382,6 +383,45 @@
 	}
 }
 
+static void parseHEXFile(std::filebuf &file) {
+	// https://lospec.com/palette-list/tag/gbc
+
+	uint16_t nbColors = 0;
+	uint16_t maxNbColors = options.nbColorsPerPal * options.nbPalettes;
+
+	for (;;) {
+		std::string line;
+		readLine(file, line);
+		if (!line.length()) {
+			break;
+		}
+
+		if (line.length() != 6
+		    || line.find_first_not_of("0123456789ABCDEFabcdef"sv) != std::string::npos) {
+			error("Failed to parse color #%" PRIu16 " (\"%s\"): invalid \"rrggbb\" line",
+			      nbColors + 1, line.c_str());
+			return;
+		}
+
+		Rgba color =
+		    Rgba(toHex(line[0], line[1]), toHex(line[2], line[3]), toHex(line[4], line[5]), 0xFF);
+
+		++nbColors;
+		if (nbColors < maxNbColors) {
+			if (nbColors % options.nbColorsPerPal == 1) {
+				options.palSpec.emplace_back();
+			}
+			options.palSpec.back()[nbColors % options.nbColorsPerPal] = color;
+		}
+	}
+
+	if (nbColors > maxNbColors) {
+		warning("HEX file contains %" PRIu16 " colors, but there can only be %" PRIu16
+		        "; ignoring extra",
+		        nbColors, maxNbColors);
+	}
+}
+
 static void parseACTFile(std::filebuf &file) {
 	// https://www.adobe.com/devnet-apps/photoshop/fileformatashtml/#50577411_pgfId-1070626
 
@@ -536,6 +576,7 @@
 	static std::array parsers{
 	    std::tuple{"PSP", &parsePSPFile, std::ios::in    },
 	    std::tuple{"GPL", &parseGPLFile, std::ios::in    },
+	    std::tuple{"HEX", &parseHEXFile, std::ios::in    },
 	    std::tuple{"ACT", &parseACTFile, std::ios::binary},
 	    std::tuple{"ACO", &parseACOFile, std::ios::binary},
 	    std::tuple{"GBC", &parseGBCFile, std::ios::binary},