diff options
Diffstat (limited to 'Client/Source/GUI')
-rw-r--r-- | Client/Source/GUI/Font.cpp | 302 | ||||
-rw-r--r-- | Client/Source/GUI/Font.h | 149 | ||||
-rw-r--r-- | Client/Source/GUI/TextMeshGenerator.cpp | 89 | ||||
-rw-r--r-- | Client/Source/GUI/TextMeshGenerator.h | 31 | ||||
-rw-r--r-- | Client/Source/GUI/UI9Slicing.cpp | 112 | ||||
-rw-r--r-- | Client/Source/GUI/UI9Slicing.h | 25 | ||||
-rw-r--r-- | Client/Source/GUI/UILine.cpp | 48 | ||||
-rw-r--r-- | Client/Source/GUI/UILine.h | 21 | ||||
-rw-r--r-- | Client/Source/GUI/UIMesh.cpp | 15 | ||||
-rw-r--r-- | Client/Source/GUI/UIMesh.h | 42 | ||||
-rw-r--r-- | Client/Source/GUI/UIQuad.cpp | 64 | ||||
-rw-r--r-- | Client/Source/GUI/UIQuad.h | 21 | ||||
-rw-r--r-- | Client/Source/GUI/UISquare.cpp | 53 | ||||
-rw-r--r-- | Client/Source/GUI/UISquare.h | 22 | ||||
-rw-r--r-- | Client/Source/GUI/UITextMesh.cpp | 278 | ||||
-rw-r--r-- | Client/Source/GUI/UITextMesh.h | 69 | ||||
-rw-r--r-- | Client/Source/GUI/freetype.h | 4 | ||||
-rw-r--r-- | Client/Source/GUI/utf8.cpp | 415 | ||||
-rw-r--r-- | Client/Source/GUI/utf8.h | 113 |
19 files changed, 1873 insertions, 0 deletions
diff --git a/Client/Source/GUI/Font.cpp b/Client/Source/GUI/Font.cpp new file mode 100644 index 0000000..1efe551 --- /dev/null +++ b/Client/Source/GUI/Font.cpp @@ -0,0 +1,302 @@ +#include "freetype.h" +#include "Font.h" + +#include "../Math/Functions.h" +#include "../Math/Math.h" +#include "../Debug/Log.h" +#include "../Utilities/Assert.h" +#include "../Graphics/ImageData.h" + +using namespace character; + +/* +* http://madig.github.io/freetype-web/documentation/tutorial/ +* 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 +*/ + +static std::string s_FontError; + +static std::vector<unsigned char> s_PixelBuffer; +static const int s_SizePerPixel = sizeof(unsigned char); + +Font::Font(std::string path, TextGeneratingSettings settings) + : m_Characters() +{ + m_AtlasMargin = settings.margin; + m_GlyphPadding = settings.padding; + m_AtlasSize = settings.atlasSize; + + if (FT_Init_FreeType(&m_FTLibrary)) + { + s_FontError = "Init freetype error. Font path " + path; + throw FontException(s_FontError.c_str()); + } + + if (FT_New_Face(m_FTLibrary, path.c_str(), 0, &m_FTFace)) + { + s_FontError = "Create freetype face error. Font path " + path; + throw FontException(s_FontError.c_str()); + } +} + +Font::Font(DataBuffer* db, TextGeneratingSettings settings) + : m_Characters() +{ + m_AtlasMargin = settings.margin; + m_GlyphPadding = settings.padding; + m_AtlasSize = settings.atlasSize; + + if (FT_Init_FreeType(&m_FTLibrary)) + { + s_FontError = "Init freetype error."; + throw FontException(s_FontError.c_str()); + } + + if (FT_New_Memory_Face(m_FTLibrary, db->udata, db->length, 0, &m_FTFace)) + { + s_FontError = "Create freetype face error."; + throw FontException(s_FontError.c_str()); + } +} + +character::Hash Font::GetHash(Unicode codepoint, int pixelSize) +{ + character::Hash hash; + hash.codepoint = codepoint; + hash.size = pixelSize; + return hash; +} + +const Character* Font::GetCharacter(character::Unicode codepoint, int pixelSize) +{ + character::Hash hash = GetHash(codepoint, pixelSize); + auto iter = m_Characters.find(hash.hashCode); + if (iter == m_Characters.end()) + { + if (RenderCharacter(codepoint, pixelSize)) + { + iter = m_Characters.find(hash.hashCode); + } + else + { + return NULL; + } + } + Assert(iter != m_Characters.end()); + return &iter->second; +} + +void Font::RenderCharacters(character::Unicode* codepoint, int n, int pixelSize) +{ + for (int i = 0; i < n; ++i) + { + RenderCharacter(codepoint[i], pixelSize); + } +} + +void Font::RenderCharacters(std::vector<character::Unicode>& codepoint, int pixelSize) +{ + int n = codepoint.size(); + for (int i = 0; i < n; ++i) + { + RenderCharacter(codepoint[i], pixelSize); + } +} + +bool Font::RenderCharacter(character::Unicode codepoint, int pixelSize) +{ + character::Hash hash = GetHash(codepoint, pixelSize); + if (m_Characters.size() > 0 && m_Characters.count(hash.hashCode) != 0) + return true; + FT_Set_Pixel_Sizes(m_FTFace, 0, pixelSize); + + // bug: 发现有时候渲染的结果是FT_PIXEL_MODE_MONO(1-bits),试过不同的flags组合还是不对,最后手动转换为8-bits + + FT_Int32 flags = FT_LOAD_RENDER; + if (FT_Load_Char(m_FTFace, codepoint, flags)) + return false; + + Character character; + + int w = m_FTFace->glyph->bitmap.width; + int h = m_FTFace->glyph->bitmap.rows; + + const unsigned char* pixels = m_FTFace->glyph->bitmap.buffer; + if (pixels != NULL) // 有些字体中space、tab没有字形的 + { + s_PixelBuffer.resize(w * h); + if (m_FTFace->glyph->bitmap.pixel_mode == FT_PIXEL_MODE_MONO) + { + // 1-bit monochrome + int pitch = m_FTFace->glyph->bitmap.pitch; + for (int y = 0; y < h; ++y) + { + for (int x = 0; x < w; ++x) + { + int index = x + y * w; + s_PixelBuffer[index] = ((pixels[pitch * y + x / 8]) & (1 << (7 - x % 8))) ? 255 : 0; + } + } + } + else if (m_FTFace->glyph->bitmap.pixel_mode) + { + // 8-bit grayscale + memcpy(&s_PixelBuffer[0], pixels, s_SizePerPixel * w * h); + } + + //TextHelper::print_glyph(&s_PixelBuffer[0], w, h); + + GlyphAtals* atlas = RequestAtlas(pixelSize, Vector2f(w, h)); + Assert(atlas); + + Rect rect = GetRenderChartAndMove(atlas, Vector2f(w, h)); + + try + { + atlas->altas->UpdateSubImage(rect, EPixelFormat::PixelFormat_R, EPixelElementType::PixelType_UNSIGNED_BYTE, &s_PixelBuffer[0]); + } + catch (TextureException& e) + { + s_PixelBuffer.clear(); + std::string error = e.what(); + error = "Render Character Error: " + error; + log_error(e.what()); + return false; + } + s_PixelBuffer.clear(); + + character.atlas = atlas->index; + character.position = rect; + character.bearing = Vector2f(m_FTFace->glyph->bitmap_left, m_FTFace->glyph->bitmap_top); + } + else // space、tab + { + character.atlas = FONT_NOT_IN_ATLAS_PLACEHOLDER; + character.position = Rect(0,0,0,0); + character.bearing = Vector2f(0, 0); + } + + character.advance = m_FTFace->glyph->advance.x * 1/64.f; + + m_Characters.insert(std::pair<unsigned int, Character>(hash.hashCode, character)); + + return true; +} + +const GlyphAtals* Font::GetGlyphAtlas(int index) +{ + if (index >= m_Atlases.size()) + return NULL; + return &m_Atlases[index]; +} + +Rect Font::GetRenderChartAndMove(GlyphAtals* atlas, Vector2f preferSize) +{ + Rect rect; + Vector2f space; + space.x = atlas->width - atlas->cursor.x - m_AtlasMargin; + space.y = atlas->height - atlas->cursor.y - m_AtlasMargin; + 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 + m_GlyphPadding; + atlas->rowHeight = MathUtils::Max((float)atlas->rowHeight, preferSize.y); + } + else if (space.y - atlas->rowHeight - m_GlyphPadding - m_AtlasMargin > 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 + m_GlyphPadding; + atlas->rowHeight = rect.height; + } + else + { + Assert(false); + } + return rect; +} + +GlyphAtals* Font::RequestAtlas(int pixelSize, Vector2f preferSize) +{ + GlyphAtals* atlas = NULL; + auto iter = m_AtlasCache.find(pixelSize); + if (iter == m_AtlasCache.end() || !HasEnoughSpace(iter->second, preferSize)) + { + Assert(m_Atlases.size() < FONT_NOT_IN_ATLAS_PLACEHOLDER); + + 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 = Vector2f(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<int, GlyphAtals*>(pixelSize, atlas)); + } + else + { + atlas = iter->second; + } + Assert(atlas); + return atlas; +} + +Texture* Font::CreateAtlas() +{ + TextureSetting setting; + setting.filterMode = ETextureFilterMode::Nearest; + 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 Font::HasEnoughSpace(GlyphAtals* atlas, Vector2f preferSize) +{ + if (atlas == NULL) + return false; + Vector2f space; + space.x = atlas->width - atlas->cursor.x - m_AtlasMargin; + space.y = atlas->height - atlas->cursor.y - m_AtlasMargin; + if (space.x > preferSize.x && space.y > preferSize.y) + return true; + if (space.y - atlas->rowHeight - m_GlyphPadding - m_AtlasMargin > preferSize.y) + return true; + return false; +} + +Character GetCharacter(character::Unicode Unicode, int pixelSize) +{ + return Character(); +} + +void TextHelper::print_glyph(unsigned char* glyph, int width, int height) +{ + 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"); + } +} diff --git a/Client/Source/GUI/Font.h b/Client/Source/GUI/Font.h new file mode 100644 index 0000000..6fd3c19 --- /dev/null +++ b/Client/Source/GUI/Font.h @@ -0,0 +1,149 @@ +#pragma once + +#include "../Utilities/Singleton.h" +#include "../Graphics/Texture.h" +#include "../Common/DataBuffer.h" +#include "../Math/Math.h" + +#include "freetype.h" + +#include <string> +#include <unordered_map> +#include <exception> +#include <vector> + +//https://github.com/kaienfr/Font + +struct Character { + unsigned int atlas; // atlas索引 + Rect position; // 在altas里的位置,以左上角为原点的,和GL的Y镜像 + Vector2f bearing; // 左上角相对于原点的偏移 + unsigned int advance; // 总宽,算上了间隔 +}; + +namespace character +{ +#if GAMELAB_WIN + typedef unsigned short Unicode; // unicode codepoint(BMP,U+0000至U+FFFF) +#else + typedef unsigned int Unicode; // unicode codepoint(BMP,U+0000至U+FFFF) +#endif + +#pragma pack(1) + union Hash { + unsigned int hashCode; + struct { + Unicode codepoint; + unsigned short size;//字体大小 + }; +#pragma pack(pop) + + bool operator==(const Hash &other) const + { + return codepoint == other.codepoint && size == other.size; + } + }; +} + +struct UnicodeString +{ + character::Unicode* str; + int length; +}; + +namespace std +{ + template <> + struct hash<character::Hash> + { + std::size_t operator()(const character::Hash& k) const + { + return k.hashCode; + } + }; +} + +struct GlyphAtals +{ + int index; + Texture* altas; // 贴图 + int width, height; // 尺寸 + + Vector2f cursor; // 游标,从左上角(0,0)开始 + int rowHeight; // 当前行的高度 +}; + +struct TextGeneratingSettings +{ + Vector2f atlasSize; // atlas的尺寸 + int margin; // atlas的边界 + int padding; // glyph相互之间的间距,防止采样的时候越界 +}; + +class FontException : public std::exception +{ +public: + FontException(const char* what) + : std::exception(what) + { + } +}; + +enum EEncoding +{ + Encoding_ASCII, + Encoding_UTF8, + Encoding_UTF16, +}; + +// 没有字形,但是有advance的文字,比如space和tab +#define FONT_NOT_IN_ATLAS_PLACEHOLDER (INT_MAX) + +// 单个字体 +class Font +{ +public: + Font(std::string path, TextGeneratingSettings setting)/*throw FontException*/; + Font(DataBuffer* db, TextGeneratingSettings setting)/*throw FontException*/; + + const Character* GetCharacter(character::Unicode codepoint, int pixelSize); + const GlyphAtals* GetGlyphAtlas(int index); + + // pre-bake + bool RenderCharacter(character::Unicode codepoint, int pixelSize); + void RenderCharacters(character::Unicode* codepoint, int n, int pixelSize); + void RenderCharacters(std::vector<character::Unicode>& codepoint, int pixelSize); + + GET(Vector2f, AtlasSize, m_AtlasSize); + +private: + Texture* CreateAtlas(); + GlyphAtals* RequestAtlas(int pixelSize, Vector2f preferSize); + Rect GetRenderChartAndMove(GlyphAtals* atlas, Vector2f preferSize); + bool HasEnoughSpace(GlyphAtals* atlas, Vector2f preferSize); + character::Hash GetHash(character::Unicode Unicode, int pixelSize); + + //------------------------------------------------------------------------- + + std::unordered_map<unsigned int , Character> m_Characters; // 渲染完的文字 + + std::vector<GlyphAtals> m_Atlases; // 当前所有的atlas,由font完全拥有所有权,所以是lightuserdata + std::unordered_map<int/*pixelSize*/, GlyphAtals*> m_AtlasCache; // 快速找到可用的atlas + + bool m_IsEnabled; + + Vector2f m_AtlasSize; + int m_AtlasMargin; + int m_GlyphPadding; + + FT_Library m_FTLibrary; + FT_Face m_FTFace; + +}; + +namespace TextHelper +{ + + void print_glyph(unsigned char* glyph, int width, int height); + +} diff --git a/Client/Source/GUI/TextMeshGenerator.cpp b/Client/Source/GUI/TextMeshGenerator.cpp new file mode 100644 index 0000000..766a9b0 --- /dev/null +++ b/Client/Source/GUI/TextMeshGenerator.cpp @@ -0,0 +1,89 @@ +#include "TextMeshGenerator.h" + +// 计算一个哈希值 +static uint32_t GetStrHash(const UnicodeString& str) +{ + if (str.length == 0) + return 0; + + int len = str.length; + uint32_t hash = (uint32_t)len; // seed + size_t step = (len >> 5) + 1; + for (int l = len; l >= step; l -= step) + { + unsigned short unicode = str.str[l - 1]; + unsigned char hicode = *((unsigned char*)&unicode); + unsigned char locode = *(((unsigned char*)&unicode) + 1); + hash = hash ^ ((hash << 5) + (hash >> 2) + hicode + locode); + } + return hash; +} + +const UITextMesh* TextMeshGenerator::GetTextMesh( + const UnicodeString& str + , Font* font + , int pixelSize + , int lineHeight + , Color32 col32 + , ETextAnchor anchor + , ETextAlignment alignment + , bool wordwrap + , float preferred +){ + uint32_t hash = GetStrHash(str); + UITextMeshList* tm = NULL; + bool hasHash = m_TextMeshes.count(hash); + if (hasHash) + { + tm = m_TextMeshes[hash]; + while (tm) + { + UITextMesh* mesh = tm->mesh; + if (mesh->GetFont() != font + || mesh->GetPixelSize() != pixelSize + || mesh->GetLineHeight() != lineHeight + || mesh->GetAnchor() != anchor + || mesh->GetAlignment() != alignment + || mesh->GetWordwrap() != wordwrap + || mesh->GetPreferred() != preferred + || mesh->GetColor() != col32 + ){ + tm = tm->next; + continue; + } + const UnicodeString& content = mesh->GetContent(); + if (content.length != str.length) + { + tm = tm->next; + continue; + } + if (memcmp(str.str, content.str, sizeof(character::Unicode) * str.length) == 0) + { + return tm->mesh; + } + tm = tm->next; + } + } + tm = new UITextMeshList(); + try { + tm->mesh = new UITextMesh(str, font, pixelSize, lineHeight, col32, anchor, alignment, wordwrap, preferred); + log_info("Text", "New UITextMesh"); + } + catch(TextMeshException& e) + { + delete tm; + throw; + } + if (hasHash) + { + UITextMeshList* list = m_TextMeshes[hash]; + tm->next = list; + m_TextMeshes[hash] = tm; + } + else + { + tm->next = NULL; + m_TextMeshes.insert(std::pair<uint32_t, UITextMeshList*>(hash, tm)); + } + return tm->mesh; +} diff --git a/Client/Source/GUI/TextMeshGenerator.h b/Client/Source/GUI/TextMeshGenerator.h new file mode 100644 index 0000000..acc0a0a --- /dev/null +++ b/Client/Source/GUI/TextMeshGenerator.h @@ -0,0 +1,31 @@ +#pragma once + +#include <vector> +#include <unordered_map> + +#include "../Debug/Log.h" +#include "../Graphics/Color.h" + +#include "UITextMesh.h" +#include "Font.h" + +// 逐步回收长期没用到的textmesh +class GraduallyReleaseTextMesh +{ +}; + +struct UITextMeshList { + UITextMesh* mesh; + UITextMeshList* next; +}; + +class TextMeshGenerator : public Singleton<TextMeshGenerator> +{ +public: + const UITextMesh* GetTextMesh(const UnicodeString& str, Font* font, int pixelSize, int lineHeight, Color32 col32, ETextAnchor anchor, ETextAlignment alignment, bool wordwrap, float preferred); + +private: + std::unordered_map<uint32_t, UITextMeshList*> m_TextMeshes; +}; + +#define g_TextMeshGenerator (*TextMeshGenerator::Instance())
\ No newline at end of file diff --git a/Client/Source/GUI/UI9Slicing.cpp b/Client/Source/GUI/UI9Slicing.cpp new file mode 100644 index 0000000..c0d8f60 --- /dev/null +++ b/Client/Source/GUI/UI9Slicing.cpp @@ -0,0 +1,112 @@ +#include "../Math/Math.h" + +#include "UI9Slicing.h" + +#include <cstring> +#include <vector> + +using namespace std; + +static vector<UIVertexLayout> s_Vertices; +static vector<UIIndex> s_Indices; + +UI9Slicing::UI9Slicing(int mode, Vector2f horizontal, Vector2f vertical, Vector2f texPixelSize, Vector2f size) +{ + m_Slicing = mode; + m_Horizontal = horizontal.Clamp(0, texPixelSize.x, 0, texPixelSize.x); + m_Vertical = vertical.Clamp(0, texPixelSize.y, 0, texPixelSize.y); + + if (m_Horizontal[0] + m_Horizontal[1] > texPixelSize.x || m_Vertical[0] + m_Vertical[1] > texPixelSize.y) + { + throw UIMeshException("UI9Slicing wrong parameter."); + } + m_TexSize = texPixelSize; + m_Size = size; +} + +void UI9Slicing::Draw() +{ + s_Indices.clear(); + s_Vertices.clear(); + + Vector2f tileSize = Vector2f(m_TexSize.x - m_Horizontal[0] - m_Horizontal[1], m_TexSize.y - m_Vertical[0] - m_Vertical[1]); + Vector2f fillSize = Vector2f(m_Size.x - m_Horizontal[0] - m_Horizontal[1], m_Size.y - m_Vertical[0] - m_Vertical[1]); + // horizonal和vertical对应的uv,注意uv在左下角,mesh在左上角 + Vector2f tileUVx = Vector2f(m_Horizontal[0] / m_TexSize.x, (m_TexSize.x - m_Horizontal[1]) / m_TexSize.x); + Vector2f tileUVy = Vector2f((m_TexSize.y - m_Vertical[0]) / m_TexSize.y, m_Vertical[1] / m_TexSize.y); + + // fill vertices + int row = 2 + ((fillSize.y <= 0) ? 0 : (m_Slicing == Slicing_Simple ? 2 : ceilf((float)fillSize.y / tileSize.y) + 1)); + int colum = 2 + ((fillSize.x <= 0) ? 0 : (m_Slicing == Slicing_Simple ? 2 : ceilf((float)fillSize.x / tileSize.x) + 1)); + + for (int r = 0; r < row; ++r) + { + UIVertexLayout vert; + vert.color = Color32::white; + if (r == 0) + { + vert.position.y = 0; + vert.uv.y = 1; + } + else if (r == row - 1) + { + vert.position.y = m_Size.y; + vert.uv.y = 0; + } + else { + if (m_Slicing == Slicing_Tiled) { + vert.position.y = clamp(m_Vertical[0] + (r - 1) * tileSize.y, m_Vertical[0], m_Size.y - m_Vertical[1]); + vert.uv.y = odd(r - 1) ? tileUVy[1] : tileUVy[0]; + } else { + vert.position.y = odd(r - 1) ? m_Size.y - m_Vertical[1] : m_Vertical[0]; + vert.uv.y = odd(r - 1) ? tileUVy[1] : tileUVy[0]; + } + } + for (int c = 0; c < colum; ++c) + { + if (c == 0) + { + vert.position.x = 0; + vert.uv.x = 0; + } + else if (c == colum - 1) + { + vert.position.x = m_Size.x; + vert.uv.x = 1; + } + else { + if (m_Slicing == Slicing_Tiled) { + vert.position.x = clamp(m_Horizontal[0] + (c - 1) * tileSize.x, m_Horizontal[0], m_Size.x - m_Horizontal[0]); + vert.uv.x = odd(c - 1) ? tileUVx[1] : tileUVx[0]; + } else { + vert.position.x = odd(c - 1) ? (m_Size.x - m_Horizontal[1]) : m_Horizontal[0]; + vert.uv.x = odd(c - 1) ? tileUVx[1] : tileUVx[0]; + } + } + s_Vertices.push_back(vert); + + if (c < colum - 1 && r < row - 1) { + int index = c + r * colum; + s_Indices.push_back(index); s_Indices.push_back(index + colum); s_Indices.push_back(index + colum + 1); + s_Indices.push_back(index + colum + 1); s_Indices.push_back(index + 1); s_Indices.push_back(index); + } + } + } + + void* vb; + void* ib; + + int vertCount = s_Vertices.size(); + int indicesCount = s_Indices.size(); + + g_SharedVBO.GetChunk(sizeof(UIVertexLayout), sizeof(UIIndex), vertCount, indicesCount, Primitive_Triangle, &vb, &ib); + + memcpy(vb, &s_Vertices[0], vertCount * sizeof(UIVertexLayout)); + memcpy(ib, &s_Indices[0], indicesCount* sizeof(UIIndex)); + + s_Indices.resize(0); + s_Vertices.resize(0); + + g_SharedVBO.ReleaseChunk(vertCount, indicesCount); + g_SharedVBO.DrawChunk(s_UIVertexLayout); +}
\ No newline at end of file diff --git a/Client/Source/GUI/UI9Slicing.h b/Client/Source/GUI/UI9Slicing.h new file mode 100644 index 0000000..6a37447 --- /dev/null +++ b/Client/Source/GUI/UI9Slicing.h @@ -0,0 +1,25 @@ +#pragma once + +#include "UIMesh.h" + +enum ESlicing +{ + Slicing_Simple, + Slicing_Tiled, +}; + +// 九宫格 +class UI9Slicing : public UIMesh +{ +public: + UI9Slicing(int mode, Vector2f horizontal, Vector2f vertical, Vector2f texPixelSize, Vector2f size)/*throw UIMeshException*/; + + void Draw() override; + +private: + int m_Slicing; + Vector2f m_Horizontal; // 左右两条切割线到左边和右边的距离 + Vector2f m_Vertical; // 上下两条切割线到上下的距离 + Vector2f m_TexSize; + Vector2f m_Size; +}; diff --git a/Client/Source/GUI/UILine.cpp b/Client/Source/GUI/UILine.cpp new file mode 100644 index 0000000..8aa8c1d --- /dev/null +++ b/Client/Source/GUI/UILine.cpp @@ -0,0 +1,48 @@ +#include "UILine.h" + +struct UILineLayout +{ + Vector2f position; +}; + +static CustomVertexLayout layout; + +InitializeStaticVariables([]() { + VertexAttributeDescriptor POSITION = VertexAttributeDescriptor(0, 2, VertexAttrFormat_Float, sizeof(UILineLayout)); + + layout.attributes.push_back(POSITION); +}); + +void UILine::Draw() +{ + const int nVerts = 2; + const int nIndices = 2; + + float pos[] = { + m_From.x, m_From.y, + m_To.x, m_To.y, + }; + + uint16 indices[] = { + 0, 1 + }; + + + uint8* vb; + uint16* ib; + + g_SharedVBO.GetChunk(sizeof(UILineLayout), sizeof(uint16), nVerts, nIndices, Primitive_Line, (void**)&vb, (void**)&ib); + + UILineLayout* dst = (UILineLayout*)vb; + + for (int i = 0; i < nVerts; ++i) + { + dst[i].position.Set(pos[2 * i], pos[2 * i + 1]); + } + + for (int i = 0; i < nIndices; ++i) + ib[i] = indices[i]; + + g_SharedVBO.ReleaseChunk(nVerts, nIndices); + g_SharedVBO.DrawChunk(layout); +}
\ No newline at end of file diff --git a/Client/Source/GUI/UILine.h b/Client/Source/GUI/UILine.h new file mode 100644 index 0000000..a47b1f1 --- /dev/null +++ b/Client/Source/GUI/UILine.h @@ -0,0 +1,21 @@ +#pragma once + +#include "../Utilities/StaticInitiator.h" +#include "UIMesh.h" + +class UILine : public UIMesh +{ +public: + UILine(Vector2f from, Vector2f to) + { + m_From = from; + m_To = to; + } + + void Draw() override; + +private: + Vector2f m_From; + Vector2f m_To; + +}; diff --git a/Client/Source/GUI/UIMesh.cpp b/Client/Source/GUI/UIMesh.cpp new file mode 100644 index 0000000..9616c46 --- /dev/null +++ b/Client/Source/GUI/UIMesh.cpp @@ -0,0 +1,15 @@ +#include "UIMesh.h" + +CustomVertexLayout UIMesh::s_UIVertexLayout; +unsigned int UIMesh::s_SizePerVertex; + +InitializeStaticVariables([]() { + VertexAttributeDescriptor POSITION = VertexAttributeDescriptor(0, 2, VertexAttrFormat_Float, sizeof(UIVertexLayout)); + VertexAttributeDescriptor UV = VertexAttributeDescriptor(sizeof(Vector2f), 2, VertexAttrFormat_Float, sizeof(UIVertexLayout)); + VertexAttributeDescriptor COLOR = VertexAttributeDescriptor(sizeof(Vector2f) * 2, 4, VertexAttrFormat_Unsigned_Byte, sizeof(UIVertexLayout), true); + UIMesh::s_UIVertexLayout.attributes.push_back(POSITION); + UIMesh::s_UIVertexLayout.attributes.push_back(UV); + UIMesh::s_UIVertexLayout.attributes.push_back(COLOR); + + UIMesh::s_SizePerVertex = sizeof(UIVertexLayout); +}); diff --git a/Client/Source/GUI/UIMesh.h b/Client/Source/GUI/UIMesh.h new file mode 100644 index 0000000..11f157a --- /dev/null +++ b/Client/Source/GUI/UIMesh.h @@ -0,0 +1,42 @@ +#pragma once + +#include "../Utilities/Exception.h" +#include "../Graphics/DynamicMesh.h" +#include "../Math/Math.h" +#include "../Utilities/StaticInitiator.h" +#include "../Graphics/CustomVertexLayout.h" +#include "../Graphics/DefaultVertexLayout.h" +#include "../Graphics/Color.h" +#include "../Graphics/GfxDevice.h" + +// UI默认的顶点布局 +struct UIVertexLayout +{ + UIVertexLayout(Vector2f pos = Vector2f::zero, Vector2f texCoord = Vector2f::zero, Color32 col = Color32()) + { + position = pos; + uv = texCoord; + color = col; + } + + Vector2f position; + Vector2f uv; + Color32 color; +}; + +typedef uint16 UIIndex; + +CustomException(UIMeshException); + +// 所有的UIMesh都是左上角为原点 +class UIMesh : public DynamicMesh +{ +public: + UIMesh() : DynamicMesh() {} + virtual ~UIMesh() {} + + virtual void Draw() = 0; + + static CustomVertexLayout s_UIVertexLayout; + static unsigned int s_SizePerVertex; +}; diff --git a/Client/Source/GUI/UIQuad.cpp b/Client/Source/GUI/UIQuad.cpp new file mode 100644 index 0000000..d3c7825 --- /dev/null +++ b/Client/Source/GUI/UIQuad.cpp @@ -0,0 +1,64 @@ +#include "../Math/Math.h" +#include "../Graphics/GfxDevice.h" + +#include "UIQuad.h" + +struct UIQuadLayout +{ + Vector2f position; + Vector2f uv; +}; + +static CustomVertexLayout layout; + +InitializeStaticVariables([]() { + VertexAttributeDescriptor POSITION = VertexAttributeDescriptor(0, 2, VertexAttrFormat_Float, sizeof(UIQuadLayout)); + VertexAttributeDescriptor UV = VertexAttributeDescriptor(sizeof(Vector2f), 2, VertexAttrFormat_Float, sizeof(UIQuadLayout)); + + layout.attributes.push_back(POSITION); + layout.attributes.push_back(UV); +}); + +void UIQuad::Draw() +{ + const int nVerts = 4; + const int nIndices = 6; + + float pos[] = { + m_Left, m_Bottom, // left-bottom + m_Right, m_Bottom, // right-bottom + m_Right, m_Top, // right-top + m_Left, m_Top, // top-left + }; + + float uv[] = { + 0, 0, + 1, 0, + 1, 1, + 0, 1, + }; + + uint16 indices[] = { + 0, 1, 3, // right-top + 1, 2, 3, // left-bottom + }; + + uint8* vb; + uint16* ib; + + g_SharedVBO.GetChunk(sizeof(UIQuadLayout), sizeof(uint16), nVerts, nIndices, Primitive_Triangle, (void**)&vb, (void**)&ib); + + UIQuadLayout* dst = (UIQuadLayout*)vb; + + for (int i = 0; i < nVerts; ++i) + { + dst[i].position.Set(pos[2 * i], pos[2 * i + 1]); + dst[i].uv.Set(uv[2 * i], uv[2 * i + 1]); + } + + for (int i = 0; i < nIndices; ++i) + ib[i] = indices[i]; + + g_SharedVBO.ReleaseChunk(nVerts, nIndices); + g_SharedVBO.DrawChunk(layout); +}
\ No newline at end of file diff --git a/Client/Source/GUI/UIQuad.h b/Client/Source/GUI/UIQuad.h new file mode 100644 index 0000000..278848a --- /dev/null +++ b/Client/Source/GUI/UIQuad.h @@ -0,0 +1,21 @@ +#pragma once +#include "../Utilities/StaticInitiator.h" +#include "UIMesh.h" + +class UIQuad : public UIMesh +{ +public : + UIQuad(float l, float r, float t, float b) + { + m_Left = l; + m_Right = r; + m_Top = t; + m_Bottom = b; + } + + void Draw() override; + +private: + float m_Left, m_Right, m_Top, m_Bottom; + +}; diff --git a/Client/Source/GUI/UISquare.cpp b/Client/Source/GUI/UISquare.cpp new file mode 100644 index 0000000..f5c3702 --- /dev/null +++ b/Client/Source/GUI/UISquare.cpp @@ -0,0 +1,53 @@ +#include "../Math/Math.h" +#include "../Graphics/GfxDevice.h" + +#include "UISquare.h" + +struct UISquareLayout +{ + Vector2f position; +}; + +static CustomVertexLayout layout; + +InitializeStaticVariables([]() { + VertexAttributeDescriptor POSITION = VertexAttributeDescriptor(0, 2, VertexAttrFormat_Float, sizeof(UISquareLayout)); + + layout.attributes.push_back(POSITION); +}); + +void UISquare::Draw() +{ + const int nVerts = 4; + const int nIndices = 6; + + float pos[] = { + m_Left, m_Bottom, // left-bottom + m_Right, m_Bottom, // right-bottom + m_Right, m_Top, // right-top + m_Left, m_Top, // top-left + }; + + int indices[] = { + 0, 1, 3, // right-top + 1, 2, 3, // left-bottom + }; + + uint8* vb; + uint16* ib; + + g_SharedVBO.GetChunk(sizeof(UISquareLayout), sizeof(uint16), 4, 6, Primitive_Triangle, (void**)&vb, (void**)&ib); + + UISquareLayout* dst = (UISquareLayout*)vb; + + for (int i = 0; i < nVerts; ++i) + { + dst[i].position.Set(pos[2 * i], pos[2 * i + 1]); + } + + for (int i = 0; i < nIndices; ++i) + ib[i] = indices[i]; + + g_SharedVBO.ReleaseChunk(4, 6); + g_SharedVBO.DrawChunk(layout); +} diff --git a/Client/Source/GUI/UISquare.h b/Client/Source/GUI/UISquare.h new file mode 100644 index 0000000..8de07f0 --- /dev/null +++ b/Client/Source/GUI/UISquare.h @@ -0,0 +1,22 @@ +#pragma once +#include "../Utilities/StaticInitiator.h" +#include "UIMesh.h" + +// 纯色方形 +class UISquare : public UIMesh +{ +public: + UISquare(float l, float r, float t, float b) + { + m_Left = l; + m_Right = r; + m_Top = t; + m_Bottom = b; + } + + void Draw() override; + +private: + float m_Left, m_Right, m_Top, m_Bottom; + +}; diff --git a/Client/Source/GUI/UITextMesh.cpp b/Client/Source/GUI/UITextMesh.cpp new file mode 100644 index 0000000..13bd8dc --- /dev/null +++ b/Client/Source/GUI/UITextMesh.cpp @@ -0,0 +1,278 @@ +#include "../Graphics/CustomVertexLayout.h" +#include "../Utilities/StaticInitiator.h" +#include "../Math/Math.h" +#include "../Graphics/Color.h" +#include "../Graphics/GfxDevice.h" +#include "../Utilities/AutoInvoke.h" +#include "../Graphics/DefaultVertexLayout.h" +#include "../Debug/Log.h" + +#include "UITextMesh.h" + +#include <vector> +#include <unordered_map> + +using namespace std; + +struct TextMeshVBOLayout +{ + Vector2f position; + Vector2f uv; + Color32 color; +}; + +static CustomVertexLayout s_TextMeshVBOLayout; + +static unsigned int s_VertexPerText; +static unsigned int s_SizePerVertex; +static unsigned int s_SizePerText; +static unsigned int s_SizePerIndex; +static unsigned int s_IndicesPerText; + +struct TextInfo { + const Character* ch; + float offset; + int line; // 从0开始 +}; + +InitializeStaticVariables([]() { + VertexAttributeDescriptor POSITION = VertexAttributeDescriptor(0, 2, VertexAttrFormat_Float, sizeof(TextMeshVBOLayout)); + VertexAttributeDescriptor UV = VertexAttributeDescriptor(sizeof(Vector2f), 2, VertexAttrFormat_Float, sizeof(TextMeshVBOLayout)); + VertexAttributeDescriptor COLOR = VertexAttributeDescriptor(sizeof(Vector2f)*2, 4, VertexAttrFormat_Unsigned_Byte, sizeof(TextMeshVBOLayout), true); + s_TextMeshVBOLayout.attributes.push_back(POSITION); + s_TextMeshVBOLayout.attributes.push_back(UV); + s_TextMeshVBOLayout.attributes.push_back(COLOR); + + s_VertexPerText = 4; + s_SizePerVertex = sizeof(TextMeshVBOLayout); + + s_IndicesPerText = 6; + s_SizePerIndex = VertexLayout::GetDefaultIndexSize(); + + s_SizePerText = sizeof(TextMeshVBOLayout) * 4; +}); + +UITextMesh::UITextMesh( + const UnicodeString& str // 文本 + , Font* font // 字体 + , int pixelSize // 大小 + , int lineHeight // 行高 + , Color32 color32 // 颜色 + , ETextAnchor anchor // 锚点 + , ETextAlignment alignment // 对齐方式 + , bool wordwrap // 自动换行 + , float preferred // 自动换行区域大小 +){ + m_Font = font; + m_PixelSize = pixelSize; + m_LineHeight = lineHeight; + m_Color = color32; + m_Alignment = alignment; + m_Anchor = anchor; + m_Wordwrap = wordwrap; + m_Preferred = preferred; + m_Content.length = str.length; + m_Content.str = (character::Unicode*)malloc(str.length * sizeof(character::Unicode)); + memcpy(m_Content.str, str.str, str.length * sizeof(character::Unicode)); + + // 记录文本按atlas分类 + static unordered_map<unsigned int, vector<TextInfo>> s_TextInfos; + s_TextInfos.clear(); + + // 记录文本每行的长度 + static unordered_map<int, int> s_LineWidths; + s_LineWidths.clear(); + + // 记录每行的偏移长度 + static unordered_map<int, int> s_LineOffsets; + s_LineOffsets.clear(); + + InvokeWhenLeave([]() { + s_LineWidths.clear(); + s_LineOffsets.clear(); + s_TextInfos.clear(); + }); + + const Vector2f atlasSize = font->GetAtlasSize(); + + //----------------------------------------------------------------- + // 处理了换行和自动换行之后的文本区域大小 + Vector2f textRegion; + // 按照不同的atlas分类到s_TextInfos + float offset = 0; + int line = 0; + for (int i = 0; i < str.length; ++i) + { + character::Unicode c = str.str[i]; + const Character* ch = font->GetCharacter(c, pixelSize); + if (ch == NULL) + continue; + + if (wordwrap) // 自动换行 + { + if (offset + ch->bearing.x + ch->position.width > preferred) + { + + ++line; + offset = 0; + } + } + + unsigned int atlasIndex = ch->atlas; + if (atlasIndex != FONT_NOT_IN_ATLAS_PLACEHOLDER) //非空格 + { +// 换行符Unix'\n', Windows'\r\n', MacOS '\r' +#define CHECK_BREAKS() \ + if (c == '\n' || c == '\r') \ + { \ + ++line; \ + offset = 0; \ + if (c == '\r' && ((i + 1) < str.length && str.str[i + 1] == '\n')) /*skip\n*/ \ + ++i; \ + continue; \ + } + + CHECK_BREAKS(); + + TextInfo info; + info.ch = ch; + info.offset = offset; + info.line = line; + + auto list = s_TextInfos.find(atlasIndex); + if (list == s_TextInfos.end()) + s_TextInfos.insert(std::pair<unsigned int, vector<TextInfo>>(atlasIndex, vector<TextInfo>())); + + vector<TextInfo>& v = s_TextInfos[atlasIndex]; + v.push_back(info); + } + else + { + // 有些字体换行没有字形,所以也需要在这里处理 + CHECK_BREAKS(); + } + + offset += ch->advance; + + textRegion.x = max(offset, textRegion.x); + + if (s_LineWidths.count(line) == 0) + s_LineWidths.insert(std::pair<int, int>(line, 0)); + s_LineWidths[line] = max(offset, s_LineWidths[line]); + } + + textRegion.y = (line + 1) * lineHeight; + + if (s_TextInfos.size() == 0) + { + return; + } + + Vector2i textOffset; + Vector2f halfRegion = Vector2f(textRegion.x/ 2.f, textRegion.y / 2.f); + switch (anchor) + { + case TextAnchor_UpperLeft: textOffset.Set(0, 0); break; + case TextAnchor_UpperCenter: textOffset.Set(-halfRegion.x, 0); break; + case TextAnchor_UpperRight: textOffset.Set(-textRegion.x, 0); break; + case TextAnchor_MiddleLeft: textOffset.Set(0, -halfRegion.y); break; + case TextAnchor_MiddleCenter: textOffset.Set(-halfRegion.x, -halfRegion.y); break; + case TextAnchor_MiddleRight: textOffset.Set(-textRegion.x, -halfRegion.y); break; + case TextAnchor_LowerLeft: textOffset.Set(0, -textRegion.y); break; + case TextAnchor_LowerCenter: textOffset.Set(-halfRegion.x, -textRegion.y); break; + case TextAnchor_LowerRight: textOffset.Set(-textRegion.x, -textRegion.y); break; + } + + for (int i = 0; i < line; ++i) + { + int lineLen = s_LineWidths.count(i) != 0 ? s_LineWidths[i] : 0; + int lineOffset = 0; + switch (alignment) + { + case TextAlignment_Left: lineOffset = 0; break; + case TextAlignment_Center: lineOffset = (textRegion.x - lineLen)/2.f; break; + case TextAlignment_Right: lineOffset = textRegion.x - lineLen; break; + } + s_LineOffsets.insert(std::pair<int, int>(i, lineOffset)); + } + + //----------------------------------------------------------------- + + // 填充VBO和IBO + for (auto iter : s_TextInfos) { + unsigned int atlasIndex = iter.first; // atlas atlasIndex + vector<TextInfo>& texts = iter.second; + int textCount = texts.size(); + + VertexBuffer* vb = new VertexBuffer(textCount * s_SizePerText, textCount * s_IndicesPerText * s_SizePerIndex, VertexBuffer::VertexBufferType_Static); + void* pVB; + uint16* pIB; + + vb->GetChunk(s_SizePerVertex, s_SizePerIndex, s_VertexPerText * textCount, s_IndicesPerText * textCount, EPrimitive::Primitive_Triangle, &pVB,(void**) &pIB); + + TextMeshVBOLayout* dst = (TextMeshVBOLayout*)pVB; + for (int i = 0; i < textCount; ++i) + { + TextInfo& text = texts[i]; + + int vOff = i * s_VertexPerText; + int lineXOff = s_LineOffsets.count(text.line) ? s_LineOffsets[text.line] : 0; + int lineYOff = text.line * lineHeight; + // 左上角是原点 + float pos[] = { + textOffset.x + lineXOff + text.offset + text.ch->bearing.x, textOffset.y + lineYOff + pixelSize - text.ch->bearing.y + text.ch->position.height, // bottom-left + textOffset.x + lineXOff + text.offset + text.ch->bearing.x + text.ch->position.width, textOffset.y + lineYOff + pixelSize - text.ch->bearing.y + text.ch->position.height, // bottom-right + textOffset.x + lineXOff + text.offset + text.ch->bearing.x + text.ch->position.width, textOffset.y + lineYOff + pixelSize - text.ch->bearing.y, // top-right + textOffset.x + lineXOff + text.offset + text.ch->bearing.x, textOffset.y + lineYOff + pixelSize - text.ch->bearing.y, // top-left + }; + Vector4f uvQuad = Vector4f(text.ch->position.x / atlasSize.x, text.ch->position.y / atlasSize.y, text.ch->position.width / atlasSize.x, text.ch->position.height / atlasSize.y); + float uv[] = { + uvQuad.x, uvQuad.y + uvQuad.w, + uvQuad.x + uvQuad.z, uvQuad.y + uvQuad.w, + uvQuad.x + uvQuad.z, uvQuad.y, + uvQuad.x, uvQuad.y, + }; + for (int j = 0; j < s_VertexPerText; ++j) + { + dst[vOff + j].position.Set(pos[2 * j], pos[2 * j + 1]); + dst[vOff + j].uv.Set(uv[2 * j], uv[2 * j + 1]); + dst[vOff + j].color = color32; + } + + int iOff = i * s_IndicesPerText; + int indices[] = { + 0, 1, 3, // right-top + 1, 2, 3, // left-bottom + }; + for (int j = 0; j < s_IndicesPerText; ++j) + pIB[iOff + j] = vOff + indices[j]; + } + + vb->FlushChunk(s_VertexPerText * textCount, s_IndicesPerText * textCount); + + m_VBOs.insert(std::pair<int, VertexBuffer*>(atlasIndex, vb)); + } + + WipeGLError(); +} + +void UITextMesh::Draw() const +{ + for (auto subText : m_VBOs) + { + int atlasIndex = subText.first; // atlasIndex of atlas + VertexBuffer* vbo = subText.second; + + const GlyphAtals* atlas = m_Font->GetGlyphAtlas(atlasIndex); + if (atlas == NULL) + { + log_error("Render text failed, no glyph atlas."); + continue; + } + + g_GfxDevice.SetUniformTexture("gamelab_main_tex", atlas->altas); + + WipeGLError(); + vbo->Draw(s_TextMeshVBOLayout); + } +} diff --git a/Client/Source/GUI/UITextMesh.h b/Client/Source/GUI/UITextMesh.h new file mode 100644 index 0000000..869db80 --- /dev/null +++ b/Client/Source/GUI/UITextMesh.h @@ -0,0 +1,69 @@ +#pragma once + +#include "../Graphics/VertexBuffer.h" +#include "../Utilities/Exception.h" +#include "../Graphics/Color.h" + +#include "Font.h" + +#include <unordered_map> + +CustomException(TextMeshException); + +enum ETextAnchor +{ + TextAnchor_UpperLeft, + TextAnchor_UpperCenter, + TextAnchor_UpperRight, + TextAnchor_MiddleLeft, + TextAnchor_MiddleCenter, + TextAnchor_MiddleRight, + TextAnchor_LowerLeft, + TextAnchor_LowerCenter, + TextAnchor_LowerRight, +}; + +enum ETextAlignment { + TextAlignment_Left, + TextAlignment_Center, + TextAlignment_Right, +}; + +namespace TextHelper +{ +} + +class UITextMesh +{ +public: + void Draw() const; + +private: + friend class TextMeshGenerator; + + UITextMesh(const UnicodeString& str, Font* font, int pixelSize, int lineHeight, Color32 color32 = Color32::white, ETextAnchor anchor = TextAnchor_UpperLeft, ETextAlignment alignment = TextAlignment_Left, bool wordwrap = false, float preferred = 0)/*throw TextMeshException*/; + ~UITextMesh(); + + GET(const Font*, Font, m_Font); + GET(int, PixelSize, m_PixelSize); + GET(int, LineHeight, m_LineHeight); + GET(Color32, Color, m_Color); + GET(ETextAlignment, Alignment, m_Alignment); + GET(ETextAnchor, Anchor, m_Anchor); + GET(bool, Wordwrap, m_Wordwrap); + GET(float, Preferred, m_Preferred); + GET(const UnicodeString&, Content, m_Content); + + std::unordered_map<int, VertexBuffer*> m_VBOs; + + Font* m_Font; + UnicodeString m_Content; + int m_PixelSize; + int m_LineHeight; + Color32 m_Color; + ETextAlignment m_Alignment; + ETextAnchor m_Anchor; + bool m_Wordwrap; + float m_Preferred; + +};
\ No newline at end of file diff --git a/Client/Source/GUI/freetype.h b/Client/Source/GUI/freetype.h new file mode 100644 index 0000000..4b54e20 --- /dev/null +++ b/Client/Source/GUI/freetype.h @@ -0,0 +1,4 @@ +#pragma once + +#include <ft2build.h> +#include FT_FREETYPE_H diff --git a/Client/Source/GUI/utf8.cpp b/Client/Source/GUI/utf8.cpp new file mode 100644 index 0000000..8a3a086 --- /dev/null +++ b/Client/Source/GUI/utf8.cpp @@ -0,0 +1,415 @@ +/* + * Description: UTF-8 瀛楃涓茬殑瑙g爜鍜岀紪鐮佸嚱鏁 + * unicode 瀛楃澶勭悊鍑芥暟 + * History: yang@haipo.me, 2013/05/29, create + * + * This code is in the public domain. + * You may use this code any way you wish, private, educational, + * or commercial. It's free. + */ + +# include <stdint.h> +# include <stddef.h> + +# include "utf8.h" + +namespace utf8 +{ + + ucs4_t getu8c(char **src, int *illegal) + { + static char umap[256] = { 0 }; + static int umap_init_flag = 0; + + if (umap_init_flag == 0) + { + int i; + + for (i = 0; i < 0x100; ++i) + { + if (i < 0x80) + { + umap[i] = 1; + } + else if (i >= 0xc0 && i < 0xe0) + { + umap[i] = 2; + } + else if (i >= 0xe0 && i < 0xf0) + { + umap[i] = 3; + } + else if (i >= 0xf0 && i < 0xf8) + { + umap[i] = 4; + } + else if (i >= 0xf8 && i < 0xfc) + { + umap[i] = 5; + } + else if (i >= 0xfc && i < 0xfe) + { + umap[i] = 6; + } + else + { + umap[i] = 0; + } + } + + umap_init_flag = 1; + } + + uint8_t *s = (uint8_t *)(*src); + int r_illegal = 0; + + while (umap[*s] == 0) + { + ++s; + ++r_illegal; + } + + uint8_t *t; + int byte_num; + uint32_t uc; + int i; + + repeat_label: + t = s; + byte_num = umap[*s]; + uc = *s++ & (0xff >> byte_num); + + for (i = 1; i < byte_num; ++i) + { + if (umap[*s]) + { + r_illegal += s - t; + goto repeat_label; + } + else + { + uc = (uc << 6) + (*s & 0x3f); + s += 1; + } + } + + *src = (char *)s; + if (illegal) + { + *illegal = r_illegal; + } + + return uc; + } + + size_t u8decode(char const *str, ucs4_t *des, size_t n, int *illegal) + { + if (n == 0) + return 0; + + char *s = (char *)str; + size_t i = 0; + ucs4_t uc = 0; + int r_illegal_all = 0, r_illegal; + + while ((uc = getu8c(&s, &r_illegal))) + { + if (i < (n - 1)) + { + des[i++] = uc; + r_illegal_all += r_illegal; + } + else + { + break; + } + } + + des[i] = 0; + if (illegal) + { + *illegal = r_illegal_all + r_illegal; + } + + return i; + } + +# define IF_CAN_HOLD(left, n) do { \ + size_t m = (size_t)(n); \ + if ((size_t)(left) < (m + 1)) return -2; \ + (left) -= m; \ +} while (0) + + int putu8c(ucs4_t uc, char **des, size_t *left) + { + if (uc < 0) + return -1; + + if (uc < (0x1 << 7)) + { + IF_CAN_HOLD(*left, 1); + + **des = (char)uc; + *des += 1; + **des = 0; + + return 1; + } + + int byte_num; + + if (uc < (0x1 << 11)) + { + byte_num = 2; + } + else if (uc < (0x1 << 16)) + { + byte_num = 3; + } + else if (uc < (0x1 << 21)) + { + byte_num = 4; + } + else if (uc < (0x1 << 26)) + { + byte_num = 5; + } + else + { + byte_num = 6; + } + + IF_CAN_HOLD(*left, byte_num); + + int i; + for (i = byte_num - 1; i > 0; --i) + { + *(uint8_t *)(*des + i) = (uc & 0x3f) | 0x80; + uc >>= 6; + } + + *(uint8_t *)(*des) = uc | (0xff << (8 - byte_num)); + + *des += byte_num; + **des = 0; + + return byte_num; + } + + size_t u8encode(ucs4_t *us, char *des, size_t n, int *illegal) + { + if (n == 0) + return 0; + + char *s = des; + size_t left = n; + size_t len = 0; + int r_illegal = 0; + + *s = 0; + while (*us) + { + int ret = putu8c(*us, &s, &left); + if (ret > 0) + { + len += ret; + } + else if (ret == -1) + { + r_illegal += 1; + } + else + { + break; + } + + ++us; + } + + if (illegal) + { + *illegal = r_illegal; + } + + return len; + } + + /* 鍏ㄨ瀛楃 */ + int isufullwidth(ucs4_t uc) + { + if (uc == 0x3000) + return 1; + + if (uc >= 0xff01 && uc <= 0xff5e) + return 1; + + return 0; + } + + /* 鍏ㄨ瀛楁瘝 */ + int isufullwidthalpha(ucs4_t uc) + { + if (uc >= 0xff21 && uc <= 0xff3a) + return 1; + + if (uc >= 0xff41 && uc <= 0xff5a) + return 2; + + return 0; + } + + /* 鍏ㄨ鏁板瓧 */ + int isufullwidthdigit(ucs4_t uc) + { + if (uc >= 0xff10 && uc <= 0xff19) + return 1; + + return 0; + } + + /* 鍏ㄨ杞崐瑙 */ + ucs4_t ufull2half(ucs4_t uc) + { + if (uc == 0x3000) + return ' '; + + if (uc >= 0xff01 && uc <= 0xff5e) + return uc - 0xfee0; + + return uc; + } + + /* 鍗婅杞叏瑙 */ + ucs4_t uhalf2full(ucs4_t uc) + { + if (uc == ' ') + return 0x3000; + + if (uc >= 0x21 && uc <= 0x7e) + return uc + 0xfee0; + + return uc; + } + + /* 涓棩闊╄秺缁熶竴琛ㄦ剰鏂囧瓧 */ + int isuchiness(ucs4_t uc) + { + /* 鏈鍒濇湡缁熶竴姹夊瓧 */ + if (uc >= 0x4e00 && uc <= 0x9fcc) + return 1; + + /* 鎵╁睍 A 鍖 */ + if (uc >= 0x3400 && uc <= 0x4db5) + return 2; + + /* 鎵╁睍 B 鍖 */ + if (uc >= 0x20000 && uc <= 0x2a6d6) + return 3; + + /* 鎵╁睍 C 鍖 */ + if (uc >= 0x2a700 && uc <= 0x2b734) + return 4; + + /* 鎵╁睍 D 鍖 */ + if (uc >= 0x2b740 && uc <= 0x2b81f) + return 5; + + /* 鎵╁睍 E 鍖 */ + if (uc >= 0x2b820 && uc <= 0x2f7ff) + return 6; + + /* 鍙版咕鍏煎姹夊瓧 */ + if (uc >= 0x2f800 && uc <= 0x2fa1d) + return 7; + + /* 鍖楁湞椴滃吋瀹规眽瀛 */ + if (uc >= 0xfa70 && uc <= 0xfad9) + return 8; + + /* 鍏煎姹夊瓧 */ + if (uc >= 0xf900 && uc <= 0xfa2d) + return 9; + + /* 鍏煎姹夊瓧 */ + if (uc >= 0xfa30 && uc <= 0xfa6d) + return 10; + + return 0; + } + + /* 涓枃鏍囩偣 */ + int isuzhpunct(ucs4_t uc) + { + if (uc >= 0x3001 && uc <= 0x3002) + return 1; + + if (uc >= 0x3008 && uc <= 0x300f) + return 1; + + if (uc >= 0xff01 && uc <= 0xff0f) + return 1; + + if (uc >= 0xff1a && uc <= 0xff20) + return 1; + + if (uc >= 0xff3b && uc <= 0xff40) + return 1; + + if (uc >= 0xff5b && uc <= 0xff5e) + return 1; + + if (uc >= 0x2012 && uc <= 0x201f) + return 1; + + if (uc >= 0xfe41 && uc <= 0xfe44) + return 1; + + if (uc >= 0xfe49 && uc <= 0xfe4f) + return 1; + + if (uc >= 0x3010 && uc <= 0x3017) + return 1; + + return 0; + } + + /* 鏃ユ枃骞冲亣鍚 */ + int isuhiragana(ucs4_t uc) + { + if (uc >= 0x3040 && uc <= 0x309f) + return 1; + + return 0; + } + + /* 鏃ユ枃鐗囧亣鍚 */ + int isukatakana(ucs4_t uc) + { + if (uc >= 0x30a0 && uc <= 0x30ff) + return 1; + + if (uc >= 0x31f0 && uc <= 0x31ff) + return 2; + + return 0; + } + + /* 闊╂枃 */ + int isukorean(ucs4_t uc) + { + /* 闊╂枃鎷奸煶 */ + if (uc >= 0xac00 && uc <= 0xd7af) + return 1; + + /* 闊╂枃瀛楁瘝 */ + if (uc >= 0x1100 && uc <= 0x11ff) + return 2; + + /* 闊╂枃鍏煎瀛楁瘝 */ + if (uc >= 0x3130 && uc <= 0x318f) + return 3; + + return 0; + } + +} diff --git a/Client/Source/GUI/utf8.h b/Client/Source/GUI/utf8.h new file mode 100644 index 0000000..b561b44 --- /dev/null +++ b/Client/Source/GUI/utf8.h @@ -0,0 +1,113 @@ +/* + * Description: UTF-8 瀛楃涓茬殑瑙g爜鍜岀紪鐮佸嚱鏁 + * unicode 瀛楃澶勭悊鍑芥暟 + * History: yang@haipo.me, 2013/05/29, create + * + * This code is in the public domain. + * You may use this code any way you wish, private, educational, + * or commercial. It's free. + */ + +# pragma once + +# include <stdint.h> +# include <stddef.h> + +namespace utf8 +{ + + + /* + * 鏍囧噯 C 骞舵病鏈夎瀹 wchar_t 鐨勪綅鏁般備絾 GNU C Lib 淇濊瘉 wchar_t 鏄 32 浣嶇殑锛 + * 鎵浠ュ彲浠ョ敤 wchar.h 涓畾涔夌殑鍑芥暟鏉ュ儚 wchar_t 涓鏍锋搷绾 ucs4_t. + * http://www.gnu.org/software/libc/manual/html_node/Extended-Char-Intro.html + */ + typedef int32_t ucs4_t; + + /* + * 浠 UTF-8 缂栫爜鐨勫瓧绗︿覆 *src 涓鍙栦竴涓 unicode 瀛楃锛屽苟鏇存柊 *src 鐨勫笺 + * + * 濡傛灉閬囧埌闈炴硶 UTF-8 缂栫爜锛屽垯璺宠繃闈炴硶閮ㄥ垎銆 + * 濡傛灉 illegal 鍙傛暟涓嶄负 NULL, 鍒 *illegal 琛ㄧず闈炴硶 UTF-8 缂栫爜瀛楄妭鏁般 + */ + ucs4_t getu8c(char **src, int *illegal); + + /* + * 灏 src 鎸囧悜鐨 UTF-8 缂栫爜瀛楃涓茶В鐮佷负 unicode锛屾斁鍦ㄩ暱搴︿负 n 鐨勬暟缁 des 涓紝 + * 骞跺湪鏈熬娣诲姞 0. 濡傛灉 des 涓嶈冻浠ュ瓨鏀炬墍鏈夌殑瀛楃锛屽垯鏈澶氫繚瀛 n - 1 涓 unicode + * 瀛楃骞惰ˉ 0. + * + * 濡傛灉閬囧埌闈炴硶 UTF-8 缂栫爜锛屽垯璺宠繃闈炴硶閮ㄥ垎銆 + * 濡傛灉 illegal 涓嶄负 NULL, 鍒 *illegal 琛ㄧず闈炴硶 UTF-8 缂栫爜鐨勫瓧鑺傛暟銆 + */ + size_t u8decode(char const *str, ucs4_t *des, size_t n, int *illegal); + + /* + * 灏 unicode 瀛楃 uc 缂栫爜涓 UTF-8 缂栫爜锛屾斁鍒伴暱搴︿负 *left 鐨勫瓧绗︿覆 *des 涓 + * + * 濡傛灉 *des 涓嶈冻浠ュ瓨鏀 uc 瀵瑰簲鐨 UTF-8 瀛楃涓诧紝杩斿洖涓涓礋鍊笺 + * 濡傛灉鎴愬姛锛屾洿鏂 *des 鍜 *left 鐨勫笺 + */ + int putu8c(ucs4_t uc, char **des, size_t *left); + + /* + * 灏嗕互 0 缁撳熬鐨 unicode 鏁扮粍 us 缂栫爜涓 UTF-8 瀛楃涓诧紝鏀惧埌闀垮害涓 n 鐨勫瓧绗︿覆 des 涓 + * + * 璐熸暟涓洪潪娉曠殑 unicode 瀛楃銆 + * 濡傛灉 illegal 涓嶄负 NULL锛屽垯 *illegal 琛ㄧず闈炴硶鐨 unicode 瀛楃鏁般 + */ + size_t u8encode(ucs4_t *us, char *des, size_t n, int *illegal); + + /* + * 鍒ゆ柇鏄惁涓哄叏瑙掑瓧绗 + */ + int isufullwidth(ucs4_t uc); + + /* + * 鍒ゆ柇鏄惁涓哄叏瑙掑瓧姣 + */ + int isufullwidthalpha(ucs4_t uc); + + /* + * 鍒ゆ柇鏄惁涓哄叏瑙掓暟瀛 + */ + int isufullwidthdigit(ucs4_t uc); + + /* + * 鍏ㄨ瀛楃杞崐瑙掑瓧绗︺ + * 濡傛灉 uc 涓哄叏瑙掑瓧绗︼紝鍒欒繑鍥炲搴旂殑鍗婅瀛楃锛屽惁鍒欒繑鍥 uc 鏈韩銆 + */ + ucs4_t ufull2half(ucs4_t uc); + + /* + * 鍗婅瀛楃杞叏瑙掑瓧绗 + * 濡傛灉 uc 涓哄崐瑙掑瓧绗︼紝鍒欒繑鍥炲搴旂殑鍏ㄨ瀛楃锛屽惁鍒欒繑鍥 uc 鏈韩銆 + */ + ucs4_t uhalf2full(ucs4_t uc); + + /* + * 鍒ゆ柇鏄惁涓烘眽瀛楀瓧绗︼紙涓棩闊╄秺缁熶竴琛ㄦ剰鏂囧瓧锛 + */ + int isuchiness(ucs4_t uc); + + /* + * 鍒ゆ柇鏄惁涓轰腑鏂囨爣鐐 + */ + int isuzhpunct(ucs4_t uc); + + /* + * 鍒ゆ柇鏄惁涓烘棩鏂囧钩鍋囧悕瀛楃 + */ + int isuhiragana(ucs4_t uc); + + /* + * 鍒ゆ柇鏄惁涓烘棩鏂囩墖鍋囧悕瀛楃 + */ + int isukatakana(ucs4_t uc); + + /* + * 鍒ゆ柇鏄惁涓洪煩鏂囧瓧绗 + */ + int isukorean(ucs4_t uc); + +} |