mirror of
https://github.com/gbdev/rgbds.git
synced 2025-11-20 10:12:06 +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/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
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/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"
|
||||||
|
|||||||
@@ -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
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