From 26f05c6e3dcac9995345fb5a2b031be7e3ea79e9 Mon Sep 17 00:00:00 2001 From: chai Date: Sat, 30 Oct 2021 22:59:42 +0800 Subject: *TextGenerator --- Runtime/GUI/TextGenerator.cpp | 205 ++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 196 insertions(+), 9 deletions(-) (limited to 'Runtime/GUI/TextGenerator.cpp') diff --git a/Runtime/GUI/TextGenerator.cpp b/Runtime/GUI/TextGenerator.cpp index 6841c4e..a518772 100644 --- a/Runtime/GUI/TextGenerator.cpp +++ b/Runtime/GUI/TextGenerator.cpp @@ -1,19 +1,206 @@ #include "freetype.h" #include "TextGenerator.h" #include "../Math/Math.h" +#include "Runtime/Debug/Log.h" +#include "Runtime/Utilities/Assert.h" +#include "Runtime/Graphics/ImageData.h" using namespace character; +//https://unicode-table.com/en/#cjk-unified-ideographs-extension-a //https://learnopengl.com/In-Practice/Text-Rendering +//https://www.zhihu.com/question/294660079 +//https://baike.baidu.com/item/%E5%9F%BA%E6%9C%AC%E5%A4%9A%E6%96%87%E7%A7%8D%E5%B9%B3%E9%9D%A2/10788078 +//https://stackoverflow.com/questions/27331819/whats-the-difference-between-a-character-a-code-point-a-glyph-and-a-grapheme -struct Character { - unsigned int textureID; // ID handle of the glyph texture - Internal::Vector2 size; // Size of glyph - Internal::Vector2 bearing; // Offset from baseline to left/top of glyph - unsigned int advance; // Offset to advance to next glyph -}; +void TextGenerator::Setup(TextGeneratingSettings settings) +{ + m_AtlasMargin = settings.margin; + m_GlyphPadding = settings.padding; + m_AtlasSize = settings.atlasSize; + + if (FT_Init_FreeType(&m_FTLibrary)) + { + return; + } + + if (FT_New_Face(m_FTLibrary, "Resources/Font/Deng.ttf", 0, &m_FTFace)) + { + return; + } +} + +character::Hash TextGenerator::GetHash(Codepoint codepoint, int pixelSize) +{ + character::Hash hash; + hash.codepoint = codepoint; + hash.size = pixelSize; + return hash; +} + +Character TextGenerator::GetCharacter(character::Codepoint codepoint, int pixelSize) +{ + character::Hash hash = GetHash(codepoint, pixelSize); + auto iter = m_Characters.find(hash); + if (iter == m_Characters.end()) + { + RenderCharacter(codepoint, pixelSize); + iter = m_Characters.find(hash); + } + Assert(iter != m_Characters.end()); + return iter->second; +} + +void TextGenerator::RenderCharacter(character::Codepoint codepoint, int pixelSize) +{ + character::Hash hash = GetHash(codepoint, pixelSize); +// if (m_Characters.count(hash) != 0) +// return; + FT_Set_Pixel_Sizes(m_FTFace, 0, pixelSize); + if (FT_Load_Char(m_FTFace, codepoint, FT_LOAD_RENDER)) + { + return; + } + + int w = m_FTFace->glyph->bitmap.width; + int h = m_FTFace->glyph->bitmap.rows; + + TextHelper::print_glyph(m_FTFace->glyph->bitmap.buffer, w, h); + + GlyphAtals* atlas = RequestAtlas(pixelSize, Internal::Vector2(w, h)); + Assert(atlas); + + Internal::Rect rect = GetRenderChartAndMove(atlas, Internal::Vector2(w, h)); + + try + { + atlas->altas->UpdateSubImage(rect, EPixelFormat::PixelFormat_R, EPixelElementType::PixelType_UNSIGNED_BYTE, m_FTFace->glyph->bitmap.buffer); + } + catch (TextureException& e) + { + std::string error = e.what(); + error = "Render Character Error: " + error; + log_error(e.what()); + return; + } + + Character character; + character.atlas = atlas->index; + character.position = rect; + character.bearing = Internal::Vector2(m_FTFace->glyph->bitmap_left, m_FTFace->glyph->bitmap_top); + character.advance = m_FTFace->glyph->advance.x; + + m_Characters.insert(std::pair(hash, character)); +} + +GlyphAtals* TextGenerator::GetGlyphAtlas(int index) +{ + if (index >= m_Atlases.size()) + return NULL; + return &m_Atlases[index]; +} + +Internal::Rect TextGenerator::GetRenderChartAndMove(GlyphAtals* atlas, Internal::Vector2 preferSize) +{ + Internal::Rect rect; + Internal::Vector2 space; + space.x = atlas->width - m_AtlasMargin * 2 - m_GlyphPadding - atlas->cursor.x; + space.y = atlas->height - m_AtlasMargin * 2 - m_GlyphPadding - atlas->cursor.y; + if (space.x > preferSize.x && space.y > preferSize.y) + { + rect.x = atlas->cursor.x; + rect.y = atlas->cursor.y; + rect.width = preferSize.x; + rect.height = preferSize.y; + atlas->cursor.x += rect.width; + atlas->rowHeight = max(atlas->rowHeight, preferSize.y); + } + else if (space.y - atlas->rowHeight > preferSize.y) + { + rect.x = m_AtlasMargin; + rect.y = atlas->cursor.y + m_GlyphPadding + atlas->rowHeight; + rect.width = preferSize.x; + rect.height = preferSize.y; + atlas->cursor.x = m_AtlasMargin; + atlas->cursor.y += atlas->rowHeight; + atlas->rowHeight = rect.height; + } + else + { + Assert(false); + } + return rect; +} + +GlyphAtals* TextGenerator::RequestAtlas(int pixelSize, Internal::Vector2 preferSize) +{ + GlyphAtals* atlas = NULL; + auto iter = m_AtlasCache.find(pixelSize); + if (iter == m_AtlasCache.end() || !HasEnoughSpace(iter->second, preferSize)) + { + Texture* tex = CreateAtlas(); + GlyphAtals newAtlas = GlyphAtals(); + newAtlas.altas = tex; + newAtlas.width = m_AtlasSize.x; + newAtlas.height = m_AtlasSize.y; + newAtlas.index = m_Atlases.size(); + newAtlas.cursor = Internal::Vector2(m_AtlasMargin, m_AtlasMargin); + + m_Atlases.push_back(newAtlas); + atlas = &m_Atlases[m_Atlases.size() - 1]; + + if (iter != m_AtlasCache.end()) + m_AtlasCache.erase(pixelSize); + m_AtlasCache.insert(std::pair(pixelSize, atlas)); + } + else + { + atlas = iter->second; + } + Assert(atlas); + return atlas; +} + +Texture* TextGenerator::CreateAtlas() +{ + TextureSetting setting; + setting.filterMode = ETextureFilterMode::Linear; + setting.wrapMode = ETextureWrapMode::Clamp; + setting.format = ETextureFormat::R8; + setting.type = ETextureType::TEX_2D; + setting.keepImageData = false; + Texture* tex = new Texture(setting, m_AtlasSize.x, m_AtlasSize.y); + return tex; +} + +bool TextGenerator::HasEnoughSpace(GlyphAtals* atlas, Internal::Vector2 preferSize) +{ + if (atlas == NULL) + return false; + Internal::Vector2 space; + space.x = atlas->width - m_AtlasMargin * 2 - m_GlyphPadding - atlas->cursor.x; + space.y = atlas->height - m_AtlasMargin * 2 - m_GlyphPadding - atlas->cursor.y; + if (space.x > preferSize.x && space.y > preferSize.y) + return true; + if (space.y - atlas->rowHeight > preferSize.y) + return true; + return false; +} + +Character GetCharacter(character::Codepoint Codepoint, int pixelSize) +{ + return Character(); +} -hash TextGenerator::GetCharacterHash(unicode unicode, int pixelSize) +void TextHelper::print_glyph(unsigned char* glyph, int width, int height) { - -} \ No newline at end of file + for (int r = 0; r < height; ++r) + { + for (int c = 0; c < width; ++c) + { + unsigned char ch = glyph[c + r * width]; + printf("%c", ch == 0 ? ' ' : '+'); + } + printf("\n"); + } +} -- cgit v1.1-26-g67d0