FreeType: Kerning API and font adjustments
This commit is contained in:
@@ -146,11 +146,11 @@ void CBlockTexture::InvalidateRect(const RECT & c_rsrcRect)
|
||||
DWORD * pdwDst = (DWORD *)lockedRect.pBits;
|
||||
DWORD dwDstWidth = lockedRect.Pitch>>2;
|
||||
DWORD dwSrcWidth = m_pDIB->GetWidth();
|
||||
for (int i = 0; i < iclipHeight; ++i)
|
||||
for (int y = 0; y < iclipHeight; ++y)
|
||||
{
|
||||
for (int i = 0; i < iclipWidth; ++i)
|
||||
for (int x = 0; x < iclipWidth; ++x)
|
||||
{
|
||||
pdwDst[i] = pdwSrc[i];
|
||||
pdwDst[x] = pdwSrc[x];
|
||||
}
|
||||
pdwDst += dwDstWidth;
|
||||
pdwSrc += dwSrcWidth;
|
||||
|
||||
@@ -110,10 +110,17 @@ std::string CFontManager::ResolveFontPath(const char* faceName)
|
||||
|
||||
// 3. Fall back to C:\Windows\Fonts
|
||||
#ifdef _WIN32
|
||||
char winDir[MAX_PATH];
|
||||
if (GetWindowsDirectoryA(winDir, MAX_PATH))
|
||||
static std::string s_fontsDir;
|
||||
if (s_fontsDir.empty())
|
||||
{
|
||||
std::string systemPath = std::string(winDir) + "\\Fonts\\" + fileName;
|
||||
char winDir[MAX_PATH];
|
||||
if (GetWindowsDirectoryA(winDir, MAX_PATH))
|
||||
s_fontsDir = std::string(winDir) + "\\Fonts\\";
|
||||
}
|
||||
|
||||
if (!s_fontsDir.empty())
|
||||
{
|
||||
std::string systemPath = s_fontsDir + fileName;
|
||||
if (FileExists(systemPath))
|
||||
return systemPath;
|
||||
}
|
||||
@@ -127,9 +134,9 @@ std::string CFontManager::ResolveFontPath(const char* faceName)
|
||||
if (FileExists(localPath))
|
||||
return localPath;
|
||||
|
||||
if (GetWindowsDirectoryA(winDir, MAX_PATH))
|
||||
if (!s_fontsDir.empty())
|
||||
{
|
||||
std::string systemPath = std::string(winDir) + "\\Fonts\\" + ttcName;
|
||||
std::string systemPath = s_fontsDir + ttcName;
|
||||
if (FileExists(systemPath))
|
||||
return systemPath;
|
||||
}
|
||||
|
||||
@@ -4,23 +4,19 @@
|
||||
#include "EterBase/Stl.h"
|
||||
|
||||
#include "Util.h"
|
||||
#include <utf8.h>
|
||||
|
||||
#include <ft2build.h>
|
||||
#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.
|
||||
// Gamma LUT to sharpen grayscale anti-aliasing edges.
|
||||
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);
|
||||
table[i] = (unsigned char)(pow(i / 255.0, 0.85) * 255.0 + 0.5);
|
||||
}
|
||||
} s_alphaGammaLUT;
|
||||
|
||||
@@ -45,11 +41,11 @@ void CGraphicFontTexture::Initialize()
|
||||
m_bItalic = false;
|
||||
m_ascender = 0;
|
||||
m_lineHeight = 0;
|
||||
m_hasKerning = false;
|
||||
m_x = 0;
|
||||
m_y = 0;
|
||||
m_step = 0;
|
||||
m_fontSize = 0;
|
||||
memset(m_fontName, 0, sizeof(m_fontName));
|
||||
}
|
||||
|
||||
bool CGraphicFontTexture::IsEmpty() const
|
||||
@@ -122,10 +118,6 @@ bool CGraphicFontTexture::Create(const char* c_szFontName, int fontSize, bool bI
|
||||
{
|
||||
Destroy();
|
||||
|
||||
// UTF-8 -> UTF-16 for font name storage
|
||||
std::wstring wFontName = Utf8ToWide(c_szFontName ? c_szFontName : "");
|
||||
wcsncpy_s(m_fontName, wFontName.c_str(), _TRUNCATE);
|
||||
|
||||
m_fontSize = fontSize;
|
||||
m_bItalic = bItalic;
|
||||
|
||||
@@ -147,9 +139,6 @@ 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));
|
||||
|
||||
// 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)
|
||||
@@ -158,12 +147,14 @@ bool CGraphicFontTexture::Create(const char* c_szFontName, int fontSize, bool bI
|
||||
return false;
|
||||
}
|
||||
|
||||
// Set pixel size
|
||||
int pixelSize = (fontSize < 0) ? -fontSize : fontSize;
|
||||
if (pixelSize == 0)
|
||||
pixelSize = 12;
|
||||
|
||||
FT_Set_Pixel_Sizes(m_ftFace, 0, pixelSize);
|
||||
|
||||
m_hasKerning = FT_HAS_KERNING(m_ftFace) != 0;
|
||||
|
||||
// Apply italic via shear matrix if needed
|
||||
if (bItalic)
|
||||
{
|
||||
@@ -234,6 +225,24 @@ bool CGraphicFontTexture::UpdateTexture()
|
||||
return true;
|
||||
}
|
||||
|
||||
float CGraphicFontTexture::GetKerning(wchar_t prev, wchar_t cur)
|
||||
{
|
||||
if (!m_hasKerning || !m_ftFace || prev == 0)
|
||||
return 0.0f;
|
||||
|
||||
FT_UInt prevIndex = FT_Get_Char_Index(m_ftFace, prev);
|
||||
FT_UInt curIndex = FT_Get_Char_Index(m_ftFace, cur);
|
||||
|
||||
if (prevIndex == 0 || curIndex == 0)
|
||||
return 0.0f;
|
||||
|
||||
FT_Vector delta;
|
||||
if (FT_Get_Kerning(m_ftFace, prevIndex, curIndex, FT_KERNING_DEFAULT, &delta) != 0)
|
||||
return 0.0f;
|
||||
|
||||
return (float)(delta.x) / 64.0f;
|
||||
}
|
||||
|
||||
CGraphicFontTexture::TCharacterInfomation* CGraphicFontTexture::GetCharacterInfomation(wchar_t keyValue)
|
||||
{
|
||||
TCharacterKey code = keyValue;
|
||||
@@ -268,7 +277,10 @@ CGraphicFontTexture::TCharacterInfomation* CGraphicFontTexture::UpdateCharacterI
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (FT_Load_Glyph(m_ftFace, glyphIndex, FT_LOAD_RENDER | FT_LOAD_TARGET_NORMAL) != 0)
|
||||
if (FT_Load_Glyph(m_ftFace, glyphIndex, FT_LOAD_DEFAULT) != 0)
|
||||
return NULL;
|
||||
|
||||
if (FT_Render_Glyph(m_ftFace->glyph, FT_RENDER_MODE_NORMAL) != 0)
|
||||
return NULL;
|
||||
|
||||
FT_GlyphSlot slot = m_ftFace->glyph;
|
||||
@@ -335,7 +347,7 @@ CGraphicFontTexture::TCharacterInfomation* CGraphicFontTexture::UpdateCharacterI
|
||||
}
|
||||
}
|
||||
|
||||
// Copy FreeType bitmap into atlas buffer at baseline-normalized position
|
||||
// Copy grayscale FreeType bitmap into atlas buffer with gamma correction
|
||||
for (int row = 0; row < glyphBitmapHeight; ++row)
|
||||
{
|
||||
int atlasY = m_y + yOffset + row;
|
||||
|
||||
@@ -6,7 +6,6 @@
|
||||
#include <ft2build.h>
|
||||
#include FT_FREETYPE_H
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
|
||||
@@ -48,6 +47,8 @@ class CGraphicFontTexture : public CGraphicTexture
|
||||
TCharacterInfomation* GetCharacterInfomation(wchar_t keyValue);
|
||||
TCharacterInfomation* UpdateCharacterInfomation(TCharacterKey keyValue);
|
||||
|
||||
float GetKerning(wchar_t prev, wchar_t cur);
|
||||
|
||||
bool IsEmpty() const;
|
||||
|
||||
protected:
|
||||
@@ -76,12 +77,11 @@ class CGraphicFontTexture : public CGraphicTexture
|
||||
int m_step;
|
||||
bool m_isDirty;
|
||||
|
||||
TCHAR m_fontName[LF_FACESIZE];
|
||||
std::string m_fontNameUTF8; // stored for device reset re-creation
|
||||
LONG m_fontSize;
|
||||
bool m_bItalic;
|
||||
|
||||
// FreeType metrics cached per-font
|
||||
int m_ascender;
|
||||
int m_lineHeight;
|
||||
bool m_hasKerning;
|
||||
};
|
||||
|
||||
@@ -38,18 +38,23 @@ int CGraphicTextInstance::Hyperlink_GetText(char* buf, int len)
|
||||
return (written > 0) ? written : 0;
|
||||
}
|
||||
|
||||
int CGraphicTextInstance::__DrawCharacter(CGraphicFontTexture * pFontTexture, wchar_t text, DWORD dwColor)
|
||||
int CGraphicTextInstance::__DrawCharacter(CGraphicFontTexture * pFontTexture, wchar_t text, DWORD dwColor, wchar_t prevChar)
|
||||
{
|
||||
CGraphicFontTexture::TCharacterInfomation* pInsCharInfo = pFontTexture->GetCharacterInfomation(text);
|
||||
|
||||
if (pInsCharInfo)
|
||||
{
|
||||
// Round kerning to nearest pixel to keep glyphs on the pixel grid.
|
||||
// Fractional offsets cause bilinear interpolation blur in D3D9.
|
||||
float kern = floorf(pFontTexture->GetKerning(prevChar, text) + 0.5f);
|
||||
|
||||
m_dwColorInfoVector.push_back(dwColor);
|
||||
m_pCharInfoVector.push_back(pInsCharInfo);
|
||||
m_kernVector.push_back(kern);
|
||||
|
||||
m_textWidth += pInsCharInfo->advance;
|
||||
m_textWidth += (int)(pInsCharInfo->advance + kern);
|
||||
m_textHeight = std::max((WORD)pInsCharInfo->height, m_textHeight);
|
||||
return pInsCharInfo->advance;
|
||||
return (int)(pInsCharInfo->advance + kern);
|
||||
}
|
||||
|
||||
return 0;
|
||||
@@ -99,6 +104,7 @@ void CGraphicTextInstance::Update()
|
||||
auto ResetState = [&, spaceHeight]()
|
||||
{
|
||||
m_pCharInfoVector.clear();
|
||||
m_kernVector.clear();
|
||||
m_dwColorInfoVector.clear();
|
||||
m_hyperlinkVector.clear();
|
||||
m_textWidth = 0;
|
||||
@@ -121,6 +127,7 @@ void CGraphicTextInstance::Update()
|
||||
}
|
||||
|
||||
m_pCharInfoVector.clear();
|
||||
m_kernVector.clear();
|
||||
m_dwColorInfoVector.clear();
|
||||
m_hyperlinkVector.clear();
|
||||
|
||||
@@ -154,8 +161,12 @@ void CGraphicTextInstance::Update()
|
||||
// Secret mode: draw '*' instead of actual characters
|
||||
if (m_isSecret)
|
||||
{
|
||||
wchar_t prevCh = 0;
|
||||
for (int i = 0; i < wTextLen; ++i)
|
||||
__DrawCharacter(pFontTexture, L'*', dwColor);
|
||||
{
|
||||
__DrawCharacter(pFontTexture, L'*', dwColor, prevCh);
|
||||
prevCh = L'*';
|
||||
}
|
||||
|
||||
pFontTexture->UpdateTexture();
|
||||
m_isUpdate = true;
|
||||
@@ -183,8 +194,12 @@ void CGraphicTextInstance::Update()
|
||||
wMsg.data(), (int)wMsg.size(),
|
||||
m_computedRTL);
|
||||
|
||||
wchar_t prevCh = 0;
|
||||
for (size_t i = 0; i < visual.size(); ++i)
|
||||
__DrawCharacter(pFontTexture, visual[i], dwColor);
|
||||
{
|
||||
__DrawCharacter(pFontTexture, visual[i], dwColor, prevCh);
|
||||
prevCh = visual[i];
|
||||
}
|
||||
|
||||
pFontTexture->UpdateTexture();
|
||||
m_isUpdate = true;
|
||||
@@ -245,8 +260,7 @@ void CGraphicTextInstance::Update()
|
||||
int hyperlinkStep = 0; // 0=normal, 1=collecting metadata, 2=visible hyperlink
|
||||
std::wstring hyperlinkMetadata;
|
||||
|
||||
// Use thread-local buffer to avoid per-call allocation
|
||||
thread_local static std::vector<wchar_t> s_currentSegment;
|
||||
static std::vector<wchar_t> s_currentSegment;
|
||||
s_currentSegment.clear();
|
||||
|
||||
SHyperlink currentHyperlink;
|
||||
@@ -267,10 +281,12 @@ void CGraphicTextInstance::Update()
|
||||
std::vector<wchar_t> visual = BuildVisualBidiText_Tagless(
|
||||
s_currentSegment.data(), (int)s_currentSegment.size(), forceRTLForBidi);
|
||||
|
||||
wchar_t prevCh = m_pCharInfoVector.empty() ? 0 : 0; // no prev across segments
|
||||
for (size_t j = 0; j < visual.size(); ++j)
|
||||
{
|
||||
int w = __DrawCharacter(pFontTexture, visual[j], segColor);
|
||||
int w = __DrawCharacter(pFontTexture, visual[j], segColor, prevCh);
|
||||
totalWidth += w;
|
||||
prevCh = visual[j];
|
||||
}
|
||||
|
||||
s_currentSegment.clear();
|
||||
@@ -285,13 +301,15 @@ void CGraphicTextInstance::Update()
|
||||
{
|
||||
outWidth = 0;
|
||||
|
||||
// Use thread-local buffers to avoid allocation
|
||||
thread_local static std::vector<CGraphicFontTexture::TCharacterInfomation*> s_newCharInfos;
|
||||
thread_local static std::vector<DWORD> s_newColors;
|
||||
static std::vector<CGraphicFontTexture::TCharacterInfomation*> s_newCharInfos;
|
||||
static std::vector<DWORD> s_newColors;
|
||||
static std::vector<float> s_newKerns;
|
||||
s_newCharInfos.clear();
|
||||
s_newColors.clear();
|
||||
s_newKerns.clear();
|
||||
s_newCharInfos.reserve(chars.size());
|
||||
s_newColors.reserve(chars.size());
|
||||
s_newKerns.reserve(chars.size());
|
||||
|
||||
for (size_t k = 0; k < chars.size(); ++k)
|
||||
{
|
||||
@@ -301,16 +319,16 @@ void CGraphicTextInstance::Update()
|
||||
|
||||
s_newCharInfos.push_back(pInfo);
|
||||
s_newColors.push_back(color);
|
||||
s_newKerns.push_back(0.0f);
|
||||
|
||||
outWidth += pInfo->advance;
|
||||
m_textHeight = std::max((WORD)pInfo->height, m_textHeight);
|
||||
}
|
||||
|
||||
// Insert at the beginning of the draw list.
|
||||
m_pCharInfoVector.insert(m_pCharInfoVector.begin(), s_newCharInfos.begin(), s_newCharInfos.end());
|
||||
m_dwColorInfoVector.insert(m_dwColorInfoVector.begin(), s_newColors.begin(), s_newColors.end());
|
||||
m_kernVector.insert(m_kernVector.begin(), s_newKerns.begin(), s_newKerns.end());
|
||||
|
||||
// Shift any already-recorded hyperlinks to the right.
|
||||
for (auto& link : m_hyperlinkVector)
|
||||
{
|
||||
link.sx += outWidth;
|
||||
@@ -366,7 +384,7 @@ void CGraphicTextInstance::Update()
|
||||
if (!s_currentSegment.empty())
|
||||
{
|
||||
// OPTIMIZED: Use thread-local buffer for visible rendering
|
||||
thread_local static std::vector<wchar_t> s_visibleToRender;
|
||||
static std::vector<wchar_t> s_visibleToRender;
|
||||
s_visibleToRender.clear();
|
||||
|
||||
// Find bracket positions: [ ... ]
|
||||
@@ -385,7 +403,7 @@ void CGraphicTextInstance::Update()
|
||||
s_visibleToRender.push_back(L'[');
|
||||
|
||||
// Extract inside content and apply BiDi
|
||||
thread_local static std::vector<wchar_t> s_content;
|
||||
static std::vector<wchar_t> s_content;
|
||||
s_content.assign(
|
||||
s_currentSegment.begin() + openBracket + 1,
|
||||
s_currentSegment.begin() + closeBracket);
|
||||
@@ -430,10 +448,12 @@ void CGraphicTextInstance::Update()
|
||||
{
|
||||
// LTR or non-chat: keep original "append" behavior
|
||||
currentHyperlink.sx = currentHyperlink.ex;
|
||||
wchar_t prevCh = 0;
|
||||
for (size_t j = 0; j < s_visibleToRender.size(); ++j)
|
||||
{
|
||||
int w = __DrawCharacter(pFontTexture, s_visibleToRender[j], currentColor);
|
||||
int w = __DrawCharacter(pFontTexture, s_visibleToRender[j], currentColor, prevCh);
|
||||
currentHyperlink.ex += w;
|
||||
prevCh = s_visibleToRender[j];
|
||||
}
|
||||
m_hyperlinkVector.push_back(currentHyperlink);
|
||||
}
|
||||
@@ -471,8 +491,14 @@ void CGraphicTextInstance::Update()
|
||||
|
||||
// Simple LTR rendering for plain text (no tags, no RTL)
|
||||
// Just draw characters in logical order
|
||||
for (int i = 0; i < wTextLen; ++i)
|
||||
__DrawCharacter(pFontTexture, wTextBuf[i], dwColor);
|
||||
{
|
||||
wchar_t prevCh = 0;
|
||||
for (int i = 0; i < wTextLen; ++i)
|
||||
{
|
||||
__DrawCharacter(pFontTexture, wTextBuf[i], dwColor, prevCh);
|
||||
prevCh = wTextBuf[i];
|
||||
}
|
||||
}
|
||||
|
||||
pFontTexture->UpdateTexture();
|
||||
m_isUpdate = true;
|
||||
@@ -580,13 +606,18 @@ void CGraphicTextInstance::Render(RECT * pClipRect)
|
||||
fCurY=fStanY;
|
||||
fFontMaxHeight=0.0f;
|
||||
|
||||
int charIdx = 0;
|
||||
CGraphicFontTexture::TPCharacterInfomationVector::iterator i;
|
||||
for (i=m_pCharInfoVector.begin(); i!=m_pCharInfoVector.end(); ++i)
|
||||
for (i=m_pCharInfoVector.begin(); i!=m_pCharInfoVector.end(); ++i, ++charIdx)
|
||||
{
|
||||
pCurCharInfo = *i;
|
||||
|
||||
float fKern = (charIdx < (int)m_kernVector.size()) ? m_kernVector[charIdx] : 0.0f;
|
||||
fCurX += fKern;
|
||||
|
||||
fFontWidth=float(pCurCharInfo->width);
|
||||
fFontHeight=float(pCurCharInfo->height);
|
||||
fFontMaxHeight=(std::max)(fFontMaxHeight, fFontHeight);
|
||||
fFontAdvance=float(pCurCharInfo->advance);
|
||||
|
||||
if ((fCurX+fFontWidth)-m_v3Position.x > m_fLimitWidth) [[unlikely]] {
|
||||
@@ -685,13 +716,16 @@ void CGraphicTextInstance::Render(RECT * pClipRect)
|
||||
fCurY=fStanY;
|
||||
fFontMaxHeight=0.0f;
|
||||
|
||||
for (int i = 0; i < m_pCharInfoVector.size(); ++i)
|
||||
for (int i = 0; i < (int)m_pCharInfoVector.size(); ++i)
|
||||
{
|
||||
pCurCharInfo = m_pCharInfoVector[i];
|
||||
|
||||
float fKern = (i < (int)m_kernVector.size()) ? m_kernVector[i] : 0.0f;
|
||||
fCurX += fKern;
|
||||
|
||||
fFontWidth=float(pCurCharInfo->width);
|
||||
fFontHeight=float(pCurCharInfo->height);
|
||||
fFontMaxHeight=(std::max)(fFontHeight, (float)pCurCharInfo->height);
|
||||
fFontMaxHeight=(std::max)(fFontMaxHeight, fFontHeight);
|
||||
fFontAdvance=float(pCurCharInfo->advance);
|
||||
|
||||
if ((fCurX + fFontWidth) - m_v3Position.x > m_fLimitWidth) [[unlikely]] {
|
||||
@@ -1267,6 +1301,7 @@ void CGraphicTextInstance::Destroy()
|
||||
{
|
||||
m_stText="";
|
||||
m_pCharInfoVector.clear();
|
||||
m_kernVector.clear();
|
||||
m_dwColorInfoVector.clear();
|
||||
m_hyperlinkVector.clear();
|
||||
m_logicalToVisualPos.clear();
|
||||
|
||||
@@ -90,7 +90,7 @@ class CGraphicTextInstance
|
||||
|
||||
protected:
|
||||
void __Initialize();
|
||||
int __DrawCharacter(CGraphicFontTexture * pFontTexture, wchar_t text, DWORD dwColor);
|
||||
int __DrawCharacter(CGraphicFontTexture * pFontTexture, wchar_t text, DWORD dwColor, wchar_t prevChar = 0);
|
||||
void __GetTextPos(DWORD index, float* x, float* y);
|
||||
|
||||
protected:
|
||||
@@ -130,7 +130,6 @@ class CGraphicTextInstance
|
||||
|
||||
private:
|
||||
bool m_isUpdate;
|
||||
bool m_isUpdateFontTexture;
|
||||
bool m_computedRTL; // Result of BiDi analysis (used when m_direction == Auto)
|
||||
bool m_isChatMessage; // True if this text was set via SetChatValue (has separated name/message)
|
||||
std::string m_chatName; // Chat sender name (only used when m_isChatMessage is true)
|
||||
@@ -138,6 +137,7 @@ class CGraphicTextInstance
|
||||
|
||||
CGraphicText::TRef m_roText;
|
||||
CGraphicFontTexture::TPCharacterInfomationVector m_pCharInfoVector;
|
||||
std::vector<float> m_kernVector;
|
||||
std::vector<DWORD> m_dwColorInfoVector;
|
||||
std::vector<SHyperlink> m_hyperlinkVector;
|
||||
std::vector<int> m_logicalToVisualPos; // Maps logical cursor pos (UTF-16 with tags) to visual pos (rendered chars)
|
||||
|
||||
@@ -10,13 +10,13 @@
|
||||
|
||||
#include <cmath>
|
||||
|
||||
// Same gamma LUT as GrpFontTexture for consistent text sharpness
|
||||
// Gamma LUT matching 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);
|
||||
table[i] = (unsigned char)(pow(i / 255.0, 0.85) * 255.0 + 0.5);
|
||||
}
|
||||
} s_textBarGammaLUT;
|
||||
|
||||
@@ -73,15 +73,27 @@ void CTextBar::GetTextExtent(const char* c_szText, SIZE* p_size)
|
||||
|
||||
std::wstring wText = Utf8ToWide(c_szText);
|
||||
|
||||
bool hasKerning = FT_HAS_KERNING(m_ftFace) != 0;
|
||||
FT_UInt prevIndex = 0;
|
||||
int totalAdvance = 0;
|
||||
|
||||
for (size_t i = 0; i < wText.size(); ++i)
|
||||
{
|
||||
FT_UInt glyphIndex = FT_Get_Char_Index(m_ftFace, wText[i]);
|
||||
if (glyphIndex == 0)
|
||||
glyphIndex = FT_Get_Char_Index(m_ftFace, L' ');
|
||||
|
||||
if (hasKerning && prevIndex && glyphIndex)
|
||||
{
|
||||
FT_Vector delta;
|
||||
if (FT_Get_Kerning(m_ftFace, prevIndex, glyphIndex, FT_KERNING_DEFAULT, &delta) == 0)
|
||||
totalAdvance += (int)(delta.x / 64);
|
||||
}
|
||||
|
||||
if (FT_Load_Glyph(m_ftFace, glyphIndex, FT_LOAD_DEFAULT) == 0)
|
||||
totalAdvance += (int)ceilf((float)(m_ftFace->glyph->advance.x) / 64.0f);
|
||||
|
||||
prevIndex = glyphIndex;
|
||||
}
|
||||
|
||||
p_size->cx = totalAdvance;
|
||||
@@ -107,14 +119,26 @@ void CTextBar::TextOut(int ix, int iy, const char * c_szText)
|
||||
|
||||
DWORD colorRGB = m_textColor; // 0x00BBGGRR in memory
|
||||
|
||||
bool hasKerning = FT_HAS_KERNING(m_ftFace) != 0;
|
||||
FT_UInt prevIndex = 0;
|
||||
|
||||
for (size_t i = 0; i < wText.size(); ++i)
|
||||
{
|
||||
FT_UInt glyphIndex = FT_Get_Char_Index(m_ftFace, wText[i]);
|
||||
if (glyphIndex == 0)
|
||||
glyphIndex = FT_Get_Char_Index(m_ftFace, L' ');
|
||||
|
||||
FT_Int32 loadFlags = FT_LOAD_RENDER | FT_LOAD_TARGET_NORMAL;
|
||||
if (FT_Load_Glyph(m_ftFace, glyphIndex, loadFlags) != 0)
|
||||
if (hasKerning && prevIndex && glyphIndex)
|
||||
{
|
||||
FT_Vector delta;
|
||||
if (FT_Get_Kerning(m_ftFace, prevIndex, glyphIndex, FT_KERNING_DEFAULT, &delta) == 0)
|
||||
penX += (int)(delta.x / 64);
|
||||
}
|
||||
|
||||
if (FT_Load_Glyph(m_ftFace, glyphIndex, FT_LOAD_DEFAULT) != 0)
|
||||
continue;
|
||||
|
||||
if (FT_Render_Glyph(m_ftFace->glyph, FT_RENDER_MODE_NORMAL) != 0)
|
||||
continue;
|
||||
|
||||
FT_GlyphSlot slot = m_ftFace->glyph;
|
||||
@@ -151,6 +175,7 @@ void CTextBar::TextOut(int ix, int iy, const char * c_szText)
|
||||
}
|
||||
|
||||
penX += (int)ceilf((float)(slot->advance.x) / 64.0f);
|
||||
prevIndex = glyphIndex;
|
||||
}
|
||||
|
||||
Invalidate();
|
||||
|
||||
Reference in New Issue
Block a user