--- /dev/null
+#include <amaltheia/Graphics.h>
+
+#include <GL/gl.h>
+#include <GL/glu.h>
+
+Graphics *g;
+
+#include <cassert>
+#include <string> // For memset
+#include <ft2build.h>
+#include <freetype/freetype.h>
+#include <freetype/ftglyph.h>
+#include <freetype/ftoutln.h>
+#include <freetype/fttrigon.h>
+
+//#include "FTGLTextureFont.h"
+//#include "FTTextureGlyph.h"
+typedef double FTGL_DOUBLE;
+typedef float FTGL_FLOAT;
+
+// Fixes for deprecated identifiers in 2.1.5
+#ifndef FT_OPEN_MEMORY
+#define FT_OPEN_MEMORY (FT_Open_Flags)1
+#endif
+
+#ifndef FT_RENDER_MODE_MONO
+#define FT_RENDER_MODE_MONO ft_render_mode_mono
+#endif
+
+#ifndef FT_RENDER_MODE_NORMAL
+#define FT_RENDER_MODE_NORMAL ft_render_mode_normal
+#endif
+
+
+
+
+
+inline unsigned int NextPowerOf2( unsigned int in)
+{
+ in -= 1;
+
+ in |= in >> 16;
+ in |= in >> 8;
+ in |= in >> 4;
+ in |= in >> 2;
+ in |= in >> 1;
+
+ return in + 1;
+}
+
+
+class FTPoint
+{
+ public:
+ FTPoint()
+ {
+ values[0] = 0;
+ values[1] = 0;
+ values[2] = 0;
+ }
+ FTPoint( const double x, const double y, const double z)
+ {
+ values[0] = x;
+ values[1] = y;
+ values[2] = z;
+ }
+
+ FTPoint( const FT_Vector& ft_vector)
+ {
+ values[0] = ft_vector.x;
+ values[1] = ft_vector.y;
+ values[2] = 0;
+ }
+
+ FTPoint& operator += ( const FTPoint& point)
+ {
+ values[0] += point.values[0];
+ values[1] += point.values[1];
+ values[2] += point.values[2];
+
+ return *this;
+ }
+
+ FTPoint operator + ( const FTPoint& point)
+ {
+ FTPoint temp;
+ temp.values[0] = values[0] + point.values[0];
+ temp.values[1] = values[1] + point.values[1];
+ temp.values[2] = values[2] + point.values[2];
+
+ return temp;
+ }
+
+ FTPoint operator * ( double multiplier)
+ {
+ FTPoint temp;
+ temp.values[0] = values[0] * multiplier;
+ temp.values[1] = values[1] * multiplier;
+ temp.values[2] = values[2] * multiplier;
+
+ return temp;
+ }
+
+
+ friend FTPoint operator*( double multiplier, FTPoint& point);
+ friend bool operator == ( const FTPoint &a, const FTPoint &b);
+ friend bool operator != ( const FTPoint &a, const FTPoint &b);
+ operator const double*() const
+ {
+ return values;
+ }
+ void X( double x) { values[0] = x;};
+ void Y( double y) { values[1] = y;};
+ void Z( double z) { values[2] = z;};
+ double X() const { return values[0];};
+ double Y() const { return values[1];};
+ double Z() const { return values[2];};
+
+ private:
+ double values[3];
+};
+
+bool operator == ( const FTPoint &a, const FTPoint &b)
+{
+ return((a.values[0] == b.values[0]) && (a.values[1] == b.values[1]) && (a.values[2] == b.values[2]));
+}
+
+bool operator != ( const FTPoint &a, const FTPoint &b)
+{
+ return((a.values[0] != b.values[0]) || (a.values[1] != b.values[1]) || (a.values[2] != b.values[2]));
+}
+
+FTPoint operator*( double multiplier, FTPoint& point)
+{
+ return point * multiplier;
+}
+
+
+class FTBBox
+{
+ public:
+
+ FTBBox()
+ : lowerX(0.0f),
+ lowerY(0.0f),
+ lowerZ(0.0f),
+ upperX(0.0f),
+ upperY(0.0f),
+ upperZ(0.0f)
+ {}
+
+ FTBBox( float lx, float ly, float lz, float ux, float uy, float uz)
+ : lowerX(lx),
+ lowerY(ly),
+ lowerZ(lz),
+ upperX(ux),
+ upperY(uy),
+ upperZ(uz)
+ {}
+
+
+ FTBBox( FT_GlyphSlot glyph)
+ : lowerX(0.0f),
+ lowerY(0.0f),
+ lowerZ(0.0f),
+ upperX(0.0f),
+ upperY(0.0f),
+ upperZ(0.0f)
+ {
+ FT_BBox bbox;
+ FT_Outline_Get_CBox( &(glyph->outline), &bbox);
+
+ lowerX = static_cast<float>( bbox.xMin) / 64.0f;
+ lowerY = static_cast<float>( bbox.yMin) / 64.0f;
+ lowerZ = 0.0f;
+ upperX = static_cast<float>( bbox.xMax) / 64.0f;
+ upperY = static_cast<float>( bbox.yMax) / 64.0f;
+ upperZ = 0.0f;
+
+ }
+
+ ~FTBBox()
+ {}
+
+ FTBBox& Move( FTPoint distance)
+ {
+ lowerX += distance.X();
+ lowerY += distance.Y();
+ lowerZ += distance.Z();
+ upperX += distance.X();
+ upperY += distance.Y();
+ upperZ += distance.Z();
+ return *this;
+ }
+
+ FTBBox& operator += ( const FTBBox& bbox)
+ {
+ lowerX = bbox.lowerX < lowerX? bbox.lowerX: lowerX;
+ lowerY = bbox.lowerY < lowerY? bbox.lowerY: lowerY;
+ lowerZ = bbox.lowerZ < lowerZ? bbox.lowerZ: lowerZ;
+ upperX = bbox.upperX > upperX? bbox.upperX: upperX;
+ upperY = bbox.upperY > upperY? bbox.upperY: upperY;
+ upperZ = bbox.upperZ > upperZ? bbox.upperZ: upperZ;
+
+ return *this;
+ }
+
+ void SetDepth( float depth)
+ {
+ upperZ = lowerZ + depth;
+ }
+
+ float lowerX, lowerY, lowerZ, upperX, upperY, upperZ;
+ protected:
+
+ private:
+};
+
+
+
+
+
+class FTGlyph
+{
+ public:
+ FTGlyph( FT_GlyphSlot glyph, bool useList = true)
+ {
+ useDisplayList = useList;
+ err = 0;
+ if( glyph)
+ {
+ bBox = FTBBox( glyph);
+ advance = FTPoint( glyph->advance.x / 64.0f, glyph->advance.y / 64.0f, 0.0f);
+ }
+ }
+
+ virtual FTGlyph::~FTGlyph()
+ {}
+
+ virtual const FTPoint& Render( const FTPoint& pen) = 0;
+ const FTPoint& Advance() const { return advance;}
+ const FTBBox& BBox() const { return bBox;}
+ FT_Error Error() const { return err;}
+
+ protected:
+ FTPoint advance;
+ FTBBox bBox;
+ bool useDisplayList;
+ FT_Error err;
+
+ private:
+};
+
+
+
+
+
+
+class FTLibrary
+{
+ public:
+ static const FTLibrary& Instance();
+ const FT_Library* const GetLibrary() const { return library;}
+ FT_Error Error() const { return err;}
+ ~FTLibrary();
+
+ private:
+ FTLibrary();
+ FTLibrary( const FT_Library&){}
+ FTLibrary& operator=( const FT_Library&) { return *this; }
+
+ bool Initialise();
+ FT_Library* library;
+ FT_Error err;
+
+};
+
+
+
+
+const FTLibrary& FTLibrary::Instance()
+{
+ static FTLibrary ftlib;
+ return ftlib;
+}
+
+
+FTLibrary::~FTLibrary()
+{
+ if( library != 0)
+ {
+ FT_Done_FreeType( *library);
+
+ delete library;
+ library= 0;
+ }
+
+// if( manager != 0)
+// {
+// FTC_Manager_Done( manager );
+ //
+// delete manager;
+// manager= 0;
+// }
+}
+
+
+FTLibrary::FTLibrary()
+ : library(0),
+ err(0)
+{
+ Initialise();
+}
+
+
+bool FTLibrary::Initialise()
+{
+ if( library != 0)
+ return true;
+
+ library = new FT_Library;
+
+ err = FT_Init_FreeType( library);
+ if( err)
+ {
+ delete library;
+ library = 0;
+ return false;
+ }
+
+// FTC_Manager* manager;
+ //
+// if( FTC_Manager_New( lib, 0, 0, 0, my_face_requester, 0, manager )
+// {
+// delete manager;
+// manager= 0;
+// return false;
+// }
+
+ return true;
+}
+
+
+
+
+class FTSize
+{
+ public:
+ FTSize();
+ virtual ~FTSize();
+ bool CharSize( FT_Face* face, unsigned int point_size, unsigned int x_resolution, unsigned int y_resolution);
+ unsigned int CharSize() const;
+ float Ascender() const;
+ float Descender() const;
+ float Height() const;
+ float Width() const;
+ float Underline() const;
+ FT_Error Error() const { return err; }
+
+ private:
+ FT_Face* ftFace;
+ FT_Size ftSize;
+ unsigned int size;
+ unsigned int xResolution;
+ unsigned int yResolution;
+ FT_Error err;
+
+};
+
+
+
+FTSize::FTSize()
+ : ftFace(0),
+ ftSize(0),
+ size(0),
+ xResolution(0),
+ yResolution(0),
+ err(0)
+{}
+
+
+ FTSize::~FTSize()
+ {}
+
+
+ bool FTSize::CharSize( FT_Face* face, unsigned int pointSize, unsigned int xRes, unsigned int yRes )
+ {
+ if( size != pointSize || xResolution != xRes || yResolution != yRes)
+ {
+ err = FT_Set_Char_Size( *face, 0L, pointSize * 64, xResolution, yResolution);
+
+ if( !err)
+ {
+ ftFace = face;
+ size = pointSize;
+ xResolution = xRes;
+ yResolution = yRes;
+ ftSize = (*ftFace)->size;
+ }
+ else
+ {
+ ftFace = 0;
+ size = 0;
+ xResolution = 0;
+ yResolution = 0;
+ ftSize = 0;
+ }
+ }
+
+ return !err;
+ }
+
+
+ unsigned int FTSize::CharSize() const
+ {
+ return size;
+ }
+
+
+ float FTSize::Ascender() const
+ {
+ return ftSize == 0 ? 0.0f : static_cast<float>( ftSize->metrics.ascender) / 64.0f;
+ }
+
+
+ float FTSize::Descender() const
+ {
+ return ftSize == 0 ? 0.0f : static_cast<float>( ftSize->metrics.descender) / 64.0f;
+ }
+
+
+ float FTSize::Height() const
+ {
+ if( 0 == ftSize)
+ {
+ return 0.0f;
+ }
+
+ if( FT_IS_SCALABLE((*ftFace)))
+ {
+ return ( (*ftFace)->bbox.yMax - (*ftFace)->bbox.yMin) * ( (float)ftSize->metrics.y_ppem / (float)(*ftFace)->units_per_EM);
+ }
+ else
+ {
+ return static_cast<float>( ftSize->metrics.height) / 64.0f;
+ }
+ }
+
+
+ float FTSize::Width() const
+ {
+ if( 0 == ftSize)
+ {
+ return 0.0f;
+ }
+
+ if( FT_IS_SCALABLE((*ftFace)))
+ {
+ return ( (*ftFace)->bbox.xMax - (*ftFace)->bbox.xMin) * ( static_cast<float>(ftSize->metrics.x_ppem) / static_cast<float>((*ftFace)->units_per_EM));
+ }
+ else
+ {
+ return static_cast<float>( ftSize->metrics.max_advance) / 64.0f;
+ }
+ }
+
+
+ float FTSize::Underline() const
+ {
+ return 0.0f;
+ }
+
+
+
+
+class FTFace
+{
+ public:
+
+ FTFace( const char* fontFilePath);
+ FTFace( const unsigned char *pBufferBytes, size_t bufferSizeInBytes );
+ virtual ~FTFace();
+
+ bool Attach( const char* fontFilePath);
+ bool Attach( const unsigned char *pBufferBytes, size_t bufferSizeInBytes);
+ FT_Face* Face() const { return ftFace;}
+ const FTSize& Size( const unsigned int size, const unsigned int res);
+ unsigned int CharMapCount();
+ FT_Encoding* CharMapList();
+ FTPoint KernAdvance( unsigned int index1, unsigned int index2);
+ FT_GlyphSlot Glyph( unsigned int index, FT_Int load_flags);
+ unsigned int GlyphCount() const { return numGlyphs;}
+ FT_Error Error() const { return err; }
+
+ private:
+ FT_Face* ftFace;
+ FTSize charSize;
+ int numGlyphs;
+ FT_Encoding* fontEncodingList;
+ bool hasKerningTable;
+ FT_Error err;
+};
+
+FTFace::FTFace( const char* fontFilePath)
+ : numGlyphs(0),
+ fontEncodingList(0),
+ err(0)
+{
+ const FT_Long DEFAULT_FACE_INDEX = 0;
+ ftFace = new FT_Face;
+
+ err = FT_New_Face( *FTLibrary::Instance().GetLibrary(), fontFilePath, DEFAULT_FACE_INDEX, ftFace);
+
+ if( err)
+ {
+ delete ftFace;
+ ftFace = 0;
+ }
+ else
+ {
+ numGlyphs = (*ftFace)->num_glyphs;
+ hasKerningTable = FT_HAS_KERNING((*ftFace));
+ }
+}
+
+
+FTFace::FTFace( const unsigned char *pBufferBytes, size_t bufferSizeInBytes)
+ : numGlyphs(0),
+ err(0)
+{
+ const FT_Long DEFAULT_FACE_INDEX = 0;
+ ftFace = new FT_Face;
+
+ err = FT_New_Memory_Face( *FTLibrary::Instance().GetLibrary(), (FT_Byte *)pBufferBytes, bufferSizeInBytes, DEFAULT_FACE_INDEX, ftFace);
+
+ if( err)
+ {
+ delete ftFace;
+ ftFace = 0;
+ }
+ else
+ {
+ numGlyphs = (*ftFace)->num_glyphs;
+ }
+}
+
+
+FTFace::~FTFace()
+{
+ if( ftFace)
+ {
+ FT_Done_Face( *ftFace);
+ delete ftFace;
+ ftFace = 0;
+ }
+}
+
+
+bool FTFace::Attach( const char* fontFilePath)
+{
+ err = FT_Attach_File( *ftFace, fontFilePath);
+ return !err;
+}
+
+
+bool FTFace::Attach( const unsigned char *pBufferBytes, size_t bufferSizeInBytes)
+{
+ FT_Open_Args open;
+
+ open.flags = FT_OPEN_MEMORY;
+ open.memory_base = (FT_Byte *)pBufferBytes;
+ open.memory_size = bufferSizeInBytes;
+
+ err = FT_Attach_Stream( *ftFace, &open);
+ return !err;
+}
+
+
+const FTSize& FTFace::Size( const unsigned int size, const unsigned int res)
+{
+ charSize.CharSize( ftFace, size, res, res);
+ err = charSize.Error();
+
+ return charSize;
+}
+
+
+unsigned int FTFace::CharMapCount()
+{
+ return (*ftFace)->num_charmaps;
+}
+
+
+FT_Encoding* FTFace::CharMapList()
+{
+ if( 0 == fontEncodingList)
+ {
+ fontEncodingList = new FT_Encoding[CharMapCount()];
+ for( size_t encodingIndex = 0; encodingIndex < CharMapCount(); ++encodingIndex)
+ {
+ fontEncodingList[encodingIndex] = (*ftFace)->charmaps[encodingIndex]->encoding;
+ }
+ }
+
+ return fontEncodingList;
+}
+
+
+FTPoint FTFace::KernAdvance( unsigned int index1, unsigned int index2)
+{
+ float x, y;
+ x = y = 0.0f;
+
+ if( hasKerningTable && index1 && index2)
+ {
+ FT_Vector kernAdvance;
+ kernAdvance.x = kernAdvance.y = 0;
+
+ err = FT_Get_Kerning( *ftFace, index1, index2, ft_kerning_unfitted, &kernAdvance);
+ if( !err)
+ {
+ x = static_cast<float>( kernAdvance.x) / 64.0f;
+ y = static_cast<float>( kernAdvance.y) / 64.0f;
+ }
+ }
+
+ return FTPoint( x, y, 0.0);
+}
+
+
+FT_GlyphSlot FTFace::Glyph( unsigned int index, FT_Int load_flags)
+{
+ err = FT_Load_Glyph( *ftFace, index, load_flags);
+ if( err)
+ {
+ return NULL;
+ }
+
+ return (*ftFace)->glyph;
+}
+
+
+
+
+
+
+
+
+
+ template <typename FT_VECTOR_ITEM_TYPE> class FTVector
+{
+ public:
+ typedef FT_VECTOR_ITEM_TYPE value_type;
+ typedef value_type& reference;
+ typedef const value_type& const_reference;
+ typedef value_type* iterator;
+ typedef const value_type* const_iterator;
+ typedef size_t size_type;
+
+ FTVector()
+ {
+ Capacity = Size = 0;
+ Items = 0;
+ }
+
+
+ virtual ~FTVector()
+ {
+ clear();
+ }
+
+ FTVector& operator =(const FTVector& v)
+ {
+ reserve(v.capacity());
+
+ iterator ptr = begin();
+ const_iterator vbegin = v.begin();
+ const_iterator vend = v.end();
+
+ while( vbegin != vend)
+ {
+ *ptr++ = *vbegin++;
+ }
+
+ Size = v.size();
+ return *this;
+ }
+
+ size_type size() const
+ {
+ return Size;
+ }
+
+ size_type capacity() const
+ {
+ return Capacity;
+ }
+
+ iterator begin()
+ {
+ return Items;
+ }
+
+ const_iterator begin() const
+ {
+ return Items;
+ }
+
+ iterator end()
+ {
+ return begin() + size();
+ }
+
+ const_iterator end() const
+ {
+ return begin() + size();
+ }
+
+ bool empty() const
+ {
+ return size() == 0;
+ }
+
+ reference operator [](size_type pos)
+ {
+ return( *(begin() + pos));
+ }
+
+ const_reference operator []( size_type pos) const
+ {
+ return( *(begin() + pos));
+ }
+
+ void clear()
+ {
+ if( Capacity)
+ {
+ delete [] Items;
+ Capacity = Size = 0;
+ Items = 0;
+ }
+ }
+
+ void reserve( size_type n)
+ {
+ if( capacity() < n)
+ {
+ expand(n);
+ }
+ }
+
+ void push_back(const value_type& x)
+ {
+ if( size() == capacity())
+ {
+ expand();
+ }
+
+ ( *this)[size()] = x;
+ ++Size;
+ }
+
+ void resize(size_type n, value_type x)
+ {
+ if( n == size())
+ {
+ return;
+ }
+
+ reserve(n);
+ iterator begin, end;
+
+ if( n >= Size)
+ {
+ begin = this->end();
+ end = this->begin() + n;
+ }
+ else
+ {
+ begin = this->begin() + n;
+ end = this->end();
+ }
+
+ while( begin != end)
+ {
+ *begin++ = x;
+ }
+
+ Size = n;
+ }
+
+
+ private:
+ void expand(size_type capacity_hint = 0)
+ {
+ size_type new_capacity =( capacity() == 0) ? 256 : capacity()* 2;
+ if( capacity_hint)
+ {
+ while( new_capacity < capacity_hint)
+ {
+ new_capacity *= 2;
+ }
+ }
+
+ value_type *new_items = new value_type[new_capacity];
+
+ iterator begin = this->begin();
+ iterator end = this->end();
+ value_type *ptr = new_items;
+
+ while( begin != end)
+ {
+ *ptr++ = *begin++;
+ }
+
+ if( Capacity)
+ {
+ delete [] Items;
+ }
+
+ Items = new_items;
+ Capacity = new_capacity;
+ }
+
+ size_type Capacity;
+ size_type Size;
+ value_type* Items;
+};
+
+
+
+
+
+
+
+
+
+
+
+
+
+class FTCharToGlyphIndexMap
+{
+ public:
+
+ typedef unsigned long CharacterCode;
+ typedef signed long GlyphIndex;
+
+ enum
+ {
+ NumberOfBuckets = 256,
+ BucketSize = 256,
+ IndexNotFound = -1
+ };
+
+ FTCharToGlyphIndexMap()
+ {
+ this->Indices = 0;
+ }
+
+ virtual ~FTCharToGlyphIndexMap()
+ {
+ if( this->Indices)
+ {
+ // Free all buckets
+ this->clear();
+
+ // Free main structure
+ delete [] this->Indices;
+ this->Indices = 0;
+ }
+ }
+
+ void clear()
+ {
+ if(this->Indices)
+ {
+ for( int i = 0; i < FTCharToGlyphIndexMap::NumberOfBuckets; i++)
+ {
+ if( this->Indices[i])
+ {
+ delete [] this->Indices[i];
+ this->Indices[i] = 0;
+ }
+ }
+ }
+ }
+
+ const GlyphIndex find( CharacterCode c)
+ {
+ if( !this->Indices)
+ {
+ return 0;
+ }
+
+ // Find position of char code in buckets
+ div_t pos = div( c, FTCharToGlyphIndexMap::BucketSize);
+
+ if( !this->Indices[pos.quot])
+ {
+ return 0;
+ }
+
+ const FTCharToGlyphIndexMap::GlyphIndex *ptr = &this->Indices[pos.quot][pos.rem];
+ if( *ptr == FTCharToGlyphIndexMap::IndexNotFound)
+ {
+ return 0;
+ }
+
+ return *ptr;
+ }
+
+ void insert( CharacterCode c, GlyphIndex g)
+ {
+ if( !this->Indices)
+ {
+ this->Indices = new GlyphIndex* [FTCharToGlyphIndexMap::NumberOfBuckets];
+ for( int i = 0; i < FTCharToGlyphIndexMap::NumberOfBuckets; i++)
+ {
+ this->Indices[i] = 0;
+ }
+ }
+
+ // Find position of char code in buckets
+ div_t pos = div(c, FTCharToGlyphIndexMap::BucketSize);
+
+ // Allocate bucket if does not exist yet
+ if( !this->Indices[pos.quot])
+ {
+ this->Indices[pos.quot] = new GlyphIndex [FTCharToGlyphIndexMap::BucketSize];
+ for( int i = 0; i < FTCharToGlyphIndexMap::BucketSize; i++)
+ {
+ this->Indices[pos.quot][i] = FTCharToGlyphIndexMap::IndexNotFound;
+ }
+ }
+
+ this->Indices[pos.quot][pos.rem] = g;
+ }
+
+ private:
+ GlyphIndex** Indices;
+};
+
+
+
+
+
+class FTCharmap
+{
+ public:
+ FTCharmap( FTFace* face);
+ virtual ~FTCharmap();
+
+ FT_Encoding Encoding() const { return ftEncoding;}
+ bool CharMap( FT_Encoding encoding);
+ unsigned int GlyphListIndex( const unsigned int characterCode);
+ unsigned int FontIndex( const unsigned int characterCode);
+ void InsertIndex( const unsigned int characterCode, const unsigned int containerIndex);
+ FT_Error Error() const { return err;}
+
+ private:
+ FT_Encoding ftEncoding;
+ const FT_Face ftFace;
+ typedef FTCharToGlyphIndexMap CharacterMap;
+ CharacterMap charMap;
+
+ FT_Error err;
+
+};
+
+
+
+
+
+FTCharmap::FTCharmap( FTFace* face)
+ : ftFace( *(face->Face())),
+ err(0)
+{
+ if( !ftFace->charmap)
+ {
+ err = FT_Set_Charmap( ftFace, ftFace->charmaps[0]);
+ }
+
+ ftEncoding = ftFace->charmap->encoding;
+}
+
+
+FTCharmap::~FTCharmap()
+{
+ charMap.clear();
+}
+
+
+bool FTCharmap::CharMap( FT_Encoding encoding)
+{
+ if( ftEncoding == encoding)
+ {
+ return true;
+ }
+
+ err = FT_Select_Charmap( ftFace, encoding );
+
+ if( !err)
+ {
+ ftEncoding = encoding;
+ }
+ else
+ {
+ ftEncoding = ft_encoding_none;
+ }
+
+ charMap.clear();
+ return !err;
+}
+
+
+unsigned int FTCharmap::GlyphListIndex( unsigned int characterCode )
+{
+ return charMap.find( characterCode);
+}
+
+
+unsigned int FTCharmap::FontIndex( unsigned int characterCode )
+{
+ return FT_Get_Char_Index( ftFace, characterCode);
+}
+
+
+void FTCharmap::InsertIndex( const unsigned int characterCode, const unsigned int containerIndex)
+{
+ charMap.insert( characterCode, containerIndex);
+}
+
+
+
+
+class FTGlyphContainer
+{
+ typedef FTVector<FTGlyph*> GlyphVector;
+ public:
+ FTGlyphContainer( FTFace* face);
+ ~FTGlyphContainer();
+ bool CharMap( FT_Encoding encoding);
+ unsigned int FontIndex( const unsigned int characterCode ) const;
+ void Add( FTGlyph* glyph, const unsigned int characterCode);
+ const FTGlyph* const Glyph( const unsigned int characterCode) const;
+ FTBBox BBox( const unsigned int characterCode) const;
+ float Advance( const unsigned int characterCode, const unsigned int nextCharacterCode);
+
+ FTPoint Render( const unsigned int characterCode, const unsigned int nextCharacterCode, FTPoint penPosition);
+
+ FT_Error Error() const { return err;}
+
+ private:
+ FTFace* face;
+ FTCharmap* charMap;
+
+ GlyphVector glyphs;
+
+ FT_Error err;
+};
+
+
+FTGlyphContainer::FTGlyphContainer( FTFace* f)
+ : face(f),
+ err(0)
+{
+ glyphs.push_back( NULL);
+ charMap = new FTCharmap( face);
+}
+
+
+FTGlyphContainer::~FTGlyphContainer()
+{
+ GlyphVector::iterator glyphIterator;
+ for( glyphIterator = glyphs.begin(); glyphIterator != glyphs.end(); ++glyphIterator)
+ {
+ delete *glyphIterator;
+ }
+
+ glyphs.clear();
+ delete charMap;
+}
+
+
+bool FTGlyphContainer::CharMap( FT_Encoding encoding)
+{
+ bool result = charMap->CharMap( encoding);
+ err = charMap->Error();
+ return result;
+}
+
+
+unsigned int FTGlyphContainer::FontIndex( const unsigned int characterCode) const
+{
+ return charMap->FontIndex( characterCode);
+}
+
+
+void FTGlyphContainer::Add( FTGlyph* tempGlyph, const unsigned int characterCode)
+{
+ charMap->InsertIndex( characterCode, glyphs.size());
+ glyphs.push_back( tempGlyph);
+}
+
+
+const FTGlyph* const FTGlyphContainer::Glyph( const unsigned int characterCode) const
+{
+ signed int index = charMap->GlyphListIndex( characterCode);
+ return glyphs[index];
+}
+
+
+FTBBox FTGlyphContainer::BBox( const unsigned int characterCode) const
+{
+ return glyphs[charMap->GlyphListIndex( characterCode)]->BBox();
+}
+
+
+float FTGlyphContainer::Advance( const unsigned int characterCode, const unsigned int nextCharacterCode)
+{
+ unsigned int left = charMap->FontIndex( characterCode);
+ unsigned int right = charMap->FontIndex( nextCharacterCode);
+
+ float width = face->KernAdvance( left, right).X();
+ width += glyphs[charMap->GlyphListIndex( characterCode)]->Advance().X();
+
+ return width;
+}
+
+
+FTPoint FTGlyphContainer::Render( const unsigned int characterCode, const unsigned int nextCharacterCode, FTPoint penPosition)
+{
+ FTPoint kernAdvance, advance;
+
+ unsigned int left = charMap->FontIndex( characterCode);
+ unsigned int right = charMap->FontIndex( nextCharacterCode);
+
+ kernAdvance = face->KernAdvance( left, right);
+
+ if( !face->Error())
+ {
+ advance = glyphs[charMap->GlyphListIndex( characterCode)]->Render( penPosition);
+ }
+
+ kernAdvance += advance;
+ return kernAdvance;
+}
+
+
+
+
+
+
+
+
+
+class FTFont
+{
+ public:
+
+ FTFont( const char* fontFilePath);
+ FTFont( const unsigned char *pBufferBytes, size_t bufferSizeInBytes);
+ virtual ~FTFont();
+
+ bool Attach( const char* fontFilePath);
+ bool Attach( const unsigned char *pBufferBytes, size_t bufferSizeInBytes);
+ bool CharMap( FT_Encoding encoding );
+ unsigned int CharMapCount();
+ FT_Encoding* CharMapList();
+ virtual bool FaceSize( const unsigned int size, const unsigned int res = 72);
+ unsigned int FaceSize() const;
+ virtual void Depth( float depth){}
+ void UseDisplayList( bool useList);
+ float Ascender() const;
+ float Descender() const;
+ float LineHeight() const;
+ void BBox( const char* string, float& llx, float& lly, float& llz, float& urx, float& ury, float& urz);
+ void BBox( const wchar_t* string, float& llx, float& lly, float& llz, float& urx, float& ury, float& urz);
+ float Advance( const wchar_t* string);
+ float Advance( const char* string);
+ virtual void Render( const char* string );
+ virtual void Render( const wchar_t* string );
+ FT_Error Error() const { return err;}
+
+ protected:
+ virtual FTGlyph* MakeGlyph( unsigned int g) = 0;
+ FTFace face;
+ FTSize charSize;
+ bool useDisplayLists;
+ FT_Error err;
+
+ private:
+ inline bool CheckGlyph( const unsigned int chr);
+ FTGlyphContainer* glyphList;
+ FTPoint pen;
+};
+
+
+
+FTFont::FTFont( const char* fontFilePath) : face( fontFilePath), useDisplayLists(true), glyphList(0)
+{
+ err = face.Error();
+ if( err == 0)
+ {
+ glyphList = new FTGlyphContainer( &face);
+ }
+}
+
+
+
+
+class FTTextureGlyph : public FTGlyph
+{
+ public:
+ FTTextureGlyph( FT_GlyphSlot glyph, int id, int xOffset, int yOffset, int width, int height);
+ virtual ~FTTextureGlyph();
+ virtual const FTPoint& Render( const FTPoint& pen);
+ static void FTTextureGlyph::ResetActiveTexture(){ activeTextureID = 0;}
+
+ private:
+
+ int destWidth;
+ int destHeight;
+ FTPoint pos;
+ FTPoint uv[2];
+ int glTextureID;
+ static int activeTextureID;
+
+};
+
+
+int FTTextureGlyph::activeTextureID = 0;
+
+FTTextureGlyph::FTTextureGlyph( FT_GlyphSlot glyph, int id, int xOffset, int yOffset, int width, int height)
+ : FTGlyph( glyph),
+ destWidth(0),
+ destHeight(0),
+ glTextureID(id)
+{
+ err = FT_Render_Glyph( glyph, FT_RENDER_MODE_NORMAL);
+ if( err || glyph->format != ft_glyph_format_bitmap)
+ {
+ return;
+ }
+
+ FT_Bitmap bitmap = glyph->bitmap;
+
+ destWidth = bitmap.width;
+ destHeight = bitmap.rows;
+
+ if( destWidth && destHeight)
+ {
+ glPushClientAttrib( GL_CLIENT_PIXEL_STORE_BIT);
+ glPixelStorei( GL_UNPACK_LSB_FIRST, GL_FALSE);
+ glPixelStorei( GL_UNPACK_ROW_LENGTH, 0);
+ glPixelStorei( GL_UNPACK_ALIGNMENT, 1);
+
+ glBindTexture( GL_TEXTURE_2D, glTextureID);
+ glTexSubImage2D( GL_TEXTURE_2D, 0, xOffset, yOffset, destWidth, destHeight, GL_ALPHA, GL_UNSIGNED_BYTE, bitmap.buffer);
+
+ glPopClientAttrib();
+ }
+
+
+ uv[0].X( static_cast<float>(xOffset) / static_cast<float>(width));
+ uv[0].Y( static_cast<float>(yOffset) / static_cast<float>(height));
+ uv[1].X( static_cast<float>( xOffset + destWidth) / static_cast<float>(width));
+ uv[1].Y( static_cast<float>( yOffset + destHeight) / static_cast<float>(height));
+
+ pos.X( glyph->bitmap_left);
+ pos.Y( glyph->bitmap_top);
+}
+
+
+FTTextureGlyph::~FTTextureGlyph()
+{}
+
+
+const FTPoint& FTTextureGlyph::Render( const FTPoint& pen)
+{
+ if( activeTextureID != glTextureID)
+ {
+ glBindTexture( GL_TEXTURE_2D, (GLuint)glTextureID);
+ activeTextureID = glTextureID;
+ }
+
+ glTranslatef( pen.X(), pen.Y(), 0.0f);
+
+ glBegin( GL_QUADS);
+ glTexCoord2f( uv[0].X(), uv[0].Y());
+ glVertex2f( pos.X(), pos.Y());
+
+ glTexCoord2f( uv[0].X(), uv[1].Y());
+ glVertex2f( pos.X(), pos.Y() - destHeight);
+
+ glTexCoord2f( uv[1].X(), uv[1].Y());
+ glVertex2f( destWidth + pos.X(), pos.Y() - destHeight);
+
+ glTexCoord2f( uv[1].X(), uv[0].Y());
+ glVertex2f( destWidth + pos.X(), pos.Y());
+ glEnd();
+
+ return advance;
+}
+
+
+
+
+
+class myFont : public FTFont
+{
+
+ private:
+ inline virtual FTGlyph* MakeGlyph( unsigned int glyphIndex)
+ {
+ FT_GlyphSlot ftGlyph = face.Glyph( glyphIndex, FT_LOAD_NO_HINTING);
+
+ if( ftGlyph)
+ {
+ glyphHeight = static_cast<int>( charSize.Height());
+ glyphWidth = static_cast<int>( charSize.Width());
+
+ if( textureIDList.empty())
+ {
+ textureIDList.push_back( CreateTexture());
+ xOffset = yOffset = padding;
+ }
+
+ if( xOffset > ( textureWidth - glyphWidth))
+ {
+ xOffset = padding;
+ yOffset += glyphHeight;
+
+ if( yOffset > ( textureHeight - glyphHeight))
+ {
+ textureIDList.push_back( CreateTexture());
+ yOffset = padding;
+ }
+ }
+
+ FTTextureGlyph* tempGlyph = new FTTextureGlyph( ftGlyph, textureIDList[textureIDList.size() - 1],
+ xOffset, yOffset, textureWidth, textureHeight);
+ xOffset += static_cast<int>( tempGlyph->BBox().upperX - tempGlyph->BBox().lowerX + padding);
+
+ --remGlyphs;
+ return tempGlyph;
+ }
+
+ err = face.Error();
+ return NULL;
+ }
+
+ inline void CalculateTextureSize()
+ {
+ if( !maximumGLTextureSize)
+ {
+ glGetIntegerv( GL_MAX_TEXTURE_SIZE, (int*)&maximumGLTextureSize);
+ assert(maximumGLTextureSize); // If you hit this then you have an invalid OpenGL context.
+ }
+
+ textureWidth = NextPowerOf2( (remGlyphs * glyphWidth) + ( padding * 2));
+ textureWidth = textureWidth > maximumGLTextureSize ? maximumGLTextureSize : textureWidth;
+
+ int h = static_cast<int>( (textureWidth - ( padding * 2)) / glyphWidth);
+
+ textureHeight = NextPowerOf2( (( numGlyphs / h) + 1) * glyphHeight);
+ textureHeight = textureHeight > maximumGLTextureSize ? maximumGLTextureSize : textureHeight;
+ }
+
+
+ inline uint CreateTexture()
+ {
+ CalculateTextureSize();
+
+ int totalMemory = textureWidth * textureHeight;
+ unsigned char* textureMemory = new unsigned char[totalMemory];
+ memset( textureMemory, 0, totalMemory);
+
+ unsigned int textID;
+ glGenTextures( 1, (unsigned int*)&textID);
+
+ glBindTexture( GL_TEXTURE_2D, textID);
+ glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
+ glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
+ glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+ glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
+
+ glTexImage2D( GL_TEXTURE_2D, 0, GL_ALPHA, textureWidth, textureHeight, 0, GL_ALPHA, GL_UNSIGNED_BYTE, textureMemory);
+
+ delete [] textureMemory;
+
+ return textID;
+ }
+
+
+
+ int maximumGLTextureSize;
+ int textureWidth;
+ int textureHeight;
+ FTVector<GLuint> textureIDList;
+ int glyphHeight;
+ int glyphWidth;
+ unsigned int padding;
+ unsigned int numGlyphs;
+ unsigned int remGlyphs;
+ int xOffset;
+ int yOffset;
+
+
+ public:
+
+
+ myFont( const char* fontFilePath) : FTFont( fontFilePath),
+ maximumGLTextureSize(0),
+ textureWidth(0),
+ textureHeight(0),
+ glyphHeight(0),
+ glyphWidth(0),
+ padding(3),
+ xOffset(0),
+ yOffset(0)
+{
+ remGlyphs = numGlyphs = face.GlyphCount();
+}
+
+~myFont()
+{
+ glDeleteTextures( textureIDList.size(), (const unsigned int*)&textureIDList[0]);
+}
+
+
+void print( char *str, int x1, int y1, int x2, int y2, colour c )
+{
+ g->enter2dMode();
+ glColor4f( c.r, c.g, c.b, c.a);
+ float h= LineHeight()*0.64f; /* factor 0.64*/
+ glTranslatef(x1, y1 + h, 0);
+ glScalef(1.0, -1.0, 1.0);
+ Render(str);
+// ft_print( our_font, x1 , g->getHeight() - y1 - fontSize, str);
+ g->leave2dMode();
+}
+
+
+
+bool FaceSize( const unsigned int size, const unsigned int res)
+{
+ if( !textureIDList.empty())
+ {
+ glDeleteTextures( textureIDList.size(), (const uint*)&textureIDList[0]);
+ textureIDList.clear();
+ remGlyphs = numGlyphs = face.GlyphCount();
+ }
+
+ return FTFont::FaceSize( size, res);
+}
+
+
+void Render( const char* string)
+{
+ glPushAttrib( GL_ENABLE_BIT | GL_COLOR_BUFFER_BIT);
+
+ glEnable(GL_BLEND);
+ glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // GL_ONE
+
+ FTTextureGlyph::ResetActiveTexture();
+
+ FTFont::Render( string);
+
+ glPopAttrib();
+}
+
+
+void Render( const wchar_t* string)
+{
+ glPushAttrib( GL_ENABLE_BIT | GL_COLOR_BUFFER_BIT);
+
+ glEnable(GL_BLEND);
+ glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // GL_ONE
+
+ FTTextureGlyph::ResetActiveTexture();
+
+ FTFont::Render( string);
+
+ glPopAttrib();
+}
+
+};
+
+
+
+myFont *f;
+Font *t;
+
+bool init(void)
+{
+ g = new Graphics(1024, 768); // bring me a 1024x768 resolution
+ g->createDisplay();
+// // g->setBackground(COLOUR_RGBA(0, 255, 0, 0)); //green background
+ f = new myFont("Test.ttf");
+ f->FaceSize(16, 72);
+ f->CharMap(ft_encoding_unicode);
+
+ t = new Font(g, "Test.ttf", 16);
+ return true;
+}
+
+
+bool renderFrame()
+{
+ g->beginScene();
+ f->print("MPLA", 10,10, 200,200, COLOUR_RGBA(255,255,255,255));
+// t->print("hello", 10,10, 200,200, COLOUR_RGBA(255,255,255,255));
+ g->endScene();
+
+ sleep(7);
+ return false;
+}
+
+bool cleanup()
+{
+ delete g;
+ return true;
+}
+