X-Git-Url: https://git.stderr.nl/gitweb?a=blobdiff_plain;f=ABM2%2FAmaltheia%2FmyFont.cpp;fp=ABM2%2FAmaltheia%2FmyFont.cpp;h=c05453541764ffb5e736a7b2c6c90f29e84d8a85;hb=9dae6dd1f05eed22b6ce5e75bd1e5893d5ee1ac6;hp=0000000000000000000000000000000000000000;hpb=0557fdb7b7567901ca7e82def55fe5ceab0ccef0;p=matthijs%2FABM2.git diff --git a/ABM2/Amaltheia/myFont.cpp b/ABM2/Amaltheia/myFont.cpp new file mode 100644 index 0000000..c054535 --- /dev/null +++ b/ABM2/Amaltheia/myFont.cpp @@ -0,0 +1,1897 @@ +#include + +#include +#include + + +#include +#include // For memset +#include +#include +#include +#include +#include + + +Graphics *g; + +//#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( bbox.xMin) / 64.0f; + lowerY = static_cast( bbox.yMin) / 64.0f; + lowerZ = 0.0f; + upperX = static_cast( bbox.xMax) / 64.0f; + upperY = static_cast( 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( ftSize->metrics.ascender) / 64.0f; + } + + + float FTSize::Descender() const + { + return ftSize == 0 ? 0.0f : static_cast( 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( 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(ftSize->metrics.x_ppem) / static_cast((*ftFace)->units_per_EM)); + } + else + { + return static_cast( 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( kernAdvance.x) / 64.0f; + y = static_cast( 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 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 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); + } +} + + + + +FTFont::FTFont( const unsigned char *pBufferBytes, size_t bufferSizeInBytes) + : face( pBufferBytes, bufferSizeInBytes), + glyphList(0) +{ + err = face.Error(); + if( err == 0) + { + glyphList = new FTGlyphContainer( &face); + } +} + + +FTFont::~FTFont() +{ + delete glyphList; +} + + +bool FTFont::Attach( const char* fontFilePath) +{ + if( face.Attach( fontFilePath)) + { + err = 0; + return true; + } + else + { + err = face.Error(); + return false; + } +} + + +bool FTFont::Attach( const unsigned char *pBufferBytes, size_t bufferSizeInBytes) +{ + if( face.Attach( pBufferBytes, bufferSizeInBytes)) + { + err = 0; + return true; + } + else + { + err = face.Error(); + return false; + } +} + + +bool FTFont::FaceSize( const unsigned int size, const unsigned int res ) +{ + charSize = face.Size( size, res); + err = face.Error(); + + if( err != 0) + { + return false; + } + + if( glyphList != NULL) + { + delete glyphList; + } + + glyphList = new FTGlyphContainer( &face); + return true; +} + + +unsigned int FTFont::FaceSize() const +{ + return charSize.CharSize(); +} + + +bool FTFont::CharMap( FT_Encoding encoding) +{ + bool result = glyphList->CharMap( encoding); + err = glyphList->Error(); + return result; +} + + +unsigned int FTFont::CharMapCount() +{ + return face.CharMapCount(); +} + + +FT_Encoding* FTFont::CharMapList() +{ + return face.CharMapList(); +} + + +void FTFont::UseDisplayList( bool useList) +{ + useDisplayLists = useList; +} + +float FTFont::Ascender() const +{ + return charSize.Ascender(); +} + + +float FTFont::Descender() const +{ + return charSize.Descender(); +} + +float FTFont::LineHeight() const +{ + return charSize.Height(); +} + +void FTFont::BBox( const char* string, + float& llx, float& lly, float& llz, float& urx, float& ury, float& urz) +{ + FTBBox totalBBox; + + if((NULL != string) && ('\0' != *string)) + { + const unsigned char* c = (unsigned char*)string; + float advance = 0; + + if(CheckGlyph( *c)) + { + totalBBox = glyphList->BBox( *c); + advance = glyphList->Advance( *c, *(c + 1)); + } + + while( *++c) + { + if(CheckGlyph( *c)) + { + FTBBox tempBBox = glyphList->BBox( *c); + tempBBox.Move( FTPoint( advance, 0.0f, 0.0f)); + totalBBox += tempBBox; + advance += glyphList->Advance( *c, *(c + 1)); + } + } + } + + llx = totalBBox.lowerX; + lly = totalBBox.lowerY; + llz = totalBBox.lowerZ; + urx = totalBBox.upperX; + ury = totalBBox.upperY; + urz = totalBBox.upperZ; +} + + +void FTFont::BBox( const wchar_t* string, + float& llx, float& lly, float& llz, float& urx, float& ury, float& urz) +{ + FTBBox totalBBox; + + if((NULL != string) && ('\0' != *string)) + { + const wchar_t* c = string; + float advance = 0; + + if(CheckGlyph( *c)) + { + totalBBox = glyphList->BBox( *c); + advance = glyphList->Advance( *c, *(c + 1)); + } + + while( *++c) + { + if(CheckGlyph( *c)) + { + FTBBox tempBBox = glyphList->BBox( *c); + tempBBox.Move( FTPoint( advance, 0.0f, 0.0f)); + totalBBox += tempBBox; + advance += glyphList->Advance( *c, *(c + 1)); + } + } + } + + llx = totalBBox.lowerX; + lly = totalBBox.lowerY; + llz = totalBBox.lowerZ; + urx = totalBBox.upperX; + ury = totalBBox.upperY; + urz = totalBBox.upperZ; +} + + +float FTFont::Advance( const wchar_t* string) +{ + const wchar_t* c = string; + float width = 0.0f; + + while( *c) + { + if(CheckGlyph( *c)) + { + width += glyphList->Advance( *c, *(c + 1)); + } + ++c; + } + + return width; +} + + +float FTFont::Advance( const char* string) +{ + const unsigned char* c = (unsigned char*)string; + float width = 0.0f; + + while( *c) + { + if(CheckGlyph( *c)) + { + width += glyphList->Advance( *c, *(c + 1)); + } + ++c; + } + + return width; +} + + +void FTFont::Render( const char* string ) +{ + const unsigned char* c = (unsigned char*)string; + pen.X(0); pen.Y(0); + + while( *c) + { + if(CheckGlyph( *c)) + { + pen = glyphList->Render( *c, *(c + 1), pen); + } + ++c; + } +} + + +void FTFont::Render( const wchar_t* string ) +{ + const wchar_t* c = string; + pen.X(0); pen.Y(0); + + while( *c) + { + if(CheckGlyph( *c)) + { + pen = glyphList->Render( *c, *(c + 1), pen); + } + ++c; + } +} + + +bool FTFont::CheckGlyph( const unsigned int characterCode) +{ + if( NULL == glyphList->Glyph( characterCode)) + { + unsigned int glyphIndex = glyphList->FontIndex( characterCode); + FTGlyph* tempGlyph = MakeGlyph( glyphIndex); + if( NULL == tempGlyph) + { + if( 0 == err) + { + err = 0x13; + } + + return false; + } + glyphList->Add( tempGlyph, characterCode); + } + + return true; +} + + + + + + +class FTTextureGlyph : public FTGlyph +{ + public: + FTTextureGlyph( FT_GlyphSlot glyph, Texture * tid, int xOffset, int yOffset, int width, int height); + virtual ~FTTextureGlyph(); + virtual const FTPoint& Render( const FTPoint& pen); + static void FTTextureGlyph::ResetActiveTexture() + { + activeTextureID = 0; + activeTexture = 0; + } + + private: + int destWidth; + int destHeight; + FTPoint pos; + FTPoint uv[2]; + int glTextureID; + Texture* textureID; + static int activeTextureID; + static Texture* activeTexture; + +}; + + +int FTTextureGlyph::activeTextureID = 0; +Texture* FTTextureGlyph::activeTexture = 0; + +FTTextureGlyph::FTTextureGlyph( FT_GlyphSlot glyph, Texture *tid, int xOffset, int yOffset, int width, int height) + : FTGlyph(glyph) +{ + destWidth = 0; + destHeight = 0; + + textureID = tid; + glTextureID = tid->gl_Tex; + + 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; + + int tW=0, tH=0; + 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); + + + g->setTexture(tid); +// glBindTexture( GL_TEXTURE_2D, glTextureID); + glGetTexLevelParameteriv(GL_TEXTURE_2D, 0,GL_TEXTURE_WIDTH, &tW); + glGetTexLevelParameteriv(GL_TEXTURE_2D, 0,GL_TEXTURE_HEIGHT, &tH); + tid->texSubImage2D(xOffset, yOffset, destWidth, destHeight, AM_ALPHA, AM_UBYTE, bitmap.buffer); + + // glTexSubImage2D( GL_TEXTURE_2D, 0, xOffset, yOffset, destWidth, destHeight, GL_ALPHA, GL_UNSIGNED_BYTE, bitmap.buffer); + + printf("TW=%d tH=%d txoff=%d yoff=%d w=%d h=%d\n", tW, tH, xOffset, yOffset, destWidth, destHeight); + glPopClientAttrib(); + } + + + uv[0].X( static_cast(xOffset) / static_cast(width)); + uv[0].Y( static_cast(yOffset) / static_cast(height)); + uv[1].X( static_cast( xOffset + destWidth) / static_cast(width)); + uv[1].Y( static_cast( yOffset + destHeight) / static_cast(height)); + + pos.X( glyph->bitmap_left); + pos.Y( glyph->bitmap_top); +} + + +FTTextureGlyph::~FTTextureGlyph() +{ } + + +const FTPoint& FTTextureGlyph::Render( const FTPoint& pen) +{ + if( activeTextureID != glTextureID) + { + g->setTexture(textureID); + printf("setT = %d\n", textureID->gl_Tex); + activeTexture = textureID; + //activeTextureID = glTextureID; +// glBindTexture( GL_TEXTURE_2D, glTextureID); + + } + + vertex array[4]; + + + + //g->setWorld(pen.X(), pen.Y(), 0.0f, 0,0,0, 1,1,1); + glTranslatef( pen.X(), pen.Y(), 0.0f); + + array[0].u = uv[0].X(); + array[0].v = 1 - uv[0].Y(); + array[0].x = pos.X(); + array[0].y = pos.Y(); + array[0].z = 0; + array[0].col = COLOUR_RGBA(255,255,255,255); + array[0].nx = array[0].ny = array[0].nz = 0; + + array[1].u = uv[0].X(); + array[1].v = 1 - uv[1].Y(); + array[1].x = pos.X(); + array[1].y = pos.Y() - destHeight; + array[1].z = 0; + array[1].col = COLOUR_RGBA(255,255,255,255); + array[1].nx = array[1].ny = array[1].nz = 0; + + array[2].u = uv[1].X(); + array[2].v = 1 - uv[1].Y(); + array[2].x = pos.X() + destWidth; + array[2].y = pos.Y() - destHeight; + array[2].z = 0; + array[2].col = COLOUR_RGBA(255,255,255,255); + array[2].nx = array[2].ny = array[2].nz = 0; + + array[3].u = uv[1].X(); + array[3].v = 1 - uv[0].Y(); + array[3].x = pos.X() + destWidth; + array[3].y = pos.Y(); + array[3].z = 0; + array[3].col = COLOUR_RGBA(255,255,255,255); + array[3].nx = array[3].ny = array[3].nz = 0; + + //g->renderVertexArray(array, 4, AM_TRIANGLE_STRIP); + g->addTriangle(array[0], array[1], array[2]); + g->addTriangle(array[2], array[3], array[0]); + +/* 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( charSize.Height()); + glyphWidth = static_cast( 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( 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. + } + printf("maximumGLTextureSize=%d\n", maximumGLTextureSize); + textureWidth = NextPowerOf2( (remGlyphs * glyphWidth) + ( padding * 2)); + textureWidth = textureWidth > maximumGLTextureSize ? maximumGLTextureSize : textureWidth; + + int h = static_cast( (textureWidth - ( padding * 2)) / glyphWidth); + + textureHeight = NextPowerOf2( (( numGlyphs / h) + 1) * glyphHeight); + textureHeight = textureHeight > maximumGLTextureSize ? maximumGLTextureSize : textureHeight; + } + + + inline Texture * CreateTexture() + { + + CalculateTextureSize(); + + int totalMemory = textureWidth * textureHeight; + unsigned char* textureMemory = new unsigned char[totalMemory]; + memset( textureMemory, 0, totalMemory); + + Texture *texID = new Texture(textureMemory, textureWidth, textureHeight, AM_ALPHA, AM_UBYTE, g); + + delete [] textureMemory; + + printf("now returning texID=%p\n", texID); + return texID; + /* + CalculateTextureSize(); + + int totalMemory = textureWidth * textureHeight; + unsigned char* textureMemory = new unsigned char[totalMemory]; + memset( textureMemory, 0, totalMemory); + + GLuint textID; + /* glGenTextures( 1, (GLuint*)&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); + */ + /* + Texture *tex = new Texture(textureMemory, textureWidth, textureHeight, AM_ALPHA, AM_UBYTE, g); +// g->setTexture(tex); + textID = tex->gl_Tex; + delete [] textureMemory; + printf("@ createTexture returning %d\n", textID); + return textID;*/ + + } + + + + int maximumGLTextureSize; + int textureWidth; + int textureHeight; + FTVector 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); + bool a = g->isAlpha(); + g->enableAlpha(true); +// glEnable(GL_BLEND); + +// glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // GL_ONE + + FTTextureGlyph::ResetActiveTexture(); + + FTFont::Render( string); + +// glPopAttrib(); + g->enableAlpha(a); +} + + +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 + bool a = g->isAlpha(); + g->enableAlpha(true); + + FTTextureGlyph::ResetActiveTexture(); + + FTFont::Render( string); + g->enableAlpha(a); +// glPopAttrib(); +} + +}; + + + +myFont *f; +Font *t; + +bool init(void) +{ + g = new Graphics(1024, 768); // bring me a 1024x768 resolution + g->createDisplay(); + g->setBackground(COLOUR_RGBA(155, 155, 155, 255)); //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(4); + return false; +} + +bool cleanup() +{ + delete g; + return true; +} +