Help - Display text

Hi,

2 months ago, I would like to display text on my OpenGL program but now, It’s always impossible to display anything on the screen.

Someone can send me a exemple for displaying text without Glut and under Windows ???

Thanks a lot.

dylan.leyder@ibelgique.com

I hope this code isn’t too complicated, but I use this for my program to generate text under Windows NT/2000. There seems to be a slight problem with Win98, something to do with the space character not reporting the right width.

// Example: how to use this class
//
// DjOpenGlFont font;
// font.CreateFont(10, false, false, “Arial”, false);
// font.MakeCurrent();
// font.RenderString(“This is a test”, 10.0f, 10.0f);

class COpenglGlyphInfo
{
public:
unsigned short m_glyph; /* Potentially support 16-bit glyphs. /
unsigned char m_width;
unsigned char m_height;
char m_xoffset;
char m_yoffset;
char m_advance;
char m_dummy; /
Space holder for alignment reasons. */
short m_x;
short m_y;
};

class COpenglGlyphVertexInfo
{
public:
GLfloat t0[2];
GLfloat v0[2];
GLfloat t1[2];
GLfloat v1[2];
GLfloat t2[2];
GLfloat v2[2];
GLfloat t3[2];
GLfloat v3[2];
GLfloat advance;
};

class COpenGlFont
{
public:
COpenGlFont();
virtual ~COpenGlFont();

int		GetFontHeight();
int		GetTextWidth(const char* pString, int len=-1);
int		GetWidth(unsigned int key);
void	RenderString(const string& string, float x, float y);
bool 	CreateFont();
void	MakeCurrent();

protected:
void CreateVertexInfo();

int						m_textureObject;
int						m_textureWidth;
int						m_textureHeight;
int						m_maxHeight;
int						m_glyphCount;
int						m_minGlyph;
unsigned char*			m_pTextureData;
COpenglGlyphInfo*		m_pGlyphInfo;
COpenglGlyphVertexInfo*	m_pGlyphVertexInfo;
bool					m_bAntiAliased;

};

