mirror of
https://github.com/gbdev/rgbds.git
synced 2025-11-20 18:22:07 +00:00
Implement inline palette spec parsing
This commit is contained in:
1
Makefile
1
Makefile
@@ -108,6 +108,7 @@ rgbgfx_obj := \
|
||||
src/gfx/main.o \
|
||||
src/gfx/pal_packing.o \
|
||||
src/gfx/pal_sorting.o \
|
||||
src/gfx/pal_spec.o \
|
||||
src/gfx/process.o \
|
||||
src/gfx/proto_palette.o \
|
||||
src/gfx/reverse.o \
|
||||
|
||||
16
include/gfx/pal_spec.hpp
Normal file
16
include/gfx/pal_spec.hpp
Normal 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 */
|
||||
@@ -73,6 +73,7 @@ set(rgbgfx_src
|
||||
"gfx/main.cpp"
|
||||
"gfx/pal_packing.cpp"
|
||||
"gfx/pal_sorting.cpp"
|
||||
"gfx/pal_spec.cpp"
|
||||
"gfx/process.cpp"
|
||||
"gfx/proto_palette.cpp"
|
||||
"gfx/reverse.cpp"
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
#include "platform.h"
|
||||
#include "version.h"
|
||||
|
||||
#include "gfx/pal_spec.hpp"
|
||||
#include "gfx/process.hpp"
|
||||
#include "gfx/reverse.hpp"
|
||||
|
||||
@@ -223,9 +224,8 @@ static void skipWhitespace(char *&arg) {
|
||||
|
||||
static void parsePaletteSpec(char const *arg) {
|
||||
if (arg[0] == '#') {
|
||||
// List of #rrggbb/#rgb colors, comma-separated, palettes are separated by colons
|
||||
options.palSpecType = Options::EXPLICIT;
|
||||
// TODO
|
||||
parseInlinePalSpec(arg);
|
||||
} else if (strcasecmp(arg, "embedded") == 0) {
|
||||
// Use PLTE, error out if missing
|
||||
options.palSpecType = Options::EMBEDDED;
|
||||
@@ -668,6 +668,14 @@ int main(int argc, char *argv[]) {
|
||||
}
|
||||
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],
|
||||
options.unitSize[1]);
|
||||
fprintf(stderr,
|
||||
|
||||
151
src/gfx/pal_spec.cpp
Normal file
151
src/gfx/pal_spec.cpp
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user