#include <windows.h>
#include <stdio.h>
#include <GL/gl.h>
#include <GL/glu.h>
#include <math.h>
#include "cfontmap.h"
// some handy math macros; slow but usefull
#define LOG(x,base) (log(x)/log(base))
/*
* make_fontmap(..)
* creates a new font texture map that can be used by CFontmap from the scratch
* given a fontname the resolution and the other stuff.
* Also parameterizes a given CFontmap in the right way.
* NOTE: The iResolution parameter sets the height of a character box in the texture,
* but the case is, that the texture size is choosen on the iResolution parameter
* and the font height ist scaled to fit. Since texture sizes must be powers of
* two (2^x) all Resolutions in [2^x;2^(x+1)] have the same effect.
* e.g. [1,1], [2,4], [5,16], [17,32] ...
* Mipmapping is done by createing a smaller font for each level to take care of
* - a good look
* - special font behaviours on smaller sizes if there
*/
bool make_fontmap(char *csFontname, int iResolution, unsigned int uiChars, unsigned int uiColumns, unsigned int uiRows, CFontmap *pFontmap)
#ifdef _WIN32
// The Windoze stuff . . .
// CreateContext, some error? ( retry? oh please! ), CreateFont, SelectObject some error? ( retry? oh please! ), DeleteFont, DeleteContext
{
printf("make_fontmap(%s, %i, %i, %i, %i, %p)\n", csFontname, iResolution, uiChars, uiColumns, uiRows, pFontmap);
HDC hdc=
CreateCompatibleDC(wglGetCurrentDC());
if(!hdc)
return false;
HBITMAP hbitmap=
CreateCompatibleBitmap(hdc, 1, 1);
HBITMAP hbitmapOld=
SelectObject(hdc, hbitmap);
if((DWORD)hbitmapOld==GDI_ERROR)
return false;
GLuint uiTexName;// Just a unsigned int of waste when pFontmap!=NULL, but we're safe!
GLuint *pTexName=&uiTexName;
if(pFontmap!=NULL)
pTexName=&pFontmap->m_uiTexName;
glGenTextures(1, pTexName);
glBindTexture(GL_TEXTURE_2D, *pTexName);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR_MIPMAP_NEAREST);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
// First estimate the next integer exponent for 2^x the calc it
// Bit shift is a trick to get power of 2 values quickly (integers only)
int iTexWidth=(1L<<(int)ceil(LOG(uiColumns*iResolution,2)))*2;
int iTexHeight=(1L<<(int)ceil(LOG(uiRows*iResolution,2)))*2;
glPixelStorei(GL_UNPACK_SWAP_BYTES, GL_FALSE);
glPixelStorei(GL_UNPACK_LSB_FIRST, GL_FALSE);
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
glPixelStorei(GL_UNPACK_SKIP_ROWS, 0);
glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0);
glPixelTransferf(GL_RED_SCALE, 4.0f);
// Mipmapping Loop
int level=-1;
do
{
iTexWidth/=2;
iTexHeight/=2;
level++;
int const iColumnWidth=iTexWidth/uiColumns;
int const iRowHeight=iTexHeight/uiRows;
printf("Creating mipmap level %d, width %d, height %d ", level, iTexWidth, iTexHeight);
HFONT hfont=
CreateFont( -iRowHeight, 0, 0, 0,
500, false, false, false,
DEFAULT_CHARSET, OUT_TT_ONLY_PRECIS, CLIP_DEFAULT_PRECIS,
DEFAULT_QUALITY, DEFAULT_PITCH| (csFontname?FF_DONTCARE:FF_SWISS),
csFontname);
if(!hfont)
return false;
HFONT hfontOld=SelectObject(hdc, hfont);
if((DWORD)hfontOld==GDI_ERROR)
return false;
// Question: Why is there no OpenGL function to create an empty Texture you can fill with glTexSubImage* ?
// Ok, doing it from the back through the body in the eye.
// ( translated german phrase for doing something easy in a complex way )
BYTE *pData=new BYTE[iTexWidth*iTexHeight];
memset(pData, 0x00, iTexWidth*iTexHeight);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);// Needs less memory ;-)
glTexImage2D(GL_TEXTURE_2D, level, GL_LUMINANCE, iTexWidth, iTexHeight, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, pData);
printf("glTexImage2D(...), ");
delete[] pData;
glPixelStorei(GL_UNPACK_ALIGNMENT, 4);// Format of the GetGlyphOutline bitmap buffer
GLYPHMETRICS gm={0,};
MAT2 const matrix22_identity={{0,1},{0,0},{0,0},{0,1}};
for(unsigned int nRow=0; nRow<uiRows; nRow++)
{
for(unsigned int nColumn=0; nColumn<uiColumns; nColumn++)
{
DWORD dwBuffSize=//GGO_GRAY8_BITMAP
GetGlyphOutline(hdc, nColumn+nRow*uiColumns, GGO_GRAY8_BITMAP, &gm, 0, NULL, &matrix22_identity);
if((signed)dwBuffSize==-1)
{
fprintf(stderr, "dwBuffSize = GetGlyphOutline(...) didn't succed on character 0x%x='%c'.\n", nColumn+nRow*uiColumns, nColumn+nRow*uiColumns);
continue;
}
if(GetGlyphOutline(hdc, nColumn+nRow*uiColumns, GGO_METRICS, &gm, 0, NULL, &matrix22_identity)==GDI_ERROR)
{
fprintf(stderr, "GetGlyphOutline(..., &gm, ...) didn't succed on character 0x%x='%c'.\n", nColumn+nRow*uiColumns, nColumn+nRow*uiColumns);
continue;
}
BYTE *pBuff=new BYTE[dwBuffSize];
BYTE *pSwapBuff=new BYTE[dwBuffSize];
memset(pBuff, 0xff, dwBuffSize);
memset(pSwapBuff, 0xff, dwBuffSize);
if(GetGlyphOutline(hdc, nColumn+nRow*uiColumns, GGO_GRAY8_BITMAP, &gm, dwBuffSize, pBuff, &matrix22_identity)==GDI_ERROR)
{
delete[] pBuff;
continue;
}
if(gm.gmBlackBoxY==0)
{
printf("black box Y zero!!!\n");
return false;
}
unsigned int const uiRowSize=dwBuffSize/gm.gmBlackBoxY;
for(unsigned int nY=0; nY<gm.gmBlackBoxY; nY++)
{
memcpy(pSwapBuff+uiRowSize*nY, pBuff+dwBuffSize-uiRowSize*(nY+1), uiRowSize);
}
// Placing the glyphs' bitmap in the center of its fontmap cell,
// and set, if avilable, the CFontmap's according m_pCBoxes values
if( gm.gmBlackBoxX>0&& gm.gmBlackBoxX<(unsigned)iTexWidth&&
gm.gmBlackBoxY>0&& gm.gmBlackBoxY<(unsigned)iTexHeight&&
(gm.gmBlackBoxX+iColumnWidth*nColumn+(iColumnWidth-gm.gmBlackBoxX)/2)<(unsigned)iTexWidth&&
(gm.gmBlackBoxY+iRowHeight*nRow+(iRowHeight-gm.gmBlackBoxY)/2)<(unsigned)iTexHeight)
{
glTexSubImage2D(GL_TEXTURE_2D, level,
iColumnWidth*nColumn+(iColumnWidth-gm.gmBlackBoxX)/2,
iRowHeight*nRow+(iRowHeight-gm.gmBlackBoxY)/2,
gm.gmBlackBoxX, gm.gmBlackBoxY,
GL_LUMINANCE, GL_UNSIGNED_BYTE,
pSwapBuff);
}
if(pFontmap&&level==0)
{
if(iColumnWidth==0&&iRowHeight==0)
{
printf("iColumnWidth and/or iRowHeight zero!!!\n");
return false;
}
CFontmap::SCharBox *pCBox=pFontmap->m_pCBoxes+(nColumn+nRow*uiColumns);
pCBox->fXOffset=(float)gm.gmptGlyphOrigin.x/(float)iColumnWidth;
pCBox->fYOffset=(float)gm.gmptGlyphOrigin.y/(float)iRowHeight;
pCBox->fXInc=(float)gm.gmCellIncX/(float)iColumnWidth;
pCBox->fXBox=(float)gm.gmBlackBoxX/(float)iColumnWidth;
pCBox->fYBox=(float)gm.gmBlackBoxY/(float)iRowHeight;
}
delete[] pBuff;
delete[] pSwapBuff;
}
}
printf("glTexSubImage2D(...)\n");
SelectObject(hdc, hfontOld);
DeleteObject(hfont);
}while(iTexWidth/2>=1||iTexHeight/2>=1);
glPixelTransferf(GL_RED_SCALE, 1.0f);
SelectObject(hdc, hbitmapOld);
DeleteObject(hbitmap);
DeleteDC(hdc);
return true;
// That was it, not very long or complex but tricky, whew!!!
}
#else
// I's there really an other real GUI system as X11 on the earth?!?
// Normally I'm using Linux and my so loved X11. But in the moment my XFree4.0
// server doesn't work correctly. So I wasn't able to implement XFonts 8-(
{
printf(stderr, "make_fontmap(...) sorry, not implemented for X11. xet\n");
return false;
}
#endif
// CFontmap implementation
CFontmap::CFontmap():
c_uiChars(256), c_uiColumns(16), c_uiRows(16), m_pCBoxes(NULL)
{
m_pCBoxes=new SCharBox[c_uiChars];
for(unsigned int i=0; i<c_uiChars; i++)
{
m_pCBoxes[i].fXOffset=0.0f;
m_pCBoxes[i].fYOffset=0.0f;
m_pCBoxes[i].fXInc=1.0f;
m_pCBoxes[i].fXInc=1.0f;
}
m_fStartX=0.0;
m_fCurrentX=0.0f;
m_fCurrentY=0.0f;
set_height(1.0f);
}
CFontmap::~CFontmap()
{
delete[] m_pCBoxes;
}
CFontmap::CFontmap(char *csFontname, int iResolution, float fHeight, ERendermode rendermode):
c_uiChars(256), c_uiColumns(16), c_uiRows(16), m_pCBoxes(NULL), m_rendermode(alpha)
{
m_pCBoxes=new SCharBox[c_uiChars];
create(csFontname, iResolution);
set_height(fHeight);
set_rendermode(rendermode);
m_fStartX=0.0;
m_fCurrentX=0.0f;
m_fCurrentY=0.0f;
}
float CFontmap::set_height(float fHeight)
{
float fOldHeight=m_fHeight;
m_fHeight=fHeight;
return fOldHeight;
}
void CFontmap::set_pos(float fX, float fY)
{
m_fCurrentX=m_fStartX=fX;
m_fCurrentY=m_fStartY=fY;
}
bool CFontmap::create(char *csFontname, int iResolution)
{
return make_fontmap(csFontname, iResolution, c_uiChars, c_uiColumns, c_uiRows, this);
}
// Prints Text beginning at the current position
void CFontmap::print(char *csText, int iMaxLength)
{
SCharBox sCBox={0,0,1,1};
SCharBox *pCBox=&sCBox;
glPushAttrib(GL_ALL_ATTRIB_BITS);
glDisable(GL_LIGHTING);
glEnable(GL_BLEND);
switch(m_rendermode)
{
case additive:
glBlendFunc(GL_ONE, GL_ONE);
break;
case alpha:
glBlendFunc(GL_SRC_COLOR, GL_ONE_MINUS_SRC_COLOR);
break;
case invert:
glBlendFunc(GL_ONE_MINUS_DST_COLOR, GL_ONE_MINUS_SRC_COLOR);
break;
case invert_additive:
glBlendFunc(GL_ONE_MINUS_DST_COLOR, GL_ONE);
break;
case invert_alpha:
glBlendFunc(GL_ONE_MINUS_SRC_COLOR, GL_ONE);
break;
}
glBindTexture(GL_TEXTURE_2D, m_uiTexName);
glEnable(GL_TEXTURE_2D);
char *pC=csText;
glBegin(GL_QUADS);
while((*pC)!='\0'&&(iMaxLength==-1||(pC-csText)<iMaxLength))
{
int const iTexCol=(*pC)%c_uiColumns;
int const iTexRow=(*pC)/c_uiColumns;
pCBox=(m_pCBoxes+(*pC));
switch(*pC)
{
case '\n':
m_fCurrentY-=m_fHeight;
case '\r':
m_fCurrentX=m_fStartX;
break;
default:
float fXOffset=(1.0f-pCBox->fXBox)/2;
float fYOffset=(1.0f-pCBox->fYBox)/2;
float fYDescentOffset=(pCBox->fYOffset-pCBox->fYBox)*m_fHeight;
float fXPitchOffset=(pCBox->fXOffset)*m_fHeight;
float fYBox=pCBox->fYBox*m_fHeight;
float fXBox=pCBox->fXBox*m_fHeight;
glTexCoord2f( (float)(iTexCol+fXOffset)/(float)c_uiColumns,
(float)(iTexRow+fYOffset)/(float)c_uiRows);
glVertex2f( m_fCurrentX+fXPitchOffset, m_fCurrentY+fYDescentOffset);
glTexCoord2f( (float)(iTexCol+1-fXOffset)/(float)c_uiColumns,
(float)(iTexRow+fYOffset)/(float)c_uiRows);
glVertex2f( m_fCurrentX+fXBox+fXPitchOffset, m_fCurrentY+fYDescentOffset);
glTexCoord2f( (float)(iTexCol+1-fXOffset)/(float)c_uiColumns,
(float)(iTexRow+1-fYOffset)/(float)c_uiRows);
glVertex2f( m_fCurrentX+fXBox+fXPitchOffset, m_fCurrentY+fYBox+fYDescentOffset);
glTexCoord2f( (float)(iTexCol+fXOffset)/(float)c_uiColumns,
(float)(iTexRow+1-fYOffset)/(float)c_uiRows);
glVertex2f( m_fCurrentX+fXPitchOffset, m_fCurrentY+fYBox+fYDescentOffset);
m_fCurrentX+=pCBox->fXInc*m_fHeight;
break;
}
++pC;
}
glEnd();
glPopAttrib();
}
|