h1

TRIUMPH!

September 12, 2008

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();
}
Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: