deargui-vpl/ref/virtools/Samples/Behaviors/Interface/Sources/CKTextureFont.cpp

1303 lines
35 KiB
C++

#include "CKAll.h"
#include "CKTextureFont.h"
#include "CKFontManager.h"
#include "CKRasterizer.h"
#define ROUND(a) (((a)<0)?(int)((a)-0.5):(int)((a)+0.5))
CKTextureFont::CKTextureFont(CKFontManager* fm,CKContext* ctx,char* name)
{
///
// Font Visual Properties
m_Leading.Set(0.0f,0.0f);
m_Scale.Set(1.0f,1.0f);
m_ShadowOffset.Set(4.0f,4.0f);
m_ShadowScale.Set(1.0f,1.0f);
m_ItalicOffset = 0.0f;
m_StartColor = RGBAITOCOLOR(255,255,255,255);
m_EndColor = RGBAITOCOLOR(0,0,0,255);
m_ShadowColor = RGBAITOCOLOR(0,0,0,128);
m_Material = 0;
m_Properties = 0;
m_FontTexture = 0;
m_FirstCharacter = 0;
m_ParagraphIndentation = 0.0f;
m_SpacingProperties = 0;
m_CaretMaterial = 0;
m_CaretSize = 0.0f;
m_SpacePercentage = 0.3f;
m_FontName = CKStrdup(name);
m_Context = ctx;
m_FontManager = fm;
}
CKTextureFont::~CKTextureFont()
{
CKStrdelete(m_FontName);
}
////////////////////////////////////////////////////////////////////////
// NEW FUNCTIONS
// Font Access
char*
CKTextureFont::GetFontName()
{
return m_FontName;
}
CKBOOL
CKTextureFont::IsFontSimilar(CKTexture* fonttexture,Vx2DVector& charnumber,CKBOOL fixed)
{
if(fixed) {
if(!(m_SpacingProperties & FIXED)) return FALSE;
} else {
if (m_SpacingProperties & FIXED) return FALSE;
}
if(m_FontTexture != CKOBJID(fonttexture)) return FALSE;
return TRUE;
}
void
CKTextureFont::CreateCKFont(CKTexture* fonttexture,VxRect& tzone,Vx2DVector& charnumber,CKBOOL fixed,int firstcharacter,float iSpaceSize)
{
if(!fonttexture) return;
m_CharNumber = charnumber;
m_SpacingProperties = (fixed)?FIXED:0;
m_FirstCharacter = firstcharacter;
// save the font texture id
m_FontTexture = CKOBJID(fonttexture);
float twidth = (float)fonttexture->GetWidth();
float theight = (float)fonttexture->GetHeight();
m_ScreenExtents.Set(twidth,theight);
if (!tzone.GetWidth() || !tzone.GetHeight()) {
tzone.SetDimension(0,0,twidth,theight);
}
m_FontZone = tzone;
m_SpacePercentage = iSpaceSize;
// the Creation
CreateFromTexture();
}
void
CKTextureFont::CreateFromTexture()
{
if (m_SpacingProperties&CREATED) return;
CKTexture* fonttexture = (CKTexture*)m_Context->GetObject(m_FontTexture);
if(!fonttexture) return;
int iWidth = fonttexture->GetWidth();
float twidth = (float)iWidth;
float theight = (float)fonttexture->GetHeight();
m_ScreenExtents.Set(twidth,theight);
float ustep = m_FontZone.GetWidth()/(twidth*m_CharNumber.x);
float vstep = m_FontZone.GetHeight()/(theight*m_CharNumber.y);
Vx2DVector v2 = m_FontZone.GetTopLeft();
float u = v2.x/twidth;
float v = v2.y/theight;
int c = m_FirstCharacter;
// Initialisation of the characters
for(int k=0; k<256; ++k) {
m_FontCoordinates[k].ustart = u;
m_FontCoordinates[k].vstart = v;
m_FontCoordinates[k].uwidth = ustep;
m_FontCoordinates[k].uprewidth = 0;
m_FontCoordinates[k].upostwidth = 0;
m_FontCoordinates[k].vwidth = vstep;
}
if (m_SpacingProperties & FIXED) { // The font must be fixed
// fill the uvs of the characters
for(int i=0;i<m_CharNumber.y;++i) {
for(int j=0;j<m_CharNumber.x;++j) {
m_FontCoordinates[c].ustart = u;
m_FontCoordinates[c].vstart = v;
m_FontCoordinates[c].uwidth = ustep;
m_FontCoordinates[c].vwidth = vstep;
u += ustep;
c++;
}
u = 0.0f;
v += vstep;
}
} else { // The font must be proportionnal
CKDWORD transcolor = 0;
CKBOOL alpha = TRUE;
if(fonttexture->IsTransparent()) {
alpha = FALSE;
transcolor = fonttexture->GetTransparentColor();
}
float width = (float)fonttexture->GetWidth();
float height= (float)fonttexture->GetHeight();
float upixel = 1.0f / width;
float vpixel = 1.0f / height;
int xpixel = 0;
int ypixel = 0;
int xwidth = (int)(width*ustep);
int ywidth = (int)(height*vstep);
#ifndef FONTMANAGER_NOSYSFONT
// Give the priority to the user defined font texture
// If we give the same name to a texture than a system font, we use the texture
// with self calculated dimensions
// Mark the font as to be saved
m_SpacingProperties |= SPACINGTOBESAVED;
// Select the font
CKTexture* texture = (CKTexture*)m_Context->GetObject(m_FontTexture);
CKSTRING textureName = (texture)?texture->GetName():NULL;
if(textureName)
if (m_FontManager->SelectFont(textureName)) {
if(m_FontManager->IsTrueTypeFont()) {
FONT_ABC fontABC[256];
if(m_FontManager->GetCharABCWidths(0, 255, fontABC)) {
for(int i = 0 ; i < m_CharNumber.y ; i++) {
for(int j = 0 ; j < m_CharNumber.x ; j++) {
m_FontCoordinates[c].ustart = u+fontABC[c].abcA*upixel;
m_FontCoordinates[c].vstart = v;
m_FontCoordinates[c].uprewidth = fontABC[c].abcA*upixel;
m_FontCoordinates[c].uwidth = fontABC[c].abcB*upixel;
m_FontCoordinates[c].upostwidth = fontABC[c].abcC*upixel;
m_FontCoordinates[c].vwidth = vstep;
u += ustep;
c++;
}
u = 0.0f;
v += vstep;
}
}
}
else {
int widths[256];
if(m_FontManager->GetCharWidths(0, 255, widths)) {
for(int i = 0 ; i < m_CharNumber.y ; i++) {
for(int j = 0 ; j < m_CharNumber.x ; j++) {
m_FontCoordinates[c].ustart = u;
m_FontCoordinates[c].vstart = v;
m_FontCoordinates[c].uprewidth = 0;
m_FontCoordinates[c].uwidth = widths[c]*upixel;
m_FontCoordinates[c].upostwidth = 0;
m_FontCoordinates[c].vwidth = vstep;
u += ustep;
c++;
}
u = 0.0f;
v += vstep;
}
}
}
m_FontManager->SelectFont(NULL);
}
else
#endif
{
CKDWORD* pixelMap = (CKDWORD*)fonttexture->LockSurfacePtr();
// fill the uvs of the characters
for(int i=0;i<m_CharNumber.y;++i) {
for(int j=0;j<m_CharNumber.x;++j) {
m_FontCoordinates[c].ustart = u;
m_FontCoordinates[c].vstart = v;
m_FontCoordinates[c].uwidth = ustep;
m_FontCoordinates[c].vwidth = vstep;
// We now try to narrow the character
// left
int k;
for(k=0;k<xwidth;k++) {
CKDWORD color;
int y;
if(alpha) {
for (y=0;y<ywidth;y++) {
// color = fonttexture->GetPixel(xpixel+k,ypixel+y);
color = pixelMap[xpixel+k+iWidth*(ypixel+y)];
if(ColorGetAlpha(color)) break;
}
} else {
for (y=0;y<ywidth;y++) {
// color = fonttexture->GetPixel(xpixel+k,ypixel+y);
color = pixelMap[xpixel+k+iWidth*(ypixel+y)];
if(color != transcolor) break;
}
}
if(y < ywidth) break;
}
if (k == xwidth) { // the whole character is empty
m_FontCoordinates[c].uwidth *= m_SpacePercentage; // Changed from 0.5 to 0.3 coz was too big
u += ustep;
xpixel += xwidth;
c++;
// We go on to the next character
continue;
} else {
m_FontCoordinates[c].ustart += k*upixel;
m_FontCoordinates[c].uwidth -= k*upixel;
}
// right
for(k=0;k<xwidth;k++) {
int y;
for(y=0;y<ywidth;y++) {
//CKDWORD color = fonttexture->GetPixel(xpixel+xwidth-1-k,ypixel+y);
CKDWORD color = pixelMap[xpixel+xwidth-1-k+iWidth*(ypixel+y)];
if(alpha) {
if(ColorGetAlpha(color)) break;
} else {
if(color != transcolor) break;
}
}
if(y < ywidth) break;
}
m_FontCoordinates[c].uwidth -= k*upixel;
if(m_FontCoordinates[c].uwidth < 0.0f) m_FontCoordinates[c].uwidth = 0.0f;
u += ustep;
xpixel += xwidth;
c++;
}
u = 0.0f;
xpixel = 0;
v += vstep;
ypixel += ywidth;
}
fonttexture->ReleaseSurfacePtr();
}
}
if (m_FirstCharacter) {
m_FontCoordinates[0].vwidth = vstep;
}
// The font is now officialy created
m_SpacingProperties |= CREATED;
}
/////////////////////////
// The leading must be in relative of the texture too (for example leading = leadinginpixels/texturewidth or leadinginpixels/devicewidth)
// Warning : Is /texturewidth working ok ?
////////////////////////////
float
CKTextureFont::GetStringWidth(CKSTRING string)
{
if(!string) return 0.0f;
// TODO : optimize this
float scale = m_Scale.x*m_ScreenExtents.x;
float leading = m_Leading.x/scale;
float italic = m_ItalicOffset/scale;
float sw = 0.0f;
while((*string>0) && *string != '\n') {
// We add the character width (relative to the texture)
CharacterTextureCoordinates* ctc = &m_FontCoordinates[(unsigned char)*string];
sw += (ctc->uprewidth+ctc->uwidth+ctc->upostwidth);
// We add the leading
sw += leading;
string++;
}
// No space after last character of the line
sw -= leading;
// Italic offset at the end
sw += italic;
return sw*scale;
}
int
CKTextureFont::GetTextExtents(float &width,float& height)
{
int linecount = m_FontManager->GetLineCount();
float vspace = m_FontCoordinates[0].vwidth*(m_Scale.y*m_ScreenExtents.y)+m_Leading.y;
for(int i=0;i<linecount;++i) {
// currrent line
LineData* data = m_FontManager->GetLine(i);
if(data->stringwidth > width) {
width = data->stringwidth;
}
// Paragraph Indentation
if((data->len < 0) && (i != 0)) {
height += m_ParagraphIndentation.y*m_FontCoordinates[0].vwidth*(m_Scale.y*m_ScreenExtents.y);
}
// We add the Y space
height += vspace;
}
return linecount;
}
void
DrawFillRectangle(CKRenderContext *dev,CKMaterial* mat,VxRect& rect,BOOL lighted,BOOL transform)
{
if(!mat) return;
VxDrawPrimitiveData* data;
if(transform && lighted) data = dev->GetDrawPrimitiveStructure(CKRST_DP_TR_CL_VNT,4);
else
if(transform) data = dev->GetDrawPrimitiveStructure(CKRST_DP_TR_CL_VCT,4);
else
if(lighted) data = dev->GetDrawPrimitiveStructure((CKRST_DPFLAGS)(CKRST_DP_TR_CL_VNT&~CKRST_DP_TRANSFORM),4);
else data = dev->GetDrawPrimitiveStructure(CKRST_DP_CL_VCT,4);
CKWORD* indices = dev->GetDrawPrimitiveIndices(4);
XPtrStrided<VxUV> uvs(data->TexCoord);
XPtrStrided<VxVector4> positions(data->Positions);
XPtrStrided<VxVector> normals(data->Normals);
XPtrStrided<CKDWORD> colors(data->Colors);
// TODO handle the multi lines
mat->SetAsCurrent(dev);
dev->SetState(VXRENDERSTATE_ZWRITEENABLE , FALSE);
if(lighted) {
/////////////////
// Normals
// Normal 0
normals->x = 0;
normals->y = 0;
normals->z = 1.0f;
++normals;
// Normal 1
normals->x = 0;
normals->y = 0;
normals->z = 1.0f;
++normals;
// Normal 2
normals->x = 0;
normals->y = 0;
normals->z = 1.0f;
++normals;
// Normal 3
normals->x = 0;
normals->y = 0;
normals->z = 1.0f;
++normals;
} else {
/////////////////
// Colors
VxColor vxcol = mat->GetDiffuse();
CKDWORD col = RGBAFTOCOLOR(&vxcol);
// Vertex 0
*colors = col;
++colors;
// Vertex 1
*colors = col;
++colors;
// Vertex 2
*colors = col;
++colors;
// Vertex 3
*colors = col;
++colors;
}
/////////////////
// UVs
// Vertex 0
uvs->u = 0.0f;
uvs->v = 0.0f;
++uvs;
// Vertex 1
uvs->u = 1.0f;
uvs->v = 0.0f;
++uvs;
// Vertex 2
uvs->u = 1.0f;
uvs->v = 1.0f;
++uvs;
// Vertex 3
uvs->u = 0.0f;
uvs->v = 1.0f;
++uvs;
/////////////////
// Positions
// Vertex 0
positions->Set( rect.left , rect.top , 0.0f , 1.0f);
++positions;
// Vertex 1
positions->Set( rect.right , rect.top , 0.0f , 1.0f);
++positions;
// Vertex 2
positions->Set( rect.right , rect.bottom , 0.0f , 1.0f);
++positions;
// Vertex 3
positions->Set( rect.left , rect.bottom , 0.0f , 1.0f);
++positions;
indices[0] = 0;
indices[1] = 1;
indices[2] = 2;
indices[3] = 3;
// the drawing itself
dev->DrawPrimitive( VX_TRIANGLEFAN, indices, 4, data );
}
void
CKTextureFont::DrawString(CKRenderContext *dev,CKSTRING string,int slen,VxVector position,VxRect& Textzone,CKDWORD textoptions,CompiledTextData* ctdata)
{
if(!slen) return;
int len = slen;
// We check if we are in Text Compilation
if (!(textoptions & TEXT_COMPILED)) ctdata = NULL;
// Screen Clipping
VxRect textzone = Textzone;
if (textoptions & TEXT_SCREENCLIP) {
if (!textzone.Clip(m_ClippingRect)) return;
}
///
// Creation of the DrawPrim Data
VxDrawPrimitiveData* data = NULL;
CKWORD* IndicesPtr = NULL;
if (ctdata) {
if (textoptions&TEXT_3D) { // 3D
if (m_Properties&FONT_LIGHTING) { // Lighting
data = ctdata->GetStructure(CKRST_DP_TR_CL_VNT,len*4);
} else {
data = ctdata->GetStructure(CKRST_DP_TR_CL_VCT,len*4);
}
} else { // 2D
data = ctdata->GetStructure(CKRST_DP_CL_VCST,len*4);
}
// Indices
IndicesPtr = ctdata->GetIndices(len*6);
} else {
// Draw Prim Data
if (textoptions&TEXT_3D) { // 3D
if (m_Properties&FONT_LIGHTING) { // Lighting
data = dev->GetDrawPrimitiveStructure(CKRST_DP_TR_CL_VNT,len*4);
} else {
data = dev->GetDrawPrimitiveStructure(CKRST_DP_TR_CL_VCT,len*4);
}
} else { // 2D
data = dev->GetDrawPrimitiveStructure(CKRST_DP_CL_VCST,len*4);
}
// Indices
IndicesPtr = dev->GetDrawPrimitiveIndices(len*6);
}
VxVector pos = position;
VxVector scale(m_Scale.x*m_ScreenExtents.x,m_Scale.y*m_ScreenExtents.y,0);
float width = scale.x;
float height= scale.y;
float italic = m_ItalicOffset*width;
CKTexture* tex = GetFontTexture();
if(!tex) {
return;
}
// Multiplication by scale removed because big fonts were cropped !
float texelwidth = /*m_Scale.x */ 0.25f/tex->GetWidth();
float texelheight= /*m_Scale.y */ 0.25f/tex->GetHeight();
WORD* indices = IndicesPtr;
XPtrStrided<VxVector4> positions(data->Positions);
XPtrStrided<VxUV> uvs(data->TexCoord);
int index = 0;
int oldlen = len;
// We reset the counter
len = 0;
// int conversion for proper rendering
if (!(textoptions&TEXT_3D)) {
pos.x = (float)(int)(pos.x+0.5f);
pos.y = (float)(int)(pos.y+0.5f);
if (!(textoptions&TEXT_JUSTIFIED)) {
m_HLeading = (float)ROUND(m_HLeading);
}
}
float lineheight = m_FontCoordinates[0].vwidth*height;
BOOL seeCaret = FALSE;
// Clipping Y
float starty = pos.y;
float endy = pos.y+lineheight;
float cutup = 0.0f;
float cutdown = 1.0f;
if (textoptions & TEXT_CLIP) {
// Bottom of the zone
if(pos.y > textzone.bottom) return;
// Top of the zone
if(endy < textzone.top) return;
// Right of the zone
if(pos.x > textzone.right) return;
// Left of the zone
if(pos.x+m_LineWidth < textzone.left) return;
// Cut on top
if(starty < textzone.top) {
cutup = (textzone.top-starty)/lineheight;
starty = textzone.top;
}
// Cut on bottom
if(endy > textzone.bottom) {
cutdown = 1.0f - (endy-textzone.bottom)/lineheight;
endy = textzone.bottom;
}
}
// Temporary Variables
float startx = -1.0f;
float endx = -1.0f;
float startu = -1.0f;
float endu = -1.0f;
while(slen--) {
// Skipping irrelevant character
if((*string == ' ')) { // Here comes a space or a null character
if (seeCaret) {
if (textoptions & TEXT_SHOWCARET) {
DrawCaret(dev,pos.x,starty,m_SpaceSize,endy-starty,data->Flags);
SetRenderStates(dev,textoptions);
}
seeCaret = FALSE;
}
pos.x += m_SpaceSize;
++string;
continue;
}
if((*string == '\b')) { // Caret character
if (!slen) { // end of string, we have to draw the caret now
if (textoptions & TEXT_SHOWCARET) {
DrawCaret(dev,pos.x,starty,m_SpaceSize,endy-starty,data->Flags);
SetRenderStates(dev,textoptions);
}
}
seeCaret = TRUE;
++string;
continue;
}
CharacterTextureCoordinates* ctc = &m_FontCoordinates[(unsigned char)*string];
// If the character is empty, we skip it
if(ctc->vwidth == 0.0f) {
string++;
continue;
}
/////////////////
// Positions
startx = pos.x+ctc->uprewidth*width;
endx = startx + width*ctc->uwidth;
startu = ctc->ustart;
endu = ctc->ustart+ctc->uwidth;
////////////////////
// Clipping
if (textoptions & TEXT_CLIP) {
if(startx > textzone.right) break; // letter totally Right of the zone : we can stop writing
if(endx < textzone.left) { // letter totally Left of the zone : skip tio the next one
pos.x = endx + m_HLeading + ctc->upostwidth*width;
++string;
continue;
}
if (pos.x < textzone.left) { // Letter partially left of the zone
startu += (textzone.left-startx)/width;
startx = textzone.left;
}
if(endx > textzone.right) { // Letter partially left of the zone
endu -= (endx-textzone.right)/width;
endx = textzone.right;
}
}
// Vertices 0 1 2 3
if (!(textoptions&TEXT_3D)) {
positions->Set((int)(startx+italic+0.5f)-0.25f, starty, 0.01f,1.0f);++positions;
positions->Set((int)(endx+italic+0.5f)-0.25f, starty, 0.01f,1.0f);++positions;
positions->Set((int)(endx+0.5f)-0.25f, endy, 0.01f,1.0f);++positions;
positions->Set((int)(startx+0.5f)-0.25f, endy, 0.01f,1.0f);++positions;
} else {
positions->Set(startx+italic, starty, 0.01f,1.0f);++positions;
positions->Set(endx+italic, starty, 0.01f,1.0f);++positions;
positions->Set(endx, endy, 0.01f,1.0f);++positions;
positions->Set(startx, endy, 0.01f,1.0f);++positions;
}
if (seeCaret) {
if (textoptions & TEXT_SHOWCARET) {
DrawCaret(dev,startx,starty,endx-startx,endy-starty,data->Flags);
SetRenderStates(dev,textoptions);
}
seeCaret = FALSE;
}
// advance to next character
pos.x = endx + ctc->upostwidth*width + m_HLeading;
/////////////////
// UVs
float topv = ctc->vstart+cutup*ctc->vwidth;
float botv = ctc->vstart+cutdown*ctc->vwidth;
startu += texelwidth;
topv += texelheight;
// Vertex 0 1 2 3
uvs->u = startu; uvs->v = topv; ++uvs;
uvs->u = endu; uvs->v = topv; ++uvs;
uvs->u = endu; uvs->v = botv; ++uvs;
uvs->u = startu; uvs->v = botv; ++uvs;
/////////////////////
// Indices
indices[0] = index;
indices[1] = index+1;
indices[2] = index+2;
indices[3] = index;
indices[4] = index+2;
indices[5] = index+3;
indices += 6;
index += 4;
// String advance
++string;
// One more face couple
++len;
}
if (!len) return;
///
// Colors & Normals
if(m_Properties&FONT_LIGHTING) {
/////////////////
// Normals
VxVector norm(0,0,1.0f);
VxFillStructure(len*4,data->Normals,sizeof(VxVector),&norm);
} else {
/////////////////
// Colors
CKDWORD cols[4];
if (m_Properties & FONT_GRADIENT) {
CKDWORD scolor = m_StartColor;
CKDWORD ecolor = m_EndColor;
if(cutup!=0.0f || cutdown!=0.0f) {
cutdown = 1.0f - cutdown;
VxColor scol(scolor);
VxColor ecol(ecolor);
VxColor delta(ecol.r-scol.r,ecol.g-scol.g,ecol.b-scol.b,ecol.a-scol.a);
scol.r += (delta.r)*cutup;
scol.g += (delta.g)*cutup;
scol.b += (delta.b)*cutup;
scol.a += (delta.a)*cutup;
ecol.r -= (delta.r)*cutdown;
ecol.g -= (delta.g)*cutdown;
ecol.b -= (delta.b)*cutdown;
ecol.a -= (delta.a)*cutdown;
scolor = RGBAFTOCOLOR(&scol);
ecolor = RGBAFTOCOLOR(&ecol);
}
cols[0] = scolor;
cols[1] = scolor;
cols[2] = ecolor;
cols[3] = ecolor;
} else {
cols[0] = m_StartColor;
cols[1] = m_StartColor;
cols[2] = m_StartColor;
cols[3] = m_StartColor;
}
// WARNING : this works only because all the colors are contiguous...
VxFillStructure(len,data->Colors.Ptr,4*data->Colors.Stride,4*sizeof(CKDWORD),&cols);
}
if (ctdata) ctdata->PatchIndices(oldlen*6,len*6);
// the drawing itself
data->VertexCount = index;
dev->DrawPrimitive( VX_TRIANGLELIST, IndicesPtr, len*6, data );
}
void
CKTextureFont::DrawCaret(CKRenderContext* context,float posx,float posy,float dimx,float dimy,CKDWORD flags)
{
VxDrawPrimitiveData data;
data.VertexCount = 4;
if(m_CaretMaterial) m_CaretMaterial->SetAsCurrent(context);
VxVector4 positions[4];
float uvs[8] = {0.0f,0.0f,1.0f,0.0f,1.0f,1.0f,0.0f,1.0f};
positions[0].Set(posx,posy+dimy,0.0f,1.0f);
positions[1].Set(posx,posy+dimy*(1.0f-m_CaretSize),0.0f,1.0f);
positions[2].Set(posx+dimx,posy+dimy*(1.0f-m_CaretSize),0.0f,1.0f);
positions[3].Set(posx+dimx,posy+dimy,0.0f,1.0f);
VxColor diffuse(1.0f,1.0f,1.0f,0.5f);
if(m_CaretMaterial) diffuse = m_CaretMaterial->GetDiffuse();
CKDWORD col = RGBAFTOCOLOR(&diffuse);
CKDWORD colors[4] = {col,col,col,col};
data.Flags = flags;
data.Positions.Set(&positions,sizeof(VxVector4));
data.TexCoord.Set(&uvs,2*sizeof(float));
data.Colors.Set(&colors,sizeof(CKDWORD));
data.Normals.Set(0,0);
context->DrawPrimitive(VX_TRIANGLEFAN,(WORD*)0,4,&data);
}
void
CKTextureFont::DrawStringShadowed(CKRenderContext *dev,CKSTRING string,int slen,VxVector position,VxRect& textzone,CKDWORD textoptions,CompiledTextData* ctdata)
{
VxVector shadowoffset(m_ShadowOffset.x,m_ShadowOffset.y,0);
VxVector shadowpos = position + shadowoffset;
CKDWORD oldproperties = m_Properties;
m_Properties &= ~FONT_GRADIENT;
CKDWORD oldstartcolor = m_StartColor;
m_StartColor = m_ShadowColor;
Vx2DVector oldscale = m_Scale;
m_Scale = m_ShadowScale;
// We draw the shadow
if (ColorGetAlpha(m_ShadowColor)) DrawString(dev,string,slen,shadowpos,textzone,textoptions,ctdata);
// We restore the normal states
m_StartColor = oldstartcolor;
m_Properties = oldproperties;
m_Scale = oldscale;
// We draw the real string
DrawString(dev,string,slen,position,textzone,textoptions,ctdata);
}
void
CKTextureFont::SetRenderStates(CKRenderContext* dev,int options)
{
///
// STATES
// Render in solid
dev->SetState(VXRENDERSTATE_FILLMODE, VXFILL_SOLID);
dev->SetState(VXRENDERSTATE_SPECULARENABLE, FALSE);
if(m_Properties & FONT_LIGHTING) {
CKMaterial* mat = (CKMaterial*)m_Context->GetObject(m_Material);
if(mat) mat->SetAsCurrent(dev);
} else {
dev->SetState(VXRENDERSTATE_CULLMODE, VXCULL_NONE);
dev->SetState(VXRENDERSTATE_WRAP0 , 0);
dev->SetState(VXRENDERSTATE_SRCBLEND, VXBLEND_SRCALPHA);
dev->SetState(VXRENDERSTATE_DESTBLEND, VXBLEND_INVSRCALPHA);
}
CKTexture* fonttexture = (CKTexture*)m_Context->GetObject(m_FontTexture);
if (!CKIsChildClassOf(fonttexture,CKCID_TEXTURE)) {
return;
}
dev->SetTexture(fonttexture);
// TODO : move these in the TExt3D and 2D Callback
dev->SetState(VXRENDERSTATE_ALPHABLENDENABLE, TRUE);
if(options & TEXT_3D) {
dev->SetState(VXRENDERSTATE_ZENABLE , TRUE);
if(options & TEXT_RESPECTZORDER)
dev->SetState(VXRENDERSTATE_ZWRITEENABLE , TRUE);
else
dev->SetState(VXRENDERSTATE_ZWRITEENABLE , FALSE);
} else {
dev->SetState(VXRENDERSTATE_ZENABLE , FALSE);
dev->SetState(VXRENDERSTATE_ZWRITEENABLE , FALSE);
}
if(m_Properties & FONT_GRADIENT) {
dev->SetState(VXRENDERSTATE_SHADEMODE, VXSHADE_GOURAUD);
} else {
dev->SetState(VXRENDERSTATE_SHADEMODE, VXSHADE_FLAT);
}
dev->SetTextureStageState(CKRST_TSS_ADDRESS,VXTEXTURE_ADDRESSCLAMP);
dev->SetTextureStageState(CKRST_TSS_TEXTUREMAPBLEND, VXTEXTUREBLEND_MODULATEALPHA);
dev->SetTextureStageState(CKRST_TSS_STAGEBLEND,0,1);
CKRasterizerContext* rst = (CKRasterizerContext*) dev->GetRasterizerContext();
rst->SetTransformMatrix(VXMATRIX_TEXTURE0,VxMatrix::Identity());
if(m_Properties & FONT_DISABLEFILTER) {
dev->SetTextureStageState(CKRST_TSS_MINFILTER,VXTEXTUREFILTER_NEAREST);
dev->SetTextureStageState(CKRST_TSS_MAGFILTER,VXTEXTUREFILTER_NEAREST);
} else {
if (fonttexture && ( (fonttexture->GetMipmapCount() > 1) || (fonttexture->GetMipmapCount() == -1)))
dev->SetTextureStageState(CKRST_TSS_MINFILTER,VXTEXTUREFILTER_LINEARMIPLINEAR);
else
dev->SetTextureStageState(CKRST_TSS_MINFILTER,VXTEXTUREFILTER_LINEAR);
dev->SetTextureStageState(CKRST_TSS_MAGFILTER,VXTEXTUREFILTER_LINEAR);
}
}
void
CKTextureFont::DrawCKText(CKRenderContext *dev,CKBeObject* obj, CKSTRING string,int align,VxRect& textzone,CKMaterial* mat,int options, CKBOOL reallyDraw)
{
CK_ID objID = obj->GetID();
CompiledTextData* ctdata = NULL;
if (options & TEXT_COMPILED) { // The user asked for text compilation
if (reallyDraw && (ctdata = m_FontManager->GetCompiledText(objID))) {
if(options & TEXT_BACKGROUND) {
// Rendering of the rectangle
DrawFillRectangle(dev,mat,ctdata->m_DrawZone,m_Properties&FONT_LIGHTING,options&TEXT_3D);
}
// set the render states
SetRenderStates(dev,options);
// Do the rendering
ctdata->Render(dev);
return;
} else {
// we have to add the compiled data
ctdata = m_FontManager->AddCompiledText(objID);
}
} else {
// we clear the data
m_FontManager->RemoveCompiledText(objID);
}
VxRect drawzone = textzone;
textzone.left += m_Margins.left;
textzone.right -= m_Margins.right;
textzone.top += m_Margins.top;
textzone.bottom -= m_Margins.bottom;
// If the textzone is smaller than a character maxwidth, we do not write anything
float inv32f = 1.0f;
if(options & TEXT_3D) inv32f = 1.0f / 32.0f;
///
// We choose the relative coordinates (screen(same size,peu importe la resolution) or textures(looks better, original fontsize)....)
if (options & TEXT_SCREEN) {
m_ScreenExtents.Set(dev->GetWidth()*inv32f,dev->GetHeight()*inv32f);
} else {
CKTexture* fonttexture = GetFontTexture();
if (fonttexture) m_ScreenExtents.Set(fonttexture->GetWidth()*inv32f,fonttexture->GetHeight()*inv32f);
}
if(options & (TEXT_JUSTIFIED|TEXT_WORDWRAP)) {
// TODO : check this cull test becaus it's buggy on wordwrap on very small font
// if(textzone.GetWidth() < inv32f*m_FontZone.GetWidth()/(m_Scale.x*m_ScreenExtents.x*m_CharNumber.x)) return;
}
// Dimensions variables
float width = m_Scale.x*m_ScreenExtents.x;
float hlead = m_Leading.x;
if (m_FontManager->m_VersionUpdate) {
hlead = 0.1f*width*(m_FontCoordinates[' '].uprewidth+m_FontCoordinates[' '].uwidth+m_FontCoordinates[' '].upostwidth)*m_Leading.x;
}
if (!(options&TEXT_3D)) {
if (!(options&TEXT_JUSTIFIED)) {
hlead = (float)ROUND(hlead);
}
}
float spacesize = width*(m_FontCoordinates[' '].uprewidth+m_FontCoordinates[' '].uwidth+m_FontCoordinates[' '].upostwidth)+hlead;
float zonewidth = textzone.GetWidth();
// We clear the line array
m_FontManager->ClearLines();
// Line Data Variables
LineData ldata;
// Wrapping variables
char* lastword = "\n";
int lastlen = 0;
float lastwidth = 0.0f;
BOOL lastcharacterwasspace = TRUE;
BOOL paragraphstart = TRUE;
// String cutting, LineData Array filling
while(*string) {
ldata.string = string;
ldata.len = 0;
ldata.nbspace = 0;
if(paragraphstart) {
ldata.stringwidth = m_ParagraphIndentation.x*m_FontCoordinates[0].uwidth*(m_Scale.x*m_ScreenExtents.x);
} else ldata.stringwidth = 0.0f;
// we iterate line by line
while(*string && *string != '\n') {
// We encounter a space
if(*string == ' ') {
if(lastcharacterwasspace) { // we already encounter a space
} else lastcharacterwasspace = TRUE;
lastlen = ldata.len;
lastword = string+1;
lastwidth = ldata.stringwidth;
ldata.nbspace++;
ldata.stringwidth += spacesize;
} else { // we encounter a character
if(lastcharacterwasspace) { // Is it a word start ?
lastcharacterwasspace = FALSE;
lastlen = ldata.len-1;
lastword = string;
lastwidth = ldata.stringwidth-spacesize;
}
CharacterTextureCoordinates* tctc = &m_FontCoordinates[(unsigned char)*string];
ldata.stringwidth += (tctc->uprewidth+tctc->uwidth+tctc->upostwidth)*width+hlead;
}
// If wordwrap, whe have to see if the text fit in the line
if(options & (TEXT_JUSTIFIED|TEXT_WORDWRAP)) {
if(ldata.stringwidth-hlead > zonewidth) { // it does not : we break the line
if(lastlen>0) { // the word was not on the beginning of the line
ldata.len = lastlen;
ldata.stringwidth = lastwidth;
// We drop the last character before this word
ldata.nbspace--;
string = lastword;
} else { // first word of the line : we have to break it
if(*string != ' ') { // the character length was already added : we subtract it
CharacterTextureCoordinates* tctc = &m_FontCoordinates[(unsigned char)*string];
ldata.stringwidth -= (tctc->uprewidth+tctc->uwidth+tctc->upostwidth)*width+hlead;
if(lastword == string) string++;
lastword = string;
}
}
lastcharacterwasspace = TRUE;
break;
}
}
ldata.len++;
string++;
}
if(paragraphstart) {
ldata.len = -ldata.len;
paragraphstart = FALSE;
}
if(*string == '\n') {
paragraphstart = TRUE;
// Patch for infinite loop
if (*lastword == '\n') string++;
}
// end of a line (or text)
m_FontManager->AddLine(ldata);
if(*string && string != lastword && *lastword != '\n') string++;
}
// now we have to draw the strings
int linecount = m_FontManager->GetLineCount();
VxVector pos(textzone.left,textzone.top,0.0f);
// we now have to calculate the text extents
float textwidth = 0.0f;
float textheight= 0.0f;
m_LineCount = GetTextExtents(textwidth,textheight);
///
// Vertical Alignment Calculation
if(align & VALIGN_TOP) { // Top
pos.y = textzone.top;
} else {
if(align & VALIGN_BOTTOM) { // Bottom
int fileVersion = GetCurrentVersion();
if ( (((fileVersion&0xffff) << 16) | ((fileVersion&0xff0000) >> 8) | ((fileVersion&0xff000000) >> 24)) >= 0x20070820)
pos.y = textzone.bottom - (textheight-m_Leading.y); //remove the vertical space at the end of the text before alignment
else
pos.y = textzone.bottom - textheight; //back compatibility
} else { // Center
pos.y = (textzone.bottom+textzone.top)*0.5f-textheight*0.5f;
}
}
///
// Horizontal Alignment Calculation
if(align & HALIGN_LEFT) { // Top
pos.x = textzone.left;
} else {
if(align & HALIGN_RIGHT) { // Bottom
pos.x = textzone.right - textwidth;
} else { // Center
pos.x = (textzone.right+textzone.left)*0.5f-textwidth*0.5f;
}
}
m_TextExtents.SetDimension(pos.x,pos.y,textwidth,textheight);
///
// Resize
if(obj) { // Entity to resize ?
if(options & (TEXT_RESIZE_VERT | TEXT_RESIZE_HORI)) {
drawzone.top = pos.y;
if(options & TEXT_JUSTIFIED) { // Only vertically
drawzone.bottom = drawzone.top + textheight;
} else {
if(options & TEXT_RESIZE_HORI) drawzone.right = drawzone.left + textwidth;
if(options & TEXT_RESIZE_VERT) drawzone.bottom = drawzone.top + textheight;
}
if(options & TEXT_3D) {
if (CKIsChildClassOf(obj,CKCID_SPRITE3D)) {
CKSprite3D* spr = (CKSprite3D*)obj;
Vx2DVector v2d(textwidth,textheight);
spr->SetSize(v2d);
// spr->SetOffset(Vx2DVector(drawzone.left,-drawzone.top));
} else {
VxVector v;
((CK3dEntity*)obj)->GetScale(&v, FALSE);
if(options & TEXT_RESIZE_HORI)
v.x = (drawzone.right - drawzone.left)/2 + EPSILON;
if(options & (TEXT_RESIZE_VERT|TEXT_JUSTIFIED))
v.y = (drawzone.bottom - drawzone.top)/2 + EPSILON;
((CK3dEntity*)obj)->SetScale(&v, TRUE, FALSE);
}
}
else {
Vx2DVector v;
((CK2dEntity*)obj)->GetSize(v);
if(options & TEXT_RESIZE_HORI) v.x = drawzone.right - drawzone.left;
if(options & (TEXT_RESIZE_VERT | TEXT_JUSTIFIED))
v.y = drawzone.bottom - drawzone.top;
((CK2dEntity*)obj)->SetSize(v);
}
}
}
///
// Scroll Vertical
if(align & (VALIGN_TOP|VALIGN_BOTTOM)) { // Top|Bottom
pos.y += m_Offset.y;
}
//BackUp Some states values
CKBOOL oldZEnable = TRUE;
oldZEnable = (CKBOOL) dev->GetState(VXRENDERSTATE_ZENABLE);
//The Fill value has to be set to VXFILL_SOLID. If it is locked to another value,
//we unlock it and we will have to restore the lock after the text rendering (see below)
CKDWORD oldFillMode = VXFILL_SOLID;
dev->GetRasterizerContext()->SetRenderState(VXRENDERSTATE_FILLMODE, VXFILL_SOLID);
dev->GetRasterizerContext()->GetRenderState(VXRENDERSTATE_FILLMODE, &oldFillMode);
if (oldFillMode!=VXFILL_SOLID) {
dev->GetRasterizerContext()->LockRenderState(VXRENDERSTATE_FILLMODE, FALSE);
}
if(reallyDraw) {
// we store the zone of the rectangle to draw
if (ctdata) ctdata->m_DrawZone = drawzone;
///
// Background
if(options & TEXT_BACKGROUND) {
DrawFillRectangle(dev,mat,drawzone,m_Properties&FONT_LIGHTING,options&TEXT_3D);
}
// States
SetRenderStates(dev,options);
}
float verticalspace = m_FontCoordinates[0].vwidth*(m_Scale.y*m_ScreenExtents.y)+m_Leading.y;
// The actual Drawing
for(int i=0;i<linecount;++i) {
// currrent line
LineData* data = m_FontManager->GetLine(i);
m_SpaceSize = spacesize;
m_HLeading = hlead;
// We update the line width
m_LineWidth = data->stringwidth;
///
// Horizontal Alignment Calculation and spacing
if(options & TEXT_JUSTIFIED) {
if(data->nbspace) { // there is space, so we work on them
float delta = data->stringwidth - zonewidth;
if(delta > 0) {
m_SpaceSize = spacesize - delta/data->nbspace;
} else {
m_SpaceSize = spacesize + -delta/data->nbspace;
}
} else { // no space, we adjust leading
float delta = zonewidth - data->stringwidth;
int dlen = data->len;
if(dlen<0) dlen = -dlen;
if(dlen>1) {
if(delta > 0) {
m_HLeading = hlead + delta/(dlen-1);
} else {
m_HLeading = hlead - -delta/(dlen-1);
}
}
}
// the text has to be on the extreme left side
pos.x = textzone.left;
} else { // The text is not justified, so we have to calculate where it should be horizontally
if(align & HALIGN_LEFT) { // Left
pos.x = textzone.left + m_Offset.x;
} else {
if(align & HALIGN_RIGHT) { // Right
pos.x = m_Offset.x + textzone.right - data->stringwidth;
} else { // Center
pos.x = (textzone.right+textzone.left)*0.5f - data->stringwidth*0.5f;
}
}
}
// Paragraph Indentation
if(data->len < 0) {
data->len = -data->len;
if(align&HALIGN_LEFT) {
pos.x += m_ParagraphIndentation.x*m_FontCoordinates[0].uwidth*(m_Scale.x*m_ScreenExtents.x);
}
if (i != 0) pos.y += m_ParagraphIndentation.y*m_FontCoordinates[0].vwidth*(m_Scale.y*m_ScreenExtents.y);
}
// WARNING quand derniere ligne ou quand ligne qui contient un \n, pas de leading ni de space modifie...
// peut etre mettre les deux a 0 lors de la creation des lines datas... et retrouver ensuite la vraie longueur
// while(*string && *string != '\n') len++;
// TODO : optimize
if(reallyDraw) {
///
// Shadow
if(m_Properties & FONT_SHADOW) {
DrawStringShadowed(dev,data->string,data->len,pos,textzone,options,ctdata);
} else {
DrawString(dev,data->string,data->len,pos,textzone,options,ctdata);
}
}
pos.y += verticalspace;
}
//Restore backedup States
dev->SetState(VXRENDERSTATE_ZENABLE,(CKDWORD)oldZEnable);
if (oldFillMode!=VXFILL_SOLID) { //it means we have modified the lock (see backup states upper)
dev->GetRasterizerContext()->SetRenderState(VXRENDERSTATE_FILLMODE,oldFillMode);
dev->GetRasterizerContext()->LockRenderState(VXRENDERSTATE_FILLMODE, TRUE);
}
}
void
CKTextureFont::DrawStringEx(CKRenderContext* iRC, const char* iString, const VxRect& iTextZone, int iOptions)
{
if (!iString) return;
VxRect textzone = iTextZone;
textzone.left += m_Margins.left;
textzone.right -= m_Margins.right;
textzone.top += m_Margins.top;
textzone.bottom -= m_Margins.bottom;
CKTexture* fonttexture = GetFontTexture();
if (fonttexture)
m_ScreenExtents.Set(fonttexture->GetWidth(),fonttexture->GetHeight());
// Dimensions variables
float width = m_Scale.x*m_ScreenExtents.x;
float hlead = m_Leading.x;
if (m_FontManager->m_VersionUpdate) {
hlead = 0.1f*width*(m_FontCoordinates[' '].uprewidth+m_FontCoordinates[' '].uwidth+m_FontCoordinates[' '].upostwidth)*m_Leading.x;
}
float spacesize = width*(m_FontCoordinates[' '].uprewidth+m_FontCoordinates[' '].uwidth+m_FontCoordinates[' '].upostwidth)+hlead;
float zonewidth = textzone.GetWidth();
VxVector pos;
pos.x = textzone.left;
pos.y = textzone.top;
// States
SetRenderStates(iRC,iOptions);
m_SpaceSize = spacesize;
m_HLeading = hlead;
// We update the line width
pos.x = textzone.left + m_Offset.x;
DrawString(iRC,(CKSTRING)iString,strlen(iString),pos,textzone,iOptions);
}