From 969412af246eb7ca8fe1f5799281eb0df67a6102 Mon Sep 17 00:00:00 2001 From: Rangi <35663410+Rangi42@users.noreply.github.com> Date: Sat, 1 Oct 2022 12:45:00 -0400 Subject: [PATCH] Parse HEX palettes (#1081) Addresses one item of #1065 --- man/rgbgfx.1 | 4 ++++ src/gfx/pal_spec.cpp | 45 ++++++++++++++++++++++++++++++++++++++++++-- 2 files changed, 47 insertions(+), 2 deletions(-) diff --git a/man/rgbgfx.1 b/man/rgbgfx.1 index 98c51b5d..eec7cf6a 100644 --- a/man/rgbgfx.1 +++ b/man/rgbgfx.1 @@ -384,6 +384,10 @@ A GBC palette memory dump, as emitted by 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 diff --git a/src/gfx/pal_spec.cpp b/src/gfx/pal_spec.cpp index 8a23228f..eb08e63b 100644 --- a/src/gfx/pal_spec.cpp +++ b/src/gfx/pal_spec.cpp @@ -55,7 +55,7 @@ constexpr uint8_t singleToHex(char c) { template // 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 @@ static std::optional parseDec(std::string const &str, std::string::size_type 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::max); } @@ -382,6 +383,45 @@ static void parseGPLFile(std::filebuf &file) { } } +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 @@ void parseExternalPalSpec(char const *arg) { 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},