From 5a572e2dc57302a43483349cecc7d41353a3118c Mon Sep 17 00:00:00 2001 From: Relintai Date: Thu, 4 Jan 2024 12:12:57 +0100 Subject: [PATCH] Font initial rework. --- game_scene.cpp | 15 ++ game_scene.h | 5 + sfw/render_core/font.cpp | 411 ++++++++++++++------------------------- sfw/render_core/font.h | 154 +++++---------- 4 files changed, 220 insertions(+), 365 deletions(-) diff --git a/game_scene.cpp b/game_scene.cpp index 2751ace..0e793d1 100644 --- a/game_scene.cpp +++ b/game_scene.cpp @@ -96,6 +96,8 @@ void GameScene::render() { sprite->render(); tile_map->render(); + _font_test_sprite->render(); + //TextRenderer::get_singleton()->font_init(); //TextRenderer::get_singleton()->font_print("test"); } @@ -113,6 +115,19 @@ GameScene::GameScene() { //float ar = static_cast(w) / static_cast(h); //camera->width = camera->height * ar; + _font.instance(); + _font->load_default(31.5); + + _font_test_sprite = memnew(Sprite); + + _font_test_mat = new TextureMaterial2D(); + _font_test_mat->texture = _font->get_texture().ptr(); + _font_test_sprite->mesh_instance->material = _font_test_mat; + _font_test_sprite->width = _font->get_atlas_width(); + _font_test_sprite->height = _font->get_atlas_height(); + _font_test_sprite->transform.set_origin(Vector2(1000, 100)); + _font_test_sprite->update_mesh(); + image.instance(); image->load_from_file("icon.png"); //image->bumpmap_to_normalmap(); diff --git a/game_scene.h b/game_scene.h index ef94721..f95d0e8 100644 --- a/game_scene.h +++ b/game_scene.h @@ -5,6 +5,7 @@ #include "render_core/color_material.h" #include "render_core/colored_material.h" +#include "render_core/font.h" #include "render_core/image.h" #include "render_core/mesh.h" #include "render_core/texture.h" @@ -36,6 +37,10 @@ public: Texture *texture; TextureMaterial2D *material; + Ref _font; + Sprite *_font_test_sprite; + TextureMaterial2D *_font_test_mat; + Camera2D *camera_2d; TileMap *tile_map; Sprite *sprite; diff --git a/sfw/render_core/font.cpp b/sfw/render_core/font.cpp index bf94492..7724c05 100644 --- a/sfw/render_core/font.cpp +++ b/sfw/render_core/font.cpp @@ -42,84 +42,21 @@ #include "font_data_bm_mini.inc.h" #include "font_data_tables.inc.h" -#define RGB4(r, g, b, a) ((((uint32_t)a) << 24) | (((uint32_t)b) << 16) | (((uint32_t)g) << 8) | ((uint32_t)r)) +#include "render_core/image.h" +#include "render_core/texture.h" -uint32_t TextRenderer::font_palette[FONT_MAX_COLORS] = { - RGB4(248, 248, 242, 255), // foreground color - RGB4(249, 38, 114, 255), // operator - RGB4(174, 129, 255, 255), // numeric - RGB4(102, 217, 239, 255), // function - RGB4(249, 38, 114, 255), // keyword - RGB4(117, 113, 94, 255), // comment - RGB4(102, 217, 239, 255), // type - RGB4(73, 72, 62, 255), // background color - RGB4(39, 40, 34, 255), // clear color -}; - -void TextRenderer::font_init() { - if (_fonts_initialized) { - return; - } - - _fonts_initialized = true; - - font_face_from_mem(FONT_FACE1, bm_mini_ttf, 20176, 42.5f, 0); - font_face_from_mem(FONT_FACE2, bm_mini_ttf, 20176, 42.5f, 0); - font_face_from_mem(FONT_FACE3, bm_mini_ttf, 20176, 42.5f, 0); - font_face_from_mem(FONT_FACE4, bm_mini_ttf, 20176, 42.5f, 0); - font_face_from_mem(FONT_FACE5, bm_mini_ttf, 20176, 42.5f, 0); - font_face_from_mem(FONT_FACE6, bm_mini_ttf, 20176, 42.5f, 0); - font_face_from_mem(FONT_FACE7, bm_mini_ttf, 20176, 42.5f, 0); - font_face_from_mem(FONT_FACE8, bm_mini_ttf, 20176, 42.5f, 0); - font_face_from_mem(FONT_FACE9, bm_mini_ttf, 20176, 42.5f, 0); - font_face_from_mem(FONT_FACE10, bm_mini_ttf, 20176, 42.5f, 0); +void Font::load_default(const float size, const uint32_t flags) { + font_face_from_mem(bm_mini_ttf, 20176, size, flags); } -// Remap color within all existing color textures -void TextRenderer::font_color(const char *tag, uint32_t color) { - unsigned index = *tag - FONT_COLOR1[0]; - if (index < FONT_MAX_COLORS) { - font_palette[index] = color; - - for (int i = 0; i < FONTS_MAX; ++i) { - font_t *f = &fonts[i]; - if (f->initialized) { - glActiveTexture(GL_TEXTURE2); - glBindTexture(GL_TEXTURE_1D, f->texture_colors); - glTexSubImage1D(GL_TEXTURE_1D, 0, 0, FONT_MAX_COLORS, GL_RGBA, GL_UNSIGNED_BYTE, font_palette); - } - } - } +float Font::get_scale() const { + return _scale; +} +void Font::set_scale(const float p_scale) { + _scale = p_scale; } -void TextRenderer::font_scales(const char *tag, float h1, float h2, float h3, float h4, float h5, float h6) { - unsigned index = *tag - FONT_FACE1[0]; - if (index > FONTS_MAX) - return; - - font_t *f = &fonts[index]; - if (!f->initialized) - return; - - f->scale[0] = h1 / f->font_size; - f->scale[1] = h1 / f->font_size; - f->scale[2] = h2 / f->font_size; - f->scale[3] = h3 / f->font_size; - f->scale[4] = h4 / f->font_size; - f->scale[5] = h5 / f->font_size; - f->scale[6] = h6 / f->font_size; -} - -// 1. Compile the shaders. -// 1. Call stb_truetype.h routines to read and parse a .ttf file. -// 1. Create a bitmap that is uploaded to the gpu using opengl. -// 1. Calculate and save a bunch of useful variables and put them in the global font variable. -void TextRenderer::font_face_from_mem(const char *tag, const void *ttf_data, unsigned ttf_len, float font_size, unsigned flags) { - unsigned index = *tag - FONT_FACE1[0]; - if (index > FONTS_MAX) { - return; - } - +void Font::font_face_from_mem(const void *ttf_data, uint32_t ttf_len, float font_size, uint32_t flags) { if (font_size <= 0 || font_size > 72) { return; } @@ -132,29 +69,18 @@ void TextRenderer::font_face_from_mem(const char *tag, const void *ttf_data, uns flags |= FONT_ASCII; // ensure this minimal range [0020-00FF] is almost always in } - font_t *f = &fonts[index]; - f->initialized = 1; + _initialized = 1; // load .ttf into a bitmap using stb_truetype.h int dim = flags & FONT_4096 ? 4096 : flags & FONT_2048 ? 2048 : flags & FONT_1024 ? 1024 : 512; - f->width = dim; - f->height = dim; + _width = dim; + _height = dim; // change size [h1(largest) to h3(regular) to h6(smallest)] - f->font_size = font_size; - f->scale[0] = 1.0000f; // H1 - f->scale[1] = 1.0000f; // H1 - f->scale[2] = 0.7500f; // H2 - f->scale[3] = 0.6600f; // H3 - f->scale[4] = 0.5000f; // H4 - f->scale[5] = 0.3750f; // H5 - f->scale[6] = 0.2500f; // H6 - - //f->program = shader(vs, fs, "vertexPosition,instanceGlyph", "outColor", NULL); - - //f->program.instance(); + _font_size = font_size; + _scale = 1.0000f; // figure out what ranges we're about to bake #define MERGE_TABLE(table) \ @@ -236,14 +162,17 @@ void TextRenderer::font_face_from_mem(const char *tag, const void *ttf_data, uns } // pack and create bitmap - unsigned char *bitmap = memnew_arr(unsigned char, f->height * f->width); + //unsigned char *bitmap = memnew_arr(unsigned char, _height * _width); + Vector bitmap_data; + bitmap_data.resize(_height * _width); + unsigned char *bitmap = (unsigned char *)bitmap_data.ptrw(); int charCount = sorted[sorted.size() - 1] - sorted[0] + 1; // 0xEFFFF; - f->cdata = (stbtt_packedchar *)calloc(1, sizeof(stbtt_packedchar) * charCount); - f->iter2cp = memnew_arr(unsigned int, charCount); - f->cp2iter = memnew_arr(unsigned int, charCount); + _cdata = (stbtt_packedchar *)calloc(1, sizeof(stbtt_packedchar) * charCount); + _iter2cp = memnew_arr(unsigned int, charCount); + _cp2iter = memnew_arr(unsigned int, charCount); for (int i = 0; i < charCount; ++i) { - f->iter2cp[i] = f->cp2iter[i] = 0xFFFD; // default invalid glyph + _iter2cp[i] = _cp2iter[i] = 0xFFFD; // default invalid glyph } // find first ch./co @@ -255,13 +184,13 @@ void TextRenderer::font_face_from_mem(const char *tag, const void *ttf_data, uns unsigned glyph = sorted[i]; if (!stbtt_FindGlyphIndex(&info, glyph)) continue; - f->begin = glyph; + _begin = glyph; break; } } stbtt_pack_context pc; - if (!stbtt_PackBegin(&pc, bitmap, f->width, f->height, 0, 1, NULL)) { + if (!stbtt_PackBegin(&pc, bitmap, _width, _height, 0, 1, NULL)) { ERR_FAIL_COND("Failed to initialize atlas font"); } stbtt_PackSetOversampling(&pc, flags & FONT_OVERSAMPLE_X ? 2 : 1, flags & FONT_OVERSAMPLE_Y ? 2 : 1); /*useful on small fonts*/ @@ -273,21 +202,21 @@ void TextRenderer::font_face_from_mem(const char *tag, const void *ttf_data, uns } //printf("(%d,%d)", (unsigned)begin, (unsigned)end); - if (begin < f->begin) + if (begin < _begin) continue; - if (stbtt_PackFontRange(&pc, (const unsigned char *)ttf_data, 0, f->font_size, begin, end - begin + 1, (stbtt_packedchar *)f->cdata + begin - f->begin)) { + if (stbtt_PackFontRange(&pc, (const unsigned char *)ttf_data, 0, _font_size, begin, end - begin + 1, (stbtt_packedchar *)_cdata + begin - _begin)) { for (uint64_t cp = begin; cp <= end; ++cp) { // unicode->index runtime lookup - f->cp2iter[cp - f->begin] = count; - f->iter2cp[count++] = cp; + _cp2iter[cp - _begin] = count; + _iter2cp[count++] = cp; } } else { ERR_PRINT("!Failed to pack atlas font. Likely out of texture mem."); } } stbtt_PackEnd(&pc); - f->num_glyphs = count; + _num_glyphs = count; // calculate vertical font metrics stbtt_fontinfo info = { 0 }; @@ -298,141 +227,122 @@ void TextRenderer::font_face_from_mem(const char *tag, const void *ttf_data, uns stbtt_GetFontVMetrics(&info, &a, &d, &l); } - f->ascent = a; - f->descent = d; - f->linegap = l; - f->linedist = (a - d + l); - f->factor = (f->font_size / (f->ascent - f->descent)); + _ascent = a; + _descent = d; + _linegap = l; + _linedist = (a - d + l); + _factor = (_font_size / (_ascent - _descent)); // save some gpu memory by truncating unused vertical space in atlas texture { int max_y1 = 0; - for (unsigned int i = 0; i < f->num_glyphs; i++) { - int cp = f->iter2cp[i]; + for (unsigned int i = 0; i < _num_glyphs; i++) { + int cp = _iter2cp[i]; if (cp == 0xFFFD) continue; - stbtt_packedchar *cd = &f->cdata[cp - f->begin]; + stbtt_packedchar *cd = &_cdata[cp - _begin]; if (cd->y1 > max_y1) { max_y1 = cd->y1; } } // cut away the unused part of the bitmap - f->height = max_y1 + 1; + _height = max_y1 + 1; } - LOG_MSG("Font atlas size %dx%d (GL_R, %5.2fKiB) (%u glyphs)\n", f->width, f->height, f->width * f->height / 1024.f, f->num_glyphs); + //LOG_MSG("Font atlas size %dx%d (GL_R, %5.2fKiB) (%u glyphs)\n", _width, _height, _width * _height / 1024.f, _num_glyphs); /* - // vao - glGenVertexArrays(1, &f->vao); - glBindVertexArray(f->vao); - - // quad vbo setup, used for glyph vertex positions, - // just uv coordinates that will be stretched accordingly by the glyphs width and height - float v[] = { 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 1 }; - - glGenBuffers(1, &f->vbo_quad); - glBindBuffer(GL_ARRAY_BUFFER, f->vbo_quad); - glBufferData(GL_ARRAY_BUFFER, sizeof(v), v, GL_STATIC_DRAW); - - glEnableVertexAttribArray(0); - glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, (void *)0); - glVertexAttribDivisor(0, 0); - - // instance vbo setup: for glyph positions, glyph index and color index - glGenBuffers(1, &f->vbo_instances); - glBindBuffer(GL_ARRAY_BUFFER, f->vbo_instances); - glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 4 * FONT_MAX_STRING_LEN, NULL, GL_DYNAMIC_DRAW); - - glEnableVertexAttribArray(1); - glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, 0, (void *)0); - glVertexAttribDivisor(1, 1); - //glEnable(GL_FRAMEBUFFER_SRGB); - // setup and upload font bitmap texture glGenTextures(1, &f->texture_fontdata); - glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, f->texture_fontdata); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, f->width, f->height, 0, GL_RED, GL_UNSIGNED_BYTE, bitmap); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - + glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, _width, _height, 0, GL_RED, GL_UNSIGNED_BYTE, bitmap); */ // last chance to inspect the font atlases //if (flag("--font-debug")) //String pngname = "font_debug" + itos(index) + " .png"; - //stbi_write_png(pngname.utf8().get_data(), f->width, f->height, 1, bitmap, 0); + //stbi_write_png(pngname.utf8().get_data(), _width, _height, 1, bitmap, 0); - memdelete_arr(bitmap); + if (!_image.is_valid()) { + _image.instance(); + } - /* + bitmap_data.resize(_width * _height); - // setup and upload font metadata texture - // used for lookup in the bitmap texture - glGenTextures(1, &f->texture_offsets); - glActiveTexture(GL_TEXTURE1); - glBindTexture(GL_TEXTURE_2D, f->texture_offsets); + _image->create(_width, _height, false, Image::FORMAT_L8, bitmap_data); - float *texture_offsets = memnew_arr(float, 8 * f->num_glyphs); + if (!_texture.is_valid()) { + _texture.instance(); + } + + _texture->create_from_image(_image); + + _texture_offsets.resize(_num_glyphs); // remap larger 0xFFFF unicodes into smaller NUM_GLYPHS glyphs - for (unsigned i = 0, count = 0; i < f->num_glyphs; i++) { - unsigned cp = f->iter2cp[i]; + for (unsigned i = 0; i < _num_glyphs; i++) { + unsigned cp = _iter2cp[i]; + if (cp == 0xFFFD) { + continue; + } + + stbtt_packedchar *cd = &_cdata[cp - _begin]; + // if(cd->x1==cd->x0) { _iter2cp[i] = _cp2iter[cp - _begin] = 0xFFFD; continue; } + + TextureOffset offset; + + offset.x = cd->x0 / (double)_width; + offset.y = cd->y0 / (double)_height; + offset.w = (cd->x1 - cd->x0) / (double)_width; + offset.h = (cd->y1 - cd->y0) / (double)_height; + + offset.xoff = cd->xoff / (double)_width; + offset.yoff = cd->yoff / (double)_height; + offset.woff = cd->xoff2 / (double)_width; + offset.hoff = cd->yoff2 / (double)_height; + + _texture_offsets.write[i] = offset; + } + + /* + float *texture_offsets = memnew_arr(float, 8 * _num_glyphs); + + // remap larger 0xFFFF unicodes into smaller NUM_GLYPHS glyphs + for (unsigned i = 0, count = 0; i < _num_glyphs; i++) { + unsigned cp = _iter2cp[i]; if (cp == 0xFFFD) continue; - stbtt_packedchar *cd = &f->cdata[cp - f->begin]; - // if(cd->x1==cd->x0) { f->iter2cp[i] = f->cp2iter[cp - f->begin] = 0xFFFD; continue; } + stbtt_packedchar *cd = &_cdata[cp - _begin]; + // if(cd->x1==cd->x0) { _iter2cp[i] = _cp2iter[cp - _begin] = 0xFFFD; continue; } - int k1 = 0 * f->num_glyphs + count; - int k2 = 1 * f->num_glyphs + count; + int k1 = 0 * _num_glyphs + count; + int k2 = 1 * _num_glyphs + count; ++count; - texture_offsets[4 * k1 + 0] = cd->x0 / (double)f->width; - texture_offsets[4 * k1 + 1] = cd->y0 / (double)f->height; - texture_offsets[4 * k1 + 2] = (cd->x1 - cd->x0) / (double)f->width; - texture_offsets[4 * k1 + 3] = (cd->y1 - cd->y0) / (double)f->height; + texture_offsets[4 * k1 + 0] = cd->x0 / (double)_width; + texture_offsets[4 * k1 + 1] = cd->y0 / (double)_height; + texture_offsets[4 * k1 + 2] = (cd->x1 - cd->x0) / (double)_width; + texture_offsets[4 * k1 + 3] = (cd->y1 - cd->y0) / (double)_height; - texture_offsets[4 * k2 + 0] = cd->xoff / (double)f->width; - texture_offsets[4 * k2 + 1] = cd->yoff / (double)f->height; - texture_offsets[4 * k2 + 2] = cd->xoff2 / (double)f->width; - texture_offsets[4 * k2 + 3] = cd->yoff2 / (double)f->height; + texture_offsets[4 * k2 + 0] = cd->xoff / (double)_width; + texture_offsets[4 * k2 + 1] = cd->yoff / (double)_height; + texture_offsets[4 * k2 + 2] = cd->xoff2 / (double)_width; + texture_offsets[4 * k2 + 3] = cd->yoff2 / (double)_height; } - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, f->num_glyphs, 2, 0, GL_RGBA, GL_FLOAT, texture_offsets); - - memfree(texture_offsets); - - // setup color texture - glGenTextures(1, &f->texture_colors); - glActiveTexture(GL_TEXTURE2); - glBindTexture(GL_TEXTURE_1D, f->texture_colors); - glTexImage1D(GL_TEXTURE_1D, 0, GL_RGBA, FONT_MAX_COLORS, 0, GL_RGBA, GL_UNSIGNED_BYTE, font_palette); - glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); - glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_REPEAT); - - // upload constant uniforms - glUseProgram(f->program->get_program()); glUniform1i(glGetUniformLocation(f->program->get_program(), "sampler_font"), 0); glUniform1i(glGetUniformLocation(f->program->get_program(), "sampler_meta"), 1); glUniform1i(glGetUniformLocation(f->program->get_program(), "sampler_colors"), 2); - glUniform2f(glGetUniformLocation(f->program->get_program(), "res_bitmap"), f->width, f->height); - glUniform2f(glGetUniformLocation(f->program->get_program(), "res_meta"), f->num_glyphs, 2); + glUniform2f(glGetUniformLocation(f->program->get_program(), "res_bitmap"), _width, _height); + glUniform2f(glGetUniformLocation(f->program->get_program(), "res_meta"), _num_glyphs, 2); glUniform1f(glGetUniformLocation(f->program->get_program(), "num_colors"), FONT_MAX_COLORS); (void)flags; */ } -void TextRenderer::font_face(const char *tag, const char *filename_ttf, float font_size, unsigned flags) { +void Font::font_face(const char *filename_ttf, float font_size, unsigned flags) { /* font_init(); @@ -445,7 +355,8 @@ void TextRenderer::font_face(const char *tag, const char *filename_ttf, float fo */ } -void TextRenderer::font_draw_cmd(font_t *f, const float *glyph_data, int glyph_idx, float factor, Vector2 offset) { +void Font::font_draw_cmd(const float *glyph_data, int glyph_idx, float factor, Vector2 offset) { + /* // Backup GL state GLint last_program, last_vertex_array; GLint last_texture0, last_texture1, last_texture2; @@ -491,7 +402,7 @@ void TextRenderer::font_draw_cmd(font_t *f, const float *glyph_data, int glyph_i glUseProgram(f->program->get_program()); glUniform1f(glGetUniformLocation(f->program->get_program(), "scale_factor"), factor); glUniform2fv(glGetUniformLocation(f->program->get_program(), "string_offset"), 1, &offset.x); - glUniform1f(glGetUniformLocation(f->program->get_program(), "offset_firstline"), f->ascent * f->factor); + glUniform1f(glGetUniformLocation(f->program->get_program(), "offset_firstline"), _ascent * _factor); GLint dims[4] = { 0 }; glGetIntegerv(GL_VIEWPORT, dims); @@ -520,12 +431,14 @@ void TextRenderer::font_draw_cmd(font_t *f, const float *glyph_data, int glyph_i (last_enable_depth_test ? glEnable(GL_DEPTH_TEST) : glDisable(GL_DEPTH_TEST)); (last_enable_blend ? glEnable(GL_BLEND) : glDisable(GL_BLEND)); + */ } // 1. call font_face() if it's the first time it's called. // 1. parse the string and update the instance vbo, then upload it // 1. draw the string -Vector2 TextRenderer::font_draw_ex(const String &text, Vector2 offset, const char *col, void (*draw_cmd)(font_t *, const float *, int, float, Vector2)) { +Vector2 Font::font_draw_ex(const String &text, Vector2 offset, const char *col, void (*draw_cmd)(const float *, int, float, Vector2)) { + /* font_init(); // sanity checks @@ -544,7 +457,7 @@ Vector2 TextRenderer::font_draw_ex(const String &text, Vector2 offset, const cha font_t *f = &fonts[0]; int S = 3; uint32_t color = 0; - float X = 0, Y = 0, W = 0, L = f->ascent * f->factor * f->scale[S], LL = L; // LL=largest linedist + float X = 0, Y = 0, W = 0, L = _ascent * _factor * _scale[S], LL = L; // LL=largest linedist offset.y = -offset.y; // invert y polarity // parse string @@ -557,25 +470,25 @@ Vector2 TextRenderer::font_draw_ex(const String &text, Vector2 offset, const cha if (X > W) W = X; X = 0.0; - Y -= f->linedist * f->factor * f->scale[S]; + Y -= _linedist * _factor * _scale[S]; if (i + 1 == end) { //@hack: ensures we terminate the height at the correct position - Y -= (f->descent + f->linegap) * f->factor * f->scale[S]; + Y -= (_descent + _linegap) * _factor * _scale[S]; } continue; } if (ch >= 1 && ch <= 6) { // flush previous state if (draw_cmd) - draw_cmd(f, text_glyph_data, (t - text_glyph_data) / 4, f->scale[S], offset); + draw_cmd(f, text_glyph_data, (t - text_glyph_data) / 4, _scale[S], offset); t = text_glyph_data; // reposition offset to align new baseline // @fixme: - // offset.y += (f->linedist - f->linegap) * ( f->scale[ch] - f->scale[S] ); + // offset.y += (_linedist - _linegap) * ( _scale[ch] - _scale[S] ); // change size S = ch; - L = f->ascent * f->factor * f->scale[S]; + L = _ascent * _factor * _scale[S]; if (L > LL) LL = L; continue; @@ -588,7 +501,7 @@ Vector2 TextRenderer::font_draw_ex(const String &text, Vector2 offset, const cha if (fonts[ch - 0x10].initialized) { // flush previous state if (draw_cmd) - draw_cmd(f, text_glyph_data, (t - text_glyph_data) / 4, f->scale[S], offset); + draw_cmd(f, text_glyph_data, (t - text_glyph_data) / 4, _scale[S], offset); t = text_glyph_data; // change face @@ -598,86 +511,47 @@ Vector2 TextRenderer::font_draw_ex(const String &text, Vector2 offset, const cha } // convert to vbo data - int cp = ch - f->begin; // f->cp2iter[ch - f->begin]; + int cp = ch - _begin; // _cp2iter[ch - _begin]; //if(cp == 0xFFFD) continue; - //if (cp > f->num_glyphs) continue; + //if (cp > _num_glyphs) continue; *t++ = X; *t++ = Y; - *t++ = f->cp2iter[cp]; + *t++ = _cp2iter[cp]; *t++ = col ? col[i] : color; - X += f->cdata[cp].xadvance * f->scale[S]; + X += _cdata[cp].xadvance * _scale[S]; } if (draw_cmd) - draw_cmd(f, text_glyph_data, (t - text_glyph_data) / 4, f->scale[S], offset); + draw_cmd(f, text_glyph_data, (t - text_glyph_data) / 4, _scale[S], offset); - //if(strstr(text, "fps")) printf("(%f,%f) (%f) L:%f LINEDIST:%f\n", X, Y, W, L, f->linedist); + //if(strstr(text, "fps")) printf("(%f,%f) (%f) L:%f LINEDIST:%f\n", X, Y, W, L, _linedist); return Vector2(W * W > X * X ? W : X, Y * Y > LL * LL ? Y : LL).abs(); + */ + + return Vector2(); } // Return cursor -Vector2 TextRenderer::font_xy() { +Vector2 Font::font_xy() { return gotoxy; } // Relocate cursor -void TextRenderer::font_goto(float x, float y) { +void Font::font_goto(float x, float y) { gotoxy = Vector2(x, y); } -// Print and linefeed. Text may include markup code -Vector2 TextRenderer::font_print(const String &text) { - // @fixme: remove this hack - if (text[0] == FONT_LEFT[0]) { - int window_width = AppWindow::get_singleton()->get_width(); - int window_height = AppWindow::get_singleton()->get_height(); - - int l = text[1] == FONT_LEFT[1]; - int c = text[1] == FONT_CENTER[1]; - int r = text[1] == FONT_RIGHT[1]; - if (l || c || r) { - String ntext = text.substr(2); - - Vector2 rect = font_rect(ntext); - gotoxy.x = l ? 0 : r ? (window_width - rect.x) - : window_width / 2 - rect.x / 2; - return font_print(ntext); - } - int t = text[1] == FONT_TOP[1]; - int b = text[1] == FONT_BOTTOM[1]; - int m = text[1] == FONT_MIDDLE[1]; - int B = text[1] == FONT_BASELINE[1]; - if (t || b || m || B) { - String ntext = text.substr(2); - - Vector2 rect = font_rect(ntext); - gotoxy.y = t ? 0 : b ? (window_height - rect.y) - : m ? window_height / 2 - rect.y / 2 - : window_height / 2 - rect.y / 1; - return font_print(ntext); - } - } - - Vector2 dims = font_draw_ex(text, gotoxy, NULL, font_draw_cmd); - - int nindex = text.find_char('\n'); - - gotoxy.y += nindex ? dims.y : 0; - gotoxy.x = nindex ? 0 : gotoxy.x + dims.x; - return dims; -} - // Calculate the size of a string, in the pixel size specified. Count stray newlines too. -Vector2 TextRenderer::font_rect(const String &str) { +Vector2 Font::font_rect(const String &str) { return font_draw_ex(str, gotoxy, NULL, NULL); } -TextRenderer::font_metrics_t TextRenderer::font_metrics(const String &text) { +Font::font_metrics_t Font::font_metrics(const String &text) { font_metrics_t m = { 0 }; - int S = 3; - font_t *f = &fonts[0]; + + /* // parse string for (int i = 0, end = text.length(); i < end; ++i) { @@ -695,20 +569,31 @@ TextRenderer::font_metrics_t TextRenderer::font_metrics(const String &text) { } } - m.ascent = f->ascent * f->factor * f->scale[S]; - m.descent = f->descent * f->factor * f->scale[S]; - m.linegap = f->linegap * f->factor * f->scale[S]; - m.linedist = f->linedist * f->factor * f->scale[S]; + */ + + m.ascent = _ascent * _factor * _scale; + m.descent = _descent * _factor * _scale; + m.linegap = _linegap * _factor * _scale; + m.linedist = _linedist * _factor * _scale; + return m; } -TextRenderer *TextRenderer::get_singleton() { - return _singleton; +int Font::get_atlas_width() { + return _width; } -TextRenderer::TextRenderer() { - _singleton = this; - _fonts_initialized = false; +int Font::get_atlas_height() { + return _height; } -TextRenderer *TextRenderer::_singleton = NULL; +Ref Font::get_image() { + return _image; +} +Ref Font::get_texture() { + return _texture; +} + +Font::Font() { + _initialized = false; +} diff --git a/sfw/render_core/font.h b/sfw/render_core/font.h index a34985d..6c5b5ce 100644 --- a/sfw/render_core/font.h +++ b/sfw/render_core/font.h @@ -5,54 +5,19 @@ // font framework originally from FWK // - rlyeh, public domain -#include "object/object.h" +#include "object/resource.h" #include "core/ustring.h" - #include "font_material.h" // TODO figure out how to forward declare stbtt_packedchar #include "3rd_stb_truetype.h" -// font size tags -#define FONT_H1 "\1" // largest -#define FONT_H2 "\2" -#define FONT_H3 "\3" -#define FONT_H4 "\4" -#define FONT_H5 "\5" -#define FONT_H6 "\6" // smallest +class Image; +class Texture; -// font color tags -#define FONT_COLOR1 "\x1a" -#define FONT_COLOR2 "\x1b" -#define FONT_COLOR3 "\x1c" -#define FONT_COLOR4 "\x1d" -#define FONT_COLOR5 "\x1e" -#define FONT_COLOR6 "\x1f" - -// font face tags -#define FONT_FACE1 "\x10" -#define FONT_FACE2 "\x11" -#define FONT_FACE3 "\x12" -#define FONT_FACE4 "\x13" -#define FONT_FACE5 "\x14" -#define FONT_FACE6 "\x15" -#define FONT_FACE7 "\x16" -#define FONT_FACE8 "\x17" // editor may override this one -#define FONT_FACE9 "\x18" // editor may override this one -#define FONT_FACE10 "\x19" // editor may override this one - -// font align tags -#define FONT_LEFT "\\<" -#define FONT_CENTER "\\|" -#define FONT_RIGHT "\\>" -#define FONT_TOP "\\^" -#define FONT_MIDDLE "\\-" -#define FONT_BASELINE "\\_" -#define FONT_BOTTOM "\\v" - -class TextRenderer : public Object { - SFW_OBJECT(TextRenderer, Object); +class Font : public Resource { + SFW_OBJECT(Font, Resource); public: // font flags @@ -93,13 +58,14 @@ public: float linedist; // distance between the baseline of two lines (ascent - descent + linegap) } font_metrics_t; - void font_init(); + void load_default(const float size, const uint32_t flags = 0); + + float get_scale() const; + void set_scale(const float p_scale); // configures - void font_face(const char *face_tag, const char *filename_ttf, float font_size, unsigned flags); - void font_face_from_mem(const char *tag, const void *ttf_buffer, unsigned ttf_len, float font_size, unsigned flags); - void font_scales(const char *face_tag, float h1, float h2, float h3, float h4, float h5, float h6); - void font_color(const char *color_tag, uint32_t color); + void font_face(const char *filename_ttf, float font_size, uint32_t flags); + void font_face_from_mem(const void *ttf_buffer, uint32_t ttf_len, float font_size, uint32_t flags); // commands Vector2 font_xy(); @@ -108,76 +74,60 @@ public: Vector2 font_rect(const String &str); font_metrics_t font_metrics(const String &text); - static TextRenderer *get_singleton(); + int get_atlas_width(); + int get_atlas_height(); - TextRenderer(); + Ref get_image(); + Ref get_texture(); + + Font(); protected: - bool _fonts_initialized; + // character info + // filled up by stb_truetype.h + stbtt_packedchar *_cdata; + unsigned _num_glyphs; + unsigned *_cp2iter; + unsigned *_iter2cp; + unsigned _begin; // first glyph. used in cp2iter table to clamp into a lesser range - enum { FONT_MAX_COLORS = 256 }; - enum { FONT_MAX_STRING_LEN = 40000 }; // more glyphs than any reasonable person would show on the screen at once. you can only fit 20736 10x10 rects in a 1920x1080 window + // font info and data + bool _initialized; - static uint32_t font_palette[FONT_MAX_COLORS]; + int _height; // bitmap height + int _width; // bitmap width + float _font_size; // font size in pixels (matches scale[0+1] size below) + float _factor; // font factor (font_size / (ascent - descent)) + float _scale; - typedef struct font_t { - bool initialized; + // displacement info + float _ascent; // max distance above baseline for all glyphs + float _descent; // max distance below baseline for all glyphs + float _linegap; // distance betwen ascent of next line and descent of current line + float _linedist; // distance between the baseline of two lines (ascent - descent + linegap) - //char filename[256]; + // opengl stuff + Ref _image; + Ref _texture; - // character info - // filled up by stb_truetype.h - stbtt_packedchar *cdata; - unsigned num_glyphs; - unsigned *cp2iter; - unsigned *iter2cp; - unsigned begin; // first glyph. used in cp2iter table to clamp into a lesser range + struct TextureOffset { + float x; + float y; + float w; + float h; - // font info and data - int height; // bitmap height - int width; // bitmap width - float font_size; // font size in pixels (matches scale[0+1] size below) - float factor; // font factor (font_size / (ascent - descent)) - float scale[7]; // user defined font scale (match H1..H6 tags) + float xoff; + float yoff; + float woff; + float hoff; + }; - // displacement info - float ascent; // max distance above baseline for all glyphs - float descent; // max distance below baseline for all glyphs - float linegap; // distance betwen ascent of next line and descent of current line - float linedist; // distance between the baseline of two lines (ascent - descent + linegap) + Vector _texture_offsets; - // opengl stuff - GLuint vao; - Ref program; - - // font bitmap texture - // generated using stb_truetype.h - GLuint texture_fontdata; - - // metadata texture. - // first row contains information on which parts of the bitmap correspond to a glyph. - // the second row contain information about the relative displacement of the glyph relative to the cursor position - GLuint texture_offsets; - - // color texture - // used to color each glyph individually, e.g. for syntax highlighting - GLuint texture_colors; - - // vbos - GLuint vbo_quad; // Vector2: simply just a regular [0,1]x[0,1] quad - GLuint vbo_instances; // vec4: (char_pos_x, char_pos_y, char_index, color_index) - } font_t; - - enum { FONTS_MAX = 10 }; - - font_t fonts[FONTS_MAX]; - - static void font_draw_cmd(font_t *f, const float *glyph_data, int glyph_idx, float factor, Vector2 offset); - Vector2 font_draw_ex(const String &text, Vector2 offset, const char *col, void (*draw_cmd)(font_t *, const float *, int, float, Vector2)); + static void font_draw_cmd(const float *glyph_data, int glyph_idx, float factor, Vector2 offset); + Vector2 font_draw_ex(const String &text, Vector2 offset, const char *col, void (*draw_cmd)(const float *, int, float, Vector2)); Vector2 gotoxy; - - static TextRenderer *_singleton; }; #endif