bool COpenGlFont::CreateFont(int pointSize, bool bBold, bool bItalic, LPCTSTR pszFontName, bool bAntiAliased)
{
HFONT hFont=NULL;
HDC hdc=NULL;
HDC hScreenDC=NULL;
HBITMAP hBitmap=NULL;
int i;
int size;
int saveDC;
GLYPHMETRICS gm;
MAT2 matrix={{0, 1}, {0, 0}, {0, 0}, {0, -1}};
BYTE* pBuffer=NULL;
bool result=false;
unsigned int uFormat;

m_bAntiAliased=bAntiAliased;
hScreenDC=CreateDC("DISPLAY", NULL, NULL, NULL);
if (hScreenDC)
	hdc=CreateCompatibleDC(hScreenDC);
if (hdc)
{
	saveDC=SaveDC(hdc);
	hBitmap=CreateCompatibleBitmap(hScreenDC, 100, 100);
}
if (hBitmap)
{
	SelectObject(hdc, (HGDIOBJ)hBitmap);
	hFont=::CreateFont(-(int)(pointSize*10.0f)*GetDeviceCaps(hdc, LOGPIXELSY)/720, 0, 0, 0, (bBold?700:400), (bItalic?1:0), 0, 0, ANSI_CHARSET, OUT_TT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH|FF_DONTCARE, pszFontName);
}
if (hFont)
{
	int maxCharHeight=0;
	int charX=0, charY=0;
	bool done=false;
	int textureSize=0;

	result=true;
	SelectObject(hdc, (HGDIOBJ)hFont);
	m_textureWidth=16;
	m_textureHeight=16;
	m_glyphCount=91;
	m_minGlyph=32;
	while (!done)
	{
		m_textureWidth*=2;
		m_textureHeight*=2;
		maxCharHeight=0;
		charX=0;
		charY=0;
		if (m_pTextureData)
		{
			delete[] m_pTextureData;
			m_pTextureData=NULL;
		}
		if (!m_pGlyphVertexInfo)
			m_pGlyphVertexInfo=new DjOpenglGlyphVertexInfo[m_glyphCount];
		if (!m_pGlyphInfo)
			m_pGlyphInfo=new COpenglGlyphInfo[m_glyphCount];
		if (m_bAntiAliased)
		{
			m_pTextureData=new unsigned char[m_textureWidth*m_textureHeight*2];
			textureSize=m_textureWidth*m_textureHeight*2;
			memset(m_pTextureData, 0, textureSize*sizeof(unsigned char));
			uFormat=GGO_GRAY8_BITMAP;
		}
		else
		{
			m_pTextureData=new unsigned char[m_textureWidth*m_textureHeight];
			textureSize=m_textureWidth*m_textureHeight;
			memset(m_pTextureData, 0, textureSize*sizeof(unsigned char));
			uFormat=GGO_BITMAP;
		}
		for (i=m_minGlyph; i<m_minGlyph+m_glyphCount; i++)
		{
			size=GetGlyphOutline(hdc, (UINT)i, uFormat, &gm, 0, NULL, &matrix);
			if (size>0)
			{
				if (pBuffer)
					delete[] pBuffer;
				pBuffer=new BYTE[size];
				if (GetGlyphOutline(hdc, (UINT)i, uFormat, &gm, size, pBuffer, &matrix)==GDI_ERROR)
				{
					string msg;		
					LPVOID lpMsgBuf;
					
					msg="Error: ";
					FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, GetLastError(), MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR) &lpMsgBuf, 0, NULL);
					msg+=(LPCTSTR)lpMsgBuf;
					MessageBox(NULL, msg.c_str(), "Error", MB_OK|MB_ICONEXCLAMATION);
					LocalFree(lpMsgBuf);
				}
			}
			if (GetGlyphOutline(hdc, (UINT)i, GGO_METRICS, &gm, 0, NULL, &matrix)!=GDI_ERROR)
			{
				ABC abc;

				GetCharABCWidths(hdc, i, i, &abc);
				if (gm.gmBlackBoxX>0 && gm.gmBlackBoxY>0)
				{
					int width,height;
					int h, w;

					width=gm.gmBlackBoxX;
					//Align DWORD
					if (uFormat==GGO_GRAY8_BITMAP)
					{
						width=size/gm.gmBlackBoxY;
					}
					else if (uFormat==GGO_BITMAP)
					{
						width=gm.gmBlackBoxX/32;
						if (gm.gmBlackBoxX%32)
							width+=1;
					}
					height=gm.gmBlackBoxY;
					if (size==0)
					{
						size=width*height;
						if (uFormat==GGO_BITMAP)
							size*=4;
						if (pBuffer)
							delete[] pBuffer;
						pBuffer=new BYTE[size];
						memset(pBuffer,0,size*sizeof(BYTE));
					}

					m_pGlyphInfo[i-m_minGlyph].m_glyph=i;
					if (uFormat==GGO_GRAY8_BITMAP)
						m_pGlyphInfo[i-m_minGlyph].m_width=width;
					else if (uFormat==GGO_BITMAP)
						m_pGlyphInfo[i-m_minGlyph].m_width=gm.gmBlackBoxX;						
					m_pGlyphInfo[i-m_minGlyph].m_height=gm.gmBlackBoxY;
					m_pGlyphInfo[i-m_minGlyph].m_xoffset=(char)abc.abcA;
					m_pGlyphInfo[i-m_minGlyph].m_yoffset=(char)-gm.gmptGlyphOrigin.y;
					m_pGlyphInfo[i-m_minGlyph].m_advance=(char)(abc.abcA+abc.abcB+abc.abcC);
					m_pGlyphInfo[i-m_minGlyph].m_dummy=0;
					if (charX+m_pGlyphInfo[i-m_minGlyph].m_width>=m_textureWidth)
					{
						charY+=maxCharHeight;
						charX=0;
						maxCharHeight=0;
					}
					if (charY+m_pGlyphInfo[i-m_minGlyph].m_height>=m_textureHeight)
					{
						break;
					}
					m_pGlyphInfo[i-m_minGlyph].m_x=charX;
					m_pGlyphInfo[i-m_minGlyph].m_y=charY;
					charX+=m_pGlyphInfo[i-m_minGlyph].m_width;
					maxCharHeight=max(maxCharHeight, m_pGlyphInfo[i-m_minGlyph].m_height);
					for (h=0;h<(int)m_pGlyphInfo[i-m_minGlyph].m_height;h++)
					{
						for (w=0;w<(int)m_pGlyphInfo[i-m_minGlyph].m_width;w++)
						{
							if (m_bAntiAliased)
							{
								int index=(m_pGlyphInfo[i-m_minGlyph].m_y+h)*m_textureWidth+m_pGlyphInfo[i-m_minGlyph].m_x+w;

								if (pBuffer[h*width+w])
								{
									ASSERT(index>=0 && index*2+1<textureSize);
									m_pTextureData[index*2]=255;
									m_pTextureData[index*2+1]=pBuffer[h*width+w]*255/64;
								}
							}
							else
							{
								int index=h*width*4+w/8;
								char mask=(1<<(7-(w%8)));

								if (pBuffer[index] & mask)
								{
									index=(m_pGlyphInfo[i-m_minGlyph].m_y+h)*m_textureWidth+m_pGlyphInfo[i-m_minGlyph].m_x+w;
									ASSERT(index>=0 && index<textureSize);
									m_pTextureData[index]=255;
								}
							}
						}
					}
					m_maxHeight=max(m_maxHeight,(int)gm.gmBlackBoxY);
				}
			}
		}
		if (i==m_minGlyph+m_glyphCount)
			done=true;
	}
	if (pBuffer)
	{
		delete[] pBuffer;
		pBuffer=NULL;
	}
	CreateVertexInfo();
}
if (hScreenDC)
	ReleaseDC(NULL, hScreenDC);
