1212 lines
24 KiB
C++
1212 lines
24 KiB
C++
//////////////////////////////////////////////////////////////////////
|
|
//
|
|
// CryFont Source Code
|
|
//
|
|
// File: FFont.cpp
|
|
// Description: Font class.
|
|
//
|
|
// History:
|
|
// - August 20, 2001: d by Alberto Demichelis
|
|
//
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
#include "stdafx.h"
|
|
#include "FFont.h"
|
|
#include "FBitmap.h"
|
|
#include "CryFont.h"
|
|
//#include <Image.h>
|
|
#ifdef PS2
|
|
#include "PS2Font.h"
|
|
#endif
|
|
|
|
#include "FontTexture.h"
|
|
#ifdef WIN64
|
|
#undef GetCharWidth
|
|
#undef GetCharHeight
|
|
#endif
|
|
///////////////////////////////////////////////
|
|
CFFont::CFFont(struct ISystem *pISystem, class CCryFont *pCryFont, const char *pszName)
|
|
{
|
|
m_bRealPixels=false;
|
|
m_fWidthScale = 1.0f;
|
|
m_bSameSize = false;
|
|
// m_pBitmap = NULL;
|
|
m_pISystem = pISystem;
|
|
m_pCryFont = pCryFont;
|
|
m_szName = pszName;
|
|
m_vSize.set(16, 16);
|
|
m_iTextureID = -1;
|
|
m_vCharSize=vector2f(-1,-1);
|
|
|
|
m_fClipX = m_fClipY = 0.0f;
|
|
m_fClipR = m_fClipB = 0.0f;
|
|
|
|
m_bClipEnabled = 0;
|
|
m_pFontBuffer = 0;
|
|
|
|
m_vColorTable[0]=0xff000000;
|
|
m_vColorTable[1]=0xffffffff;
|
|
m_vColorTable[2]=0xffff0000;
|
|
m_vColorTable[3]=0xff00ff00;
|
|
m_vColorTable[4]=0xff0000ff;
|
|
m_vColorTable[5]=0xffffff00;
|
|
m_vColorTable[6]=0xff00ffff;
|
|
m_vColorTable[7]=0xffff00ff;
|
|
m_vColorTable[8]=0xff0080ff;
|
|
m_vColorTable[9]=0xff8f8f8f;
|
|
|
|
// create the default effect
|
|
SEffect *pEffect = NewEffect();
|
|
pEffect->strName = "default";
|
|
pEffect->NewPass();
|
|
SetEffect("default");
|
|
}
|
|
|
|
///////////////////////////////////////////////
|
|
CFFont::~CFFont()
|
|
{
|
|
/*if (m_pCryFont)
|
|
{
|
|
FontMapItor itor;
|
|
itor=m_pCryFont->m_mapFonts.find(m_sName.c_str());
|
|
if (itor!=m_pCryFont->m_mapFonts.end())
|
|
m_pCryFont->m_mapFonts.erase(itor);
|
|
m_pCryFont=NULL;
|
|
}*/
|
|
Free();
|
|
}
|
|
|
|
void CFFont::Reset()
|
|
{
|
|
m_fWidthScale = 1.0f;
|
|
m_bClipEnabled = 0;
|
|
m_vSize = vector2f(16.0f, 16.0f);
|
|
|
|
m_bSameSize = 0;
|
|
m_bRealPixels = 0;
|
|
m_vCharSize = vector2f(-1.0f, -1.0f);
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////
|
|
// Release the memory...
|
|
void CFFont::Release()
|
|
{
|
|
delete this;
|
|
}
|
|
|
|
///////////////////////////////////////////////
|
|
// Load a font from a CImage
|
|
/*bool CFFont::Load(class CImage *pImg)
|
|
{
|
|
Free();
|
|
|
|
m_pBitmap = new CFBitmap(pImg);
|
|
if(!m_pBitmap)
|
|
return false;
|
|
|
|
for(int i = 0; i < 256; ++i)
|
|
m_TexCooMap[i].z = 1.0f;
|
|
|
|
return RenderInit();
|
|
}*/
|
|
|
|
///////////////////////////////////////////////
|
|
// Load a font from a TTF file
|
|
bool CFFont::Load(const char *szFile, unsigned long nWidth, unsigned long nHeight, unsigned long nTTFFlags)
|
|
{
|
|
Free();
|
|
|
|
int i = 0;
|
|
|
|
#ifdef PS2
|
|
// [marco] for ps2 use the game path to prepend
|
|
// the file name or load from pack as shown below
|
|
if((m_pBitmap=LoadXtfFont(szFile, &m_TexCooMap[0])))
|
|
{
|
|
m_bOK = true;
|
|
return RenderInit();
|
|
}
|
|
#endif
|
|
|
|
|
|
int iSmoothMethod = (nTTFFlags & TTFFLAG_SMOOTH_MASK) >> TTFFLAG_SMOOTH_SHIFT;
|
|
int iSmoothAmount = (nTTFFlags & TTFFLAG_SMOOTH_AMOUNT_MASK) >> TTFFLAG_SMOOTH_AMOUNT_SHIFT;
|
|
|
|
ICryPak *pPak = m_pISystem->GetIPak();
|
|
|
|
FILE *pFile = pPak->FOpen(szFile,"rb");
|
|
|
|
if (!pFile)
|
|
return false;
|
|
|
|
pPak->FSeek(pFile, 0, SEEK_END);
|
|
int nSize = pPak->FTell(pFile);
|
|
pPak->FSeek(pFile, 0, SEEK_SET);
|
|
|
|
if (!nSize)
|
|
{
|
|
pPak->FClose(pFile);
|
|
|
|
return false;
|
|
}
|
|
|
|
unsigned char *pBuffer = new unsigned char[nSize];
|
|
|
|
if (!pPak->FRead(pBuffer, nSize, 1, pFile))
|
|
{
|
|
pPak->FClose(pFile);
|
|
|
|
delete[] pBuffer;
|
|
|
|
return false;
|
|
}
|
|
|
|
pPak->FClose(pFile);
|
|
|
|
if (!m_pFontTexture.CreateFromMemory(pBuffer, nSize, nWidth, nHeight, iSmoothMethod, iSmoothAmount))
|
|
{
|
|
delete[] pBuffer;
|
|
|
|
return false;
|
|
}
|
|
|
|
m_pFontBuffer = pBuffer;
|
|
|
|
//-------------------------------------------------------------------------------------------------
|
|
|
|
m_bOK = true;
|
|
#ifdef PS2
|
|
ConvertXtfFont(m_pBitmap, m_pBitmap->GetWidth(),m_pBitmap->GetHeight(), (uchar *)m_pBitmap->m_pData);
|
|
SaveXtfFont(szFile, m_pBitmap, &m_TexCooMap[0], m_pBitmap->GetWidth(),m_pBitmap->GetHeight());
|
|
#endif
|
|
return RenderInit();
|
|
}
|
|
|
|
///////////////////////////////////////////////
|
|
// Free the memory
|
|
void CFFont::Free()
|
|
{
|
|
RenderCleanup();
|
|
|
|
if (m_pFontBuffer)
|
|
{
|
|
delete[] m_pFontBuffer;
|
|
}
|
|
m_pFontBuffer = 0;
|
|
|
|
m_bOK = false;
|
|
}
|
|
|
|
///////////////////////////////////////////////
|
|
// Set the current effect to use
|
|
void CFFont::SetEffect(const char *szEffect)
|
|
{
|
|
m_pCurrentEffect = NULL;
|
|
if(!szEffect)
|
|
szEffect = "default";
|
|
|
|
for(int i = 0; i < (int)m_vEffects.size(); ++i)
|
|
{
|
|
if(strcmp(m_vEffects[i].strName.c_str(), szEffect) == 0)
|
|
{
|
|
m_pCurrentEffect = &m_vEffects[i];
|
|
return;
|
|
}
|
|
}
|
|
|
|
if(!m_pCurrentEffect)
|
|
{
|
|
m_pCurrentEffect = &m_vEffects[0];
|
|
}
|
|
}
|
|
|
|
// Set clipping rectangle
|
|
void CFFont::SetClippingRect(float fX, float fY, float fX2, float fY2)
|
|
{
|
|
m_fClipX = fX;
|
|
m_fClipY = fY;
|
|
m_fClipR = fX2;
|
|
m_fClipB = fY2;
|
|
}
|
|
|
|
// Enable / Disable clipping (off by default)
|
|
void CFFont::EnableClipping(bool bEnable)
|
|
{
|
|
m_bClipEnabled = bEnable;
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////
|
|
void CFFont::SetColor(const color4f& col, int nPass)
|
|
{
|
|
SRenderingPass *pPass;
|
|
if(nPass < 0)
|
|
{
|
|
for(std::vector<SRenderingPass>::iterator i = m_pCurrentEffect->vPass.begin();
|
|
i != m_pCurrentEffect->vPass.end(); ++i)
|
|
{
|
|
pPass = &(*i);
|
|
pPass->SetColor(col);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(nPass >= (int)m_pCurrentEffect->vPass.size())
|
|
return;
|
|
pPass = &m_pCurrentEffect->vPass[nPass];
|
|
pPass->SetColor(col);
|
|
}
|
|
}
|
|
|
|
///////////////////////////////////////////////
|
|
// Set the characters base size
|
|
void CFFont::SetSize(const vector2f &vSize)
|
|
{
|
|
m_vCharSize = vector2f(-1.0f, -1.0f);
|
|
m_vSize = vSize;
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////
|
|
// Set the same size flag
|
|
void CFFont::SetSameSize(bool bSameSize)
|
|
{
|
|
m_bSameSize = bSameSize;
|
|
m_vCharSize = vector2f(-1.0f, -1.0f);
|
|
}
|
|
|
|
///////////////////////////////////////////////
|
|
// Get the same size flag
|
|
bool CFFont::GetSameSize()
|
|
{
|
|
return m_bSameSize;
|
|
}
|
|
|
|
///////////////////////////////////////////////
|
|
// Return the seted size
|
|
vector2f &CFFont::GetSize()
|
|
{
|
|
return m_vSize;
|
|
}
|
|
|
|
|
|
///////////////////////////////////////////////
|
|
// Return the char width
|
|
float CFFont::GetCharWidth()
|
|
{
|
|
IRenderer *pRenderer = m_pISystem->GetIRenderer();
|
|
assert(pRenderer);
|
|
|
|
if (m_vCharSize.x == -1.0f)
|
|
{
|
|
float fMaxW = m_vSize.x;
|
|
|
|
if (m_pCurrentEffect)
|
|
{
|
|
|
|
for(int i = m_pCurrentEffect->vPass.size()-1; i >= 0; --i)
|
|
{
|
|
SRenderingPass *Pass = &m_pCurrentEffect->vPass[i];
|
|
|
|
float fScale = Pass->vSizeScale.x;
|
|
float fOffset = Pass->vPosOffset.x;
|
|
float fPassW = fScale * m_vSize.x + fOffset;
|
|
|
|
if (!m_bRealPixels)
|
|
{
|
|
fPassW = pRenderer->ScaleCoordX(fScale * m_vSize.x) + fOffset;
|
|
}
|
|
|
|
if (m_bSameSize)
|
|
{
|
|
fPassW *= m_fWidthScale;
|
|
}
|
|
|
|
if (m_bRealPixels)
|
|
|
|
if (fPassW > fMaxW)
|
|
{
|
|
fMaxW = fPassW;
|
|
}
|
|
}
|
|
}
|
|
|
|
return fMaxW;
|
|
}
|
|
|
|
return m_vCharSize.x;
|
|
}
|
|
|
|
///////////////////////////////////////////////
|
|
// Return the char height
|
|
float CFFont::GetCharHeight()
|
|
{
|
|
IRenderer *pRenderer = m_pISystem->GetIRenderer();
|
|
assert(pRenderer);
|
|
|
|
if (m_vCharSize.y == -1.0f)
|
|
{
|
|
float fMaxH = m_vSize.y;
|
|
|
|
if (m_pCurrentEffect)
|
|
{
|
|
|
|
for(int i = m_pCurrentEffect->vPass.size()-1; i >= 0; --i)
|
|
{
|
|
SRenderingPass *Pass = &m_pCurrentEffect->vPass[i];
|
|
|
|
float fScale = Pass->vSizeScale.y;
|
|
float fOffset = Pass->vPosOffset.y;
|
|
float fPassH = fScale * m_vSize.y + fOffset;
|
|
|
|
if (!m_bRealPixels)
|
|
{
|
|
fPassH = pRenderer->ScaleCoordY(fScale * m_vSize.y) + fOffset;
|
|
}
|
|
|
|
if (fPassH > fMaxH)
|
|
{
|
|
fMaxH = fPassH;
|
|
}
|
|
}
|
|
}
|
|
|
|
return fMaxH;
|
|
}
|
|
|
|
return m_vCharSize.y;
|
|
}
|
|
|
|
///////////////////////////////////////////////
|
|
void CFFont::SetCharWidthScale(float fScale)
|
|
{
|
|
m_fWidthScale = fScale;
|
|
}
|
|
|
|
///////////////////////////////////////////////
|
|
float CFFont::GetCharWidthScale()
|
|
{
|
|
return m_fWidthScale;
|
|
}
|
|
|
|
/*
|
|
#define FONT_RGBA(r, g, b, a) \
|
|
( (((long)((a) * 255)) << 24) | (((long)((r) * 255)) << 16) \
|
|
| (((long)((g) * 255)) << 8) | (long)((b) * 255) \
|
|
)
|
|
*/
|
|
|
|
#define FONT_RGBA(r, g, b, a) \
|
|
( (((long)((a) * 255)) << 24) | (((long)((b) * 255)) << 16) \
|
|
| (((long)((g) * 255)) << 8) | (long)((r) * 255) \
|
|
)
|
|
|
|
_inline DWORD COLCONV (DWORD clr)
|
|
{
|
|
return ((clr & 0xff00ff00) | ((clr & 0xff0000)>>16) | ((clr & 0xff)<<16));
|
|
}
|
|
|
|
///////////////////////////////////////////////
|
|
// Draw a formated string
|
|
|
|
void CFFont::DrawString( float fBaseX, float fBaseY, const char *szMsg, const bool bASCIIMultiLine )
|
|
{
|
|
if (!szMsg)
|
|
{
|
|
return;
|
|
}
|
|
|
|
int iSize = min(1023, strlen(szMsg));
|
|
|
|
static wchar_t szwMsg[1024];
|
|
|
|
szwMsg[iSize] = 0;
|
|
while (iSize--)
|
|
{
|
|
szwMsg[iSize] = (unsigned char)szMsg[iSize];
|
|
}
|
|
|
|
DrawStringW(fBaseX, fBaseY, szwMsg, bASCIIMultiLine);
|
|
}
|
|
|
|
void CFFont::DrawStringW(float fBaseX, float fBaseY, const wchar_t *szMsg, const bool bASCIIMultiLine)
|
|
{
|
|
// please terminate your strings with a '\0'
|
|
// if you want to draw a string with more than 682 char, tell me (marcio)
|
|
// and will allocate two buffers
|
|
//assert(wcslen(szMsg) <= 682);
|
|
|
|
IRenderer *pRenderer = m_pISystem->GetIRenderer();
|
|
assert(pRenderer);
|
|
|
|
if (!szMsg)
|
|
{
|
|
return;
|
|
}
|
|
|
|
Prepare(szMsg);
|
|
|
|
float fTexHeight = m_pFontTexture.GetCellHeight() / (float)m_pFontTexture.GetHeight();
|
|
bool bRGB = (pRenderer->GetFeatures() & RFT_RGBA) != 0;
|
|
float fAlpha = m_pCurrentEffect->vPass[0].cColor.v[3];
|
|
struct_VERTEX_FORMAT_P3F_COL4UB_TEX2F *pVertex = 0;
|
|
int iVertexOffset = 0;
|
|
int iTextLength = GetTextLengthW(szMsg);
|
|
|
|
pRenderer->FontSetTexture(m_iTextureID, FILTER_TRILINEAR);
|
|
pRenderer->FontSetRenderingState(0, 0);
|
|
|
|
for(int i = m_pCurrentEffect->vPass.size()-1; i >= 0; --i)
|
|
{
|
|
if (!i)
|
|
{
|
|
fAlpha = 1.0f;
|
|
}
|
|
|
|
SRenderingPass *Pass = &m_pCurrentEffect->vPass[i];
|
|
|
|
// gather pass data
|
|
vector2f vBaseXY = vector2f(fBaseX, fBaseY);
|
|
vector2f vOffset = Pass->vPosOffset;
|
|
vector2f vSize = vector2f(m_vSize.x * Pass->vSizeScale.x, m_vSize.y * Pass->vSizeScale.y);
|
|
DWORD dwPassColor = 0;
|
|
DWORD dwColor = 0;
|
|
int iVBLen = 0;
|
|
|
|
if (!m_bRealPixels)
|
|
{
|
|
vSize.x = pRenderer->ScaleCoordX(vSize.x);
|
|
vSize.y = pRenderer->ScaleCoordY(vSize.y);
|
|
vBaseXY.x = pRenderer->ScaleCoordX(vBaseXY.x);
|
|
vBaseXY.y = pRenderer->ScaleCoordY(vBaseXY.y);
|
|
}
|
|
|
|
float fRcpCellWidth = (1.0f / (float)m_pFontTexture.GetCellWidth()) * vSize.x;
|
|
float fCharX = vBaseXY.x + vOffset.x;
|
|
float fCharY = vBaseXY.y + vOffset.y;
|
|
|
|
if (bRGB)
|
|
{
|
|
dwPassColor = FONT_RGBA(Pass->cColor.r, Pass->cColor.g, Pass->cColor.b, Pass->cColor.a * fAlpha);
|
|
}
|
|
else
|
|
{
|
|
dwPassColor = FONT_RGBA(Pass->cColor.b, Pass->cColor.g, Pass->cColor.r, Pass->cColor.a * fAlpha);
|
|
}
|
|
|
|
dwColor = dwPassColor;
|
|
|
|
pRenderer->FontSetBlending(Pass->blendSrc, Pass->blendDest);
|
|
pVertex = (struct_VERTEX_FORMAT_P3F_COL4UB_TEX2F *)pRenderer->GetDynVBPtr(iTextLength * 6, iVertexOffset, 0);
|
|
assert(pVertex);
|
|
|
|
wchar_t *pcChar = (wchar_t *)szMsg;
|
|
wchar_t ch;
|
|
|
|
// parse the string, ignoring control characters
|
|
while (ch = *pcChar++)
|
|
{
|
|
switch(ch)
|
|
{
|
|
case L'\\':
|
|
{
|
|
if (*pcChar != L'n' || !bASCIIMultiLine)
|
|
{
|
|
break;
|
|
}
|
|
++pcChar;
|
|
}
|
|
case L'\n':
|
|
{
|
|
fCharX = vBaseXY.x + vOffset.x;
|
|
fCharY += vSize.y;
|
|
continue;
|
|
}
|
|
break;
|
|
case L'\r':
|
|
{
|
|
fCharX = vBaseXY.x + vOffset.x;
|
|
|
|
continue;
|
|
}
|
|
break;
|
|
case L'\t':
|
|
{
|
|
if (m_bSameSize)
|
|
{
|
|
fCharX += 4 * vSize.x * m_fWidthScale;
|
|
}
|
|
else
|
|
{
|
|
fCharX += FONT_SPACE_SIZE * 4 * vSize.x;
|
|
}
|
|
|
|
continue;
|
|
}
|
|
break;
|
|
case L' ':
|
|
{
|
|
if (m_bSameSize)
|
|
{
|
|
fCharX += vSize.x * m_fWidthScale;
|
|
}
|
|
else
|
|
{
|
|
fCharX += FONT_SPACE_SIZE * vSize.x;
|
|
}
|
|
|
|
continue;
|
|
}
|
|
break;
|
|
case L'$':
|
|
{
|
|
if (*pcChar == L'$')
|
|
{
|
|
++pcChar;
|
|
}
|
|
else if(isdigit(*pcChar))
|
|
{
|
|
if (!i)
|
|
{
|
|
int iColorIndex = (*pcChar) - L'0';
|
|
|
|
dwColor = m_vColorTable[iColorIndex];
|
|
|
|
if (bRGB)
|
|
{
|
|
dwColor = m_vColorTable[iColorIndex];
|
|
}
|
|
else
|
|
{
|
|
dwColor = COLCONV(m_vColorTable[iColorIndex]);
|
|
}
|
|
|
|
// apply the correct alpha
|
|
dwColor = (dwColor & 0x00ffffff) | ((long)((Pass->cColor.a * fAlpha) * 255.0f)) << 24;
|
|
}
|
|
|
|
++pcChar;
|
|
|
|
continue;
|
|
}
|
|
else if ((*pcChar == L'O' || *pcChar == L'o') && !i)
|
|
{
|
|
if (!i)
|
|
{
|
|
dwColor = dwPassColor;
|
|
}
|
|
|
|
++pcChar;
|
|
|
|
continue;
|
|
}
|
|
else if (*pcChar)
|
|
{
|
|
++pcChar;
|
|
|
|
continue;
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
int iCharWidth = m_pFontTexture.GetCharWidth(ch);
|
|
float fWidth = iCharWidth * fRcpCellWidth;
|
|
float fAdvance = (iCharWidth + 1) * fRcpCellWidth;
|
|
float fTexWidth = (iCharWidth + 1) / (float)m_pFontTexture.GetWidth();
|
|
|
|
// get texture coordinates
|
|
float vTexCoord[4];
|
|
|
|
m_pFontTexture.GetTextureCoord(ch, &vTexCoord[0], &vTexCoord[1]);
|
|
|
|
vTexCoord[2] = vTexCoord[0] + fTexWidth;
|
|
vTexCoord[3] = vTexCoord[1] + fTexHeight;
|
|
|
|
float fX = fCharX;
|
|
float fY = fCharY;
|
|
|
|
if (m_bSameSize)
|
|
{
|
|
fX = fCharX + ((vSize.x * m_fWidthScale) - fWidth) * 0.5f;
|
|
fAdvance = vSize.x * m_fWidthScale;
|
|
}
|
|
|
|
float fR = fX + fWidth;
|
|
float fB = fY + vSize.y;
|
|
|
|
// compute clipping
|
|
float fNewX = fX;
|
|
float fNewY = fY;
|
|
float fNewR = fR;
|
|
float fNewB = fB;
|
|
|
|
if (m_bClipEnabled)
|
|
{
|
|
// clip non visible
|
|
if ((fX >= m_fClipR) || (fY >= m_fClipB) || (fR < m_fClipX) || (fB < m_fClipY))
|
|
{
|
|
fCharX += fAdvance;
|
|
|
|
continue;
|
|
}
|
|
// clip partially visible
|
|
else
|
|
{
|
|
if ((fWidth <= 0.0f) || (vSize.y <= 0.0f))
|
|
{
|
|
fCharX += fAdvance;
|
|
|
|
continue;
|
|
}
|
|
|
|
// clip the image to the scissor rect
|
|
fNewX = max(m_fClipX, fX);
|
|
fNewY = max(m_fClipY, fY);
|
|
fNewR = min(m_fClipR, fR);
|
|
fNewB = min(m_fClipB, fB);
|
|
|
|
float fRcpWidth = 1.0f / fWidth;
|
|
float fRcpHeight = 1.0f / vSize.y;
|
|
|
|
float fTexW = vTexCoord[2] - vTexCoord[0];
|
|
float fTexH = vTexCoord[3] - vTexCoord[1];
|
|
|
|
// clip horizontal
|
|
vTexCoord[0] = vTexCoord[0] + (fTexW * ((fNewX - fX) * fRcpWidth));
|
|
vTexCoord[2] = vTexCoord[2] + (fTexW * ((fNewR - (fX + fWidth)) * fRcpWidth));
|
|
|
|
// clip vertical
|
|
vTexCoord[1] = vTexCoord[1] + (fTexH * ((fNewY - fY) * fRcpHeight));
|
|
vTexCoord[3] = vTexCoord[3] + (fTexH * ((fNewB - (fY + vSize.y)) * fRcpHeight));
|
|
}
|
|
}
|
|
|
|
int iOffset = iVBLen * 6;
|
|
|
|
// define char quad
|
|
pVertex[iOffset].xyz.x = fNewX;
|
|
pVertex[iOffset].xyz.y = fNewY;
|
|
pVertex[iOffset].xyz.z = 1.0;
|
|
pVertex[iOffset].color.dcolor = dwColor;
|
|
pVertex[iOffset].st[0] = vTexCoord[0];
|
|
pVertex[iOffset++].st[1] = vTexCoord[1];
|
|
|
|
pVertex[iOffset].xyz.x = fNewR;
|
|
pVertex[iOffset].xyz.y = fNewY;
|
|
pVertex[iOffset].xyz.z = 1.0;
|
|
pVertex[iOffset].color.dcolor = dwColor;
|
|
pVertex[iOffset].st[0] = vTexCoord[2];
|
|
pVertex[iOffset++].st[1] = vTexCoord[1];
|
|
|
|
pVertex[iOffset].xyz.x = fNewR;
|
|
pVertex[iOffset].xyz.y = fNewB;
|
|
pVertex[iOffset].xyz.z = 1.0;
|
|
pVertex[iOffset].color.dcolor = dwColor;
|
|
pVertex[iOffset].st[0] = vTexCoord[2];
|
|
pVertex[iOffset++].st[1] = vTexCoord[3];
|
|
|
|
pVertex[iOffset].xyz.x = fNewR;
|
|
pVertex[iOffset].xyz.y = fNewB;
|
|
pVertex[iOffset].xyz.z = 1.0;
|
|
pVertex[iOffset].color.dcolor = dwColor;
|
|
pVertex[iOffset].st[0] = vTexCoord[2];
|
|
pVertex[iOffset++].st[1] = vTexCoord[3];
|
|
|
|
pVertex[iOffset].xyz.x = fNewX;
|
|
pVertex[iOffset].xyz.y = fNewB;
|
|
pVertex[iOffset].xyz.z = 1.0;
|
|
pVertex[iOffset].color.dcolor = dwColor;
|
|
pVertex[iOffset].st[0] = vTexCoord[0];
|
|
pVertex[iOffset++].st[1] = vTexCoord[3];
|
|
|
|
pVertex[iOffset].xyz.x = fNewX;
|
|
pVertex[iOffset].xyz.y = fNewY;
|
|
pVertex[iOffset].xyz.z = 1.0;
|
|
pVertex[iOffset].color.dcolor = dwColor;
|
|
pVertex[iOffset].st[0] = vTexCoord[0];
|
|
pVertex[iOffset++].st[1] = vTexCoord[1];
|
|
|
|
if (iVBLen >= 682)
|
|
{
|
|
break;
|
|
}
|
|
|
|
++iVBLen;
|
|
|
|
fCharX += fAdvance;
|
|
}
|
|
|
|
// draw this pass
|
|
pRenderer->DrawDynVB(iVertexOffset, 0, iVBLen * 6);
|
|
}
|
|
|
|
// restore the old states
|
|
pRenderer->FontRestoreRenderingState();
|
|
}
|
|
|
|
void CFFont::DrawWrappedStringW( float fBaseX, float fBaseY, float w, const wchar_t *szMsg, const bool bASCIIMultiLine )
|
|
{
|
|
wstring szWrapped;
|
|
|
|
WrapText(szWrapped, w, szMsg);
|
|
DrawStringW(fBaseX, fBaseY, szWrapped.c_str(), bASCIIMultiLine);
|
|
}
|
|
|
|
vector2f CFFont::GetWrappedTextSizeW(const wchar_t *swStr, float w, const bool bASCIIMultiLine)
|
|
{
|
|
wstring szWrapped;
|
|
|
|
WrapText(szWrapped, w, swStr);
|
|
return GetTextSizeW(szWrapped.c_str(), bASCIIMultiLine);
|
|
}
|
|
|
|
vector2f CFFont::GetTextSize(const char *szMsg, const bool bASCIIMultiLine)
|
|
{
|
|
if (!szMsg)
|
|
{
|
|
return vector2f(0.0f, 0.0f);
|
|
}
|
|
|
|
int iSize = min(1023, strlen(szMsg));
|
|
|
|
static wchar_t szwMsg[1024];
|
|
|
|
szwMsg[iSize] = 0;
|
|
while (iSize--)
|
|
{
|
|
szwMsg[iSize] = (unsigned char)szMsg[iSize];
|
|
}
|
|
|
|
return GetTextSizeW(szwMsg,bASCIIMultiLine);
|
|
}
|
|
|
|
///////////////////////////////////////////////
|
|
// Compute the text size
|
|
vector2f CFFont::GetTextSizeW(const wchar_t *szMsg, const bool bASCIIMultiLine)
|
|
{
|
|
IRenderer *pRenderer = m_pISystem->GetIRenderer();
|
|
assert(pRenderer);
|
|
|
|
if (wcslen(szMsg) == 1 && *szMsg == L'$')
|
|
{
|
|
int x = 0;
|
|
}
|
|
|
|
if (!szMsg)
|
|
{
|
|
return vector2f(0,0);
|
|
}
|
|
|
|
Prepare(szMsg);
|
|
|
|
float fMaxW = 0.0f;
|
|
float fMaxH = 0.0f;
|
|
|
|
for(int i = m_pCurrentEffect->vPass.size()-1; i >= 0; --i)
|
|
{
|
|
SRenderingPass *Pass = &m_pCurrentEffect->vPass[i];
|
|
|
|
// gather pass data
|
|
vector2f vOffset = Pass->vPosOffset;
|
|
vector2f vSize = vector2f(m_vSize.x * Pass->vSizeScale.x, m_vSize.y * Pass->vSizeScale.y);
|
|
|
|
if (!m_bRealPixels)
|
|
{
|
|
vSize.x = pRenderer->ScaleCoordX(vSize.x);
|
|
vSize.y = pRenderer->ScaleCoordY(vSize.y);
|
|
}
|
|
|
|
float fRcpCellWidth = (1.0f / (float)m_pFontTexture.GetCellWidth()) * vSize.x;
|
|
float fCharX = vOffset.x;
|
|
float fCharY = vOffset.y + vSize.y;
|
|
|
|
if (fCharY > fMaxH)
|
|
{
|
|
fMaxH = fCharY;
|
|
}
|
|
|
|
wchar_t *pcChar = (wchar_t *)szMsg;
|
|
wchar_t ch;
|
|
|
|
// parse the string, ignoring control characters
|
|
while (ch = *pcChar++)
|
|
{
|
|
switch(ch)
|
|
{
|
|
case L'\\':
|
|
{
|
|
if (*pcChar != L'n' || !bASCIIMultiLine)
|
|
{
|
|
break;
|
|
}
|
|
++pcChar;
|
|
}
|
|
case L'\n':
|
|
{
|
|
if (fCharX > fMaxW)
|
|
{
|
|
fMaxW = fCharX;
|
|
}
|
|
|
|
fCharX = vOffset.x;
|
|
fCharY += vSize.y;
|
|
|
|
if (fCharY > fMaxH)
|
|
{
|
|
fMaxH = fCharY;
|
|
}
|
|
|
|
continue;
|
|
}
|
|
break;
|
|
case L'\r':
|
|
{
|
|
if (fCharX > fMaxW)
|
|
{
|
|
fMaxW = fCharX;
|
|
}
|
|
|
|
fCharX = vOffset.x;
|
|
|
|
continue;
|
|
}
|
|
break;
|
|
case L'\t':
|
|
{
|
|
fCharX += FONT_SPACE_SIZE * 4 * vSize.x;
|
|
|
|
continue;
|
|
}
|
|
break;
|
|
case L' ':
|
|
{
|
|
fCharX += FONT_SPACE_SIZE * vSize.x;
|
|
|
|
continue;
|
|
}
|
|
break;
|
|
case L'$':
|
|
{
|
|
if (*pcChar == L'$')
|
|
{
|
|
++pcChar;
|
|
}
|
|
else if (*pcChar)
|
|
{
|
|
++pcChar;
|
|
|
|
continue;
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
int iCharWidth = m_pFontTexture.GetCharWidth(ch);
|
|
float fAdvance = (iCharWidth + 1) * fRcpCellWidth;
|
|
|
|
fCharX += fAdvance;
|
|
}
|
|
|
|
if (fCharX > fMaxW)
|
|
{
|
|
fMaxW = fCharX;
|
|
}
|
|
}
|
|
|
|
return vector2f(fMaxW, fMaxH);
|
|
}
|
|
|
|
///////////////////////////////////////////////
|
|
int CFFont::GetTextLengthW(const wchar_t *szMsg, const bool bASCIIMultiLine)
|
|
{
|
|
int iLength = 0;
|
|
|
|
wchar_t *pcChar = (wchar_t *)szMsg;
|
|
wchar_t ch;
|
|
|
|
// parse the string, ignoring control characters
|
|
while (ch = *pcChar++)
|
|
{
|
|
switch(ch)
|
|
{
|
|
case L'\\':
|
|
{
|
|
if (*pcChar != L'n' || !bASCIIMultiLine)
|
|
{
|
|
break;
|
|
}
|
|
++pcChar;
|
|
}
|
|
case L'\n':
|
|
case L'\r':
|
|
case L'\t':
|
|
{
|
|
continue;
|
|
}
|
|
break;
|
|
case L'$':
|
|
{
|
|
if (*pcChar == L'$')
|
|
{
|
|
++pcChar;
|
|
}
|
|
else if (*pcChar)
|
|
{
|
|
++pcChar;
|
|
|
|
continue;
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
++iLength;
|
|
}
|
|
|
|
return iLength;
|
|
}
|
|
|
|
///////////////////////////////////////////////
|
|
int CFFont::GetTextLength(const char *szMsg, const bool bASCIIMultiLine)
|
|
{
|
|
int iLength = 0;
|
|
|
|
char *pcChar = (char *)szMsg;
|
|
char ch;
|
|
|
|
// parse the string, ignoring control characters
|
|
while (ch = *pcChar++)
|
|
{
|
|
switch(ch)
|
|
{
|
|
case '\\':
|
|
{
|
|
if (*pcChar != L'n' || !bASCIIMultiLine)
|
|
{
|
|
break;
|
|
}
|
|
++pcChar;
|
|
}
|
|
case '\n':
|
|
case '\r':
|
|
case '\t':
|
|
{
|
|
continue;
|
|
}
|
|
break;
|
|
case '$':
|
|
{
|
|
if (*pcChar == L'$')
|
|
{
|
|
++pcChar;
|
|
}
|
|
else if (*pcChar)
|
|
{
|
|
++pcChar;
|
|
|
|
continue;
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
++iLength;
|
|
}
|
|
|
|
return iLength;
|
|
}
|
|
|
|
///////////////////////////////////////////////
|
|
// Push a new effect in the vector and return a pointer to it
|
|
CFFont::SEffect* CFFont::NewEffect()
|
|
{
|
|
SEffect effect;
|
|
m_vEffects.push_back(effect);
|
|
return &m_vEffects[m_vEffects.size()-1];
|
|
}
|
|
|
|
///////////////////////////////////////////////
|
|
// Return the current effect
|
|
CFFont::SEffect* CFFont::GetCurrentEffect()
|
|
{
|
|
return m_pCurrentEffect;
|
|
}
|
|
|
|
///////////////////////////////////////////////
|
|
bool CFFont::RenderInit()
|
|
{
|
|
#ifdef FONT_USE_32BIT_TEXTURE
|
|
m_iTextureID = m_pISystem->GetIRenderer()->FontCreateTexture(m_pFontTexture.GetWidth(), m_pFontTexture.GetHeight(), (unsigned char *)m_pFontTexture.GetBuffer(), eTF_8888);
|
|
#else
|
|
m_iTextureID = m_pISystem->GetIRenderer()->FontCreateTexture(m_pFontTexture.GetWidth(), m_pFontTexture.GetHeight(), m_pFontTexture.GetBuffer(), eTF_8000);
|
|
#endif
|
|
|
|
if (m_iTextureID < 0)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
///////////////////////////////////////////////
|
|
void CFFont::RenderCleanup()
|
|
{
|
|
if (m_iTextureID > -1)
|
|
{
|
|
m_pISystem->GetIRenderer()->RemoveTexture(m_iTextureID);
|
|
}
|
|
|
|
m_pFontTexture.Release();
|
|
}
|
|
|
|
void CFFont::GetMemoryUsage (class ICrySizer* pSizer)
|
|
{
|
|
if (!pSizer->Add (*this))
|
|
return;
|
|
|
|
pSizer->AddObject(&m_pFontTexture, m_pFontTexture.GetWidth() * m_pFontTexture.GetHeight() * 4);
|
|
|
|
// <<FIXME>> glyph cache bitmaps should also be added here
|
|
|
|
pSizer->AddString (m_szName);
|
|
pSizer->Add (&m_vEffects[0],m_vEffects.size());
|
|
for (VecEffect::iterator it = m_vEffects.begin(); it != m_vEffects.end(); ++it)
|
|
{
|
|
pSizer->Add (it->strName.c_str(), it->strName.capacity()+1);
|
|
pSizer->Add (&it->vPass[0], it->vPass.size());
|
|
}
|
|
}
|
|
|
|
//-------------------------------------------------------------------------------------------------
|
|
void CFFont::Prepare(const wchar_t *szString)
|
|
{
|
|
static int n = 0;
|
|
if (m_pFontTexture.PreCacheString(szString) == 1)
|
|
{
|
|
m_pISystem->GetIRenderer()->FontUpdateTexture(m_iTextureID, 0, 0, m_pFontTexture.GetWidth(), m_pFontTexture.GetHeight(), (unsigned char *)m_pFontTexture.GetBuffer());
|
|
}
|
|
}
|
|
|
|
//-------------------------------------------------------------------------------------------------
|
|
void CFFont::WrapText(wstring &szResult, float fMaxWidth, const wchar_t *szString)
|
|
{
|
|
szResult = szString;
|
|
|
|
if (!m_bRealPixels)
|
|
{
|
|
fMaxWidth = m_pISystem->GetIRenderer()->ScaleCoordX(fMaxWidth);
|
|
}
|
|
|
|
vector2f vStringSize = GetTextSizeW(szResult.c_str());
|
|
|
|
if (vStringSize.x <= fMaxWidth)
|
|
{
|
|
return;
|
|
}
|
|
|
|
int iLastSpace = -1;
|
|
float fLastSpaceWidth = 0.0f;
|
|
|
|
float fCurrentCharWidth = 0.0f;
|
|
float fCurrentLineWidth = 0.0f;
|
|
float fBiggestLineWidth = 0.0f;
|
|
float fWidthSum = 0.0f;
|
|
|
|
int iCurrentChar = 0;
|
|
wchar_t *pChar = (wchar_t *)szResult.c_str();
|
|
wchar_t szChar[2] = {0, 0};
|
|
|
|
while(szChar[0] = *pChar++)
|
|
{
|
|
// ignore color codes
|
|
if (szChar[0] == L'$')
|
|
{
|
|
if (*pChar)
|
|
{
|
|
++pChar;
|
|
++iCurrentChar;
|
|
|
|
if ((*pChar) != L'$')
|
|
{
|
|
++iCurrentChar;
|
|
|
|
continue;
|
|
}
|
|
szChar[0] = *pChar;
|
|
}
|
|
}
|
|
|
|
// get char width and sum it to the line width
|
|
fCurrentCharWidth = GetTextSizeW(szChar).x;
|
|
|
|
// keep track of spaces
|
|
// they are good for spliting the string :D
|
|
if (szChar[0] == L' ')
|
|
{
|
|
iLastSpace = iCurrentChar;
|
|
fLastSpaceWidth = fCurrentLineWidth + fCurrentCharWidth;
|
|
}
|
|
|
|
// if line exceed allowed width, split it
|
|
if ((fCurrentLineWidth + fCurrentCharWidth >= fMaxWidth) && (*pChar))
|
|
{
|
|
if ((iLastSpace > 0) && ((iCurrentChar - iLastSpace) < 16) && (iCurrentChar - iLastSpace > 0)) // 16 is the default treshold
|
|
{
|
|
szResult.insert(iLastSpace + 1, 1, L'\n');
|
|
|
|
++iCurrentChar;
|
|
pChar = (wchar_t *)(szResult.c_str() + iCurrentChar + 1);
|
|
|
|
if (fLastSpaceWidth > fBiggestLineWidth)
|
|
{
|
|
fBiggestLineWidth = fLastSpaceWidth;
|
|
}
|
|
|
|
fCurrentLineWidth = fCurrentLineWidth - fLastSpaceWidth + fCurrentCharWidth;
|
|
fWidthSum += fCurrentLineWidth;
|
|
}
|
|
else
|
|
{
|
|
szResult.insert(iCurrentChar, 1, L'\n');
|
|
|
|
++iCurrentChar;
|
|
pChar = (wchar_t *)(szResult.c_str() + iCurrentChar + 1);
|
|
|
|
if (fCurrentLineWidth > fBiggestLineWidth)
|
|
{
|
|
fBiggestLineWidth = fCurrentLineWidth;
|
|
}
|
|
|
|
fWidthSum += fCurrentLineWidth;
|
|
fCurrentLineWidth = fCurrentCharWidth;
|
|
}
|
|
|
|
// if we don't need any more line breaks, then just stop
|
|
if (vStringSize.x - fWidthSum <= fMaxWidth)
|
|
{
|
|
break;
|
|
}
|
|
|
|
fLastSpaceWidth = 0;
|
|
iLastSpace = 0;
|
|
}
|
|
else
|
|
{
|
|
fCurrentLineWidth += fCurrentCharWidth;
|
|
}
|
|
|
|
++iCurrentChar;
|
|
}
|
|
}
|
|
|
|
|