initial commit
This commit is contained in:
@@ -0,0 +1,129 @@
|
||||
#pragma once
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// UTF-8 helpers for the per-glyph scroller atlas.
|
||||
//
|
||||
// [This implementation] -- the scroller scrambles individual glyphs rather
|
||||
// than whole words (see scramble/fibonacci_scrambler.h and scroller_page.cpp),
|
||||
// so the word cache's string keys must be decomposed into independent code
|
||||
// points before scrambling and re-encoded one at a time for rasterization.
|
||||
// These helpers are that bridge.
|
||||
//
|
||||
// Because every valid code point is handled -- not just A-Za-z0-9 --
|
||||
// punctuation such as backtick, comma, apostrophe, single/double quotes, em
|
||||
// dashes, and curly quotes all get their own scrambled tile. This is the
|
||||
// requested "include the most common non-alpha-numeric text characters"
|
||||
// behavior.
|
||||
//
|
||||
// The decoder is strict: it rejects overlong encodings, UTF-16 surrogate code
|
||||
// points, values above U+10FFFF, and malformed continuation bytes, so garbage
|
||||
// bytes never reach the font rasterizer.
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
namespace radiantwave {
|
||||
|
||||
// Split `text` into its Unicode scalar values (code points), in order.
|
||||
// Returns false (leaving `out` untouched) when `text` is not valid UTF-8.
|
||||
inline bool splitUtf8(const std::string& text, std::vector<uint32_t>& out)
|
||||
{
|
||||
std::vector<uint32_t> result;
|
||||
const char* p = text.data();
|
||||
const char* end = p + text.size();
|
||||
while (p < end)
|
||||
{
|
||||
const uint8_t lead = static_cast<uint8_t>(*p);
|
||||
uint32_t cp = 0;
|
||||
size_t extra = 0;
|
||||
uint32_t min = 0;
|
||||
if (lead < 0x80)
|
||||
{
|
||||
cp = lead;
|
||||
}
|
||||
else if ((lead & 0xE0) == 0xC0)
|
||||
{
|
||||
cp = lead & 0x1F;
|
||||
extra = 1;
|
||||
min = 0x80;
|
||||
}
|
||||
else if ((lead & 0xF0) == 0xE0)
|
||||
{
|
||||
cp = lead & 0x0F;
|
||||
extra = 2;
|
||||
min = 0x800;
|
||||
}
|
||||
else if ((lead & 0xF8) == 0xF0)
|
||||
{
|
||||
cp = lead & 0x07;
|
||||
extra = 3;
|
||||
min = 0x10000;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (static_cast<size_t>(end - p) < extra + 1)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
for (size_t i = 1; i <= extra; ++i)
|
||||
{
|
||||
const uint8_t cc = static_cast<uint8_t>(p[i]);
|
||||
if ((cc & 0xC0) != 0x80)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
cp = (cp << 6) | (cc & 0x3F);
|
||||
}
|
||||
if (cp < min || cp > 0x10FFFF)
|
||||
{
|
||||
return false; // overlong or out of range
|
||||
}
|
||||
if (cp >= 0xD800 && cp <= 0xDFFF)
|
||||
{
|
||||
return false; // UTF-16 surrogate
|
||||
}
|
||||
|
||||
result.push_back(cp);
|
||||
p += extra + 1;
|
||||
}
|
||||
out = std::move(result);
|
||||
return true;
|
||||
}
|
||||
|
||||
// Encode a single Unicode scalar value back to UTF-8. Returns an empty string
|
||||
// for values outside the scalar range (surrogates, > U+10FFFF); callers treat
|
||||
// an empty result as "no glyph".
|
||||
inline std::string encodeUtf8(uint32_t cp)
|
||||
{
|
||||
if (cp > 0x10FFFF || (cp >= 0xD800 && cp <= 0xDFFF))
|
||||
{
|
||||
return {};
|
||||
}
|
||||
if (cp < 0x80)
|
||||
{
|
||||
return std::string(1, static_cast<char>(cp));
|
||||
}
|
||||
if (cp < 0x800)
|
||||
{
|
||||
return {static_cast<char>(0xC0 | (cp >> 6)),
|
||||
static_cast<char>(0x80 | (cp & 0x3F))};
|
||||
}
|
||||
if (cp < 0x10000)
|
||||
{
|
||||
return {static_cast<char>(0xE0 | (cp >> 12)),
|
||||
static_cast<char>(0x80 | ((cp >> 6) & 0x3F)),
|
||||
static_cast<char>(0x80 | (cp & 0x3F))};
|
||||
}
|
||||
return {static_cast<char>(0xF0 | (cp >> 18)),
|
||||
static_cast<char>(0x80 | ((cp >> 12) & 0x3F)),
|
||||
static_cast<char>(0x80 | ((cp >> 6) & 0x3F)),
|
||||
static_cast<char>(0x80 | (cp & 0x3F))};
|
||||
}
|
||||
|
||||
} // namespace radiantwave
|
||||
Reference in New Issue
Block a user