From 8d28d35bd125f3821df1b8362a90dd1344be563c Mon Sep 17 00:00:00 2001 From: fallenoak Date: Tue, 9 Dec 2025 08:58:42 -0600 Subject: [PATCH] chore(gx): split out font utilities (#143) --- src/gx/Font.cpp | 566 +----------------- src/gx/Font.hpp | 60 +- src/gx/FontInternal.hpp | 12 - src/gx/font/CGxFont.cpp | 1 + src/gx/font/CGxString.cpp | 7 +- src/gx/font/GxuFont.cpp | 470 +++++++++++++++ src/gx/font/GxuFont.hpp | 62 ++ .../GxuFontInternal.cpp} | 102 +++- src/gx/font/GxuFontInternal.hpp | 18 + 9 files changed, 663 insertions(+), 635 deletions(-) delete mode 100644 src/gx/FontInternal.hpp create mode 100644 src/gx/font/GxuFont.cpp create mode 100644 src/gx/font/GxuFont.hpp rename src/gx/{FontInternal.cpp => font/GxuFontInternal.cpp} (67%) create mode 100644 src/gx/font/GxuFontInternal.hpp diff --git a/src/gx/Font.cpp b/src/gx/Font.cpp index 659b721..b7ff46b 100644 --- a/src/gx/Font.cpp +++ b/src/gx/Font.cpp @@ -1,33 +1,22 @@ #include "gx/Font.hpp" -#include "gx/font/CGxFont.hpp" -#include "gx/font/CGxString.hpp" -#include "gx/font/CGxStringBatch.hpp" -#include "gx/font/Wrap.hpp" #include "gx/Coordinate.hpp" #include "gx/Device.hpp" -#include "gx/FontInternal.hpp" -#include "gx/Gx.hpp" -#include "gx/Shader.hpp" -#include -#include -#include +#include "gx/font/CGxFont.hpp" +#include "gx/font/GxuFont.hpp" #include #include #include -#include #include +#include +#include CGxShader* g_fontPixelShader[1]; CGxShader* g_fontVertexShader[2]; -TSList> g_fonts; TSHashTable s_fontHash; uint32_t g_heightPixels; uint32_t g_widthPixels; float g_indentPixelWidth; float g_indentNormWidth; -TSList> s_unusedBatches; -STORM_LIST(CGxString) g_freeStrings; -STORM_LIST(CGxString) g_strings; FONTHASHOBJ::~FONTHASHOBJ() { if (this->font) { @@ -39,33 +28,6 @@ TEXTBLOCK::~TEXTBLOCK() { GxuFontDestroyString(this->string); } -void CalculateYOffset(uint32_t pixelHeight, uint32_t a2, FT_Face face, uint32_t glyphHeight, int32_t* yOffset, int32_t* yStart) { - uint32_t v6 = 0; - int32_t v8 = 0; - - if (glyphHeight <= pixelHeight) { - uint32_t v9 = face->glyph->bitmap_top; - - if (v9 <= a2) { - v8 = a2 - v9; - } else { - v8 = 0; - v6 = v9 - a2; - } - } - - uint32_t v10 = v8 <= 0 ? 0 : v8; - uint32_t v11 = pixelHeight - glyphHeight; - - if (v11 >= v10) { - *yOffset = v6; - *yStart = v10; - } else { - *yOffset = pixelHeight - v10 - glyphHeight; - *yStart = v11; - } -} - uint32_t ConvertStringFlags(uint32_t flags) { uint32_t convertedFlags = 0x0; @@ -177,526 +139,6 @@ uint32_t GetScreenPixelWidth() { return g_widthPixels; } -QUOTEDCODE GxuDetermineQuotedCode(const char* text, int32_t& advance, CImVector* color, uint32_t flags, uint32_t& wide) { - wide = SUniSGetUTF8(reinterpret_cast(text), &advance); - - switch (wide) { - case 0x0: - case 0xFFFFFFFF: - return CODE_INVALIDCODE; - - case '\r': - advance = 2 - (SUniSGetUTF8(reinterpret_cast(text + 1), &advance) != '\n'); - return CODE_NEWLINE; - - case '\n': - advance = 1; - return CODE_NEWLINE; - } - - if (wide != '|' || flags & 0x800) { - return CODE_INVALIDCODE; - } - - auto quotedCode = text[advance]; - - if (!quotedCode) { - return CODE_INVALIDCODE; - } - - switch (quotedCode) { - case 'N': - case 'n': { - if (flags & 0x200) { - return CODE_INVALIDCODE; - } - - advance = 2; - return CODE_NEWLINE; - } - - // TODO handle other control codes - } - - // TODO remainder of function - - return CODE_INVALIDCODE; -} - -int32_t GxuFontAddToBatch(CGxStringBatch* batch, CGxString* string) { - if (batch && string) { - batch->AddString(string); - return 1; - } else { - return 0; - } -} - -void GxuFontAddShadow(CGxString* string, const CImVector& color, const C2Vector& offset) { - if (string) { - if (!(string->m_flags & 0x80)) { - string->AddShadow(offset, color); - } - } -} - -CGxStringBatch* GxuFontCreateBatch(bool a1, bool a2) { - CGxStringBatch* batch; - - if (s_unusedBatches.Head()) { - batch = s_unusedBatches.Head(); - s_unusedBatches.UnlinkNode(batch); - } else { - auto m = SMemAlloc(sizeof(CGxStringBatch), __FILE__, __LINE__, 0x8); - batch = new (m) CGxStringBatch(); - } - - if (a1) { - batch->m_flags |= 0x1; - } else { - batch->m_flags &= ~0x1; - } - - if (a2) { - batch->m_flags |= 0x2; - } else { - batch->m_flags &= ~0x2; - } - - return batch; -} - -int32_t GxuFontCreateFont(const char* name, float fontHeight, CGxFont*& face, uint32_t flags) { - STORM_ASSERT(name); - STORM_ASSERT(*name); - STORM_ASSERT((fontHeight <= 1.0f) && (fontHeight > 0.0f)); - - CGxFont* newFace = g_fonts.NewNode(2, 0, 0); - - uint32_t v12 = flags; - - if (flags & 0x8) { - v12 |= 0x1; - } - - int32_t v13 = newFace->Initialize(name, v12, fontHeight); - - if (!v13) { - g_fonts.DeleteNode(newFace); - newFace = nullptr; - } - - face = newFace; - - return v13; -} - -int32_t GxuFontCreateString(CGxFont* face, const char* text, float fontHeight, const C3Vector& position, float blockWidth, float blockHeight, float spacing, CGxString*& string, EGxFontVJusts vertJustification, EGxFontHJusts horzJustification, uint32_t flags, const CImVector& color, float charSpacing, float scale) { - STORM_ASSERT(face); - STORM_ASSERT(text); - // TODO - // STORM_ASSERT(fontHeight || (flags & EGxStringFlags_FixedSize)); - STORM_ASSERT(vertJustification < GxVJ_Last); - STORM_ASSERT(horzJustification < GxHJ_Last); - - auto newString = CGxString::GetNewString(1); - - int32_t result = newString->Initialize( - fontHeight, - position, - blockWidth, - blockHeight, - face, - text, - vertJustification, - horzJustification, - spacing, - flags & ~0x1, - color, - scale - ); - - if (result) { - string = newString; - } else { - GxuFontDestroyString(newString); - } - - return result; -} - -int32_t GxuFontDestroyBatch(CGxStringBatch* batch) { - if (!batch) { - return 0; - } - - batch->m_fontBatch.Clear(); - - s_unusedBatches.LinkToTail(batch); - - return 1; -} - -void GxuFontDestroyFont(CGxFont*& font) { - if (font) { - g_fonts.DeleteNode(font); - } - - font = nullptr; -} - -void GxuFontDestroyString(CGxString*& string) { - if (string) { - string->Unlink(); - string->Recycle(); - string = nullptr; - } -} - -uint32_t GxuFontGetFontFlags(CGxFont* font) { - if (font) { - return font->m_flags; - } - - return 0; -} - -const char* GxuFontGetFontName(CGxFont* font) { - return font - ? font->GetName() - : nullptr; -} - -uint32_t GxuFontGetMaxCharsWithinWidth(CGxFont* font, const char* text, float height, float maxWidth, uint32_t lineBytes, float* extent, float a7, float scale, float a9, uint32_t flags) { - return InternalGetMaxCharsWithinWidth( - font, - text, - height, - maxWidth, - lineBytes, - extent, - flags, - a7, - scale, - nullptr, - nullptr, - nullptr - ); -} - -uint32_t GxuFontGetMaxCharsWithinWidthAndHeight(CGxFont* font, const char* text, float fontHeight, float maxWidth, float maxHeight, uint32_t lineBytes, float a7, float scale, float a9, uint32_t flags) { - if (!font) { - return 0; - } - - if (!text || !*text) { - return 0; - } - - if (fontHeight == 0.0f || maxWidth == 0.0f || maxHeight == 0.0f || g_heightPixels == 0.0f) { - return 0; - } - - if (lineBytes == 0) { - return 0; - } - - if (flags & 0x4) { - fontHeight = GxuFontGetOneToOneHeight(font); - } - - auto v24 = static_cast(g_heightPixels) * a9; - auto v22 = CMath::fuint_pi(v24); - auto v12 = static_cast(v22) / static_cast(g_heightPixels); - bool v26 = true; - auto v27 = v12; - v24 = 0.0f; - auto v13 = fontHeight; - const char* nextText; - - uint32_t maxChars = 0; - - auto currentText = text; - int32_t v25 = 0; - while (*currentText) { - if (flags & 0x2000 && v25 == 1) { - maxWidth -= g_indentNormWidth; - } - - float v21; - CalcWrapPoint( - font, - currentText, - fontHeight, - maxWidth, - &v22, - &v21, - &nextText, - a7, - flags, - &v26, - 0, - scale - ); - - v22 = nextText - currentText; - if (nextText == currentText) { - break; - } - - auto v15 = v24; - auto v16 = v25 + 1 < 0; - auto v17 = v24 + fontHeight; - - v25++; - - float v18 = v25; - - if (v16) { - v18 = v18 + 4294967300.0; - } - - if (v18 * 0.00000095367431640625 + maxHeight < v17) { - break; - } - - maxChars += nextText - currentText; - currentText = nextText; - v24 = fontHeight + v15 + v27; - - if (!v26 || !nextText) { - break; - } - } - - return maxChars; -} - -float GxuFontGetOneToOneHeight(CGxFont* font) { - STORM_ASSERT(font); - - return PixelToScreenHeight(font->GetPixelSize()); -} - -void GxuFontGetTextExtent(CGxFont* font, const char* text, uint32_t numBytes, float height, float* extent, float a6, float scale, float a8, uint32_t flags) { - InternalGetTextExtent(font, text, numBytes, height, extent, flags, a6, scale); -} - -float GxuFontGetWrappedTextHeight(CGxFont* font, const char* text, float a3, float a4, const C2Vector& a5, float a6, float a7, uint32_t flags) { - STORM_ASSERT(font); - STORM_ASSERT(text); - - if (flags & 0x04) { - a3 = GxuFontGetOneToOneHeight(font); - } - - int32_t advance; - uint32_t numBytes; - uint32_t code; - int32_t numLines = 0; - float extent = 0.0f; - float v17 = 0.0f; - float v18 = 0.0f; - const char* currentText = text; - const char* nextText = nullptr; - bool v21 = true; - - while (currentText && *currentText) { - QUOTEDCODE quotedCode = GxuDetermineQuotedCode(currentText, advance, nullptr, flags, code); - - if (flags & 0x2000 && numLines == 1) { - a4 = a4 - g_indentNormWidth; - } - - if (quotedCode == CODE_NEWLINE) { - nextText = currentText + advance; - } else { - CalcWrapPoint( - font, - currentText, - a3, - a4, - &numBytes, - &extent, - &nextText, - a5.x, - flags, - &v21, - &v17, - a6 - ); - - if (currentText == nextText) { - break; - } - } - - numLines++; - currentText = nextText; - - float v14 = (float)GetScreenPixelHeight() * a3; - if (v14 < v17) { - v18 = v17 - v14 + v18; - } - - if (flags & 0x02) { - break; - } - } - - if (!(flags & 0x02) && currentText > text && GxuDetermineQuotedCode(currentText - 1, advance, nullptr, flags, code) == CODE_NEWLINE) { - numLines++; - } - - if (!numLines) { - return 0.0f; - } - - float v22 = GetScreenPixelHeight(); - float v14 = v22 * a7; - auto v13 = CMath::fuint_pi(v14); - auto v23 = static_cast(v13) / v22; - - return (v18 / v22 + v23 * (float)(numLines - 1) + (float)numLines * a3); -} - -void GxuFontInitialize() { - g_theGxDevicePtr->ShaderCreate(g_fontVertexShader, GxSh_Vertex, "Shaders\\Vertex", "UI", 2); - g_theGxDevicePtr->ShaderCreate(g_fontPixelShader, GxSh_Pixel, "Shaders\\Pixel", "UI", 1); - - BATCHEDRENDERFONTDESC::Initialize(); - - FreeTypeInitialize(); - - GxuFontWindowSizeChanged(); - - // TODO - // sub_6BD160(); -} - -void GxuFontRenderBatch(CGxStringBatch* batch) { - if (batch) { - batch->RenderBatch(); - } -} - -int32_t GxuFontSetStringColor(CGxString* string, const CImVector& color) { - STORM_ASSERT(string); - - string->SetColor(color); - - return 1; -} - -void GxuFontSetStringPosition(CGxString* string, const C3Vector& position) { - STORM_ASSERT(string); - - string->SetStringPosition(position); -} - -void GxuFontUpdate() { - for (auto string = g_strings.Head(); string; string = g_strings.Link(string)->Next()) { - string->Tick(); - } - - // TODO -} - -void GxuFontWindowSizeChanged() { - static CRect s_currentRect = { 0.0f, 0.0f, 0.0f, 0.0f }; - - CRect rect = { 0.0f, 0.0f, 0.0f, 0.0f }; - - GxCapsWindowSize(rect); - - if (rect.maxY - rect.minY == 0.0f || rect.maxX - rect.minX == 0.0f) { - rect = { 0.0f, 0.0f, 480.0f, 640.0f }; - } - - if (s_currentRect == rect) { - return; - } - - s_currentRect = rect; - - g_widthPixels = rect.maxX - rect.minX; - g_heightPixels = rect.maxY - rect.minY; - g_indentPixelWidth = 15.0f; - g_indentNormWidth = 15.0f / (rect.maxX - rect.minX); - - // TODO - // - walk s_fonts and trigger HandleScreenSizeChange -} - -int32_t IGxuFontGlyphRenderGlyph(FT_Face face, uint32_t pixelHeight, uint32_t code, uint32_t baseline, GLYPHBITMAPDATA* dataPtr, int32_t monochrome, uint32_t a7) { - STORM_ASSERT(face); - STORM_ASSERT(pixelHeight); - STORM_ASSERT(dataPtr); - - if (!FT_Get_Char_Index(face, code)) { - return 0; - } - - if (!FREETYPE_RenderGlyph(code, monochrome != 0, face)) { - return 0; - } - - auto glyph = face->glyph; - - uint32_t width = glyph->bitmap.width; - uint32_t height = std::min(static_cast(glyph->bitmap.rows), pixelHeight); - size_t dataSize = glyph->bitmap.pitch * glyph->bitmap.rows; - auto srcData = glyph->bitmap.buffer; - uint32_t pitch = glyph->bitmap.pitch; - - int32_t dummyGlyph = 0; - - if (!width || !height || !srcData || !pitch || !dataSize) { - width = (pixelHeight + 3) >> 2; - height = pixelHeight; - - if (!width) { - width = pixelHeight; - } - - if (monochrome) { - pitch = (width + 7) & 0xFFFFFFF8; - } else { - pitch = width; - } - - dataSize = pixelHeight * pitch; - dummyGlyph = 1; - } - - void* data = SMemAlloc(dataSize, __FILE__, __LINE__, 0x0); - - if (data) { - memset(data, 0, dataSize); - } - - if (srcData) { - memcpy(data, srcData, dataSize); - } - - dataPtr->m_data = data; - dataPtr->m_dataSize = dataSize; - dataPtr->m_glyphPitch = pitch; - dataPtr->m_glyphWidth = width; - dataPtr->m_glyphHeight = height; - dataPtr->m_glyphCellWidth = width + a7; - dataPtr->m_glyphAdvance = (double)(face->glyph->metrics.horiAdvance / 64) + 1.0; - dataPtr->m_glyphBearing = (double)face->glyph->metrics.horiBearingX * 0.015625; - dataPtr->m_yOffset = 0; - dataPtr->m_yStart = 0; - - if (width && height && data && !dummyGlyph) { - CalculateYOffset(pixelHeight, baseline, face, height, &dataPtr->m_yOffset, &dataPtr->m_yStart); - } - - return 1; -} - float PixelToScreenHeight(int32_t height) { return (double)height / (double)g_heightPixels; } diff --git a/src/gx/Font.hpp b/src/gx/Font.hpp index 62913db..ab67c2b 100644 --- a/src/gx/Font.hpp +++ b/src/gx/Font.hpp @@ -2,9 +2,8 @@ #define GX_FONT_HPP #include "gx/font/FreeType.hpp" +#include "gx/font/GxuFont.hpp" #include "gx/font/Types.hpp" -#include "gx/Types.hpp" -#include #include #include @@ -34,14 +33,11 @@ class TEXTBLOCK : public CHandleObject { }; extern CGxShader* g_fontPixelShader[1]; - extern CGxShader* g_fontVertexShader[2]; - -extern STORM_LIST(CGxString) g_freeStrings; - -extern STORM_LIST(CGxString) g_strings; - -void CalculateYOffset(uint32_t, uint32_t, FT_Face, uint32_t, int32_t*, int32_t*); +extern uint32_t g_heightPixels; +extern uint32_t g_widthPixels; +extern float g_indentPixelWidth; +extern float g_indentNormWidth; float GetCharacterWidth(const char*, uint32_t, uint32_t, CGxFont*, float); @@ -53,52 +49,6 @@ uint32_t GetScreenPixelHeight(void); uint32_t GetScreenPixelWidth(void); -QUOTEDCODE GxuDetermineQuotedCode(const char*, int32_t&, CImVector*, uint32_t, uint32_t&); - -int32_t GxuFontAddToBatch(CGxStringBatch*, CGxString*); - -void GxuFontAddShadow(CGxString* string, const CImVector& color, const C2Vector& offset); - -CGxStringBatch* GxuFontCreateBatch(bool, bool); - -int32_t GxuFontCreateFont(const char*, float, CGxFont*&, uint32_t); - -int32_t GxuFontCreateString(CGxFont*, const char*, float, const C3Vector&, float, float, float, CGxString*&, EGxFontVJusts, EGxFontHJusts, uint32_t, const CImVector&, float, float); - -int32_t GxuFontDestroyBatch(CGxStringBatch*); - -void GxuFontDestroyFont(CGxFont*& font); - -void GxuFontDestroyString(CGxString*&); - -uint32_t GxuFontGetFontFlags(CGxFont*); - -const char* GxuFontGetFontName(CGxFont*); - -uint32_t GxuFontGetMaxCharsWithinWidth(CGxFont*, const char*, float, float, uint32_t, float*, float, float, float, uint32_t); - -uint32_t GxuFontGetMaxCharsWithinWidthAndHeight(CGxFont*, const char*, float, float, float, uint32_t, float, float, float, uint32_t); - -float GxuFontGetOneToOneHeight(CGxFont*); - -void GxuFontGetTextExtent(CGxFont* font, const char* text, uint32_t numBytes, float height, float* extent, float a6, float scale, float a8, uint32_t flags); - -float GxuFontGetWrappedTextHeight(CGxFont*, const char*, float, float, const C2Vector&, float, float, uint32_t); - -void GxuFontInitialize(void); - -void GxuFontRenderBatch(CGxStringBatch*); - -int32_t GxuFontSetStringColor(CGxString*, const CImVector&); - -void GxuFontSetStringPosition(CGxString* string, const C3Vector& position); - -void GxuFontUpdate(); - -void GxuFontWindowSizeChanged(void); - -int32_t IGxuFontGlyphRenderGlyph(FT_Face, uint32_t, uint32_t, uint32_t, GLYPHBITMAPDATA*, int32_t, uint32_t); - void TextBlockAddShadow(HTEXTBLOCK, CImVector, const C2Vector&); HTEXTBLOCK TextBlockCreate(HTEXTFONT, const char*, const CImVector&, const C3Vector&, float, float, float, uint32_t, float, float, float); diff --git a/src/gx/FontInternal.hpp b/src/gx/FontInternal.hpp deleted file mode 100644 index 0679539..0000000 --- a/src/gx/FontInternal.hpp +++ /dev/null @@ -1,12 +0,0 @@ -#ifndef GX_FONT_INTERNAL_HPP -#define GX_FONT_INTERNAL_HPP - -#include - -class CGxFont; - -uint32_t InternalGetMaxCharsWithinWidth(CGxFont*, const char*, float, float, uint32_t, float*, uint32_t, float, float, uint32_t*, float*, float*); - -void InternalGetTextExtent(CGxFont*, const char*, uint32_t, float, float*, uint32_t, float, float); - -#endif diff --git a/src/gx/font/CGxFont.cpp b/src/gx/font/CGxFont.cpp index 5c01fd8..ef671b4 100644 --- a/src/gx/font/CGxFont.cpp +++ b/src/gx/font/CGxFont.cpp @@ -1,5 +1,6 @@ #include "gx/font/CGxFont.hpp" #include "gx/font/FontFace.hpp" +#include "gx/font/GxuFontInternal.hpp" #include "gx/Texture.hpp" #include #include diff --git a/src/gx/font/CGxString.cpp b/src/gx/font/CGxString.cpp index 49ed542..8a38f1b 100644 --- a/src/gx/font/CGxString.cpp +++ b/src/gx/font/CGxString.cpp @@ -1,15 +1,16 @@ #include "gx/font/CGxString.hpp" #include "gx/Buffer.hpp" #include "gx/font/CGxFont.hpp" +#include "gx/font/GxuFont.hpp" #include "gx/font/Wrap.hpp" #include "gx/Font.hpp" #include "gx/Gx.hpp" -#include -#include -#include #include #include #include +#include +#include +#include TEXTLINETEXTURE* TEXTLINETEXTURE::NewTextLineTexture() { // TODO diff --git a/src/gx/font/GxuFont.cpp b/src/gx/font/GxuFont.cpp new file mode 100644 index 0000000..326873d --- /dev/null +++ b/src/gx/font/GxuFont.cpp @@ -0,0 +1,470 @@ +#include "gx/font/GxuFont.hpp" +#include "gx/font/CGxFont.hpp" +#include "gx/font/CGxStringBatch.hpp" +#include "gx/font/FreeType.hpp" +#include "gx/font/GxuFontInternal.hpp" +#include "gx/font/Wrap.hpp" +#include "gx/Device.hpp" +#include "gx/Gx.hpp" +#include "gx/Types.hpp" +#include +#include +#include +#include + +STORM_LIST(CGxFont) g_fonts; +STORM_LIST(CGxString) g_freeStrings; +STORM_LIST(CGxString) g_strings; + +static TSList> s_unusedBatches; + +QUOTEDCODE GxuDetermineQuotedCode(const char* text, int32_t& advance, CImVector* color, uint32_t flags, uint32_t& wide) { + wide = SUniSGetUTF8(reinterpret_cast(text), &advance); + + switch (wide) { + case 0x0: + case 0xFFFFFFFF: + return CODE_INVALIDCODE; + + case '\r': + advance = 2 - (SUniSGetUTF8(reinterpret_cast(text + 1), &advance) != '\n'); + return CODE_NEWLINE; + + case '\n': + advance = 1; + return CODE_NEWLINE; + } + + if (wide != '|' || flags & 0x800) { + return CODE_INVALIDCODE; + } + + auto quotedCode = text[advance]; + + if (!quotedCode) { + return CODE_INVALIDCODE; + } + + switch (quotedCode) { + case 'N': + case 'n': { + if (flags & 0x200) { + return CODE_INVALIDCODE; + } + + advance = 2; + return CODE_NEWLINE; + } + + // TODO handle other control codes + } + + // TODO remainder of function + + return CODE_INVALIDCODE; +} + +int32_t GxuFontAddToBatch(CGxStringBatch* batch, CGxString* string) { + if (batch && string) { + batch->AddString(string); + return 1; + } else { + return 0; + } +} + +void GxuFontAddShadow(CGxString* string, const CImVector& color, const C2Vector& offset) { + if (string) { + if (!(string->m_flags & 0x80)) { + string->AddShadow(offset, color); + } + } +} + +CGxStringBatch* GxuFontCreateBatch(bool a1, bool a2) { + CGxStringBatch* batch; + + if (s_unusedBatches.Head()) { + batch = s_unusedBatches.Head(); + s_unusedBatches.UnlinkNode(batch); + } else { + auto m = SMemAlloc(sizeof(CGxStringBatch), __FILE__, __LINE__, 0x8); + batch = new (m) CGxStringBatch(); + } + + if (a1) { + batch->m_flags |= 0x1; + } else { + batch->m_flags &= ~0x1; + } + + if (a2) { + batch->m_flags |= 0x2; + } else { + batch->m_flags &= ~0x2; + } + + return batch; +} + +int32_t GxuFontCreateFont(const char* name, float fontHeight, CGxFont*& face, uint32_t flags) { + STORM_ASSERT(name); + STORM_ASSERT(*name); + STORM_ASSERT((fontHeight <= 1.0f) && (fontHeight > 0.0f)); + + CGxFont* newFace = g_fonts.NewNode(2, 0, 0); + + uint32_t v12 = flags; + + if (flags & 0x8) { + v12 |= 0x1; + } + + int32_t v13 = newFace->Initialize(name, v12, fontHeight); + + if (!v13) { + g_fonts.DeleteNode(newFace); + newFace = nullptr; + } + + face = newFace; + + return v13; +} + +int32_t GxuFontCreateString(CGxFont* face, const char* text, float fontHeight, const C3Vector& position, float blockWidth, float blockHeight, float spacing, CGxString*& string, EGxFontVJusts vertJustification, EGxFontHJusts horzJustification, uint32_t flags, const CImVector& color, float charSpacing, float scale) { + STORM_ASSERT(face); + STORM_ASSERT(text); + // TODO + // STORM_ASSERT(fontHeight || (flags & EGxStringFlags_FixedSize)); + STORM_ASSERT(vertJustification < GxVJ_Last); + STORM_ASSERT(horzJustification < GxHJ_Last); + + auto newString = CGxString::GetNewString(1); + + int32_t result = newString->Initialize( + fontHeight, + position, + blockWidth, + blockHeight, + face, + text, + vertJustification, + horzJustification, + spacing, + flags & ~0x1, + color, + scale + ); + + if (result) { + string = newString; + } else { + GxuFontDestroyString(newString); + } + + return result; +} + +int32_t GxuFontDestroyBatch(CGxStringBatch* batch) { + if (!batch) { + return 0; + } + + batch->m_fontBatch.Clear(); + + s_unusedBatches.LinkToTail(batch); + + return 1; +} + +void GxuFontDestroyFont(CGxFont*& font) { + if (font) { + g_fonts.DeleteNode(font); + } + + font = nullptr; +} + +void GxuFontDestroyString(CGxString*& string) { + if (string) { + string->Unlink(); + string->Recycle(); + string = nullptr; + } +} + +uint32_t GxuFontGetFontFlags(CGxFont* font) { + if (font) { + return font->m_flags; + } + + return 0; +} + +const char* GxuFontGetFontName(CGxFont* font) { + return font + ? font->GetName() + : nullptr; +} + +uint32_t GxuFontGetMaxCharsWithinWidth(CGxFont* font, const char* text, float height, float maxWidth, uint32_t lineBytes, float* extent, float a7, float scale, float a9, uint32_t flags) { + return InternalGetMaxCharsWithinWidth( + font, + text, + height, + maxWidth, + lineBytes, + extent, + flags, + a7, + scale, + nullptr, + nullptr, + nullptr + ); +} + +uint32_t GxuFontGetMaxCharsWithinWidthAndHeight(CGxFont* font, const char* text, float fontHeight, float maxWidth, float maxHeight, uint32_t lineBytes, float a7, float scale, float a9, uint32_t flags) { + if (!font) { + return 0; + } + + if (!text || !*text) { + return 0; + } + + if (fontHeight == 0.0f || maxWidth == 0.0f || maxHeight == 0.0f || g_heightPixels == 0.0f) { + return 0; + } + + if (lineBytes == 0) { + return 0; + } + + if (flags & 0x4) { + fontHeight = GxuFontGetOneToOneHeight(font); + } + + auto v24 = static_cast(g_heightPixels) * a9; + auto v22 = CMath::fuint_pi(v24); + auto v12 = static_cast(v22) / static_cast(g_heightPixels); + bool v26 = true; + auto v27 = v12; + v24 = 0.0f; + auto v13 = fontHeight; + const char* nextText; + + uint32_t maxChars = 0; + + auto currentText = text; + int32_t v25 = 0; + while (*currentText) { + if (flags & 0x2000 && v25 == 1) { + maxWidth -= g_indentNormWidth; + } + + float v21; + CalcWrapPoint( + font, + currentText, + fontHeight, + maxWidth, + &v22, + &v21, + &nextText, + a7, + flags, + &v26, + 0, + scale + ); + + v22 = nextText - currentText; + if (nextText == currentText) { + break; + } + + auto v15 = v24; + auto v16 = v25 + 1 < 0; + auto v17 = v24 + fontHeight; + + v25++; + + float v18 = v25; + + if (v16) { + v18 = v18 + 4294967300.0; + } + + if (v18 * 0.00000095367431640625 + maxHeight < v17) { + break; + } + + maxChars += nextText - currentText; + currentText = nextText; + v24 = fontHeight + v15 + v27; + + if (!v26 || !nextText) { + break; + } + } + + return maxChars; +} + +float GxuFontGetOneToOneHeight(CGxFont* font) { + STORM_ASSERT(font); + + return PixelToScreenHeight(font->GetPixelSize()); +} + +void GxuFontGetTextExtent(CGxFont* font, const char* text, uint32_t numBytes, float height, float* extent, float a6, float scale, float a8, uint32_t flags) { + InternalGetTextExtent(font, text, numBytes, height, extent, flags, a6, scale); +} + +float GxuFontGetWrappedTextHeight(CGxFont* font, const char* text, float a3, float a4, const C2Vector& a5, float a6, float a7, uint32_t flags) { + STORM_ASSERT(font); + STORM_ASSERT(text); + + if (flags & 0x04) { + a3 = GxuFontGetOneToOneHeight(font); + } + + int32_t advance; + uint32_t numBytes; + uint32_t code; + int32_t numLines = 0; + float extent = 0.0f; + float v17 = 0.0f; + float v18 = 0.0f; + const char* currentText = text; + const char* nextText = nullptr; + bool v21 = true; + + while (currentText && *currentText) { + QUOTEDCODE quotedCode = GxuDetermineQuotedCode(currentText, advance, nullptr, flags, code); + + if (flags & 0x2000 && numLines == 1) { + a4 = a4 - g_indentNormWidth; + } + + if (quotedCode == CODE_NEWLINE) { + nextText = currentText + advance; + } else { + CalcWrapPoint( + font, + currentText, + a3, + a4, + &numBytes, + &extent, + &nextText, + a5.x, + flags, + &v21, + &v17, + a6 + ); + + if (currentText == nextText) { + break; + } + } + + numLines++; + currentText = nextText; + + float v14 = (float)GetScreenPixelHeight() * a3; + if (v14 < v17) { + v18 = v17 - v14 + v18; + } + + if (flags & 0x02) { + break; + } + } + + if (!(flags & 0x02) && currentText > text && GxuDetermineQuotedCode(currentText - 1, advance, nullptr, flags, code) == CODE_NEWLINE) { + numLines++; + } + + if (!numLines) { + return 0.0f; + } + + float v22 = GetScreenPixelHeight(); + float v14 = v22 * a7; + auto v13 = CMath::fuint_pi(v14); + auto v23 = static_cast(v13) / v22; + + return (v18 / v22 + v23 * (float)(numLines - 1) + (float)numLines * a3); +} + +void GxuFontInitialize() { + g_theGxDevicePtr->ShaderCreate(g_fontVertexShader, GxSh_Vertex, "Shaders\\Vertex", "UI", 2); + g_theGxDevicePtr->ShaderCreate(g_fontPixelShader, GxSh_Pixel, "Shaders\\Pixel", "UI", 1); + + BATCHEDRENDERFONTDESC::Initialize(); + + FreeTypeInitialize(); + + GxuFontWindowSizeChanged(); + + // TODO + // sub_6BD160(); +} + +void GxuFontRenderBatch(CGxStringBatch* batch) { + if (batch) { + batch->RenderBatch(); + } +} + +int32_t GxuFontSetStringColor(CGxString* string, const CImVector& color) { + STORM_ASSERT(string); + + string->SetColor(color); + + return 1; +} + +void GxuFontSetStringPosition(CGxString* string, const C3Vector& position) { + STORM_ASSERT(string); + + string->SetStringPosition(position); +} + +void GxuFontUpdate() { + for (auto string = g_strings.Head(); string; string = g_strings.Link(string)->Next()) { + string->Tick(); + } + + // TODO +} + +void GxuFontWindowSizeChanged() { + static CRect s_currentRect = { 0.0f, 0.0f, 0.0f, 0.0f }; + + CRect rect = { 0.0f, 0.0f, 0.0f, 0.0f }; + + GxCapsWindowSize(rect); + + if (rect.maxY - rect.minY == 0.0f || rect.maxX - rect.minX == 0.0f) { + rect = { 0.0f, 0.0f, 480.0f, 640.0f }; + } + + if (s_currentRect == rect) { + return; + } + + s_currentRect = rect; + + g_widthPixels = rect.maxX - rect.minX; + g_heightPixels = rect.maxY - rect.minY; + g_indentPixelWidth = 15.0f; + g_indentNormWidth = 15.0f / (rect.maxX - rect.minX); + + // TODO + // - walk s_fonts and trigger HandleScreenSizeChange +} diff --git a/src/gx/font/GxuFont.hpp b/src/gx/font/GxuFont.hpp new file mode 100644 index 0000000..dbf2077 --- /dev/null +++ b/src/gx/font/GxuFont.hpp @@ -0,0 +1,62 @@ +#ifndef GX_FONT_GXU_FONT_HPP +#define GX_FONT_GXU_FONT_HPP + +#include "gx/Types.hpp" +#include "gx/font/Types.hpp" +#include +#include +#include + +class CGxFont; +class CGxString; +class CGxStringBatch; + +extern STORM_LIST(CGxFont) g_fonts; +extern STORM_LIST(CGxString) g_freeStrings; +extern STORM_LIST(CGxString) g_strings; + +QUOTEDCODE GxuDetermineQuotedCode(const char*, int32_t&, CImVector*, uint32_t, uint32_t&); + +void GxuFontAddShadow(CGxString* string, const CImVector& color, const C2Vector& offset); + +int32_t GxuFontAddToBatch(CGxStringBatch*, CGxString*); + +CGxStringBatch* GxuFontCreateBatch(bool, bool); + +int32_t GxuFontCreateFont(const char*, float, CGxFont*&, uint32_t); + +int32_t GxuFontCreateString(CGxFont*, const char*, float, const C3Vector&, float, float, float, CGxString*&, EGxFontVJusts, EGxFontHJusts, uint32_t, const CImVector&, float, float); + +int32_t GxuFontDestroyBatch(CGxStringBatch*); + +void GxuFontDestroyFont(CGxFont*& font); + +void GxuFontDestroyString(CGxString*&); + +uint32_t GxuFontGetFontFlags(CGxFont*); + +const char* GxuFontGetFontName(CGxFont*); + +uint32_t GxuFontGetMaxCharsWithinWidth(CGxFont*, const char*, float, float, uint32_t, float*, float, float, float, uint32_t); + +uint32_t GxuFontGetMaxCharsWithinWidthAndHeight(CGxFont*, const char*, float, float, float, uint32_t, float, float, float, uint32_t); + +float GxuFontGetOneToOneHeight(CGxFont*); + +void GxuFontGetTextExtent(CGxFont* font, const char* text, uint32_t numBytes, float height, float* extent, float a6, float scale, float a8, uint32_t flags); + +float GxuFontGetWrappedTextHeight(CGxFont*, const char*, float, float, const C2Vector&, float, float, uint32_t); + +void GxuFontInitialize(); + +void GxuFontRenderBatch(CGxStringBatch*); + +int32_t GxuFontSetStringColor(CGxString*, const CImVector&); + +void GxuFontSetStringPosition(CGxString* string, const C3Vector& position); + +void GxuFontUpdate(); + +void GxuFontWindowSizeChanged(); + +#endif diff --git a/src/gx/FontInternal.cpp b/src/gx/font/GxuFontInternal.cpp similarity index 67% rename from src/gx/FontInternal.cpp rename to src/gx/font/GxuFontInternal.cpp index 6979fbe..d3ef6f2 100644 --- a/src/gx/FontInternal.cpp +++ b/src/gx/font/GxuFontInternal.cpp @@ -1,9 +1,105 @@ -#include "gx/FontInternal.hpp" -#include "gx/Font.hpp" +#include "gx/font/GxuFontInternal.hpp" #include "gx/font/CGxFont.hpp" -#include +#include "gx/font/GxuFont.hpp" #include #include +#include + +void CalculateYOffset(uint32_t pixelHeight, uint32_t a2, FT_Face face, uint32_t glyphHeight, int32_t* yOffset, int32_t* yStart) { + uint32_t v6 = 0; + int32_t v8 = 0; + + if (glyphHeight <= pixelHeight) { + uint32_t v9 = face->glyph->bitmap_top; + + if (v9 <= a2) { + v8 = a2 - v9; + } else { + v8 = 0; + v6 = v9 - a2; + } + } + + uint32_t v10 = v8 <= 0 ? 0 : v8; + uint32_t v11 = pixelHeight - glyphHeight; + + if (v11 >= v10) { + *yOffset = v6; + *yStart = v10; + } else { + *yOffset = pixelHeight - v10 - glyphHeight; + *yStart = v11; + } +} + +int32_t IGxuFontGlyphRenderGlyph(FT_Face face, uint32_t pixelHeight, uint32_t code, uint32_t baseline, GLYPHBITMAPDATA* dataPtr, int32_t monochrome, uint32_t a7) { + STORM_ASSERT(face); + STORM_ASSERT(pixelHeight); + STORM_ASSERT(dataPtr); + + if (!FT_Get_Char_Index(face, code)) { + return 0; + } + + if (!FREETYPE_RenderGlyph(code, monochrome != 0, face)) { + return 0; + } + + auto glyph = face->glyph; + + uint32_t width = glyph->bitmap.width; + uint32_t height = std::min(static_cast(glyph->bitmap.rows), pixelHeight); + size_t dataSize = glyph->bitmap.pitch * glyph->bitmap.rows; + auto srcData = glyph->bitmap.buffer; + uint32_t pitch = glyph->bitmap.pitch; + + int32_t dummyGlyph = 0; + + if (!width || !height || !srcData || !pitch || !dataSize) { + width = (pixelHeight + 3) >> 2; + height = pixelHeight; + + if (!width) { + width = pixelHeight; + } + + if (monochrome) { + pitch = (width + 7) & 0xFFFFFFF8; + } else { + pitch = width; + } + + dataSize = pixelHeight * pitch; + dummyGlyph = 1; + } + + void* data = SMemAlloc(dataSize, __FILE__, __LINE__, 0x0); + + if (data) { + memset(data, 0, dataSize); + } + + if (srcData) { + memcpy(data, srcData, dataSize); + } + + dataPtr->m_data = data; + dataPtr->m_dataSize = dataSize; + dataPtr->m_glyphPitch = pitch; + dataPtr->m_glyphWidth = width; + dataPtr->m_glyphHeight = height; + dataPtr->m_glyphCellWidth = width + a7; + dataPtr->m_glyphAdvance = (double)(face->glyph->metrics.horiAdvance / 64) + 1.0; + dataPtr->m_glyphBearing = (double)face->glyph->metrics.horiBearingX * 0.015625; + dataPtr->m_yOffset = 0; + dataPtr->m_yStart = 0; + + if (width && height && data && !dummyGlyph) { + CalculateYOffset(pixelHeight, baseline, face, height, &dataPtr->m_yOffset, &dataPtr->m_yStart); + } + + return 1; +} uint32_t InternalGetMaxCharsWithinWidth(CGxFont* face, const char* text, float height, float maxWidth, uint32_t lineBytes, float* extent, uint32_t flags, float a8, float scale, uint32_t* bytesInString, float* widthArray, float* widthArrayGuard) { STORM_ASSERT(face); diff --git a/src/gx/font/GxuFontInternal.hpp b/src/gx/font/GxuFontInternal.hpp new file mode 100644 index 0000000..a16d221 --- /dev/null +++ b/src/gx/font/GxuFontInternal.hpp @@ -0,0 +1,18 @@ +#ifndef GX_FONT_GXU_FONT_INTERNAL_HPP +#define GX_FONT_GXU_FONT_INTERNAL_HPP + +#include "gx/font/FreeType.hpp" +#include + +class CGxFont; +struct GLYPHBITMAPDATA; + +void CalculateYOffset(uint32_t, uint32_t, FT_Face, uint32_t, int32_t*, int32_t*); + +int32_t IGxuFontGlyphRenderGlyph(FT_Face face, uint32_t pixelHeight, uint32_t code, uint32_t baseline, GLYPHBITMAPDATA* dataPtr, int32_t monochrome, uint32_t a7); + +uint32_t InternalGetMaxCharsWithinWidth(CGxFont* face, const char* text, float height, float maxWidth, uint32_t lineBytes, float* extent, uint32_t flags, float a8, float scale, uint32_t* bytesInString, float* widthArray, float* widthArrayGuard); + +void InternalGetTextExtent(CGxFont* font, const char* text, uint32_t numBytes, float height, float* extent, uint32_t flags, float a7, float a8); + +#endif