FreeType: Fixed mem leak and adjustments
This commit is contained in:
@@ -73,12 +73,7 @@ bool CFontManager::Initialize()
|
||||
|
||||
void CFontManager::Destroy()
|
||||
{
|
||||
for (auto& pair : m_faceCache)
|
||||
{
|
||||
if (pair.second)
|
||||
FT_Done_Face(pair.second);
|
||||
}
|
||||
m_faceCache.clear();
|
||||
m_resolvedPathCache.clear();
|
||||
m_fontPathMap.clear();
|
||||
|
||||
if (m_ftLibrary)
|
||||
@@ -142,7 +137,7 @@ std::string CFontManager::ResolveFontPath(const char* faceName)
|
||||
return "";
|
||||
}
|
||||
|
||||
FT_Face CFontManager::GetFace(const char* faceName)
|
||||
FT_Face CFontManager::CreateFace(const char* faceName)
|
||||
{
|
||||
if (!m_bInitialized)
|
||||
{
|
||||
@@ -150,24 +145,34 @@ FT_Face CFontManager::GetFace(const char* faceName)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::string path = ResolveFontPath(faceName);
|
||||
if (path.empty())
|
||||
if (!faceName || !faceName[0])
|
||||
return nullptr;
|
||||
|
||||
// Check cache
|
||||
auto it = m_faceCache.find(path);
|
||||
if (it != m_faceCache.end())
|
||||
return it->second;
|
||||
std::string lowerName = ToLower(faceName);
|
||||
|
||||
// Load new face
|
||||
// Check resolved path cache (avoids repeated disk stat calls)
|
||||
std::string path;
|
||||
auto cacheIt = m_resolvedPathCache.find(lowerName);
|
||||
if (cacheIt != m_resolvedPathCache.end())
|
||||
{
|
||||
path = cacheIt->second;
|
||||
}
|
||||
else
|
||||
{
|
||||
path = ResolveFontPath(faceName);
|
||||
if (path.empty())
|
||||
return nullptr;
|
||||
m_resolvedPathCache[lowerName] = path;
|
||||
}
|
||||
|
||||
// Create a new FT_Face — caller owns it
|
||||
FT_Face face = nullptr;
|
||||
FT_Error err = FT_New_Face(m_ftLibrary, path.c_str(), 0, &face);
|
||||
if (err != 0 || !face)
|
||||
{
|
||||
TraceError("CFontManager::GetFace - FT_New_Face failed for '%s' (error %d)", path.c_str(), err);
|
||||
TraceError("CFontManager::CreateFace - FT_New_Face failed for '%s' (error %d)", path.c_str(), err);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
m_faceCache[path] = face;
|
||||
return face;
|
||||
}
|
||||
|
||||
@@ -14,9 +14,9 @@ public:
|
||||
bool Initialize();
|
||||
void Destroy();
|
||||
|
||||
// Get an FT_Face for a given face name. The face is owned by CFontManager.
|
||||
// Callers must NOT call FT_Done_Face on it.
|
||||
FT_Face GetFace(const char* faceName);
|
||||
// Create a NEW FT_Face for the given font name.
|
||||
// The caller OWNS the returned face and must call FT_Done_Face on it when done.
|
||||
FT_Face CreateFace(const char* faceName);
|
||||
|
||||
FT_Library GetLibrary() const { return m_ftLibrary; }
|
||||
|
||||
@@ -34,6 +34,6 @@ private:
|
||||
// faceName (lowercase) -> file path
|
||||
std::unordered_map<std::string, std::string> m_fontPathMap;
|
||||
|
||||
// filePath -> FT_Face (cached, shared across sizes)
|
||||
std::unordered_map<std::string, FT_Face> m_faceCache;
|
||||
// faceName (lowercase) -> resolved file system path (caches disk lookups)
|
||||
std::unordered_map<std::string, std::string> m_resolvedPathCache;
|
||||
};
|
||||
|
||||
@@ -10,6 +10,20 @@
|
||||
#include FT_FREETYPE_H
|
||||
#include FT_GLYPH_H
|
||||
|
||||
#include <cmath>
|
||||
|
||||
// Precomputed gamma LUT to sharpen FreeType's grayscale anti-aliasing.
|
||||
// GDI ClearType has high-contrast edges; FreeType grayscale is softer.
|
||||
// Gamma < 1.0 boosts mid-range alpha, making edges crisper.
|
||||
static struct SAlphaGammaLUT {
|
||||
unsigned char table[256];
|
||||
SAlphaGammaLUT() {
|
||||
table[0] = 0;
|
||||
for (int i = 1; i < 256; ++i)
|
||||
table[i] = (unsigned char)(pow(i / 255.0, 0.80) * 255.0 + 0.5);
|
||||
}
|
||||
} s_alphaGammaLUT;
|
||||
|
||||
CGraphicFontTexture::CGraphicFontTexture()
|
||||
{
|
||||
Initialize();
|
||||
@@ -53,19 +67,55 @@ void CGraphicFontTexture::Destroy()
|
||||
stl_wipe(m_pFontTextureVector);
|
||||
m_charInfoMap.clear();
|
||||
|
||||
// FT_Face is owned by CFontManager, do NOT free it here
|
||||
m_ftFace = nullptr;
|
||||
if (m_ftFace)
|
||||
{
|
||||
FT_Done_Face(m_ftFace);
|
||||
m_ftFace = nullptr;
|
||||
}
|
||||
|
||||
Initialize();
|
||||
}
|
||||
|
||||
bool CGraphicFontTexture::CreateDeviceObjects()
|
||||
{
|
||||
if (!m_ftFace)
|
||||
return true;
|
||||
|
||||
// After device reset: wipe GPU textures, clear atlas state, and
|
||||
// re-render all previously cached characters on demand.
|
||||
// We keep m_charInfoMap keys but clear the entries so glyphs get re-rasterized.
|
||||
std::vector<TCharacterKey> cachedKeys;
|
||||
cachedKeys.reserve(m_charInfoMap.size());
|
||||
for (const auto& pair : m_charInfoMap)
|
||||
cachedKeys.push_back(pair.first);
|
||||
|
||||
stl_wipe(m_pFontTextureVector);
|
||||
m_charInfoMap.clear();
|
||||
m_x = 0;
|
||||
m_y = 0;
|
||||
m_step = 0;
|
||||
m_isDirty = false;
|
||||
|
||||
// Reset CPU atlas buffer
|
||||
if (m_pAtlasBuffer)
|
||||
memset(m_pAtlasBuffer, 0, m_atlasWidth * m_atlasHeight * sizeof(DWORD));
|
||||
|
||||
// Create first GPU texture page
|
||||
if (!AppendTexture())
|
||||
return false;
|
||||
|
||||
// Re-rasterize all previously cached glyphs
|
||||
for (TCharacterKey key : cachedKeys)
|
||||
UpdateCharacterInfomation(key);
|
||||
|
||||
UpdateTexture();
|
||||
return true;
|
||||
}
|
||||
|
||||
void CGraphicFontTexture::DestroyDeviceObjects()
|
||||
{
|
||||
m_lpd3dTexture = NULL;
|
||||
stl_wipe(m_pFontTextureVector);
|
||||
}
|
||||
|
||||
bool CGraphicFontTexture::Create(const char* c_szFontName, int fontSize, bool bItalic)
|
||||
@@ -97,19 +147,17 @@ bool CGraphicFontTexture::Create(const char* c_szFontName, int fontSize, bool bI
|
||||
m_pAtlasBuffer = new DWORD[width * height];
|
||||
memset(m_pAtlasBuffer, 0, width * height * sizeof(DWORD));
|
||||
|
||||
// Get FT_Face from FontManager
|
||||
m_ftFace = CFontManager::Instance().GetFace(c_szFontName);
|
||||
// Store UTF-8 name for device reset re-creation
|
||||
m_fontNameUTF8 = c_szFontName ? c_szFontName : "";
|
||||
|
||||
// Create a per-instance FT_Face (this instance owns it)
|
||||
m_ftFace = CFontManager::Instance().CreateFace(c_szFontName);
|
||||
if (!m_ftFace)
|
||||
{
|
||||
TraceError("CGraphicFontTexture::Create - Failed to get face for '%s'", c_szFontName ? c_szFontName : "(null)");
|
||||
TraceError("CGraphicFontTexture::Create - Failed to create face for '%s'", c_szFontName ? c_szFontName : "(null)");
|
||||
return false;
|
||||
}
|
||||
|
||||
Tracef(" FontTexture: loaded '%s' size=%d family='%s' style='%s'\n",
|
||||
c_szFontName ? c_szFontName : "(null)", fontSize,
|
||||
m_ftFace->family_name ? m_ftFace->family_name : "?",
|
||||
m_ftFace->style_name ? m_ftFace->style_name : "?");
|
||||
|
||||
// Set pixel size
|
||||
int pixelSize = (fontSize < 0) ? -fontSize : fontSize;
|
||||
if (pixelSize == 0)
|
||||
@@ -207,26 +255,6 @@ CGraphicFontTexture::TCharacterInfomation* CGraphicFontTexture::UpdateCharacterI
|
||||
if (!m_ftFace)
|
||||
return NULL;
|
||||
|
||||
// Re-apply face state (FT_Face is shared across instances via CFontManager)
|
||||
int pixelSize = (m_fontSize < 0) ? -m_fontSize : m_fontSize;
|
||||
if (pixelSize == 0)
|
||||
pixelSize = 12;
|
||||
FT_Set_Pixel_Sizes(m_ftFace, 0, pixelSize);
|
||||
|
||||
if (m_bItalic)
|
||||
{
|
||||
FT_Matrix matrix;
|
||||
matrix.xx = 0x10000L;
|
||||
matrix.xy = 0x5800L;
|
||||
matrix.yx = 0;
|
||||
matrix.yy = 0x10000L;
|
||||
FT_Set_Transform(m_ftFace, &matrix, NULL);
|
||||
}
|
||||
else
|
||||
{
|
||||
FT_Set_Transform(m_ftFace, NULL, NULL);
|
||||
}
|
||||
|
||||
if (keyValue == 0x08)
|
||||
keyValue = L' ';
|
||||
|
||||
@@ -248,8 +276,9 @@ CGraphicFontTexture::TCharacterInfomation* CGraphicFontTexture::UpdateCharacterI
|
||||
|
||||
int glyphBitmapWidth = bitmap.width;
|
||||
int glyphBitmapHeight = bitmap.rows;
|
||||
int bearingX = slot->bitmap_left;
|
||||
int bearingY = slot->bitmap_top;
|
||||
float advance = (float)(slot->advance.x >> 6);
|
||||
float advance = ceilf((float)(slot->advance.x) / 64.0f);
|
||||
|
||||
// Normalize glyph placement to common baseline
|
||||
// yOffset = distance from atlas row top to where the glyph bitmap starts
|
||||
@@ -273,6 +302,7 @@ CGraphicFontTexture::TCharacterInfomation* CGraphicFontTexture::UpdateCharacterI
|
||||
rNewCharInfo.right = 0;
|
||||
rNewCharInfo.bottom = 0;
|
||||
rNewCharInfo.advance = advance;
|
||||
rNewCharInfo.bearingX = 0.0f;
|
||||
return &rNewCharInfo;
|
||||
}
|
||||
|
||||
@@ -319,7 +349,10 @@ CGraphicFontTexture::TCharacterInfomation* CGraphicFontTexture::UpdateCharacterI
|
||||
{
|
||||
unsigned char alpha = srcRow[col];
|
||||
if (alpha)
|
||||
dstRow[col] = ((DWORD)alpha << 24) | 0x00FFFFFF; // White + alpha
|
||||
{
|
||||
alpha = s_alphaGammaLUT.table[alpha];
|
||||
dstRow[col] = ((DWORD)alpha << 24) | 0x00FFFFFF;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -336,6 +369,7 @@ CGraphicFontTexture::TCharacterInfomation* CGraphicFontTexture::UpdateCharacterI
|
||||
rNewCharInfo.right = float(m_x + cellWidth) * rhwidth;
|
||||
rNewCharInfo.bottom = float(m_y + cellHeight) * rhheight;
|
||||
rNewCharInfo.advance = advance;
|
||||
rNewCharInfo.bearingX = (float)bearingX;
|
||||
|
||||
m_x += cellWidth;
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
#include <ft2build.h>
|
||||
#include FT_FREETYPE_H
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
|
||||
@@ -24,6 +25,7 @@ class CGraphicFontTexture : public CGraphicTexture
|
||||
float right;
|
||||
float bottom;
|
||||
float advance;
|
||||
float bearingX;
|
||||
} TCharacterInfomation;
|
||||
|
||||
typedef std::vector<TCharacterInfomation*> TPCharacterInfomationVector;
|
||||
@@ -75,6 +77,7 @@ class CGraphicFontTexture : public CGraphicTexture
|
||||
bool m_isDirty;
|
||||
|
||||
TCHAR m_fontName[LF_FACESIZE];
|
||||
std::string m_fontNameUTF8; // stored for device reset re-creation
|
||||
LONG m_fontSize;
|
||||
bool m_bItalic;
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@ CGraphicText::TType CGraphicText::Type()
|
||||
|
||||
bool CGraphicText::OnLoad(int /*iSize*/, const void* /*c_pvBuf*/)
|
||||
{
|
||||
static char strName[32];
|
||||
char strName[32];
|
||||
int size;
|
||||
bool bItalic = false;
|
||||
|
||||
@@ -50,7 +50,7 @@ bool CGraphicText::OnLoad(int /*iSize*/, const void* /*c_pvBuf*/)
|
||||
strName[nameLen] = '\0';
|
||||
++p;
|
||||
|
||||
static char num[8];
|
||||
char num[8];
|
||||
|
||||
int i = 0;
|
||||
while (*p && isdigit(*p))
|
||||
|
||||
@@ -534,9 +534,7 @@ void CGraphicTextInstance::Render(RECT * pClipRect)
|
||||
}
|
||||
|
||||
static std::unordered_map<LPDIRECT3DTEXTURE9, std::vector<SVertex>> s_vtxBatches;
|
||||
for (auto& [pTexture, vtxBatch] : s_vtxBatches) {
|
||||
vtxBatch.clear();
|
||||
}
|
||||
s_vtxBatches.clear();
|
||||
|
||||
STATEMANAGER.SaveRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA);
|
||||
STATEMANAGER.SaveRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);
|
||||
@@ -612,7 +610,7 @@ void CGraphicTextInstance::Render(RECT * pClipRect)
|
||||
}
|
||||
}
|
||||
|
||||
fFontSx = fCurX - 0.5f;
|
||||
fFontSx = fCurX + pCurCharInfo->bearingX - 0.5f;
|
||||
fFontSy = fCurY - 0.5f;
|
||||
fFontEx = fFontSx + fFontWidth;
|
||||
fFontEy = fFontSy + fFontHeight;
|
||||
@@ -717,7 +715,7 @@ void CGraphicTextInstance::Render(RECT * pClipRect)
|
||||
}
|
||||
}
|
||||
|
||||
fFontSx = fCurX-0.5f;
|
||||
fFontSx = fCurX + pCurCharInfo->bearingX - 0.5f;
|
||||
fFontSy = fCurY-0.5f;
|
||||
fFontEx = fFontSx + fFontWidth;
|
||||
fFontEy = fFontSy + fFontHeight;
|
||||
|
||||
@@ -8,17 +8,22 @@
|
||||
#include <ft2build.h>
|
||||
#include FT_BITMAP_H
|
||||
|
||||
#include <cmath>
|
||||
|
||||
// Same gamma LUT as GrpFontTexture for consistent text sharpness
|
||||
static struct STextBarGammaLUT {
|
||||
unsigned char table[256];
|
||||
STextBarGammaLUT() {
|
||||
table[0] = 0;
|
||||
for (int i = 1; i < 256; ++i)
|
||||
table[i] = (unsigned char)(pow(i / 255.0, 0.80) * 255.0 + 0.5);
|
||||
}
|
||||
} s_textBarGammaLUT;
|
||||
|
||||
void CTextBar::__SetFont(int fontSize, bool isBold)
|
||||
{
|
||||
m_ftFace = CFontManager::Instance().GetFace("Tahoma");
|
||||
if (!m_ftFace)
|
||||
return;
|
||||
|
||||
__ApplyFaceState();
|
||||
}
|
||||
|
||||
void CTextBar::__ApplyFaceState()
|
||||
{
|
||||
// Create a per-instance FT_Face (this instance owns it)
|
||||
m_ftFace = CFontManager::Instance().CreateFace("Tahoma");
|
||||
if (!m_ftFace)
|
||||
return;
|
||||
|
||||
@@ -27,7 +32,7 @@ void CTextBar::__ApplyFaceState()
|
||||
pixelSize = 12;
|
||||
|
||||
FT_Set_Pixel_Sizes(m_ftFace, 0, pixelSize);
|
||||
FT_Set_Transform(m_ftFace, NULL, NULL); // TextBar never uses italic
|
||||
FT_Set_Transform(m_ftFace, NULL, NULL);
|
||||
|
||||
m_ascender = (int)(m_ftFace->size->metrics.ascender >> 6);
|
||||
m_lineHeight = (int)(m_ftFace->size->metrics.height >> 6);
|
||||
@@ -57,9 +62,6 @@ void CTextBar::GetTextExtent(const char* c_szText, SIZE* p_size)
|
||||
return;
|
||||
}
|
||||
|
||||
// Re-apply face state (shared FT_Face may have been changed by another user)
|
||||
__ApplyFaceState();
|
||||
|
||||
std::wstring wText = Utf8ToWide(c_szText);
|
||||
|
||||
int totalAdvance = 0;
|
||||
@@ -70,7 +72,7 @@ void CTextBar::GetTextExtent(const char* c_szText, SIZE* p_size)
|
||||
glyphIndex = FT_Get_Char_Index(m_ftFace, L' ');
|
||||
|
||||
if (FT_Load_Glyph(m_ftFace, glyphIndex, FT_LOAD_DEFAULT) == 0)
|
||||
totalAdvance += (int)(m_ftFace->glyph->advance.x >> 6);
|
||||
totalAdvance += (int)ceilf((float)(m_ftFace->glyph->advance.x) / 64.0f);
|
||||
}
|
||||
|
||||
p_size->cx = totalAdvance;
|
||||
@@ -82,9 +84,6 @@ void CTextBar::TextOut(int ix, int iy, const char * c_szText)
|
||||
if (!c_szText || !*c_szText || !m_ftFace)
|
||||
return;
|
||||
|
||||
// Re-apply face state (shared FT_Face may have been changed by another user)
|
||||
__ApplyFaceState();
|
||||
|
||||
DWORD* pdwBuf = (DWORD*)m_dib.GetPointer();
|
||||
if (!pdwBuf)
|
||||
return;
|
||||
@@ -111,9 +110,9 @@ void CTextBar::TextOut(int ix, int iy, const char * c_szText)
|
||||
|
||||
FT_GlyphSlot slot = m_ftFace->glyph;
|
||||
|
||||
// Apply synthetic bold by embolden
|
||||
// Apply synthetic bold (32 = 0.5px embolden; 64 = 1px was too aggressive)
|
||||
if (m_isBold && slot->bitmap.buffer)
|
||||
FT_Bitmap_Embolden(CFontManager::Instance().GetLibrary(), &slot->bitmap, 64, 0);
|
||||
FT_Bitmap_Embolden(CFontManager::Instance().GetLibrary(), &slot->bitmap, 32, 0);
|
||||
|
||||
FT_Bitmap& bitmap = slot->bitmap;
|
||||
|
||||
@@ -138,8 +137,7 @@ void CTextBar::TextOut(int ix, int iy, const char * c_szText)
|
||||
unsigned char alpha = srcRow[col];
|
||||
if (alpha)
|
||||
{
|
||||
// D3DFMT_A8R8G8B8 = ARGB in DWORD
|
||||
// colorRGB is stored as 0x00BBGGRR, convert to ARGB
|
||||
alpha = s_textBarGammaLUT.table[alpha];
|
||||
DWORD r = (colorRGB >> 0) & 0xFF;
|
||||
DWORD g = (colorRGB >> 8) & 0xFF;
|
||||
DWORD b = (colorRGB >> 16) & 0xFF;
|
||||
@@ -148,7 +146,7 @@ void CTextBar::TextOut(int ix, int iy, const char * c_szText)
|
||||
}
|
||||
}
|
||||
|
||||
penX += (int)(slot->advance.x >> 6);
|
||||
penX += (int)ceilf((float)(slot->advance.x) / 64.0f);
|
||||
}
|
||||
|
||||
Invalidate();
|
||||
@@ -171,5 +169,9 @@ CTextBar::CTextBar(int fontSize, bool isBold)
|
||||
|
||||
CTextBar::~CTextBar()
|
||||
{
|
||||
// FT_Face is owned by CFontManager, do NOT free it here
|
||||
if (m_ftFace)
|
||||
{
|
||||
FT_Done_Face(m_ftFace);
|
||||
m_ftFace = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,6 @@ class CTextBar : public CDibBar
|
||||
|
||||
protected:
|
||||
void __SetFont(int fontSize, bool isBold);
|
||||
void __ApplyFaceState();
|
||||
|
||||
void OnCreate();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user