if (hdc)
{
	RestoreDC(hdc, saveDC);
	DeleteDC(hdc);
}
if (hBitmap)
	DeleteObject((HGDIOBJ)hBitmap);
if (hFont)
	DeleteObject((HGDIOBJ)hFont);
return result;

}

void COpenGlFont::MakeCurrent()
{
if (m_textureObject)
{
DjGetDesktop().SetCurrentTexture(m_textureObject);
}
else
{
glGenTextures(1, &m_textureObject);
glBindTexture(GL_TEXTURE_2D, m_textureObject);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
if (m_bAntiAliased)
glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE_ALPHA, m_textureWidth, m_textureHeight, 0, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, m_pTextureData);
else
glTexImage2D(GL_TEXTURE_2D, 0, 3, m_textureWidth, m_textureHeight, 0, GL_RGB, GL_UNSIGNED_BYTE, m_pTextureData);
}
}

void COpenGlFont::CreateVertexInfo()
{
GLfloat w=(GLfloat)m_textureWidth;
GLfloat h=(GLfloat)m_textureHeight;
GLfloat xStep=0.0f;
GLfloat yStep=0.0f;
int i;

if (m_pGlyphVertexInfo)
	delete[] m_pGlyphVertexInfo;
m_pGlyphVertexInfo=new DjOpenglGlyphVertexInfo[m_glyphCount];
for (i=0;i<m_glyphCount;i++)
{
	COpenglGlyphInfo* pGlyphInfo=&m_pGlyphInfo[i];

	m_pGlyphVertexInfo[i].t0[0]=pGlyphInfo->m_x/w+xStep;
	m_pGlyphVertexInfo[i].t0[1]=pGlyphInfo->m_y/h+yStep;
	m_pGlyphVertexInfo[i].v0[0]=pGlyphInfo->m_xoffset;
	m_pGlyphVertexInfo[i].v0[1]=pGlyphInfo->m_yoffset;
	m_pGlyphVertexInfo[i].t1[0]=(pGlyphInfo->m_x+pGlyphInfo->m_width)/w + xStep;
	m_pGlyphVertexInfo[i].t1[1]=pGlyphInfo->m_y/h + yStep;
	m_pGlyphVertexInfo[i].v1[0]=pGlyphInfo->m_xoffset + pGlyphInfo->m_width;
	m_pGlyphVertexInfo[i].v1[1]=pGlyphInfo->m_yoffset;
	m_pGlyphVertexInfo[i].t2[0]=(pGlyphInfo->m_x + pGlyphInfo->m_width)/w + xStep;
	m_pGlyphVertexInfo[i].t2[1]=(pGlyphInfo->m_y + pGlyphInfo->m_height)/h + yStep;
	m_pGlyphVertexInfo[i].v2[0]=pGlyphInfo->m_xoffset + pGlyphInfo->m_width;
	m_pGlyphVertexInfo[i].v2[1]=pGlyphInfo->m_yoffset + pGlyphInfo->m_height;
	m_pGlyphVertexInfo[i].t3[0]=pGlyphInfo->m_x/w + xStep;
	m_pGlyphVertexInfo[i].t3[1]=(pGlyphInfo->m_y + pGlyphInfo->m_height)/h + yStep;
	m_pGlyphVertexInfo[i].v3[0]=pGlyphInfo->m_xoffset;
	m_pGlyphVertexInfo[i].v3[1]=pGlyphInfo->m_yoffset + pGlyphInfo->m_height;
	m_pGlyphVertexInfo[i].advance=pGlyphInfo->m_advance;
}

}

int COpenGlFont::GetFontHeight()
{
return m_maxHeight+2;
}

int COpenGlFont::GetTextWidth(const char* pString, int len/=-1/)
{
int i, width=0;

if (pString)
{
	if (len<0)
		len=strlen(pString);
	for (i=0; i<len; i++)
		width+=(int)m_pGlyphVertexInfo[pString[i]-m_minGlyph].advance;
}
return width;

}

int COpenGlFont::GetWidth(unsigned int key)
{
return (int)m_pGlyphVertexInfo[key-m_minGlyph].advance;
}

void COpenGlFont::RenderString(LPCTSTR pString, float x, float y)
{
COpenglGlyphVertexInfo* tgvi;
int i;
int len=strlen(pString);

for (i=0;i<len;i++)
{
	tgvi=&m_pGlyphVertexInfo[pString[i]-m_minGlyph];

	glTexCoord2fv(tgvi->t0);
	glVertex2f(tgvi->v0[0]+x,tgvi->v0[1]+y);
	glTexCoord2fv(tgvi->t1);
	glVertex2f(tgvi->v1[0]+x,tgvi->v1[1]+y);
	glTexCoord2fv(tgvi->t2);
	glVertex2f(tgvi->v2[0]+x,tgvi->v2[1]+y);
	glTexCoord2fv(tgvi->t3);
	glVertex2f(tgvi->v3[0]+x,tgvi->v3[1]+y);
	x+=tgvi->advance;
}

}