refactor(editor): extract parseHexOrError + hoist parseHex

parseHex was a static helper in cli_gen_texture.cpp. 81 sites
across the file each open-coded the same 6-line "if !parseHex
then fprintf 'X is not a valid hex color' and return 1" block.

Hoist parseHex into cli_png_emit.hpp as inline (header-only,
non-trivial but only called once per handler at startup so
inlining cost is negligible). Add inline parseHexOrError that
wraps it with the canonical error message.

Each call site collapses from 6 lines to 1:

  if (!parseHexOrError(stoneHex, sr, sg, sb,
                       "gen-texture-cobble")) return 1;

Bonus: 29 compound-color sites that used a single
"one of the hex colors is invalid" message are split into
separate parseHexOrError calls per color — now the error
identifies WHICH color failed instead of just saying
"one of them".

cli_gen_texture.cpp drops by ~320 lines (4877 → 4557). Output
bytes verified identical for cobble / mosaic / stained-glass.
Error message contract improved: 'NOTHEX' now reports
"'NOTHEX' is not a valid hex color" everywhere instead of
the generic compound message.
This commit is contained in:
Kelsi 2026-05-09 12:06:49 -07:00
parent 4cb1c33335
commit e1e505984f
2 changed files with 174 additions and 530 deletions

File diff suppressed because it is too large Load diff

View file

@ -1,6 +1,8 @@
#pragma once
#include "stb_image_write.h"
#include <algorithm>
#include <cctype>
#include <cstdint>
#include <cstdio>
#include <string>
@ -10,6 +12,56 @@ namespace wowee {
namespace editor {
namespace cli {
// Parse "RRGGBB" (6 hex chars), "RGB" (3 hex chars), or with a "#"
// prefix into 8-bit channel components. Returns false on malformed
// input. Used by every --gen-texture-* handler that takes color
// arguments. Was a static helper inside cli_gen_texture.cpp before
// being hoisted here so other texture-side modules can use it.
inline bool parseHex(std::string hex, uint8_t& r, uint8_t& g, uint8_t& b) {
std::transform(hex.begin(), hex.end(), hex.begin(),
[](unsigned char c) { return std::tolower(c); });
if (!hex.empty() && hex[0] == '#') hex.erase(0, 1);
auto fromHexC = [](char c) -> int {
if (c >= '0' && c <= '9') return c - '0';
if (c >= 'a' && c <= 'f') return 10 + c - 'a';
return -1;
};
int v[6];
if (hex.size() == 6) {
for (int k = 0; k < 6; ++k) {
v[k] = fromHexC(hex[k]);
if (v[k] < 0) return false;
}
r = static_cast<uint8_t>((v[0] << 4) | v[1]);
g = static_cast<uint8_t>((v[2] << 4) | v[3]);
b = static_cast<uint8_t>((v[4] << 4) | v[5]);
return true;
}
if (hex.size() == 3) {
for (int k = 0; k < 3; ++k) {
v[k] = fromHexC(hex[k]);
if (v[k] < 0) return false;
}
r = static_cast<uint8_t>((v[0] << 4) | v[0]);
g = static_cast<uint8_t>((v[1] << 4) | v[1]);
b = static_cast<uint8_t>((v[2] << 4) | v[2]);
return true;
}
return false;
}
// parseHex + canonical "invalid hex" error message, used by 81+
// sites in cli_gen_texture.cpp. Returns true on success so callers
// do `if (!parseHexOrError(hex, r, g, b, "cmd")) return 1;`.
inline bool parseHexOrError(const std::string& hex,
uint8_t& r, uint8_t& g, uint8_t& b,
const char* cmdName) {
if (parseHex(hex, r, g, b)) return true;
std::fprintf(stderr, "%s: '%s' is not a valid hex color\n",
cmdName, hex.c_str());
return false;
}
// Shared PNG-write wrapper used by every --gen-texture-* handler.
// Calls stbi_write_png with the standard RGB/24-bit/W*3-stride
// arguments and reports a stderr message on failure. Returns true