openDecal/Native/Inject/FontCache.cpp
erik d1442e3747 Initial commit: Complete open-source Decal rebuild
All 5 phases of the open-source Decal rebuild:

Phase 1: 14 decompiled .NET projects (Interop.*, Adapter, FileService, DecalUtil)
Phase 2: 10 native DLLs rewritten as C# COM servers with matching GUIDs
  - DecalDat, DHS, SpellFilter, DecalInput, DecalNet, DecalFilters
  - Decal.Core, DecalControls, DecalRender, D3DService
Phase 3: C++ shims for Inject.DLL (D3D9 hooking) and LauncherHook.DLL
Phase 4: DenAgent WinForms tray application
Phase 5: WiX installer and build script

25 C# projects building with 0 errors.
Native C++ projects require VS 2022 + Windows SDK (x86).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 18:27:56 +01:00

284 lines
6.6 KiB
C++

// FontCache.cpp : Implementation of cFontCache
#include "stdafx.h"
#include "Inject.h"
#include "FontCache.h"
#include "Manager.h"
/////////////////////////////////////////////////////////////////////////////
// cFontCache
cFontCache::~cFontCache()
{
cManager::_p->removeFont( this );
}
#define BUFFER_WIDTHCHARS 32
#define BUFFER_HEIGHTCHARS 8
#define BUFFER_CHARCOUNT (BUFFER_WIDTHCHARS * BUFFER_HEIGHTCHARS)
bool cFontCache::checkBuffer()
{
if( m_pBuffer.p )
{
VARIANT_BOOL bWasLost;
m_pBuffer->get_WasLost( &bWasLost );
if( bWasLost )
m_pBuffer.Release();
}
if( m_pBuffer.p )
// The character buff is intact
return true;
// Get a dummy DC from the primary surface
CComPtr< ICanvas > pSurf;
if( FAILED(cManager::_p->GetPrimarySurface( &pSurf )) )
return false;
// Madar: This can happen if you set an edit-text during plugin initialization.
if (!pSurf)
{
return false;
}
HDC hdc;
pSurf->GetDC( &hdc );
// Drakier: reset error, try to create font, then check for error.
SetLastError(ERROR_SUCCESS);
HFONT hfnt = ::CreateFontIndirect( &m_lf );
if (GetLastError() != ERROR_SUCCESS)
{
_ASSERTE(GetLastError());
// Drakier: try arial instead
strcpy(m_lf.lfFaceName, "Arial");
SetLastError(ERROR_SUCCESS);
HFONT hfnt = ::CreateFontIndirect( &m_lf );
if (GetLastError() != ERROR_SUCCESS)
return false;
}
HFONT hfntOld = reinterpret_cast< HFONT >( ::SelectObject( hdc, hfnt ) );
TEXTMETRIC tm;
::GetTextMetrics( hdc, &tm );
m_szCharCell.cy = tm.tmHeight;
m_szCharCell.cx = tm.tmMaxCharWidth;
// Create the buffer
SIZE szBuffer = { m_szCharCell.cx * BUFFER_WIDTHCHARS, m_szCharCell.cy * BUFFER_HEIGHTCHARS };
cManager::_p->CreateCanvas( &szBuffer, &m_pBuffer );
INT widths[ BUFFER_CHARCOUNT ];
BOOL b = ::GetCharWidth( hdc, 0, ( BUFFER_CHARCOUNT - 1 ), widths );
_ASSERTE( b );
DWORD dwError = ::GetLastError();
for( short nChar = 0; nChar < BUFFER_CHARCOUNT; ++ nChar )
{
// TODO: Render the chars
// Figure out the character cell size
m_nWidths[ nChar ].m_nWidth = widths[ nChar ];
}
// TODO: Render all the chars into the buffer
::SelectObject( hdc, hfntOld );
::DeleteObject( hfnt );
pSurf->ReleaseDC();
return true;
}
STDMETHODIMP cFontCache::DrawText( LPPOINT ppt, BSTR strText, long clr, ICanvas *pCanvas )
{
/* _ASSERTE( ppt != NULL );
_ASSERTE( strText != NULL );
_ASSERTE( pCanvas != NULL );
if( !checkBuffer() )
return E_FAIL;
USES_CONVERSION;
LPCTSTR szText = OLE2T( strText );
// TODO: Draw this a lot faster from a cached bitmap value
HDC hdc;
pCanvas->GetDC( &hdc );
// Draw the text
HFONT hfnt = ::CreateFontIndirect( &m_lf ),
hfntOld = reinterpret_cast< HFONT >( ::SelectObject( hdc, hfnt ) );
::SetBkMode( hdc, TRANSPARENT );
::SetTextColor( hdc, clr );
::SetTextAlign( hdc, TA_TOP | TA_LEFT );
LPCSTR szAnsi = OLE2A( strText );
int cbLength = ::strlen( szAnsi );
INT *pDeltas = new INT[ cbLength ];
// Fill in the character deltas
const char *i_src = szAnsi;
INT *i_end_deltas = pDeltas + cbLength;
for( INT *i_delta = pDeltas; i_delta != i_end_deltas; ++ i_delta, ++ i_src )
*i_delta = m_nWidths[ *i_src ].m_nWidth;
if( m_bFontSmoothing )
SystemParametersInfo( SPI_SETFONTSMOOTHING, FALSE, NULL, 0 );
::ExtTextOut( hdc, ppt->x, ppt->y, 0, NULL, szAnsi, cbLength, pDeltas );
if( m_bFontSmoothing )
SystemParametersInfo( SPI_SETFONTSMOOTHING, TRUE, NULL, 0 );
delete[] pDeltas;
::SelectObject( hdc, hfntOld );
::DeleteObject( hfnt );
pCanvas->ReleaseDC();
return S_OK;*/
return DrawTextEx( ppt, strText, clr, 0, 0, pCanvas );
}
STDMETHODIMP cFontCache::DrawTextEx( LPPOINT ppt, BSTR strText, long clr1, long clr2, long lFlags, ICanvas *pCanvas )
{
_ASSERTE( ppt != NULL );
_ASSERTE( strText != NULL );
_ASSERTE( pCanvas != NULL );
if( !checkBuffer() )
return E_FAIL;
USES_CONVERSION;
LPCTSTR szText = OLE2T( strText );
// TODO: Draw this a lot faster from a cached bitmap value
HDC hdc;
pCanvas->GetDC( &hdc );
// Draw the text
// Drakier: reset error, try to create font, then check for error.
SetLastError(ERROR_SUCCESS);
HFONT hfnt = ::CreateFontIndirect( &m_lf );
if (GetLastError() != ERROR_SUCCESS)
{
_ASSERTE(GetLastError());
// Drakier: try arial instead
strcpy(m_lf.lfFaceName, "Arial");
SetLastError(ERROR_SUCCESS);
HFONT hfnt = ::CreateFontIndirect( &m_lf );
if (GetLastError() != ERROR_SUCCESS)
return E_FAIL;
}
HFONT hfntOld = reinterpret_cast< HFONT >( ::SelectObject( hdc, hfnt ) );
::SetBkMode( hdc, TRANSPARENT );
::SetTextAlign( hdc, TA_TOP | TA_LEFT );
LPCSTR szAnsi = OLE2A( strText );
int cbLength = ::strlen( szAnsi );
INT *pDeltas = new INT[ cbLength ];
// Fill in the character deltas
const char *i_src = szAnsi;
INT *i_end_deltas = pDeltas + cbLength;
for( INT *i_delta = pDeltas; i_delta != i_end_deltas; ++ i_delta, ++ i_src )
*i_delta = m_nWidths[ *i_src ].m_nWidth;
if( !(lFlags & eAA) )
if( m_bFontSmoothing )
SystemParametersInfo( SPI_SETFONTSMOOTHING, FALSE, NULL, 0 );
if( lFlags & eOutlined )
{
::SetTextColor( hdc, clr2 );
::ExtTextOut( hdc, ppt->x - 1, ppt->y - 1, 0, NULL, szAnsi, cbLength, pDeltas );
::ExtTextOut( hdc, ppt->x - 1, ppt->y + 1, 0, NULL, szAnsi, cbLength, pDeltas );
::ExtTextOut( hdc, ppt->x + 1, ppt->y - 1, 0, NULL, szAnsi, cbLength, pDeltas );
::ExtTextOut( hdc, ppt->x + 1, ppt->y + 1, 0, NULL, szAnsi, cbLength, pDeltas );
}
::SetTextColor( hdc, clr1 );
::ExtTextOut( hdc, ppt->x, ppt->y, 0, NULL, szAnsi, cbLength, pDeltas );
if( !(lFlags & eAA) )
if( m_bFontSmoothing )
SystemParametersInfo( SPI_SETFONTSMOOTHING, TRUE, NULL, 0 );
delete[] pDeltas;
::SelectObject( hdc, hfntOld );
::DeleteObject( hfnt );
pCanvas->ReleaseDC();
return S_OK;
}
STDMETHODIMP cFontCache::MeasureText( BSTR strText, LPSIZE pszExt )
{
_ASSERTE( strText != NULL );
_ASSERTE( pszExt != NULL );
if( !checkBuffer() )
return E_FAIL;
USES_CONVERSION;
LPCSTR szText = OLE2T( strText ),
i_end_text = szText + strlen( szText );
pszExt->cy = m_szCharCell.cy;
pszExt->cx = 0;
for( LPCSTR i_text = szText; i_text != i_end_text; ++ i_text )
pszExt->cx += m_nWidths[ *i_text ].m_nWidth;
return S_OK;
}
STDMETHODIMP cFontCache::HitTest(BSTR szText, long nPos, long *nIndex)
{
_ASSERTE( szText != NULL );
_ASSERTE( nIndex != NULL );
if( !checkBuffer() )
return E_FAIL;
USES_CONVERSION;
LPCSTR szAText = OLE2T( szText ),
i_end_text = szAText + strlen( szAText );
for( LPCSTR i_text = szAText; i_text != i_end_text; ++ i_text )
{
short nWidth = m_nWidths[ *i_text ].m_nWidth;
if( nPos < nWidth )
{
*nIndex = ( i_text - szAText );
return S_OK;
}
nPos -= nWidth;
}
*nIndex = -1;
return S_OK;
}