Implementation FreeType2-13.3
This commit is contained in:
@@ -67,6 +67,7 @@ add_compile_definitions("$<$<CONFIG:Release>:_DISTRIBUTE>")
|
||||
include_directories("src")
|
||||
include_directories("extern/include")
|
||||
include_directories("vendor/libsodium/src/libsodium/include")
|
||||
include_directories("vendor/freetype-2.13.3/include")
|
||||
|
||||
# Add subdirectories for libraries and executables
|
||||
add_subdirectory(vendor)
|
||||
|
||||
@@ -5,6 +5,7 @@ add_library(EterLib STATIC ${FILE_SOURCES})
|
||||
target_link_libraries(EterLib
|
||||
sodium
|
||||
mio
|
||||
freetype
|
||||
)
|
||||
|
||||
GroupSourcesByFolder(EterLib)
|
||||
|
||||
@@ -138,9 +138,9 @@ void CDibBar::__BuildTextureBlockList(DWORD dwWidth, DWORD dwHeight, DWORD dwMax
|
||||
}
|
||||
}
|
||||
|
||||
bool CDibBar::Create(HDC hdc, DWORD dwWidth, DWORD dwHeight)
|
||||
bool CDibBar::Create(DWORD dwWidth, DWORD dwHeight)
|
||||
{
|
||||
if (!m_dib.Create(hdc, dwWidth, dwHeight))
|
||||
if (!m_dib.Create(dwWidth, dwHeight))
|
||||
{
|
||||
Tracef(" Failed to create CDibBar\n");
|
||||
return false;
|
||||
|
||||
@@ -10,7 +10,7 @@ class CDibBar
|
||||
CDibBar();
|
||||
virtual ~CDibBar();
|
||||
|
||||
bool Create(HDC hdc, DWORD dwWidth, DWORD dwHeight);
|
||||
bool Create(DWORD dwWidth, DWORD dwHeight);
|
||||
void Invalidate();
|
||||
void SetClipRect(const RECT & c_rRect);
|
||||
void ClearBar();
|
||||
|
||||
173
src/EterLib/FontManager.cpp
Normal file
173
src/EterLib/FontManager.cpp
Normal file
@@ -0,0 +1,173 @@
|
||||
#include "StdAfx.h"
|
||||
#include "FontManager.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cctype>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#include <shlobj.h>
|
||||
#endif
|
||||
|
||||
static std::string ToLower(const std::string& s)
|
||||
{
|
||||
std::string result = s;
|
||||
std::transform(result.begin(), result.end(), result.begin(),
|
||||
[](unsigned char c) { return std::tolower(c); });
|
||||
return result;
|
||||
}
|
||||
|
||||
static bool FileExists(const std::string& path)
|
||||
{
|
||||
struct stat st;
|
||||
return stat(path.c_str(), &st) == 0;
|
||||
}
|
||||
|
||||
CFontManager::CFontManager()
|
||||
: m_ftLibrary(nullptr)
|
||||
, m_bInitialized(false)
|
||||
{
|
||||
}
|
||||
|
||||
CFontManager::~CFontManager()
|
||||
{
|
||||
Destroy();
|
||||
}
|
||||
|
||||
CFontManager& CFontManager::Instance()
|
||||
{
|
||||
static CFontManager instance;
|
||||
return instance;
|
||||
}
|
||||
|
||||
bool CFontManager::Initialize()
|
||||
{
|
||||
if (m_bInitialized)
|
||||
return true;
|
||||
|
||||
if (FT_Init_FreeType(&m_ftLibrary) != 0)
|
||||
{
|
||||
TraceError("CFontManager::Initialize - FT_Init_FreeType failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
m_bInitialized = true;
|
||||
|
||||
// Register default font name -> file mappings
|
||||
// Korean system fonts
|
||||
m_fontPathMap["gulim"] = "gulim.ttc";
|
||||
m_fontPathMap["\xea\xb5\xb4\xeb\xa6\xbc"] = "gulim.ttc"; // 굴림 (UTF-8)
|
||||
m_fontPathMap["\xea\xb5\xb4\xeb\xa6\xbc\xec\xb2\xb4"] = "gulimche.ttc"; // 굴림체
|
||||
|
||||
// Common Latin fonts
|
||||
m_fontPathMap["arial"] = "arial.ttf";
|
||||
m_fontPathMap["tahoma"] = "tahoma.ttf";
|
||||
m_fontPathMap["verdana"] = "verdana.ttf";
|
||||
m_fontPathMap["times new roman"] = "times.ttf";
|
||||
m_fontPathMap["courier new"] = "cour.ttf";
|
||||
m_fontPathMap["segoe ui"] = "segoeui.ttf";
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void CFontManager::Destroy()
|
||||
{
|
||||
for (auto& pair : m_faceCache)
|
||||
{
|
||||
if (pair.second)
|
||||
FT_Done_Face(pair.second);
|
||||
}
|
||||
m_faceCache.clear();
|
||||
m_fontPathMap.clear();
|
||||
|
||||
if (m_ftLibrary)
|
||||
{
|
||||
FT_Done_FreeType(m_ftLibrary);
|
||||
m_ftLibrary = nullptr;
|
||||
}
|
||||
|
||||
m_bInitialized = false;
|
||||
}
|
||||
|
||||
std::string CFontManager::ResolveFontPath(const char* faceName)
|
||||
{
|
||||
if (!faceName || !faceName[0])
|
||||
return "";
|
||||
|
||||
std::string lowerName = ToLower(faceName);
|
||||
|
||||
// 1. Check explicit mappings
|
||||
auto it = m_fontPathMap.find(lowerName);
|
||||
std::string fileName;
|
||||
if (it != m_fontPathMap.end())
|
||||
fileName = it->second;
|
||||
else
|
||||
fileName = lowerName + ".ttf";
|
||||
|
||||
// 2. Check local fonts/ directory first
|
||||
std::string localPath = "fonts/" + fileName;
|
||||
if (FileExists(localPath))
|
||||
return localPath;
|
||||
|
||||
// 3. Fall back to C:\Windows\Fonts
|
||||
#ifdef _WIN32
|
||||
char winDir[MAX_PATH];
|
||||
if (GetWindowsDirectoryA(winDir, MAX_PATH))
|
||||
{
|
||||
std::string systemPath = std::string(winDir) + "\\Fonts\\" + fileName;
|
||||
if (FileExists(systemPath))
|
||||
return systemPath;
|
||||
}
|
||||
|
||||
// 4. Try .ttc variant if .ttf wasn't found
|
||||
if (fileName.size() > 4 && fileName.substr(fileName.size() - 4) == ".ttf")
|
||||
{
|
||||
std::string ttcName = fileName.substr(0, fileName.size() - 4) + ".ttc";
|
||||
|
||||
localPath = "fonts/" + ttcName;
|
||||
if (FileExists(localPath))
|
||||
return localPath;
|
||||
|
||||
if (GetWindowsDirectoryA(winDir, MAX_PATH))
|
||||
{
|
||||
std::string systemPath = std::string(winDir) + "\\Fonts\\" + ttcName;
|
||||
if (FileExists(systemPath))
|
||||
return systemPath;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
TraceError("CFontManager::ResolveFontPath - Could not find font: %s", faceName);
|
||||
return "";
|
||||
}
|
||||
|
||||
FT_Face CFontManager::GetFace(const char* faceName)
|
||||
{
|
||||
if (!m_bInitialized)
|
||||
{
|
||||
if (!Initialize())
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::string path = ResolveFontPath(faceName);
|
||||
if (path.empty())
|
||||
return nullptr;
|
||||
|
||||
// Check cache
|
||||
auto it = m_faceCache.find(path);
|
||||
if (it != m_faceCache.end())
|
||||
return it->second;
|
||||
|
||||
// Load new face
|
||||
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);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
m_faceCache[path] = face;
|
||||
return face;
|
||||
}
|
||||
39
src/EterLib/FontManager.h
Normal file
39
src/EterLib/FontManager.h
Normal file
@@ -0,0 +1,39 @@
|
||||
#pragma once
|
||||
|
||||
#include <ft2build.h>
|
||||
#include FT_FREETYPE_H
|
||||
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
class CFontManager
|
||||
{
|
||||
public:
|
||||
static CFontManager& Instance();
|
||||
|
||||
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);
|
||||
|
||||
FT_Library GetLibrary() const { return m_ftLibrary; }
|
||||
|
||||
private:
|
||||
CFontManager();
|
||||
~CFontManager();
|
||||
CFontManager(const CFontManager&) = delete;
|
||||
CFontManager& operator=(const CFontManager&) = delete;
|
||||
|
||||
std::string ResolveFontPath(const char* faceName);
|
||||
|
||||
FT_Library m_ftLibrary;
|
||||
bool m_bInitialized;
|
||||
|
||||
// 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;
|
||||
};
|
||||
@@ -1,6 +1,5 @@
|
||||
#include "StdAfx.h"
|
||||
#include "GrpDIB.h"
|
||||
#include <utf8.h>
|
||||
|
||||
CGraphicDib::CGraphicDib()
|
||||
{
|
||||
@@ -14,98 +13,30 @@ CGraphicDib::~CGraphicDib()
|
||||
|
||||
void CGraphicDib::Initialize()
|
||||
{
|
||||
m_hDC=NULL;
|
||||
m_hBmp=NULL;
|
||||
m_pvBuf=NULL;
|
||||
m_width=0;
|
||||
m_height=0;
|
||||
m_pvBuf = NULL;
|
||||
m_width = 0;
|
||||
m_height = 0;
|
||||
}
|
||||
|
||||
void CGraphicDib::Destroy()
|
||||
{
|
||||
if (m_hBmp) DeleteObject(m_hBmp);
|
||||
if (m_hDC) DeleteDC(m_hDC);
|
||||
|
||||
Initialize();
|
||||
{
|
||||
delete[] (DWORD*)m_pvBuf;
|
||||
Initialize();
|
||||
}
|
||||
|
||||
bool CGraphicDib::Create(HDC hDC, int width, int height)
|
||||
bool CGraphicDib::Create(int width, int height)
|
||||
{
|
||||
Destroy();
|
||||
|
||||
m_width = width;
|
||||
m_height = height;
|
||||
|
||||
ZeroMemory(&m_bmi.bmiHeader, sizeof(BITMAPINFOHEADER));
|
||||
m_bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
|
||||
m_bmi.bmiHeader.biWidth = m_width;
|
||||
m_bmi.bmiHeader.biHeight = -m_height;
|
||||
m_bmi.bmiHeader.biPlanes = 1;
|
||||
m_bmi.bmiHeader.biBitCount = 32;
|
||||
m_bmi.bmiHeader.biCompression = BI_RGB;
|
||||
|
||||
m_hDC=CreateCompatibleDC(hDC);
|
||||
if (!m_hDC)
|
||||
{
|
||||
assert(!"CGraphicDib::Create CreateCompatibleDC Error");
|
||||
return false;
|
||||
}
|
||||
|
||||
m_hBmp=CreateDIBSection(m_hDC, &m_bmi, DIB_RGB_COLORS, &m_pvBuf, NULL, 0);
|
||||
if (!m_hBmp)
|
||||
{
|
||||
assert(!"CGraphicDib::Create CreateDIBSection Error");
|
||||
return false;
|
||||
}
|
||||
|
||||
SelectObject(m_hDC, m_hBmp);
|
||||
|
||||
::SetTextColor(m_hDC, RGB(255, 255, 255));
|
||||
m_pvBuf = new DWORD[width * height];
|
||||
memset(m_pvBuf, 0, width * height * sizeof(DWORD));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
HDC CGraphicDib::GetDCHandle()
|
||||
{
|
||||
return m_hDC;
|
||||
}
|
||||
|
||||
void CGraphicDib::SetBkMode(int iBkMode)
|
||||
{
|
||||
::SetBkMode(m_hDC, iBkMode);
|
||||
}
|
||||
|
||||
void CGraphicDib::TextOut(int ix, int iy, const char* c_szText)
|
||||
{
|
||||
::SetBkColor(m_hDC, 0);
|
||||
|
||||
if (!c_szText || !*c_szText)
|
||||
return;
|
||||
|
||||
std::wstring wText = Utf8ToWide(c_szText);
|
||||
|
||||
if (!wText.empty())
|
||||
::TextOutW(m_hDC, ix, iy, wText.c_str(), (int)wText.length());
|
||||
}
|
||||
|
||||
void CGraphicDib::Put(HDC hDC, int x, int y)
|
||||
{
|
||||
SetDIBitsToDevice(
|
||||
hDC,
|
||||
x,
|
||||
y,
|
||||
m_width,
|
||||
m_height,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
m_height,
|
||||
m_pvBuf,
|
||||
&m_bmi,
|
||||
DIB_RGB_COLORS
|
||||
);
|
||||
}
|
||||
|
||||
void* CGraphicDib::GetPointer()
|
||||
{
|
||||
return m_pvBuf;
|
||||
@@ -119,4 +50,4 @@ int CGraphicDib::GetWidth()
|
||||
int CGraphicDib::GetHeight()
|
||||
{
|
||||
return m_height;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,33 +1,23 @@
|
||||
#pragma once
|
||||
|
||||
class CGraphicDib
|
||||
class CGraphicDib
|
||||
{
|
||||
public:
|
||||
CGraphicDib();
|
||||
virtual ~CGraphicDib();
|
||||
|
||||
void Destroy();
|
||||
bool Create(HDC hDC, int width, int height);
|
||||
|
||||
void SetBkMode(int iBkMode);
|
||||
void TextOut(int ix, int iy, const char * c_szText);
|
||||
void Put(HDC hDC, int x, int y);
|
||||
void Destroy();
|
||||
bool Create(int width, int height);
|
||||
|
||||
int GetWidth();
|
||||
int GetHeight();
|
||||
|
||||
void* GetPointer();
|
||||
|
||||
HDC GetDCHandle();
|
||||
|
||||
protected:
|
||||
void Initialize();
|
||||
|
||||
protected:
|
||||
HDC m_hDC;
|
||||
HBITMAP m_hBmp;
|
||||
BITMAPINFO m_bmi;
|
||||
|
||||
protected:
|
||||
int m_width;
|
||||
int m_height;
|
||||
|
||||
|
||||
@@ -1,10 +1,15 @@
|
||||
#include "StdAfx.h"
|
||||
#include "GrpText.h"
|
||||
#include "FontManager.h"
|
||||
#include "EterBase/Stl.h"
|
||||
|
||||
#include "Util.h"
|
||||
#include <utf8.h>
|
||||
|
||||
#include <ft2build.h>
|
||||
#include FT_FREETYPE_H
|
||||
#include FT_GLYPH_H
|
||||
|
||||
CGraphicFontTexture::CGraphicFontTexture()
|
||||
{
|
||||
Initialize();
|
||||
@@ -18,43 +23,39 @@ CGraphicFontTexture::~CGraphicFontTexture()
|
||||
void CGraphicFontTexture::Initialize()
|
||||
{
|
||||
CGraphicTexture::Initialize();
|
||||
m_hFontOld = NULL;
|
||||
m_hFont = NULL;
|
||||
m_ftFace = nullptr;
|
||||
m_pAtlasBuffer = nullptr;
|
||||
m_atlasWidth = 0;
|
||||
m_atlasHeight = 0;
|
||||
m_isDirty = false;
|
||||
m_bItalic = false;
|
||||
m_ascender = 0;
|
||||
m_lineHeight = 0;
|
||||
m_x = 0;
|
||||
m_y = 0;
|
||||
m_step = 0;
|
||||
m_fontSize = 0;
|
||||
memset(m_fontName, 0, sizeof(m_fontName));
|
||||
}
|
||||
|
||||
bool CGraphicFontTexture::IsEmpty() const
|
||||
{
|
||||
return m_fontMap.size() == 0;
|
||||
return m_ftFace == nullptr;
|
||||
}
|
||||
|
||||
void CGraphicFontTexture::Destroy()
|
||||
{
|
||||
HDC hDC = m_dib.GetDCHandle();
|
||||
if (hDC)
|
||||
SelectObject(hDC, m_hFontOld);
|
||||
|
||||
m_dib.Destroy();
|
||||
delete[] m_pAtlasBuffer;
|
||||
m_pAtlasBuffer = nullptr;
|
||||
|
||||
m_lpd3dTexture = NULL;
|
||||
CGraphicTexture::Destroy();
|
||||
stl_wipe(m_pFontTextureVector);
|
||||
m_charInfoMap.clear();
|
||||
|
||||
if (m_fontMap.size())
|
||||
{
|
||||
TFontMap::iterator i = m_fontMap.begin();
|
||||
// FT_Face is owned by CFontManager, do NOT free it here
|
||||
m_ftFace = nullptr;
|
||||
|
||||
while(i != m_fontMap.end())
|
||||
{
|
||||
DeleteObject((HGDIOBJ)i->second);
|
||||
++i;
|
||||
}
|
||||
|
||||
m_fontMap.clear();
|
||||
}
|
||||
|
||||
Initialize();
|
||||
}
|
||||
|
||||
@@ -71,7 +72,7 @@ bool CGraphicFontTexture::Create(const char* c_szFontName, int fontSize, bool bI
|
||||
{
|
||||
Destroy();
|
||||
|
||||
// UTF-8 -> UTF-16 for font name
|
||||
// UTF-8 -> UTF-16 for font name storage
|
||||
std::wstring wFontName = Utf8ToWide(c_szFontName ? c_szFontName : "");
|
||||
wcsncpy_s(m_fontName, wFontName.c_str(), _TRUNCATE);
|
||||
|
||||
@@ -82,22 +83,57 @@ bool CGraphicFontTexture::Create(const char* c_szFontName, int fontSize, bool bI
|
||||
m_y = 0;
|
||||
m_step = 0;
|
||||
|
||||
// Determine atlas dimensions
|
||||
DWORD width = 256, height = 256;
|
||||
if (GetMaxTextureWidth() > 512)
|
||||
width = 512;
|
||||
if (GetMaxTextureHeight() > 512)
|
||||
height = 512;
|
||||
|
||||
if (!m_dib.Create(ms_hDC, width, height))
|
||||
m_atlasWidth = width;
|
||||
m_atlasHeight = height;
|
||||
|
||||
// Allocate CPU-side atlas buffer
|
||||
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);
|
||||
if (!m_ftFace)
|
||||
{
|
||||
TraceError("CGraphicFontTexture::Create - Failed to get face for '%s'", c_szFontName ? c_szFontName : "(null)");
|
||||
return false;
|
||||
}
|
||||
|
||||
HDC hDC = m_dib.GetDCHandle();
|
||||
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 : "?");
|
||||
|
||||
m_hFont = GetFont();
|
||||
// Set pixel size
|
||||
int pixelSize = (fontSize < 0) ? -fontSize : fontSize;
|
||||
if (pixelSize == 0)
|
||||
pixelSize = 12;
|
||||
FT_Set_Pixel_Sizes(m_ftFace, 0, pixelSize);
|
||||
|
||||
m_hFontOld = (HFONT)SelectObject(hDC, m_hFont);
|
||||
SetTextColor(hDC, RGB(255, 255, 255));
|
||||
SetBkColor(hDC, 0);
|
||||
// Apply italic via shear matrix if needed
|
||||
if (bItalic)
|
||||
{
|
||||
FT_Matrix matrix;
|
||||
matrix.xx = 0x10000L;
|
||||
matrix.xy = 0x5800L; // ~0.34 shear for synthetic italic
|
||||
matrix.yx = 0;
|
||||
matrix.yy = 0x10000L;
|
||||
FT_Set_Transform(m_ftFace, &matrix, NULL);
|
||||
}
|
||||
else
|
||||
{
|
||||
FT_Set_Transform(m_ftFace, NULL, NULL);
|
||||
}
|
||||
|
||||
// Cache font metrics
|
||||
m_ascender = (int)(m_ftFace->size->metrics.ascender >> 6);
|
||||
m_lineHeight = (int)(m_ftFace->size->metrics.height >> 6);
|
||||
|
||||
if (!AppendTexture())
|
||||
return false;
|
||||
@@ -105,48 +141,11 @@ bool CGraphicFontTexture::Create(const char* c_szFontName, int fontSize, bool bI
|
||||
return true;
|
||||
}
|
||||
|
||||
HFONT CGraphicFontTexture::GetFont()
|
||||
{
|
||||
HFONT hFont = nullptr;
|
||||
|
||||
// For Unicode, codePage should NOT affect font selection anymore
|
||||
static const WORD kUnicodeFontKey = 0;
|
||||
|
||||
TFontMap::iterator it = m_fontMap.find(kUnicodeFontKey);
|
||||
if (it != m_fontMap.end())
|
||||
return it->second;
|
||||
|
||||
LOGFONTW logFont{};
|
||||
|
||||
logFont.lfHeight = m_fontSize;
|
||||
logFont.lfEscapement = 0;
|
||||
logFont.lfOrientation = 0;
|
||||
logFont.lfWeight = FW_NORMAL;
|
||||
logFont.lfItalic = (BYTE)m_bItalic;
|
||||
logFont.lfUnderline = FALSE;
|
||||
logFont.lfStrikeOut = FALSE;
|
||||
logFont.lfCharSet = DEFAULT_CHARSET;
|
||||
logFont.lfOutPrecision = OUT_DEFAULT_PRECIS;
|
||||
logFont.lfClipPrecision = CLIP_DEFAULT_PRECIS;
|
||||
logFont.lfQuality = ANTIALIASED_QUALITY;
|
||||
logFont.lfPitchAndFamily = DEFAULT_PITCH;
|
||||
|
||||
// Copy Unicode font face name safely
|
||||
wcsncpy_s(logFont.lfFaceName, m_fontName, _TRUNCATE);
|
||||
|
||||
hFont = CreateFontIndirectW(&logFont);
|
||||
|
||||
if (hFont)
|
||||
m_fontMap.insert(TFontMap::value_type(kUnicodeFontKey, hFont));
|
||||
|
||||
return hFont;
|
||||
}
|
||||
|
||||
bool CGraphicFontTexture::AppendTexture()
|
||||
{
|
||||
CGraphicImageTexture * pNewTexture = new CGraphicImageTexture;
|
||||
CGraphicImageTexture* pNewTexture = new CGraphicImageTexture;
|
||||
|
||||
if (!pNewTexture->Create(m_dib.GetWidth(), m_dib.GetHeight(), D3DFMT_A4R4G4B4))
|
||||
if (!pNewTexture->Create(m_atlasWidth, m_atlasHeight, D3DFMT_A8R8G8B8))
|
||||
{
|
||||
delete pNewTexture;
|
||||
return false;
|
||||
@@ -158,33 +157,31 @@ bool CGraphicFontTexture::AppendTexture()
|
||||
|
||||
bool CGraphicFontTexture::UpdateTexture()
|
||||
{
|
||||
if(!m_isDirty)
|
||||
if (!m_isDirty)
|
||||
return true;
|
||||
|
||||
m_isDirty = false;
|
||||
|
||||
CGraphicImageTexture * pFontTexture = m_pFontTextureVector.back();
|
||||
CGraphicImageTexture* pFontTexture = m_pFontTextureVector.back();
|
||||
|
||||
if (!pFontTexture)
|
||||
return false;
|
||||
|
||||
WORD* pwDst;
|
||||
DWORD* pdwDst;
|
||||
int pitch;
|
||||
|
||||
if (!pFontTexture->Lock(&pitch, (void**)&pwDst))
|
||||
if (!pFontTexture->Lock(&pitch, (void**)&pdwDst))
|
||||
return false;
|
||||
|
||||
pitch /= 2;
|
||||
pitch /= 4; // pitch in DWORDs (A8R8G8B8 = 4 bytes per pixel)
|
||||
|
||||
int width = m_dib.GetWidth();
|
||||
int height = m_dib.GetHeight();
|
||||
DWORD* pdwSrc = m_pAtlasBuffer;
|
||||
|
||||
DWORD * pdwSrc = (DWORD*)m_dib.GetPointer();
|
||||
for (int y = 0; y < m_atlasHeight; ++y, pdwDst += pitch, pdwSrc += m_atlasWidth)
|
||||
{
|
||||
memcpy(pdwDst, pdwSrc, m_atlasWidth * sizeof(DWORD));
|
||||
}
|
||||
|
||||
for (int y = 0; y < height; ++y, pwDst += pitch, pdwSrc += width)
|
||||
for (int x = 0; x < width; ++x)
|
||||
pwDst[x]=pdwSrc[x];
|
||||
|
||||
pFontTexture->Unlock();
|
||||
return true;
|
||||
}
|
||||
@@ -207,68 +204,122 @@ CGraphicFontTexture::TCharacterInfomation* CGraphicFontTexture::GetCharacterInfo
|
||||
|
||||
CGraphicFontTexture::TCharacterInfomation* CGraphicFontTexture::UpdateCharacterInfomation(TCharacterKey keyValue)
|
||||
{
|
||||
HDC hDC = m_dib.GetDCHandle();
|
||||
SelectObject(hDC, GetFont());
|
||||
|
||||
if (keyValue == 0x08)
|
||||
keyValue = L' '; // 탭은 공백으로 바꾼다 (아랍 출력시 탭 사용: NAME:\tTEXT -> TEXT\t:NAME 로 전환됨 )
|
||||
|
||||
ABCFLOAT stABC;
|
||||
SIZE size;
|
||||
|
||||
if (!GetTextExtentPoint32W(hDC, &keyValue, 1, &size) || !GetCharABCWidthsFloatW(hDC, keyValue, keyValue, &stABC))
|
||||
if (!m_ftFace)
|
||||
return NULL;
|
||||
|
||||
size.cx = stABC.abcfB;
|
||||
if( stABC.abcfA > 0.0f )
|
||||
size.cx += ceilf(stABC.abcfA);
|
||||
if( stABC.abcfC > 0.0f )
|
||||
size.cx += ceilf(stABC.abcfC);
|
||||
size.cx++;
|
||||
// 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);
|
||||
|
||||
LONG lAdvance = ceilf( stABC.abcfA + stABC.abcfB + stABC.abcfC );
|
||||
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);
|
||||
}
|
||||
|
||||
int width = m_dib.GetWidth();
|
||||
int height = m_dib.GetHeight();
|
||||
if (keyValue == 0x08)
|
||||
keyValue = L' ';
|
||||
|
||||
if (m_x + size.cx >= (width - 1))
|
||||
// Load and render the glyph
|
||||
FT_UInt glyphIndex = FT_Get_Char_Index(m_ftFace, keyValue);
|
||||
if (glyphIndex == 0 && keyValue != L' ')
|
||||
{
|
||||
// Try space as fallback for unknown characters
|
||||
glyphIndex = FT_Get_Char_Index(m_ftFace, L' ');
|
||||
if (glyphIndex == 0)
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (FT_Load_Glyph(m_ftFace, glyphIndex, FT_LOAD_RENDER | FT_LOAD_TARGET_NORMAL) != 0)
|
||||
return NULL;
|
||||
|
||||
FT_GlyphSlot slot = m_ftFace->glyph;
|
||||
FT_Bitmap& bitmap = slot->bitmap;
|
||||
|
||||
int glyphBitmapWidth = bitmap.width;
|
||||
int glyphBitmapHeight = bitmap.rows;
|
||||
int bearingY = slot->bitmap_top;
|
||||
float advance = (float)(slot->advance.x >> 6);
|
||||
|
||||
// Normalize glyph placement to common baseline
|
||||
// yOffset = distance from atlas row top to where the glyph bitmap starts
|
||||
int yOffset = m_ascender - bearingY;
|
||||
if (yOffset < 0)
|
||||
yOffset = 0;
|
||||
|
||||
// The effective cell height is the full line height
|
||||
int cellHeight = m_lineHeight;
|
||||
int cellWidth = glyphBitmapWidth;
|
||||
|
||||
// For spacing characters (space, etc.)
|
||||
if (glyphBitmapWidth == 0 || glyphBitmapHeight == 0)
|
||||
{
|
||||
TCharacterInfomation& rNewCharInfo = m_charInfoMap[keyValue];
|
||||
rNewCharInfo.index = static_cast<short>(m_pFontTextureVector.size() - 1);
|
||||
rNewCharInfo.width = 0;
|
||||
rNewCharInfo.height = (short)cellHeight;
|
||||
rNewCharInfo.left = 0;
|
||||
rNewCharInfo.top = 0;
|
||||
rNewCharInfo.right = 0;
|
||||
rNewCharInfo.bottom = 0;
|
||||
rNewCharInfo.advance = advance;
|
||||
return &rNewCharInfo;
|
||||
}
|
||||
|
||||
// Make sure cell fits the glyph including offset
|
||||
int requiredHeight = yOffset + glyphBitmapHeight;
|
||||
if (requiredHeight > cellHeight)
|
||||
cellHeight = requiredHeight;
|
||||
|
||||
int width = m_atlasWidth;
|
||||
int height = m_atlasHeight;
|
||||
|
||||
// Atlas packing (row-based)
|
||||
if (m_x + cellWidth >= (width - 1))
|
||||
{
|
||||
m_y += (m_step + 1);
|
||||
m_step = 0;
|
||||
m_x = 0;
|
||||
|
||||
if (m_y + size.cy >= (height - 1))
|
||||
if (m_y + cellHeight >= (height - 1))
|
||||
{
|
||||
if (!UpdateTexture())
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (!AppendTexture())
|
||||
return NULL;
|
||||
|
||||
// Reset atlas buffer for new texture
|
||||
memset(m_pAtlasBuffer, 0, m_atlasWidth * m_atlasHeight * sizeof(DWORD));
|
||||
m_y = 0;
|
||||
}
|
||||
}
|
||||
|
||||
TextOutW(hDC, m_x, m_y, &keyValue, 1);
|
||||
|
||||
int nChrX;
|
||||
int nChrY;
|
||||
int nChrWidth = size.cx;
|
||||
int nChrHeight = size.cy;
|
||||
int nDIBWidth = m_dib.GetWidth();
|
||||
|
||||
DWORD*pdwDIBData=(DWORD*)m_dib.GetPointer();
|
||||
DWORD*pdwDIBBase=pdwDIBData+nDIBWidth*m_y+m_x;
|
||||
DWORD*pdwDIBRow;
|
||||
|
||||
pdwDIBRow=pdwDIBBase;
|
||||
for (nChrY=0; nChrY<nChrHeight; ++nChrY, pdwDIBRow+=nDIBWidth)
|
||||
// Copy FreeType bitmap into atlas buffer at baseline-normalized position
|
||||
for (int row = 0; row < glyphBitmapHeight; ++row)
|
||||
{
|
||||
for (nChrX=0; nChrX<nChrWidth; ++nChrX)
|
||||
int atlasY = m_y + yOffset + row;
|
||||
if (atlasY < 0 || atlasY >= height)
|
||||
continue;
|
||||
|
||||
unsigned char* srcRow = bitmap.buffer + row * bitmap.pitch;
|
||||
DWORD* dstRow = m_pAtlasBuffer + atlasY * m_atlasWidth + m_x;
|
||||
|
||||
for (int col = 0; col < glyphBitmapWidth; ++col)
|
||||
{
|
||||
pdwDIBRow[nChrX]=(pdwDIBRow[nChrX]&0xff) ? 0xffff : 0;
|
||||
unsigned char alpha = srcRow[col];
|
||||
if (alpha)
|
||||
dstRow[col] = ((DWORD)alpha << 24) | 0x00FFFFFF; // White + alpha
|
||||
}
|
||||
}
|
||||
|
||||
@@ -278,18 +329,18 @@ CGraphicFontTexture::TCharacterInfomation* CGraphicFontTexture::UpdateCharacterI
|
||||
TCharacterInfomation& rNewCharInfo = m_charInfoMap[keyValue];
|
||||
|
||||
rNewCharInfo.index = static_cast<short>(m_pFontTextureVector.size() - 1);
|
||||
rNewCharInfo.width = size.cx;
|
||||
rNewCharInfo.height = size.cy;
|
||||
rNewCharInfo.width = (short)cellWidth;
|
||||
rNewCharInfo.height = (short)cellHeight;
|
||||
rNewCharInfo.left = float(m_x) * rhwidth;
|
||||
rNewCharInfo.top = float(m_y) * rhheight;
|
||||
rNewCharInfo.right = float(m_x+size.cx) * rhwidth;
|
||||
rNewCharInfo.bottom = float(m_y+size.cy) * rhheight;
|
||||
rNewCharInfo.advance = (float) lAdvance;
|
||||
rNewCharInfo.right = float(m_x + cellWidth) * rhwidth;
|
||||
rNewCharInfo.bottom = float(m_y + cellHeight) * rhheight;
|
||||
rNewCharInfo.advance = advance;
|
||||
|
||||
m_x += size.cx;
|
||||
m_x += cellWidth;
|
||||
|
||||
if (m_step < size.cy)
|
||||
m_step = size.cy;
|
||||
if (m_step < cellHeight)
|
||||
m_step = cellHeight;
|
||||
|
||||
m_isDirty = true;
|
||||
|
||||
|
||||
@@ -2,7 +2,9 @@
|
||||
|
||||
#include "GrpTexture.h"
|
||||
#include "GrpImageTexture.h"
|
||||
#include "GrpDIB.h"
|
||||
|
||||
#include <ft2build.h>
|
||||
#include FT_FREETYPE_H
|
||||
|
||||
#include <vector>
|
||||
#include <map>
|
||||
@@ -51,25 +53,22 @@ class CGraphicFontTexture : public CGraphicTexture
|
||||
|
||||
bool AppendTexture();
|
||||
|
||||
HFONT GetFont();
|
||||
|
||||
protected:
|
||||
typedef std::vector<CGraphicImageTexture*> TGraphicImageTexturePointerVector;
|
||||
typedef std::map<TCharacterKey, TCharacterInfomation> TCharacterInfomationMap;
|
||||
typedef std::map<WORD, HFONT> TFontMap;
|
||||
|
||||
protected:
|
||||
CGraphicDib m_dib;
|
||||
FT_Face m_ftFace;
|
||||
|
||||
HFONT m_hFontOld;
|
||||
HFONT m_hFont;
|
||||
// CPU-side atlas buffer (replaces CGraphicDib)
|
||||
DWORD* m_pAtlasBuffer;
|
||||
int m_atlasWidth;
|
||||
int m_atlasHeight;
|
||||
|
||||
TGraphicImageTexturePointerVector m_pFontTextureVector;
|
||||
|
||||
TCharacterInfomationMap m_charInfoMap;
|
||||
|
||||
TFontMap m_fontMap;
|
||||
|
||||
int m_x;
|
||||
int m_y;
|
||||
int m_step;
|
||||
@@ -78,4 +77,8 @@ class CGraphicFontTexture : public CGraphicTexture
|
||||
TCHAR m_fontName[LF_FACESIZE];
|
||||
LONG m_fontSize;
|
||||
bool m_bItalic;
|
||||
|
||||
// FreeType metrics cached per-font
|
||||
int m_ascender;
|
||||
int m_lineHeight;
|
||||
};
|
||||
|
||||
@@ -45,7 +45,9 @@ bool CGraphicText::OnLoad(int /*iSize*/, const void* /*c_pvBuf*/)
|
||||
|
||||
if (p)
|
||||
{
|
||||
strncpy(strName, GetFileName(), MIN(31, p - GetFileName()));
|
||||
int nameLen = MIN(31, (int)(p - GetFileName()));
|
||||
strncpy(strName, GetFileName(), nameLen);
|
||||
strName[nameLen] = '\0';
|
||||
++p;
|
||||
|
||||
static char num[8];
|
||||
@@ -71,8 +73,12 @@ bool CGraphicText::OnLoad(int /*iSize*/, const void* /*c_pvBuf*/)
|
||||
strName[0] = '\0';
|
||||
}
|
||||
else
|
||||
strncpy(strName, GetFileName(), MIN(31, p - GetFileName()));
|
||||
|
||||
{
|
||||
int nameLen = MIN(31, (int)(p - GetFileName()));
|
||||
strncpy(strName, GetFileName(), nameLen);
|
||||
strName[nameLen] = '\0';
|
||||
}
|
||||
|
||||
size = 12;
|
||||
}
|
||||
|
||||
|
||||
@@ -644,7 +644,8 @@ void CGraphicTextInstance::Render(RECT * pClipRect)
|
||||
akVertex[2].x=fFontEx-fFontHalfWeight+feather;
|
||||
akVertex[3].x=fFontEx-fFontHalfWeight+feather;
|
||||
|
||||
vtxBatch.insert(vtxBatch.end(), std::begin(akVertex), std::end(akVertex));
|
||||
vtxBatch.push_back(akVertex[0]); vtxBatch.push_back(akVertex[1]); vtxBatch.push_back(akVertex[2]);
|
||||
vtxBatch.push_back(akVertex[2]); vtxBatch.push_back(akVertex[1]); vtxBatch.push_back(akVertex[3]);
|
||||
|
||||
// 오른
|
||||
akVertex[0].x=fFontSx+fFontHalfWeight-feather;
|
||||
@@ -652,7 +653,8 @@ void CGraphicTextInstance::Render(RECT * pClipRect)
|
||||
akVertex[2].x=fFontEx+fFontHalfWeight+feather;
|
||||
akVertex[3].x=fFontEx+fFontHalfWeight+feather;
|
||||
|
||||
vtxBatch.insert(vtxBatch.end(), std::begin(akVertex), std::end(akVertex));
|
||||
vtxBatch.push_back(akVertex[0]); vtxBatch.push_back(akVertex[1]); vtxBatch.push_back(akVertex[2]);
|
||||
vtxBatch.push_back(akVertex[2]); vtxBatch.push_back(akVertex[1]); vtxBatch.push_back(akVertex[3]);
|
||||
|
||||
akVertex[0].x=fFontSx-feather;
|
||||
akVertex[1].x=fFontSx-feather;
|
||||
@@ -665,7 +667,8 @@ void CGraphicTextInstance::Render(RECT * pClipRect)
|
||||
akVertex[2].y=fFontSy-fFontHalfWeight-feather;
|
||||
akVertex[3].y=fFontEy-fFontHalfWeight+feather;
|
||||
|
||||
vtxBatch.insert(vtxBatch.end(), std::begin(akVertex), std::end(akVertex));
|
||||
vtxBatch.push_back(akVertex[0]); vtxBatch.push_back(akVertex[1]); vtxBatch.push_back(akVertex[2]);
|
||||
vtxBatch.push_back(akVertex[2]); vtxBatch.push_back(akVertex[1]); vtxBatch.push_back(akVertex[3]);
|
||||
|
||||
// 아래
|
||||
akVertex[0].y=fFontSy+fFontHalfWeight-feather;
|
||||
@@ -673,7 +676,8 @@ void CGraphicTextInstance::Render(RECT * pClipRect)
|
||||
akVertex[2].y=fFontSy+fFontHalfWeight-feather;
|
||||
akVertex[3].y=fFontEy+fFontHalfWeight+feather;
|
||||
|
||||
vtxBatch.insert(vtxBatch.end(), std::begin(akVertex), std::end(akVertex));
|
||||
vtxBatch.push_back(akVertex[0]); vtxBatch.push_back(akVertex[1]); vtxBatch.push_back(akVertex[2]);
|
||||
vtxBatch.push_back(akVertex[2]); vtxBatch.push_back(akVertex[1]); vtxBatch.push_back(akVertex[3]);
|
||||
|
||||
fCurX += fFontAdvance;
|
||||
}
|
||||
@@ -743,7 +747,8 @@ void CGraphicTextInstance::Render(RECT * pClipRect)
|
||||
|
||||
akVertex[0].color = akVertex[1].color = akVertex[2].color = akVertex[3].color = m_dwColorInfoVector[i];
|
||||
|
||||
vtxBatch.insert(vtxBatch.end(), std::begin(akVertex), std::end(akVertex));
|
||||
vtxBatch.push_back(akVertex[0]); vtxBatch.push_back(akVertex[1]); vtxBatch.push_back(akVertex[2]);
|
||||
vtxBatch.push_back(akVertex[2]); vtxBatch.push_back(akVertex[1]); vtxBatch.push_back(akVertex[3]);
|
||||
|
||||
fCurX += fFontAdvance;
|
||||
}
|
||||
@@ -827,7 +832,7 @@ void CGraphicTextInstance::Render(RECT * pClipRect)
|
||||
continue;
|
||||
|
||||
STATEMANAGER.SetTexture(0, pTexture);
|
||||
STATEMANAGER.DrawPrimitiveUP(D3DPT_TRIANGLESTRIP, vtxBatch.size() - 2, vtxBatch.data(), sizeof(SVertex));
|
||||
STATEMANAGER.DrawPrimitiveUP(D3DPT_TRIANGLELIST, vtxBatch.size() / 3, vtxBatch.data(), sizeof(SVertex));
|
||||
}
|
||||
|
||||
if (m_isCursor)
|
||||
|
||||
@@ -1,37 +1,41 @@
|
||||
#include "StdAfx.h"
|
||||
#include "TextBar.h"
|
||||
#include "EterLib/Util.h"
|
||||
#include "FontManager.h"
|
||||
#include "Util.h"
|
||||
|
||||
#include <utf8.h>
|
||||
|
||||
#include <ft2build.h>
|
||||
#include FT_BITMAP_H
|
||||
|
||||
void CTextBar::__SetFont(int fontSize, bool isBold)
|
||||
{
|
||||
LOGFONTW logFont{};
|
||||
m_ftFace = CFontManager::Instance().GetFace("Tahoma");
|
||||
if (!m_ftFace)
|
||||
return;
|
||||
|
||||
logFont.lfHeight = fontSize;
|
||||
logFont.lfEscapement = 0;
|
||||
logFont.lfOrientation = 0;
|
||||
logFont.lfWeight = isBold ? FW_BOLD : FW_NORMAL;
|
||||
logFont.lfItalic = FALSE;
|
||||
logFont.lfUnderline = FALSE;
|
||||
logFont.lfStrikeOut = FALSE;
|
||||
logFont.lfCharSet = DEFAULT_CHARSET;
|
||||
logFont.lfOutPrecision = OUT_DEFAULT_PRECIS;
|
||||
logFont.lfClipPrecision = CLIP_DEFAULT_PRECIS;
|
||||
logFont.lfQuality = ANTIALIASED_QUALITY;
|
||||
logFont.lfPitchAndFamily = DEFAULT_PITCH;
|
||||
wcscpy_s(logFont.lfFaceName, LF_FACESIZE, L"Tahoma");
|
||||
__ApplyFaceState();
|
||||
}
|
||||
|
||||
m_hFont = CreateFontIndirect(&logFont);
|
||||
void CTextBar::__ApplyFaceState()
|
||||
{
|
||||
if (!m_ftFace)
|
||||
return;
|
||||
|
||||
HDC hdc = m_dib.GetDCHandle();
|
||||
m_hOldFont = (HFONT)SelectObject(hdc, m_hFont);
|
||||
int pixelSize = (m_fontSize < 0) ? -m_fontSize : m_fontSize;
|
||||
if (pixelSize == 0)
|
||||
pixelSize = 12;
|
||||
|
||||
FT_Set_Pixel_Sizes(m_ftFace, 0, pixelSize);
|
||||
FT_Set_Transform(m_ftFace, NULL, NULL); // TextBar never uses italic
|
||||
|
||||
m_ascender = (int)(m_ftFace->size->metrics.ascender >> 6);
|
||||
m_lineHeight = (int)(m_ftFace->size->metrics.height >> 6);
|
||||
}
|
||||
|
||||
void CTextBar::SetTextColor(int r, int g, int b)
|
||||
{
|
||||
HDC hDC = m_dib.GetDCHandle();
|
||||
::SetTextColor(hDC, RGB(r, g, b));
|
||||
m_textColor = ((DWORD)r) | ((DWORD)g << 8) | ((DWORD)b << 16);
|
||||
}
|
||||
|
||||
void CTextBar::GetTextExtent(const char* c_szText, SIZE* p_size)
|
||||
@@ -46,35 +50,126 @@ void CTextBar::GetTextExtent(const char* c_szText, SIZE* p_size)
|
||||
return;
|
||||
}
|
||||
|
||||
HDC hDC = m_dib.GetDCHandle();
|
||||
if (!m_ftFace)
|
||||
{
|
||||
p_size->cx = 0;
|
||||
p_size->cy = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
// Re-apply face state (shared FT_Face may have been changed by another user)
|
||||
__ApplyFaceState();
|
||||
|
||||
// UTF-8 → UTF-16
|
||||
std::wstring wText = Utf8ToWide(c_szText);
|
||||
GetTextExtentPoint32W(hDC, wText.c_str(), static_cast<int>(wText.length()), p_size);
|
||||
|
||||
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 (FT_Load_Glyph(m_ftFace, glyphIndex, FT_LOAD_DEFAULT) == 0)
|
||||
totalAdvance += (int)(m_ftFace->glyph->advance.x >> 6);
|
||||
}
|
||||
|
||||
p_size->cx = totalAdvance;
|
||||
p_size->cy = m_lineHeight;
|
||||
}
|
||||
|
||||
void CTextBar::TextOut(int ix, int iy, const char * c_szText)
|
||||
{
|
||||
m_dib.TextOut(ix, iy, 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;
|
||||
|
||||
int bufWidth = m_dib.GetWidth();
|
||||
int bufHeight = m_dib.GetHeight();
|
||||
|
||||
std::wstring wText = Utf8ToWide(c_szText);
|
||||
|
||||
int penX = ix;
|
||||
int penY = iy;
|
||||
|
||||
DWORD colorRGB = m_textColor; // 0x00BBGGRR in memory
|
||||
|
||||
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)
|
||||
continue;
|
||||
|
||||
FT_GlyphSlot slot = m_ftFace->glyph;
|
||||
|
||||
// Apply synthetic bold by embolden
|
||||
if (m_isBold && slot->bitmap.buffer)
|
||||
FT_Bitmap_Embolden(CFontManager::Instance().GetLibrary(), &slot->bitmap, 64, 0);
|
||||
|
||||
FT_Bitmap& bitmap = slot->bitmap;
|
||||
|
||||
int bmpX = penX + slot->bitmap_left;
|
||||
int bmpY = penY + m_ascender - slot->bitmap_top;
|
||||
|
||||
for (int row = 0; row < (int)bitmap.rows; ++row)
|
||||
{
|
||||
int destY = bmpY + row;
|
||||
if (destY < 0 || destY >= bufHeight)
|
||||
continue;
|
||||
|
||||
unsigned char* srcRow = bitmap.buffer + row * bitmap.pitch;
|
||||
DWORD* dstRow = pdwBuf + destY * bufWidth;
|
||||
|
||||
for (int col = 0; col < (int)bitmap.width; ++col)
|
||||
{
|
||||
int destX = bmpX + col;
|
||||
if (destX < 0 || destX >= bufWidth)
|
||||
continue;
|
||||
|
||||
unsigned char alpha = srcRow[col];
|
||||
if (alpha)
|
||||
{
|
||||
// D3DFMT_A8R8G8B8 = ARGB in DWORD
|
||||
// colorRGB is stored as 0x00BBGGRR, convert to ARGB
|
||||
DWORD r = (colorRGB >> 0) & 0xFF;
|
||||
DWORD g = (colorRGB >> 8) & 0xFF;
|
||||
DWORD b = (colorRGB >> 16) & 0xFF;
|
||||
dstRow[destX] = ((DWORD)alpha << 24) | (r << 16) | (g << 8) | b;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
penX += (int)(slot->advance.x >> 6);
|
||||
}
|
||||
|
||||
Invalidate();
|
||||
}
|
||||
|
||||
void CTextBar::OnCreate()
|
||||
{
|
||||
m_dib.SetBkMode(TRANSPARENT);
|
||||
|
||||
__SetFont(m_fontSize, m_isBold);
|
||||
}
|
||||
|
||||
CTextBar::CTextBar(int fontSize, bool isBold)
|
||||
{
|
||||
m_hOldFont = NULL;
|
||||
m_ftFace = nullptr;
|
||||
m_textColor = 0x00FFFFFF; // White (RGB)
|
||||
m_fontSize = fontSize;
|
||||
m_isBold = isBold;
|
||||
m_ascender = 0;
|
||||
m_lineHeight = 0;
|
||||
}
|
||||
|
||||
CTextBar::~CTextBar()
|
||||
{
|
||||
HDC hdc = m_dib.GetDCHandle();
|
||||
SelectObject(hdc, m_hOldFont);
|
||||
// FT_Face is owned by CFontManager, do NOT free it here
|
||||
}
|
||||
|
||||
@@ -2,25 +2,32 @@
|
||||
|
||||
#include "DibBar.h"
|
||||
|
||||
#include <ft2build.h>
|
||||
#include FT_FREETYPE_H
|
||||
|
||||
class CTextBar : public CDibBar
|
||||
{
|
||||
public:
|
||||
CTextBar(int fontSize, bool isBold);
|
||||
virtual ~CTextBar();
|
||||
|
||||
|
||||
void TextOut(int ix, int iy, const char * c_szText);
|
||||
void SetTextColor(int r, int g, int b);
|
||||
void GetTextExtent(const char * c_szText, SIZE* p_size);
|
||||
|
||||
protected:
|
||||
void __SetFont(int fontSize, bool isBold);
|
||||
void __ApplyFaceState();
|
||||
|
||||
void OnCreate();
|
||||
|
||||
protected:
|
||||
HFONT m_hFont;
|
||||
HFONT m_hOldFont;
|
||||
|
||||
FT_Face m_ftFace;
|
||||
DWORD m_textColor;
|
||||
|
||||
int m_fontSize;
|
||||
bool m_isBold;
|
||||
|
||||
int m_ascender;
|
||||
int m_lineHeight;
|
||||
};
|
||||
|
||||
@@ -16,7 +16,7 @@ PyObject* grpCreateTextBar(PyObject* poSelf, PyObject* poArgs)
|
||||
return Py_BuildException();
|
||||
|
||||
CTextBar * pTextBar = new CTextBar(12, false);
|
||||
if (!pTextBar->Create(NULL, iWidth, iHeight))
|
||||
if (!pTextBar->Create(iWidth, iHeight))
|
||||
{
|
||||
delete pTextBar;
|
||||
return Py_BuildValue("K", NULL);
|
||||
@@ -39,7 +39,7 @@ PyObject* grpCreateBigTextBar(PyObject* poSelf, PyObject* poArgs)
|
||||
return Py_BuildException();
|
||||
|
||||
CTextBar * pTextBar = new CTextBar(iFontSize, true);
|
||||
if (!pTextBar->Create(NULL, iWidth, iHeight))
|
||||
if (!pTextBar->Create(iWidth, iHeight))
|
||||
{
|
||||
delete pTextBar;
|
||||
return Py_BuildValue("K", NULL);
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#include <stdlib.h>
|
||||
#include <utf8.h>
|
||||
#include <sodium.h>
|
||||
#include "EterLib/FontManager.h"
|
||||
|
||||
extern "C" {
|
||||
extern int _fltused;
|
||||
@@ -297,6 +298,12 @@ static bool Main(HINSTANCE hInstance, LPSTR lpCmdLine)
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!CFontManager::Instance().Initialize())
|
||||
{
|
||||
LogBox("FreeType initialization failed");
|
||||
return false;
|
||||
}
|
||||
|
||||
static CLZO lzo;
|
||||
CPackManager packMgr;
|
||||
|
||||
@@ -324,6 +331,8 @@ static bool Main(HINSTANCE hInstance, LPSTR lpCmdLine)
|
||||
|
||||
app->Destroy();
|
||||
delete app;
|
||||
|
||||
CFontManager::Instance().Destroy();
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
11
vendor/CMakeLists.txt
vendored
11
vendor/CMakeLists.txt
vendored
@@ -7,10 +7,19 @@ set(ZSTD_BUILD_SHARED OFF CACHE BOOL "BUILD SHARED LIBRARIES" FORCE)
|
||||
add_subdirectory(zstd-1.5.7/build/cmake zstd)
|
||||
include_directories("zstd/lib")
|
||||
|
||||
## FreeType - disable optional dependencies we don't need
|
||||
set(FT_DISABLE_HARFBUZZ ON CACHE BOOL "" FORCE)
|
||||
set(FT_DISABLE_BROTLI ON CACHE BOOL "" FORCE)
|
||||
set(FT_DISABLE_PNG ON CACHE BOOL "" FORCE)
|
||||
set(FT_DISABLE_BZIP2 ON CACHE BOOL "" FORCE)
|
||||
set(FT_DISABLE_ZLIB ON CACHE BOOL "" FORCE)
|
||||
add_subdirectory(freetype-2.13.3)
|
||||
|
||||
if (WIN32)
|
||||
set_target_properties(lzo2 PROPERTIES FOLDER vendor)
|
||||
set_target_properties(sodium PROPERTIES FOLDER vendor)
|
||||
|
||||
set_target_properties(freetype PROPERTIES FOLDER vendor)
|
||||
|
||||
## zstd stuff
|
||||
set_target_properties(zstd PROPERTIES FOLDER vendor/zstd)
|
||||
set_target_properties(libzstd_static PROPERTIES FOLDER vendor/zstd)
|
||||
|
||||
Reference in New Issue
Block a user