So, news flash, I win at everything. Or actually, just font rendering. BUT I WON! I implemented the FreeType library to render luminance-alpha format pixels to power of 2 textures, then drew them as textured quads from display lists. My sources for technique are OGLFT and the excelent NeHeGL tutorial on the matter. If you want to see how I did it, I have added my code below.
font.h
#ifndef FONT_SYSTEM_H
#define FONT_SYSTEM_H
#include <stdlib.h>
#include <stdio.h>
struct rat_texture_font;
struct rat_glyph_font;
typedef struct rat_texture_font rat_texture_font;
typedef struct rat_glyph_font rat_glyph_font;
int rat_start_font_system();
void rat_stop_font_system();
rat_glyph_font *rat_glyph_font_load(char *filename,int pt);
rat_texture_font *rat_texture_font_load(rat_glyph_font *font);
void rat_glyph_font_destroy(rat_glyph_font *font);
void rat_texture_font_destroy(rat_texture_font *font);
void rat_texture_font_render_text(rat_texture_font *font,float x,float y,char *text);
#endif
cfont.c
#include <stdlib.h>
#include <stdio.h>
#include <ft2build.h>
#include <freetype/freetype.h>
#include <freetype/ftglyph.h>
#include <freetype/ftoutln.h>
#include <freetype/fttrigon.h>
#include <GL/gl.h>
static FT_Library ftlib;
typedef struct rat_texture_font
{
float pt;
unsigned int *textures;
unsigned int list_base;
} rat_texture_font;
typedef struct rat_glyph_font
{
float pt;
FT_Face face;
} rat_glyph_font;
int rat_start_font_system()
{
return !(FT_Init_FreeType(&ftlib));
}
void rat_stop_font_system()
{
FT_Done_FreeType(ftlib);
}
rat_glyph_font *rat_glyph_font_load(char *filename,int pt);
rat_texture_font *rat_texture_font_load(rat_glyph_font *font);
void rat_glyph_font_destroy(rat_glyph_font *font);
void rat_texture_font_destroy(rat_texture_font *font);
rat_glyph_font *rat_glyph_font_load(char *filename,int pt)
{
rat_glyph_font *font=(rat_glyph_font *)malloc(sizeof(rat_glyph_font));
printf("Loading font from file \"%s\" at ptsize %i...",filename,pt);
// load the font from the file
if (FT_New_Face(ftlib,filename,0,&(font->face)))
{
printf("failed load!\n");
free((void *)font);
return NULL;
}
// freetype measures fonts in 64ths of pixels, which
// I will never understand. 6 left bit shift multiplies
// the pt size by 64.
FT_Set_Char_Size(font->face,pt<<6,pt<<6,96,96);
font->pt=pt;
printf("done.\n");
return font;
}
void rat_glyph_font_destroy(rat_glyph_font *font)
{
printf("Destroying glyph font...");
FT_Done_Face(font->face);
free((void *)font);
printf("done.\n");
}
inline static unsigned int _pow2(unsigned int i)
{
register unsigned int p2;
for (p2=1; p2<i; p2<<=1);
return p2;
}
static int make_glyph_texture(rat_glyph_font *gf,rat_texture_font *tf,unsigned char ch)
{
register unsigned int i,j;
FT_Face face=gf->face;
unsigned int *textures=tf->textures;
unsigned int list_base=tf->list_base;
if (FT_Load_Glyph(face,FT_Get_Char_Index(face,ch),FT_LOAD_DEFAULT))
return 0;
FT_Glyph glyph;
if (FT_Get_Glyph(face->glyph,&glyph))
return 0;
FT_Glyph_To_Bitmap(&glyph,ft_render_mode_normal,0,1);
FT_BitmapGlyph bitmap_glyph=(FT_BitmapGlyph)glyph;
FT_Bitmap bitmap=bitmap_glyph->bitmap;
unsigned int width=_pow2(bitmap.width);
unsigned int height=_pow2(bitmap.rows);
GLubyte* expanded_data=(GLubyte *)malloc(sizeof(GLubyte)*2*width*height);
for (j=0; j<height;j++)
{
for (i=0; i<width; i++)
{
expanded_data[2*(i+j*width)]=
expanded_data[2*(i+j*width)+1]=
(i>=bitmap.width||j>=bitmap.rows)?
0:bitmap.buffer[i+bitmap.width*j];
}
}
glBindTexture(GL_TEXTURE_2D,textures[ch]);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
glTexImage2D(GL_TEXTURE_2D,0,GL_ALPHA,width,height,
0,GL_LUMINANCE_ALPHA,GL_UNSIGNED_BYTE,expanded_data);
free((void *)expanded_data);
glNewList(list_base+ch,GL_COMPILE);
glBindTexture(GL_TEXTURE_2D,textures[ch]);
glTranslatef(bitmap_glyph->left,0,0);
glPushMatrix();
glTranslatef(0,bitmap_glyph->top-bitmap.rows,0);
float texx=(float)bitmap.width/(float)width;
float texy=(float)bitmap.rows/(float)height;
glBegin(GL_QUADS);
glTexCoord2f(0,0); glVertex2f(0,bitmap.rows);
glTexCoord2f(0,texy); glVertex2f(0,0);
glTexCoord2f(texx,texy); glVertex2f(bitmap.width,0);
glTexCoord2f(texx,0); glVertex2f(bitmap.width,bitmap.rows);
glEnd();
glPopMatrix();
glTranslatef(face->glyph->advance.x>>6,0,0);
glEndList();
return 1;
}
rat_texture_font *rat_texture_font_load(rat_glyph_font *font)
{
register unsigned char i;
rat_texture_font *tf=(rat_texture_font *)malloc(sizeof(rat_texture_font));
tf->pt=font->pt;
// prepare the OpenGL textures / display lists
tf->list_base=glGenLists(255);
tf->textures=(unsigned int *)malloc(sizeof(unsigned int)*255);
glGenTextures(255,tf->textures);
for (i=0;i<255;i++)
{
if (!make_glyph_texture(font,tf,i))
{
glDeleteLists(tf->list_base,255);
glDeleteTextures(255,tf->textures);
free((void *)tf->textures);
free((void *)tf);
return NULL;
}
}
return tf;
}
void rat_texture_font_destroy(rat_texture_font *font)
{
glDeleteLists(font->list_base,255);
glDeleteTextures(255,font->textures);
free((void *)font->textures);
free((void *)font);
}
void rat_texture_font_render_text(rat_texture_font *font,float x,float y,char *text)
{
const char *ch;
register unsigned int i;
glPushAttrib(GL_LIST_BIT|GL_CURRENT_BIT|GL_ENABLE_BIT|GL_TRANSFORM_BIT);
glMatrixMode(GL_MODELVIEW);
glDisable(GL_LIGHTING);
glEnable(GL_TEXTURE_2D);
glDisable(GL_DEPTH_TEST);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
glListBase(font->list_base);
glPushMatrix();
glScalef(1,-1,1);
glTranslatef(x,-y,0);
glCallLists(strlen(text),GL_UNSIGNED_BYTE,text);
glPopMatrix();
glPopAttrib();
}