diff options
Diffstat (limited to 'font.c')
-rw-r--r-- | font.c | 128 |
1 files changed, 128 insertions, 0 deletions
@@ -0,0 +1,128 @@ +/* + * Copyright (C) 2020 Mikhail Burakov. This file is part of Chrand. + * + * Chrand is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Chrand is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Chrand. If not, see <https://www.gnu.org/licenses/>. + */ + +#include "font.h" + +extern uint32_t kCp00[256 * 3]; +extern uint32_t kCp04[256 * 3]; + +static int GetUnicode(const char** str) { + const char* character = *str; + if ((character[0] & 0xf8) == 0xf0 && (character[1] & 0xc0) == 0x80 && + (character[2] & 0xc0) == 0x80 && (character[3] & 0xc0) == 0x80) { + (*str) += 4; + return ((character[0] & 0x07) << 18) | ((character[1] & 0x3f) << 12) | + ((character[2] & 0x3f) << 6) | (character[3] & 0x3f); + } + if ((character[0] & 0xf0) == 0xe0 && (character[1] & 0xc0) == 0x80 && + (character[2] & 0xc0) == 0x80) { + (*str) += 3; + return ((character[0] & 0x0f) << 12) | ((character[1] & 0x3f) << 6) | + (character[2] & 0x3f); + } + if ((character[0] & 0xe0) == 0xc0 && (character[1] & 0xc0) == 0x80) { + (*str) += 2; + return ((character[0] & 0x1f) << 6) | (character[1] & 0x3f); + } + if ((character[0] & 0x80) == 0x00) { + (*str) += 1; + return character[0]; + } + return -1; +} + +static size_t GlyphWidth(int point) { + const uint32_t* page; + switch (point >> 8) { + case 0: + if (point == 0x20) return 4; + page = kCp00; + break; + case 4: + page = kCp04; + point &= 0xff; + break; + default: + return 0; + } + uint32_t glyph = page[point * 3]; + return (glyph >> 24) + 1; +} + +static void BlockRender(uint32_t block, uint32_t* buffer, size_t stride, + uint32_t color) { + for (size_t counter = 31; block; block >>= 1, counter--) { + if (!(block & 1)) continue; + size_t x = counter & 0x7u; + size_t y = counter >> 3u; + buffer[x + y * stride] = color; + } +} + +static void GlyphRender(int point, uint32_t* buffer, size_t stride, + uint32_t color) { + const uint32_t* page; + switch (point >> 8) { + case 0: + if (point == 0x20) return; + page = kCp00; + break; + case 4: + page = kCp04; + point &= 0xff; + break; + default: + return; + } + const uint32_t* glyph = page + point * 3; + BlockRender(glyph[2], buffer + stride * 7, stride, color); + BlockRender(glyph[1], buffer + stride * 3, stride, color); + BlockRender(glyph[0] & 0xffffff, buffer - stride, stride, color); +} + +size_t PuiStringWidth(const char* str) { + size_t result = 0; + for (;;) { + int cp = GetUnicode(&str); + if (cp < 1) break; + result += GlyphWidth(cp); + } + return result; +} + +size_t PuiStringCut(const char* str, size_t width) { + size_t result = 0; + for (size_t current = 0;;) { + const char* before = str; + int cp = GetUnicode(&str); + if (cp < 1) break; + current += GlyphWidth(cp); + if (current >= width) break; + result += (size_t)(str - before); + } + return result; +} + +void PuiStringRender(const char* str, void* buffer, size_t stride, + uint32_t color) { + for (uint32_t* ptr = buffer;;) { + int cp = GetUnicode(&str); + if (cp < 1) break; + GlyphRender(cp, ptr, stride, color); + ptr += GlyphWidth(cp); + } +} |