//http://www.freetype.org/freetype2/docs/tutorial/step2.html
#include <ft2build.h>
#include <freetype/freetype.h>
#include <freetype/ftglyph.h>
#include <freetype/ftoutln.h>
#include <freetype/fttrigon.h>
#include <vector>
#include <iostream>
#include "TextTexture2D.h"
using namespace std;
namespace ssf
{
///This function gets the first power of 2 >= the
///int that we pass it.
inline int next_p2(int a)
{
int rval=1;
while(rval<a) rval<<=1;
return rval;
}
void TextTexture2D::draw(FT_Bitmap &bitmap, int x, int y, GLubyte *data)
{
//TODO - use memcpy or mmx/sse to speed this up
for(int j = 0; j < bitmap.rows; j++)
{
for(int i = 0; i < bitmap.width; i++)
{
if(i+x > 0 && i+x < texWidth && j+y > 0 && j+y < texHeight)
{
data[(i+x+(j+y)*texWidth)] = bitmap.buffer[i + bitmap.width*j];
}
}
}
}
static void flip(GLubyte *src, GLubyte *dest, int w, int h, int pSz)
{
for(int i = 0; i < h; i++)
{
memcpy(dest+(h-1-i)*w*pSz, src+(i*w*pSz), w*pSz);
}
}
TextTexture2D::TextTexture2D(const string &text,
const string &fontFilename, int fontPointSz, int texWidth, int texHeight)
{
vector<float2> textureAtlasOffsets;
FT_Library library;
if(FT_Init_FreeType( &library ))
throw std::runtime_error("FT_Init_FreeType failed");
//TODO cache this stuff better
FT_Face face;
if(FT_New_Face(library, fontFilename.c_str(), 0, &face))
throw std::runtime_error("FT_New_Face failed (there is probably a problem with your font file)");
//For some twisted reason, Freetype measures font size
//in terms of 1/64ths of pixels. Thus, to make a font
//h pixels high, we need to request a size of h*64.
FT_Set_Char_Size(face, fontPointSz << 6, fontPointSz << 6, 96, 96);
GLubyte *data = new GLubyte[texWidth * texHeight];
memset(data, 0, texWidth*texHeight);
width = this->texWidth = texWidth;
height = this->texHeight = texHeight;
alpha = true;
mipmap = false;
FT_GlyphSlot slot = face->glyph; /* a small shortcut */
FT_UInt glyph_index;
FT_Bool use_kerning = FT_HAS_KERNING(face);
FT_UInt previous = 0;
int pen_x = 0, pen_y = texHeight, error;
for(int n = 0; n < text.length(); n++)
{
/* convert character code to glyph index */
glyph_index = FT_Get_Char_Index(face, text[n]);
/* retrieve kerning distance and move pen position */
if(use_kerning && previous && glyph_index)
{
FT_Vector delta;
FT_Get_Kerning(face, previous, glyph_index,
FT_KERNING_DEFAULT, &delta);
pen_x += delta.x >> 6;
}
/* load glyph image into the slot (erase previous one) */
error = FT_Load_Glyph(face, glyph_index, FT_LOAD_RENDER);
if(error)
continue; /* ignore errors */
/* now draw to our target surface */
draw(slot->bitmap, pen_x + slot->bitmap_left, pen_y - slot->bitmap_top, data);
/* increment pen position */
pen_x += slot->advance.x >> 6;
/* record current glyph index */
previous = glyph_index;
}
GLubyte *dataFlipped = new GLubyte[texWidth * texHeight];
flip(data, dataFlipped, texWidth, texHeight, 1);
glGenTextures(1, &tex);
glBindTexture( GL_TEXTURE_2D, tex);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
//Here we actually create the texture itself
glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, width, height,
0, GL_RED, GL_UNSIGNED_BYTE, dataFlipped);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
delete dataFlipped;
delete data;
FT_Done_Face(face);
FT_Done_FreeType(library);
optimalTexCoords = NULL;
}
}