Implement inline palette spec parsing

This commit is contained in:
ISSOtm
2022-04-09 13:47:38 +02:00
committed by Eldred Habert
parent cc27169ecd
commit 6b0cab32a6
5 changed files with 179 additions and 2 deletions

View File

@@ -108,6 +108,7 @@ rgbgfx_obj := \
src/gfx/main.o \ src/gfx/main.o \
src/gfx/pal_packing.o \ src/gfx/pal_packing.o \
src/gfx/pal_sorting.o \ src/gfx/pal_sorting.o \
src/gfx/pal_spec.o \
src/gfx/process.o \ src/gfx/process.o \
src/gfx/proto_palette.o \ src/gfx/proto_palette.o \
src/gfx/reverse.o \ src/gfx/reverse.o \

16
include/gfx/pal_spec.hpp Normal file
View File

@@ -0,0 +1,16 @@
/*
* This file is part of RGBDS.
*
* Copyright (c) 2022, Eldred Habert and RGBDS contributors.
*
* SPDX-License-Identifier: MIT
*/
#ifndef RGBDS_GFX_PAL_SPEC_HPP
#define RGBDS_GFX_PAL_SPEC_HPP
#include <string_view>
void parseInlinePalSpec(char const * const arg);
#endif /* RGBDS_GFX_PAL_SPEC_HPP */

View File

@@ -73,6 +73,7 @@ set(rgbgfx_src
"gfx/main.cpp" "gfx/main.cpp"
"gfx/pal_packing.cpp" "gfx/pal_packing.cpp"
"gfx/pal_sorting.cpp" "gfx/pal_sorting.cpp"
"gfx/pal_spec.cpp"
"gfx/process.cpp" "gfx/process.cpp"
"gfx/proto_palette.cpp" "gfx/proto_palette.cpp"
"gfx/reverse.cpp" "gfx/reverse.cpp"

View File

@@ -26,6 +26,7 @@
#include "platform.h" #include "platform.h"
#include "version.h" #include "version.h"
#include "gfx/pal_spec.hpp"
#include "gfx/process.hpp" #include "gfx/process.hpp"
#include "gfx/reverse.hpp" #include "gfx/reverse.hpp"
@@ -223,9 +224,8 @@ static void skipWhitespace(char *&arg) {
static void parsePaletteSpec(char const *arg) { static void parsePaletteSpec(char const *arg) {
if (arg[0] == '#') { if (arg[0] == '#') {
// List of #rrggbb/#rgb colors, comma-separated, palettes are separated by colons
options.palSpecType = Options::EXPLICIT; options.palSpecType = Options::EXPLICIT;
// TODO parseInlinePalSpec(arg);
} else if (strcasecmp(arg, "embedded") == 0) { } else if (strcasecmp(arg, "embedded") == 0) {
// Use PLTE, error out if missing // Use PLTE, error out if missing
options.palSpecType = Options::EMBEDDED; options.palSpecType = Options::EMBEDDED;
@@ -668,6 +668,14 @@ int main(int argc, char *argv[]) {
} }
return "???"; return "???";
}()); }());
if (options.palSpecType == Options::EXPLICIT) {
fputs("\t[\n", stderr);
for (std::array<Rgba, 4> const &pal : options.palSpec) {
fprintf(stderr, "\t\t#%06x, #%06x, #%06x, #%06x,\n", pal[0].toCSS() >> 8,
pal[1].toCSS() >> 8, pal[2].toCSS() >> 8, pal[3].toCSS() >> 8);
}
fputs("\t]\n", stderr);
}
fprintf(stderr, "\tDedup unit: %" PRIu16 "x%" PRIu16 " tiles\n", options.unitSize[0], fprintf(stderr, "\tDedup unit: %" PRIu16 "x%" PRIu16 " tiles\n", options.unitSize[0],
options.unitSize[1]); options.unitSize[1]);
fprintf(stderr, fprintf(stderr,

151
src/gfx/pal_spec.cpp Normal file
View File

@@ -0,0 +1,151 @@
/*
* This file is part of RGBDS.
*
* Copyright (c) 2022, Eldred Habert and RGBDS contributors.
*
* SPDX-License-Identifier: MIT
*/
#include "gfx/pal_spec.hpp"
#include <algorithm>
#include <cassert>
#include <cstdio>
#include "gfx/main.hpp"
using namespace std::string_view_literals;
constexpr uint8_t nibble(char c) {
if (c >= 'a') {
assert(c <= 'f');
return c - 'a' + 10;
} else if (c >= 'A') {
assert(c <= 'F');
return c - 'A' + 10;
} else {
assert(c >= '0' && c <= '9');
return c - '0';
}
}
constexpr uint8_t toHex(char c1, char c2) {
return nibble(c1) * 16 + nibble(c2);
}
constexpr uint8_t singleToHex(char c) {
return toHex(c, c);
}
void parseInlinePalSpec(char const * const rawArg) {
// List of #rrggbb/#rgb colors, comma-separated, palettes are separated by colons
std::string_view arg(rawArg);
using size_type = decltype(arg)::size_type;
auto parseError = [&rawArg, &arg](size_type ofs, size_type len, char const *fmt,
auto &&...args) {
(void)arg; // With NDEBUG, `arg` is otherwise not used
assert(ofs <= arg.length());
assert(len <= arg.length());
error(fmt, args...);
fprintf(stderr,
"In inline palette spec: %s\n"
" ",
rawArg);
for (auto i = ofs; i; --i) {
putc(' ', stderr);
}
for (auto i = len; i; --i) {
putc('^', stderr);
}
putc('\n', stderr);
};
auto skipWhitespace = [&arg](size_type &pos) {
pos = std::min(arg.find_first_not_of(" \t", pos), arg.length());
};
options.palSpec.clear();
options.palSpec
.emplace_back(); // Not default-initialized, but value-initialized, so we get zeros
size_type n = 0; // Index into the argument
// TODO: store max `nbColors` ever reached, and compare against palette size later
size_t nbColors = 0; // Number of colors in the current palette
for (;;) {
++n; // Ignore the '#' (checked either by caller or previous loop iteration)
Rgba &color = options.palSpec.back()[nbColors];
auto pos = std::min(arg.find_first_not_of("0123456789ABCDEFabcdef"sv, n), arg.length());
switch (pos - n) {
case 3:
color = Rgba(singleToHex(arg[n + 0]), singleToHex(arg[n + 1]), singleToHex(arg[n + 2]),
0xFF);
break;
case 6:
color = Rgba(toHex(arg[n + 0], arg[n + 1]), toHex(arg[n + 2], arg[n + 3]),
toHex(arg[n + 4], arg[n + 5]), 0xFF);
break;
case 0:
parseError(n - 1, 1, "Missing color after '#'");
return;
default:
parseError(n, pos - n, "Unknown color specification");
return;
}
n = pos;
// Skip whitespace, if any
skipWhitespace(n);
// Skip comma/colon, or end
if (n == arg.length()) {
break;
}
switch (arg[n]) {
case ',':
++n; // Skip it
++nbColors;
// A trailing comma may be followed by a colon
skipWhitespace(n);
if (n == arg.length()) {
break;
} else if (arg[n] != ':') {
if (nbColors == 4) {
parseError(n, 1, "Each palette can only contain up to 4 colors");
return;
}
break;
}
[[fallthrough]];
case ':':
++n;
skipWhitespace(n);
nbColors = 0; // Start a new palette
// Avoid creating a spurious empty palette
if (n != arg.length()) {
options.palSpec.emplace_back();
}
break;
default:
parseError(n, 1, "Unexpected character, expected ',', ':', or end of argument");
return;
}
// Check again to allow trailing a comma/colon
if (n == arg.length()) {
break;
}
if (arg[n] != '#') {
parseError(n, 1, "Unexpected character, expected '#'");
return;
}
}
}