#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;iIsTransparent()) { 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;iGetPixel(xpixel+k,ypixel+y); color = pixelMap[xpixel+k+iWidth*(ypixel+y)]; if(ColorGetAlpha(color)) break; } } else { for (y=0;yGetPixel(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;kGetPixel(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;iGetLine(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 uvs(data->TexCoord); XPtrStrided positions(data->Positions); XPtrStrided normals(data->Normals); XPtrStrided 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 positions(data->Positions); XPtrStrided 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;iGetLine(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); }