diff options
-rw-r--r-- | buffer.c | 56 | ||||
-rw-r--r-- | main.c | 9 | ||||
-rw-r--r-- | text.c | 27 | ||||
-rw-r--r-- | text.h | 10 | ||||
-rw-r--r-- | unicode.h | 5 |
5 files changed, 86 insertions, 21 deletions
@@ -3,7 +3,9 @@ // blocks of a few thousand bytes. Block boundaries are not necessarily // on character boundaries, so one block might have part of a UTF-8 character // with the next block having the rest. +#include "unicode.h" #include "util.c" +#include "text.h" #define TEXT_BLOCK_MAX_SIZE 400 // Once two adjacent blocks are at most this big, combine them into @@ -114,3 +116,57 @@ void text_buffer_free(TextBuffer *buffer) { } free(blocks); } + +// Render the text buffer in the given rectangle +void text_buffer_render(TextBuffer *buffer, Font *font, float box_x, float box_y, float box_w, float box_h) { + mbstate_t mbstate = {0}; + uint nblocks = buffer->nblocks; + TextBlock *blocks = buffer->blocks; + text_chars_begin(font); + TextRenderState text_state = { + .x = box_x, .y = box_y, + .edge_right = box_x+box_w, + .edge_bottom = box_y+box_h + }; + + for (uint block_idx = 0; block_idx < nblocks; ++block_idx) { + TextBlock *block = &blocks[block_idx]; + char *p = block->contents, *end = p + block->len; + while (p != end) { + char32_t c; + size_t n = mbrtoc32(&c, p, (size_t)(end - p), &mbstate); + if (n == 0) { + // null character + c = UNICODE_BOX_CHARACTER; + ++p; + } else if (n == (size_t)(-3)) { + // no bytes consumed, but a character was produced + } else if (n == (size_t)(-2)) { + // incomplete character at end of block. + c = 0; + p = end; + } else if (n == (size_t)(-1)) { + // invalid UTF-8 + c = UNICODE_BOX_CHARACTER; + ++p; + } else { + p += n; + } + switch (c) { + case L'\n': + text_state.x = box_x; + text_state.y -= text_font_char_height(font); + break; + case L'\r': break; // for CRLF line endings + case L'\t': + for (int i = 0; i < 4; ++i) + text_render_char(font, L' ', &text_state); + break; + default: + text_render_char(font, c, &text_state); + break; + } + } + } + text_chars_end(font); +} @@ -38,7 +38,8 @@ int main(void) { if (SDL_Init(SDL_INIT_VIDEO|SDL_INIT_TIMER) < 0) die("%s", SDL_GetError()); - SDL_Window *window = SDL_CreateWindow("ted", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 1280, 720, SDL_WINDOW_SHOWN|SDL_WINDOW_OPENGL); + SDL_Window *window = SDL_CreateWindow("ted", SDL_WINDOWPOS_UNDEFINED, + SDL_WINDOWPOS_UNDEFINED, 1280, 720, SDL_WINDOW_SHOWN|SDL_WINDOW_OPENGL|SDL_WINDOW_RESIZABLE); if (!window) die("%s", SDL_GetError()); @@ -51,7 +52,7 @@ int main(void) { SDL_GL_SetSwapInterval(1); // vsync - Font *font = text_font_load("assets/font.ttf", 24); + Font *font = text_font_load("assets/font.ttf", 16); if (!font) { die("Couldn't load font: %s", text_get_err()); } @@ -81,6 +82,7 @@ int main(void) { int window_width = 0, window_height = 0; SDL_GetWindowSize(window, &window_width, &window_height); + float window_widthf = (float)window_width, window_heightf = (float)window_height; // set up GL glEnable(GL_BLEND); @@ -93,7 +95,8 @@ int main(void) { glClear(GL_COLOR_BUFFER_BIT); glColor3f(1,1,1); - text_render(font, "hellσ! öθ☺", 50, 50); + //text_render(font, "hellσ! öθ☺", 50, 50); + text_buffer_render(&text_buffer, font, 50, window_heightf-50, window_widthf-100, window_heightf-100); if (text_has_err()) { printf("Text error: %s\n", text_get_err()); break; @@ -1,5 +1,6 @@ #include "base.h" #include "text.h" +#include "unicode.h" #define STB_TRUETYPE_IMPLEMENTATION #define STBTT_STATIC no_warn_start @@ -8,7 +9,6 @@ no_warn_end #include <stdlib.h> #include <GL/gl.h> -#define UNICODE_CODE_POINTS 0x110000 // number of Unicode code points // We split up code points into a bunch of pages, so we don't have to load all of the font at // once into one texture. #define CHAR_PAGE_SIZE 2048 @@ -136,9 +136,9 @@ Font *text_font_load(char const *ttf_filename, float font_size) { return font; } -typedef struct { - float x, y; -} TextRenderState; +float text_font_char_height(Font *font) { + return font->char_height; +} static void text_render_with_page(Font *font, int page) { if (font->curr_page != page) { @@ -164,14 +164,14 @@ void text_chars_end(Font *font) { font->curr_page = -1; } -static void text_render_char_internal(Font *font, char32_t c, TextRenderState *state) { +void text_render_char(Font *font, char32_t c, TextRenderState *state) { if (c >= 0x30000 && c < 0xE0000){ // these Unicode code points are currently unassigned. replace them with a Unicode box. // (specifically, we don't want to use extra memory for pages which // won't even have any valid characters in them) - c = 0x2610; + c = UNICODE_BOX_CHARACTER; } - if (c >= UNICODE_CODE_POINTS) c = 0x2610; // code points this big should never appear in valid Unicode + if (c >= UNICODE_CODE_POINTS) c = UNICODE_BOX_CHARACTER; // code points this big should never appear in valid Unicode uint page = c / CHAR_PAGE_SIZE; uint index = c % CHAR_PAGE_SIZE; text_render_with_page(font, (int)page); @@ -191,13 +191,6 @@ static void text_render_char_internal(Font *font, char32_t c, TextRenderState *s } } -void text_render_char(Font *font, char32_t c, float *x, float *y) { - TextRenderState state = {*x, *y}; - text_render_char_internal(font, c, &state); - *x = state.x; - *y = state.y; -} - static void text_render_internal(Font *font, char const *text, float *x, float *y) { mbstate_t mbstate = {0}; TextRenderState render_state = {*x, *y}; @@ -208,18 +201,18 @@ static void text_render_internal(Font *font, char const *text, float *x, float * size_t ret = mbrtoc32(&c, text, (size_t)(end - text), &mbstate); if (ret == 0) break; if (ret == (size_t)(-2)) { // incomplete multi-byte character - text_render_char_internal(font, '?', &render_state); + text_render_char(font, '?', &render_state); text = end; // done reading text } else if (ret == (size_t)(-1)) { // invalid UTF-8; skip this byte - text_render_char_internal(font, '?', &render_state); + text_render_char(font, '?', &render_state); ++text; } else { if (ret != (size_t)(-3)) text += ret; // character consists of `ret` bytes switch (c) { default: - text_render_char_internal(font, (char32_t)c, &render_state); + text_render_char(font, (char32_t)c, &render_state); break; } } @@ -10,6 +10,12 @@ typedef struct Font Font; +typedef struct { + float x, y; + // points at which the text should be cut off in the x and y directions + float edge_right, edge_bottom; +} TextRenderState; + extern bool text_has_err(void); // Get the current error. Errors will NOT be overwritten with newer errors. extern char const *text_get_err(void); @@ -17,6 +23,8 @@ extern char const *text_get_err(void); extern void text_clear_err(void); // Load a TTF font found in ttf_filename with the given font size (character pixel height) extern Font *text_font_load(char const *ttf_filename, float font_size); +// Height of a character of this font in pixels. +extern float text_font_char_height(Font *font); // Render some UTF-8 text to the screen (simple interface). extern void text_render(Font *font, char const *text, float x, float y); // Get the dimensions of some text. @@ -26,7 +34,7 @@ extern void text_chars_begin(Font *font); // Finish writing characters. extern void text_chars_end(Font *font); // Render a single character. -extern void text_render_char(Font *font, char32_t c, float *x, float *y); +extern void text_render_char(Font *font, char32_t c, TextRenderState *state); // Free memory used by font. extern void text_font_free(Font *font); diff --git a/unicode.h b/unicode.h new file mode 100644 index 0000000..387c08d --- /dev/null +++ b/unicode.h @@ -0,0 +1,5 @@ +#ifndef UNICODE_H_ +#define UNICODE_H_ +#define UNICODE_BOX_CHARACTER 0x2610 +#define UNICODE_CODE_POINTS 0x110000 // number of Unicode code points +#endif |