summaryrefslogtreecommitdiff
path: root/font.c
diff options
context:
space:
mode:
Diffstat (limited to 'font.c')
-rw-r--r--font.c128
1 files changed, 128 insertions, 0 deletions
diff --git a/font.c b/font.c
new file mode 100644
index 0000000..f7c7380
--- /dev/null
+++ b/font.c
@@ -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);
+ }
+}