49 lines
1.5 KiB
C++
49 lines
1.5 KiB
C++
#include <plot/render/text.hpp>
|
|
|
|
namespace plot::render {
|
|
|
|
// The decoder lives with the interface, not with a font, because it is what keeps
|
|
// "what a string is worth" (`TextBackend::measure`) and "what a string draws"
|
|
// (`TextBatch::add_text`) the same thing: both walk a label with this function, so
|
|
// neither can disagree with the other about where a codepoint starts or how many
|
|
// bytes a malformed sequence costs.
|
|
u32 next_codepoint(const char*& p) noexcept {
|
|
const auto b0 = static_cast<u32>(static_cast<unsigned char>(p[0]));
|
|
if (b0 < 0x80u) {
|
|
++p;
|
|
return b0;
|
|
}
|
|
u32 codepoint = 0;
|
|
u32 extra = 0;
|
|
if ((b0 & 0xE0u) == 0xC0u) {
|
|
codepoint = b0 & 0x1Fu;
|
|
extra = 1u;
|
|
} else if ((b0 & 0xF0u) == 0xE0u) {
|
|
codepoint = b0 & 0x0Fu;
|
|
extra = 2u;
|
|
} else if ((b0 & 0xF8u) == 0xF0u) {
|
|
codepoint = b0 & 0x07u;
|
|
extra = 3u;
|
|
} else {
|
|
++p;
|
|
return kInvalidCodepoint;
|
|
}
|
|
for (u32 i = 1; i <= extra; ++i) {
|
|
const auto b = static_cast<u32>(static_cast<unsigned char>(p[i]));
|
|
if ((b & 0xC0u) != 0x80u) { // truncated: stop before the terminator
|
|
++p;
|
|
return kInvalidCodepoint;
|
|
}
|
|
codepoint = (codepoint << 6) | (b & 0x3Fu);
|
|
}
|
|
p += extra + 1u;
|
|
constexpr u32 kMinForLength[4] = {0u, 0x80u, 0x800u, 0x10000u};
|
|
if (codepoint < kMinForLength[extra] || codepoint > 0x10FFFFu ||
|
|
(codepoint >= 0xD800u && codepoint <= 0xDFFFu)) {
|
|
return kInvalidCodepoint; // overlong, out of range or a surrogate
|
|
}
|
|
return codepoint;
|
|
}
|
|
|
|
} // namespace plot::render
|