diff --git a/src/osgText/FTBitmapGlyph.cpp b/src/osgText/FTBitmapGlyph.cpp new file mode 100644 index 000000000..07af04acd --- /dev/null +++ b/src/osgText/FTBitmapGlyph.cpp @@ -0,0 +1,76 @@ +#include "GL/gl.h" + +#include "FTBitmapGlyph.h" +#include "FTGL.h" + + +FTBitmapGlyph::FTBitmapGlyph( FT_Glyph glyph) +: FTGlyph(), + destWidth(0), + destHeight(0), + data(0) +{ + // This function will always fail if the glyph's format isn't scalable???? + FT_Error err = FT_Glyph_To_Bitmap( &glyph, ft_render_mode_mono, 0, 1); + if( err || ft_glyph_format_bitmap != glyph->format) + {return;} + + FT_BitmapGlyph bitmap = (FT_BitmapGlyph)glyph; + FT_Bitmap* source = &bitmap->bitmap; + + //check the pixel mode + //ft_pixel_mode_grays + + int srcWidth = source->width; + int srcHeight = source->rows; + int srcPitch = source->pitch; + + advance = glyph->advance.x >> 16; + + pos.x = bitmap->left; + pos.y = srcHeight - bitmap->top; + + // FIXME What about dest alignment? + destWidth = srcWidth; + destHeight = srcHeight; + + data = new unsigned char[srcPitch * destHeight]; + + for(int y = 0; y < srcHeight; ++y) + { + --destHeight; + for(int x = 0; x < srcPitch; ++x) + { + *( data + ( destHeight * srcPitch + x)) = *( source->buffer + ( y * srcPitch) + x); + } + } + + destHeight = srcHeight; + + // discard glyph image (bitmap or not) + // Is this the right place to do this? + FT_Done_Glyph( glyph ); +} + + +FTBitmapGlyph::~FTBitmapGlyph() +{ + delete[] data; +} + + +float FTBitmapGlyph::Render( const FT_Vector& pen) +{ + if( data != 0 ) + { + // Move the glyph origin + glBitmap( 0, 0, 0.0, 0.0, pen.x + pos.x, pen.y - pos.y, (const GLubyte *)0 ); + + glBitmap( destWidth, destHeight, 0.0f, 0.0, 0.0, 0.0, (const GLubyte *)data); + + // Restore the glyph origin + glBitmap( 0, 0, 0.0, 0.0, -pen.x - pos.x, -pen.y + pos.y, (const GLubyte *)0 ); + } + + return advance; +} diff --git a/src/osgText/FTCharmap.cpp b/src/osgText/FTCharmap.cpp new file mode 100644 index 000000000..31107c6b0 --- /dev/null +++ b/src/osgText/FTCharmap.cpp @@ -0,0 +1,91 @@ +#include "FTCharmap.h" + + +FTCharmap::FTCharmap( FT_Face face) +: err(0), + ftFace( face) +{ + ftEncoding = face->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; + charMap.clear(); + } + + return !err; +} + + +bool FTCharmap::CharMap( FT_UShort platform, FT_UShort encoding) +{ + FT_CharMap found = 0; + FT_CharMap charmap; + + for( int n = 0; n < ftFace->num_charmaps; n++ ) + { + charmap = ftFace->charmaps[n]; + + if( charmap->platform_id == platform && charmap->encoding_id == encoding) + { + found = charmap; + break; + } + } + + if( !found ) + { + return false; + } + + if( ftEncoding == found->encoding) + { + return true; + } + + /* now, select the charmap for the face object */ + err = FT_Set_Charmap( ftFace, found ); + + if( !err) + { + ftEncoding = found->encoding; + charMap.clear(); + } + + return !err; +} + + +unsigned int FTCharmap::CharIndex( unsigned int index ) +{ + CharacterMap::const_iterator result = charMap.find( index); + + if( result == charMap.end()) + { + unsigned int glyph = FT_Get_Char_Index( ftFace, index); + charMap.insert( CharacterMap::value_type( index, glyph)); + return glyph; + } + else + { + return result->second; + } + +} diff --git a/src/osgText/FTFace.cpp b/src/osgText/FTFace.cpp new file mode 100644 index 000000000..ad47ca332 --- /dev/null +++ b/src/osgText/FTFace.cpp @@ -0,0 +1,110 @@ +#include "FTFace.h" +#include "FTLibrary.h" +#include "FTCharmap.h" +#include "FTGL.h" + + +FTFace::FTFace() +: ftFace(0), + numCharMaps(0), + charMap(0), + numGlyphs(0), + err(0) +{} + + +FTFace::~FTFace() +{ + delete charMap; + charMap = 0; + Close(); +} + + +bool FTFace::Open( const char* filename) +{ + ftFace = new FT_Face; + err = FT_New_Face( *FTLibrary::Instance().GetLibrary(), filename, 0, ftFace); + + if( err) + { + delete ftFace; + ftFace = 0; + return false; + } + else + { + charMap = new FTCharmap( *ftFace); + return true; + } +} + + +void FTFace::Close() +{ + if( ftFace) + { + FT_Done_Face( *ftFace); + delete ftFace; + ftFace = 0; + } +} + + +FTSize& FTFace::Size( const unsigned int size, const unsigned int res) +{ + if( !charSize.CharSize( ftFace, size, res, res)) + { + err = charSize.Error(); + } + + return charSize; +} + + +bool FTFace::CharMap( FT_Encoding encoding) +{ + return charMap->CharMap( encoding); +} + + +unsigned int FTFace::CharIndex( unsigned int index) const +{ + return charMap->CharIndex( index); +} + + +FT_Vector& FTFace::KernAdvance( unsigned int index1, unsigned int index2) +{ + kernAdvance.x = 0; kernAdvance.y = 0; + + if( FT_HAS_KERNING((*ftFace)) && index1 && index2) + { + err = FT_Get_Kerning( *ftFace, index1, index2, ft_kerning_unfitted, &kernAdvance); + if( !err) + { + kernAdvance.x /= 64; kernAdvance.y /= 64; + } + } + + return kernAdvance; +} + + +FT_Glyph* FTFace::Glyph( unsigned int index, FT_Int load_flags) +{ + err = FT_Load_Glyph( *ftFace, index, load_flags); + err = FT_Get_Glyph( (*ftFace)->glyph, &ftGlyph); + + if( !err) + { + return &ftGlyph; + } + else + { + return NULL; + } +} + + + diff --git a/src/osgText/FTFont.cpp b/src/osgText/FTFont.cpp new file mode 100644 index 000000000..fb9321378 --- /dev/null +++ b/src/osgText/FTFont.cpp @@ -0,0 +1,148 @@ +#include "FTFace.h" +#include "FTFont.h" +#include "FTGlyphContainer.h" +#include "FTGL.h" + + +FTFont::FTFont() +: numFaces(0), + glyphList(0), + err(0) +{ + pen.x = 0; + pen.y = 0; +} + + +FTFont::~FTFont() +{ + Close(); +} + + +bool FTFont::Open( const char* fontname ) +{ + if( face.Open( fontname)) + { + FT_Face* ftFace = face.Face(); + numGlyphs = (*ftFace)->num_glyphs; + + return true; + } + else + { + err = face.Error(); + return false; + } +} + + +void FTFont::Close() +{ + delete glyphList; +} + + +bool FTFont::FaceSize( const unsigned int size, const unsigned int res ) +{ + charSize = face.Size( size, res); + + if( glyphList) + delete glyphList; + + glyphList = new FTGlyphContainer( &face, numGlyphs); + + if( MakeGlyphList()) + { + return true; + } + else + { + return false; + } +} + + +bool FTFont::CharMap( FT_Encoding encoding) +{ + err = face.CharMap( encoding); + return !err; +} + + +int FTFont::Ascender() const +{ + return charSize.Ascender(); +} + + +int FTFont::Descender() const +{ + return charSize.Descender(); +} + + +float FTFont::Advance( const wchar_t* string) +{ + const wchar_t* c = string; // wchar_t IS unsigned? + float width = 0; + + while( *c) + { + width += glyphList->Advance( *c, *(c + 1)); + ++c; + } + + return width; +} + + +float FTFont::Advance( const char* string) +{ + const unsigned char* c = (unsigned char*)string; // This is ugly, what is the c++ way? + float width = 0; + + while( *c) + { + width += glyphList->Advance( *c, *(c + 1)); + ++c; + } + + return width; +} + + +void FTFont::render( const char* string ) +{ + const unsigned char* c = (unsigned char*)string; // This is ugly, what is the c++ way? + FT_Vector kernAdvance; + pen.x = 0; pen.y = 0; + + while( *c) + { + kernAdvance = glyphList->render( *c, *(c + 1), pen); + + pen.x += kernAdvance.x; + pen.y += kernAdvance.y; + + ++c; + } +} + + +void FTFont::render( const wchar_t* string ) +{ + const wchar_t* c = string; // wchar_t IS unsigned? + FT_Vector kernAdvance; + pen.x = 0; pen.y = 0; + + while( *c) + { + kernAdvance = glyphList->render( *c, *(c + 1), pen); + + pen.x += kernAdvance.x; + pen.y += kernAdvance.y; + + ++c; + } +} diff --git a/src/osgText/FTGLBitmapFont.cpp b/src/osgText/FTGLBitmapFont.cpp new file mode 100644 index 000000000..c85e18c46 --- /dev/null +++ b/src/osgText/FTGLBitmapFont.cpp @@ -0,0 +1,70 @@ +#include "GL/gl.h" + +#include "FTGLBitmapFont.h" +#include "FTGlyphContainer.h" +#include "FTBitmapGlyph.h" + + +FTGLBitmapFont::FTGLBitmapFont() +: tempGlyph(0) +{} + + +FTGLBitmapFont::~FTGLBitmapFont() +{} + + +// OPSignature: bool FTGlyphContainer:MakeGlyphList() +bool FTGLBitmapFont::MakeGlyphList() +{ +// if( preCache) + for( unsigned int c = 0; c < numGlyphs; ++c) + { + FT_Glyph* ftGlyph = face.Glyph( c, FT_LOAD_DEFAULT); +// FT_HAS_VERTICAL(face) + + if( ftGlyph) + { + tempGlyph = new FTBitmapGlyph( *ftGlyph); + glyphList->Add( tempGlyph); + } + else + { + err = face.Error(); + } + } + + return !err; +} + + +void FTGLBitmapFont::render( const char* string) +{ + glPushClientAttrib( GL_CLIENT_PIXEL_STORE_BIT); + + // doing this every frame is a bad? + glPixelStorei( GL_UNPACK_LSB_FIRST, GL_FALSE); + glPixelStorei( GL_UNPACK_ROW_LENGTH, 0); + glPixelStorei( GL_UNPACK_ALIGNMENT, 1); + + FTFont::render( string); + + glPopClientAttrib(); + +} + + +void FTGLBitmapFont::render( const wchar_t* string) +{ + glPushClientAttrib( GL_CLIENT_PIXEL_STORE_BIT); + + // doing this every frame is a bad? + glPixelStorei( GL_UNPACK_LSB_FIRST, GL_FALSE); + glPixelStorei( GL_UNPACK_ROW_LENGTH, 0); + glPixelStorei( GL_UNPACK_ALIGNMENT, 1); + + FTFont::render( string); + + glPopClientAttrib(); + +} diff --git a/src/osgText/FTGLOutlineFont.cpp b/src/osgText/FTGLOutlineFont.cpp new file mode 100644 index 000000000..9a06af92b --- /dev/null +++ b/src/osgText/FTGLOutlineFont.cpp @@ -0,0 +1,68 @@ +#include "GL/gl.h" + +#include "FTGLOutlineFont.h" +#include "FTGlyphContainer.h" +#include "FTGL.h" +#include "FTOutlineGlyph.h" + + +FTGLOutlineFont::FTGLOutlineFont() +: tempGlyph(0) +{} + + +FTGLOutlineFont::~FTGLOutlineFont() +{} + + +bool FTGLOutlineFont::MakeGlyphList() +{ + for( unsigned int n = 0; n < numGlyphs; ++n) + { + FT_Glyph* ftGlyph = face.Glyph( n, FT_LOAD_NO_HINTING | FT_LOAD_NO_BITMAP); + + if( ftGlyph) + { + tempGlyph = new FTOutlineGlyph( *ftGlyph); + glyphList->Add( tempGlyph); + } + else + { + err = face.Error(); + } + } + + return !err; +} + + +void FTGLOutlineFont::render( const char* string) +{ + glPushAttrib( GL_ENABLE_BIT | GL_HINT_BIT | GL_LINE_BIT | GL_PIXEL_MODE_BIT); + + glEnable( GL_LINE_SMOOTH); + glHint( GL_LINE_SMOOTH_HINT, GL_DONT_CARE); + glEnable(GL_BLEND); + glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // GL_ONE + + FTFont::render( string); + + glPopAttrib(); + +} + + +void FTGLOutlineFont::render( const wchar_t* string) +{ + glPushAttrib( GL_ENABLE_BIT | GL_HINT_BIT | GL_LINE_BIT | GL_PIXEL_MODE_BIT); + + glEnable( GL_LINE_SMOOTH); + glHint( GL_LINE_SMOOTH_HINT, GL_DONT_CARE); + glEnable(GL_BLEND); + glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // GL_ONE + + FTFont::render( string); + + glPopAttrib(); + +} diff --git a/src/osgText/FTGLPixmapFont.cpp b/src/osgText/FTGLPixmapFont.cpp new file mode 100644 index 000000000..e00d1f9ad --- /dev/null +++ b/src/osgText/FTGLPixmapFont.cpp @@ -0,0 +1,66 @@ +#include "GL/gl.h" + +#include "FTGLPixmapFont.h" +#include "FTGlyphContainer.h" +#include "FTPixmapGlyph.h" + + +FTGLPixmapFont::FTGLPixmapFont() +: tempGlyph(0) +{} + + +FTGLPixmapFont::~FTGLPixmapFont() +{} + + +// OPSignature: bool FTGlyphContainer:MakeGlyphList() +bool FTGLPixmapFont::MakeGlyphList() +{ +// if( preCache) + for( unsigned int c = 0; c < numGlyphs; ++c) + { + FT_Glyph* ftGlyph = face.Glyph( c, FT_LOAD_DEFAULT); +// FT_HAS_VERTICAL(face) + + if( ftGlyph) + { + tempGlyph = new FTPixmapGlyph( *ftGlyph); + glyphList->Add( tempGlyph); + } + else + { + err = face.Error(); + } + } + + return !err; +} + + +void FTGLPixmapFont::render( const char* string) +{ + glPushAttrib( GL_ENABLE_BIT | GL_PIXEL_MODE_BIT); + + glEnable(GL_BLEND); + glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + FTFont::render( string); + + glPopAttrib(); + +} + + +void FTGLPixmapFont::render( const wchar_t* string) +{ + glPushAttrib( GL_ENABLE_BIT | GL_PIXEL_MODE_BIT); + + glEnable(GL_BLEND); + glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + + FTFont::render( string); + + glPopAttrib(); + +} diff --git a/src/osgText/FTGLPolygonFont.cpp b/src/osgText/FTGLPolygonFont.cpp new file mode 100644 index 000000000..7cbb1ed67 --- /dev/null +++ b/src/osgText/FTGLPolygonFont.cpp @@ -0,0 +1,35 @@ +#include "FTGLPolygonFont.h" +#include "FTGlyphContainer.h" +#include "FTGL.h" +#include "FTPolyGlyph.h" + + + +FTGLPolygonFont::FTGLPolygonFont() +: tempGlyph(0) +{} + + +FTGLPolygonFont::~FTGLPolygonFont() +{} + + +bool FTGLPolygonFont::MakeGlyphList() +{ + for( unsigned int n = 0; n < numGlyphs; ++n) + { + FT_Glyph* ftGlyph = face.Glyph( n, FT_LOAD_NO_HINTING | FT_LOAD_NO_BITMAP); + + if( ftGlyph) + { + tempGlyph = new FTPolyGlyph( *ftGlyph); + glyphList->Add( tempGlyph); + } + else + { + err = face.Error(); + } + } + + return !err; +} diff --git a/src/osgText/FTGLTextureFont.cpp b/src/osgText/FTGLTextureFont.cpp new file mode 100644 index 000000000..8a3e21d5a --- /dev/null +++ b/src/osgText/FTGLTextureFont.cpp @@ -0,0 +1,210 @@ +#include "GL/gl.h" + +#include "FTGLTextureFont.h" +#include "FTGlyphContainer.h" +#include "FTGL.h" +#include "FTTextureGlyph.h" + +using namespace std; + +typedef unsigned long UInt32; // a mac thing? + +inline UInt32 NextPowerOf2( UInt32 in) +{ + in -= 1; + + in |= in >> 16; + in |= in >> 8; + in |= in >> 4; + in |= in >> 2; + in |= in >> 1; + + return in + 1; +} + + +FTGLTextureFont::FTGLTextureFont() +: numTextures(1), + textMem(0), + padding(1), + tempGlyph(0), + maxTextSize(0), + textureWidth(0), + textureHeight(0), + glyphHeight(0), + glyphWidth(0) +{} + + +FTGLTextureFont::~FTGLTextureFont() +{ + glDeleteTextures( numTextures, (const GLuint*)glTextureID); +} + + +bool FTGLTextureFont::MakeGlyphList() +{ + if( !maxTextSize) + glGetIntegerv( GL_MAX_TEXTURE_SIZE, (GLint*)&maxTextSize); + + glyphHeight = ( charSize.Height()) + padding; + glyphWidth = ( charSize.Width()) + padding; + + GetSize(); + int totalMem; + + if( textureHeight > maxTextSize) + { + numTextures = static_cast( textureHeight / maxTextSize) + 1; + if( numTextures > 15) // FIXME + numTextures = 15; + + int heightRemain = NextPowerOf2( textureHeight % maxTextSize); + totalMem = ((maxTextSize * ( numTextures - 1)) + heightRemain) * textureWidth; + + glGenTextures( numTextures, (GLuint*)&glTextureID[0]); + + textMem = new unsigned char[totalMem]; // GL_ALPHA texture; + memset( textMem, 0, totalMem); + + unsigned int glyphNum = 0; + unsigned char* currTextPtr = textMem; + + for( int x = 0; x < numTextures - 1; ++x) + { + glyphNum = FillGlyphs( glyphNum, glTextureID[x], textureWidth, maxTextSize, currTextPtr); + + CreateTexture( x, textureWidth, maxTextSize, currTextPtr); + + currTextPtr += ( textureWidth * maxTextSize); + ++glyphNum; + } + + glyphNum = FillGlyphs( glyphNum, glTextureID[numTextures - 1], textureWidth, heightRemain, currTextPtr); + CreateTexture( numTextures - 1, textureWidth, heightRemain, currTextPtr); + } + else + { + textureHeight = NextPowerOf2( textureHeight); + totalMem = textureWidth * textureHeight; + + glGenTextures( numTextures, (GLuint*)&glTextureID[0]); + + textMem = new unsigned char[totalMem]; // GL_ALPHA texture; + std::memset( textMem, 0, totalMem); + + FillGlyphs( 0, glTextureID[0], textureWidth, textureHeight, textMem); + CreateTexture( 0, textureWidth, textureHeight, textMem); + } + + delete [] textMem; + return !err; +} + + +unsigned int FTGLTextureFont::FillGlyphs( unsigned int glyphStart, int id, int width, int height, unsigned char* textdata) +{ + int currentTextX = padding; + int currentTextY = padding;// + padding; + + float currTextU = (float)padding / (float)width; + float currTextV = (float)padding / (float)height; + +// numGlyphs = 256; // FIXME hack + unsigned int n; + + for( n = glyphStart; n <= numGlyphs; ++n) + { + FT_Glyph* ftGlyph = face.Glyph( n, FT_LOAD_NO_HINTING); + + if( ftGlyph) + { + unsigned char* data = textdata + (( currentTextY * width) + currentTextX); + + currTextU = (float)currentTextX / (float)width; + + tempGlyph = new FTTextureGlyph( *ftGlyph, id, data, width, height, currTextU, currTextV); + glyphList->Add( tempGlyph); + + currentTextX += glyphWidth; + if( currentTextX > ( width - glyphWidth)) + { + currentTextY += glyphHeight; + if( currentTextY > ( height - glyphHeight)) + return n; + + currentTextX = padding; + currTextV = (float)currentTextY / (float)height; + } + } + else + { + err = face.Error(); + } + } + + return n; +} + + +void FTGLTextureFont::GetSize() +{ + //work out the max width. Most likely maxTextSize + textureWidth = NextPowerOf2( numGlyphs * glyphWidth); + if( textureWidth > maxTextSize) + { + textureWidth = maxTextSize; + } + + int h = static_cast( textureWidth / glyphWidth); + textureHeight = (( numGlyphs / h) + 1) * glyphHeight; +} + + +void FTGLTextureFont::CreateTexture( int id, int width, int height, unsigned char* data) +{ + glPixelStorei( GL_UNPACK_ALIGNMENT, 1); //What does this do exactly? + glBindTexture( GL_TEXTURE_2D, glTextureID[id]); + glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP); + glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); + glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + + glTexImage2D( GL_TEXTURE_2D, 0, GL_ALPHA, width, height, 0, GL_ALPHA, GL_UNSIGNED_BYTE, data); +} + + +void FTGLTextureFont::render( const char* string) +{ + glPushAttrib( GL_ENABLE_BIT | GL_HINT_BIT | GL_LINE_BIT | GL_PIXEL_MODE_BIT); + + glEnable(GL_BLEND); + glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // GL_ONE + + glBindTexture( GL_TEXTURE_2D, (GLuint)FTTextureGlyph::activeTextureID); + + // QUADS are faster!? Less function call overhead? + glBegin( GL_QUADS); + FTFont::render( string); + glEnd(); + + glPopAttrib(); +} + + +void FTGLTextureFont::render( const wchar_t* string) +{ + glPushAttrib( GL_ENABLE_BIT | GL_HINT_BIT | GL_LINE_BIT | GL_PIXEL_MODE_BIT); + + glEnable(GL_BLEND); + glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); // GL_ONE + + glBindTexture( GL_TEXTURE_2D, (GLuint)FTTextureGlyph::activeTextureID); + + // QUADS are faster!? Less function call overhead? + glBegin( GL_QUADS); + FTFont::render( string); + glEnd(); + + glPopAttrib(); +} diff --git a/src/osgText/FTGlyph.cpp b/src/osgText/FTGlyph.cpp new file mode 100644 index 000000000..de8d87833 --- /dev/null +++ b/src/osgText/FTGlyph.cpp @@ -0,0 +1,14 @@ +#include "FTGlyph.h" + + +FTGlyph::FTGlyph() +: advance(0), + err(0) +{ + pos.x = 0; + pos.y = 0; +} + + +FTGlyph::~FTGlyph() +{} diff --git a/src/osgText/FTGlyphContainer.cpp b/src/osgText/FTGlyphContainer.cpp new file mode 100644 index 000000000..33ea20e51 --- /dev/null +++ b/src/osgText/FTGlyphContainer.cpp @@ -0,0 +1,66 @@ +#include "FTGlyphContainer.h" +#include "FTGlyph.h" +#include "FTFace.h" + + +FTGlyphContainer::FTGlyphContainer( FTFace* f, int g, bool p) +: preCache( p), + numGlyphs( g), + face( f), + err( 0) +{ + glyphs.reserve( g); +} + + + +FTGlyphContainer::~FTGlyphContainer() +{ + vector::iterator iter; + for( iter = glyphs.begin(); iter != glyphs.end(); ++iter) + { + delete *iter; + } + + glyphs.clear(); +} + + +bool FTGlyphContainer::Add( FTGlyph* tempGlyph) +{ + // At the moment we are using a vector. Vectors don't return bool. + glyphs.push_back( tempGlyph); + return true; +} + + +float FTGlyphContainer::Advance( unsigned int index, unsigned int next) +{ + unsigned int left = face->CharIndex( index); + unsigned int right = face->CharIndex( next); + + float width = face->KernAdvance( left, right).x; + width += glyphs[left]->Advance(); + + return width; +} + + +FT_Vector& FTGlyphContainer::render( unsigned int index, unsigned int next, FT_Vector pen) +{ + kernAdvance.x = 0; kernAdvance.y = 0; + + unsigned int left = face->CharIndex( index); + unsigned int right = face->CharIndex( next); + + kernAdvance = face->KernAdvance( left, right); + + if( !face->Error()) + { + advance = glyphs[left]->Render( pen); + } + + kernAdvance.x = advance + kernAdvance.x; // FIXME float to long +// kernAdvance.y = advance.y + kernAdvance.y; + return kernAdvance; +} diff --git a/src/osgText/FTLibrary.cpp b/src/osgText/FTLibrary.cpp new file mode 100644 index 000000000..5a6369c1f --- /dev/null +++ b/src/osgText/FTLibrary.cpp @@ -0,0 +1,65 @@ +#include "FTLibrary.h" +#include "FTGL.h" + + +FTLibrary& FTLibrary::Instance() +{ + static FTLibrary ftlib; + return ftlib; +} + + +FTLibrary::~FTLibrary() +{ + if( lib != 0) + { + FT_Done_FreeType( *lib); + + delete lib; + lib= 0; + } + +// if( manager != 0) +// { +// FTC_Manager_Done( manager ); +// +// delete manager; +// manager= 0; +// } +} + + +FTLibrary::FTLibrary() +: lib(0), + err(0) +{ + Init(); +} + + +bool FTLibrary::Init() +{ + if( lib != 0 ) + return true; + + lib = new FT_Library; + + err = FT_Init_FreeType( lib); + if( err) + { + delete lib; + lib = 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; +} diff --git a/src/osgText/FTOutlineGlyph.cpp b/src/osgText/FTOutlineGlyph.cpp new file mode 100644 index 000000000..8e5387112 --- /dev/null +++ b/src/osgText/FTOutlineGlyph.cpp @@ -0,0 +1,83 @@ +#include "GL/gl.h" + +#include "FTOutlineGlyph.h" +#include "FTVectoriser.h" +#include "FTGL.h" + + + +FTOutlineGlyph::FTOutlineGlyph( FT_Glyph glyph) +: FTGlyph(), + vectoriser(0), + numPoints(0), + numContours(0), + contourLength(0), + data(0), + glList(0) +{ + if( ft_glyph_format_outline != glyph->format) + { + return; + } + + vectoriser = new FTVectoriser( glyph); + + vectoriser->Ingest(); + numContours = vectoriser->contours(); + contourLength = new int[ numContours]; + + for( int cn = 0; cn < numContours; ++cn) + { + contourLength[cn] = vectoriser->contourSize( cn); + } + + numPoints = vectoriser->points(); + data = new double[ numPoints * 3]; + vectoriser->Output( data); + + advance = glyph->advance.x >> 16; + + delete vectoriser; + + if ( ( numContours < 1) || ( numPoints < 3)) + return; + + glList = glGenLists(1); + int d = 0; + + glNewList( glList, GL_COMPILE); + for( int c = 0; c < numContours; ++c) + { + glBegin( GL_LINE_LOOP); + for( int p = 0; p < ( contourLength[c]); ++p) + { + glVertex2dv( data + d); + d += 3; + } + glEnd(); + } + glEndList(); + + // discard glyph image (bitmap or not) + FT_Done_Glyph( glyph); // Why does this have to be HERE +} + + +FTOutlineGlyph::~FTOutlineGlyph() +{ + delete [] data; + delete [] contourLength; +} + + +float FTOutlineGlyph::Render( const FT_Vector& pen) +{ + if( glList) + { + glTranslatef( pen.x, pen.y, 0); + glCallList( glList); + glTranslatef( -pen.x, -pen.y, 0); + } + + return advance; +} diff --git a/src/osgText/FTPixmapGlyph.cpp b/src/osgText/FTPixmapGlyph.cpp new file mode 100644 index 000000000..1970be9ff --- /dev/null +++ b/src/osgText/FTPixmapGlyph.cpp @@ -0,0 +1,94 @@ +#include "GL/gl.h" + +#include "FTPixmapGlyph.h" +#include "FTGL.h" + + +FTPixmapGlyph::FTPixmapGlyph( FT_Glyph glyph) +: FTGlyph(), + destWidth(0), + destHeight(0), + numGreys(0), + data(0) +{ + // This function will always fail if the glyph's format isn't scalable???? + FT_Error err = FT_Glyph_To_Bitmap( &glyph, ft_render_mode_normal, 0, 1); + if( err || ft_glyph_format_bitmap != glyph->format) + { + return; + } + + FT_BitmapGlyph bitmap = (FT_BitmapGlyph)glyph; + FT_Bitmap* source = &bitmap->bitmap; + + //check the pixel mode + //ft_pixel_mode_grays + + int srcWidth = source->width; + int srcHeight = source->rows; + int srcPitch = source->pitch; + + numGreys = source->num_grays; + advance = glyph->advance.x >> 16; + + pos.x = bitmap->left; + pos.y = srcHeight - bitmap->top; + + // FIXME What about dest alignment? + destWidth = srcWidth; + destHeight = srcHeight; + + data = new unsigned char[destWidth * destHeight * 4]; + + // Get the current glColor. + float ftglColour[4]; + glGetFloatv( GL_CURRENT_COLOR, ftglColour); + + for(int y = 0; y < srcHeight; ++y) + { + --destHeight; + for(int x = 0; x < srcWidth; ++x) + { + *( data + ( destHeight * destWidth + x) * 4 + 0) = static_cast( ftglColour[0] * 255.0f); + *( data + ( destHeight * destWidth + x) * 4 + 1) = static_cast( ftglColour[1] * 255.0f); + *( data + ( destHeight * destWidth + x) * 4 + 2) = static_cast( ftglColour[2] * 255.0f); + *( data + ( destHeight * destWidth + x) * 4 + 3) = static_cast( ftglColour[3] * (*( source->buffer + ( y * srcPitch) + x))); + } + } + + destHeight = srcHeight; + + // discard glyph image (bitmap or not) + // Is this the right place to do this? + FT_Done_Glyph( glyph ); +} + + +FTPixmapGlyph::~FTPixmapGlyph() +{ + delete[] data; +} + + +float FTPixmapGlyph::Render( const FT_Vector& pen) +{ + if( data != 0 ) + { + glPushClientAttrib( GL_CLIENT_PIXEL_STORE_BIT); + + // Move the glyph origin + glBitmap( 0, 0, 0.0, 0.0, pen.x + pos.x, pen.y - pos.y, (const GLubyte *)0); + + glPixelStorei( GL_UNPACK_ROW_LENGTH, destWidth); + + glDrawPixels( destWidth, destHeight, GL_RGBA, GL_UNSIGNED_BYTE, (const GLvoid*)data); + + + // Restore the glyph origin + glBitmap( 0, 0, 0.0, 0.0, -pen.x - pos.x, -pen.y + pos.y, (const GLubyte *)0); + + glPopClientAttrib(); + } + + return advance; +} diff --git a/src/osgText/FTPolyGlyph.cpp b/src/osgText/FTPolyGlyph.cpp new file mode 100644 index 000000000..f400bbecd --- /dev/null +++ b/src/osgText/FTPolyGlyph.cpp @@ -0,0 +1,156 @@ +#include "GL/gl.h" +#include "GL/glu.h" + +#include "FTPolyGlyph.h" +#include "FTVectoriser.h" +#include "FTGL.h" + + +#ifndef CALLBACK +#define CALLBACK +#endif + + +void CALLBACK ftglError( GLenum errCode) +{ +// const GLubyte* estring; +// estring = gluErrorString( errCode); +// fprintf( stderr, "ERROR : %s\n", estring); +// exit(1); +} + +void CALLBACK ftglVertex( void* data) +{ + glVertex3dv( (double*)data); +} + + +void CALLBACK ftglBegin( GLenum type) +{ + glBegin( type); +} + + +void CALLBACK ftglEnd() +{ + glEnd(); +} + + +void CALLBACK ftglCombine( GLdouble coords[3], void* vertex_data[4], GLfloat weight[4], void** outData) +{ + double* vertex = new double[3]; // FIXME MEM LEAK + + vertex[0] = coords[0]; + vertex[1] = coords[1]; + vertex[2] = coords[2]; + + *outData = vertex; +} + + +FTPolyGlyph::FTPolyGlyph( FT_Glyph glyph) +: FTGlyph(), + vectoriser(0), + numPoints(0), + numContours(0), + contourLength(0), + data(0), + glList(0) +{ + if( ft_glyph_format_outline != glyph->format) + { return;} + + vectoriser = new FTVectoriser( glyph); + + vectoriser->Ingest(); + numContours = vectoriser->contours(); + contourLength = new int[ numContours]; + + for( int c = 0; c < numContours; ++c) + { + contourLength[c] = vectoriser->contourSize( c); + } + + numPoints = vectoriser->points(); + data = new double[ numPoints * 3]; + vectoriser->Output( data); + + contourFlag = vectoriser->ContourFlag(); + advance = glyph->advance.x >> 16; + + delete vectoriser; + + if ( ( numContours < 1) || ( numPoints < 3)) + return; + + Tesselate(); + + // discard glyph image (bitmap or not) + FT_Done_Glyph( glyph); // Why does this have to be HERE +} + + +void FTPolyGlyph::Tesselate() +{ + glList = glGenLists(1); + GLUtesselator* tobj = gluNewTess(); + int d = 0; + + gluTessCallback( tobj, GLU_TESS_BEGIN, (void (CALLBACK*)())ftglBegin); + gluTessCallback( tobj, GLU_TESS_VERTEX, (void (CALLBACK*)())ftglVertex); + gluTessCallback( tobj, GLU_TESS_COMBINE, (void (CALLBACK*)())ftglCombine); + gluTessCallback( tobj, GLU_TESS_END, ftglEnd); + gluTessCallback( tobj, GLU_TESS_ERROR, (void (CALLBACK*)())ftglError); + + glNewList( glList, GL_COMPILE); + + if( contourFlag & ft_outline_even_odd_fill) // ft_outline_reverse_fill + { + gluTessProperty( tobj, GLU_TESS_WINDING_RULE, GLU_TESS_WINDING_ODD); + } + else + { + gluTessProperty( tobj, GLU_TESS_WINDING_RULE, GLU_TESS_WINDING_NONZERO); + } + + gluTessProperty( tobj, GLU_TESS_TOLERANCE, 0); + gluTessBeginPolygon( tobj, NULL); + + for( int c = 0; c < numContours; ++c) + { + gluTessBeginContour( tobj); + for( int p = 0; p < ( contourLength[c]); ++p) + { + gluTessVertex( tobj, data + d, data + d); + d += 3; + } + gluTessEndContour( tobj); + } + + gluTessEndPolygon( tobj); + + glEndList(); + + gluDeleteTess( tobj); +} + + +FTPolyGlyph::~FTPolyGlyph() +{ + delete [] data; + delete [] contourLength; +} + + +float FTPolyGlyph::Render( const FT_Vector& pen) +{ + if( glList) + { + glTranslatef( pen.x, pen.y, 0); + glCallList( glList); + glTranslatef( -pen.x, -pen.y, 0); + } + + return advance; +} diff --git a/src/osgText/FTSize.cpp b/src/osgText/FTSize.cpp new file mode 100644 index 000000000..f5bf65f89 --- /dev/null +++ b/src/osgText/FTSize.cpp @@ -0,0 +1,91 @@ +#include "FTSize.h" +#include "FTGL.h" + + +FTSize::FTSize() +: size(0), + ftFace(0), + err(0) +{} + + +FTSize::~FTSize() +{} + + +bool FTSize::CharSize( FT_Face* face, unsigned int point_size, unsigned int x_resolution, unsigned int y_resolution ) +{ + ftFace = face; + size = point_size; + err = FT_Set_Char_Size( *ftFace, 0L, point_size * 64, x_resolution, y_resolution); + + ftSize = (*ftFace)->size; + + return !err; +} + + +int FTSize::Ascender() const +{ + return ftSize->metrics.ascender >> 6; +} + + +int FTSize::Descender() const +{ + return ftSize->metrics.descender >> 6; +} + + +int FTSize::Height() const +{ + if( FT_IS_SCALABLE((*ftFace))) + { + float height; + if( FT_IS_SFNT((*ftFace))) // Don't think this is correct + { + height = (*ftFace)->bbox.yMax - (*ftFace)->bbox.yMin; // bbox.yMax-bbox.yMin + } + else + { + height = (*ftFace)->bbox.yMax - (*ftFace)->bbox.yMin >> 16; // bbox.yMax-bbox.yMin + } + + height = height * ( (float)ftSize->metrics.y_ppem / (float)(*ftFace)->units_per_EM); + return static_cast(height); + } + else + { + return ftSize->metrics.height >> 6; + } +} + + +int FTSize::Width() const +{ + if( FT_IS_SCALABLE((*ftFace))) + { + float width; + if( FT_IS_SFNT((*ftFace))) // Don't think this is correct + { + width = ((*ftFace)->bbox.xMax - (*ftFace)->bbox.xMin); // bbox.xMax-bbox.xMin + } + else + { + width = ((*ftFace)->bbox.xMax - (*ftFace)->bbox.xMin) >> 16; // bbox.xMax-bbox.xMin + } + + width = width * ( (float)ftSize->metrics.x_ppem / (float)(*ftFace)->units_per_EM); + return static_cast(width); + } + else + { + return ftSize->metrics.max_advance >> 6; + } +} + + +int FTSize::Underline() const +{ + return 0; +} diff --git a/src/osgText/FTTextureGlyph.cpp b/src/osgText/FTTextureGlyph.cpp new file mode 100644 index 000000000..d15f8787d --- /dev/null +++ b/src/osgText/FTTextureGlyph.cpp @@ -0,0 +1,92 @@ +#include "GL/gl.h" + +#include "FTTextureGlyph.h" +#include "FTGL.h" + + +int FTTextureGlyph::activeTextureID = 0; + +FTTextureGlyph::FTTextureGlyph( FT_Glyph glyph, int id, unsigned char* data, int stride, int height, float u, float v) +: FTGlyph(), + destWidth(0), + destHeight(0), + numGreys(0), + glTextureID(id) +{ + // This function will always fail if the glyph's format isn't scalable???? + err = FT_Glyph_To_Bitmap( &glyph, ft_render_mode_normal, 0, 1); + if( err || glyph->format != ft_glyph_format_bitmap) + { + return; + } + + FT_BitmapGlyph bitmap = ( FT_BitmapGlyph)glyph; + FT_Bitmap* source = &bitmap->bitmap; + + //check the pixel mode + //ft_pixel_mode_grays + + int srcWidth = source->width; + int srcHeight = source->rows; + int srcPitch = source->pitch; + + numGreys = source->num_grays; + advance = glyph->advance.x >> 16; + + pos.x = bitmap->left; + pos.y = bitmap->top; + + destWidth = srcWidth; + destHeight = srcHeight; + + for(int y = 0; y < srcHeight; ++y) + { + for(int x = 0; x < srcWidth; ++x) + { + *( data + ( y * stride + x)) = *( source->buffer + ( y * srcPitch) + x); + } + } + +// 0 +// +----+ +// | | +// | | +// | | +// +----+ +// 1 + + uv[0].x = u; + uv[0].y = v; + uv[1].x = uv[0].x + ( (float)destWidth / (float)stride); + uv[1].y = uv[0].y + ( (float)destHeight / (float)height); + + // discard glyph image (bitmap or not) + // Is this the right place to do this? + FT_Done_Glyph( glyph); +} + + +FTTextureGlyph::~FTTextureGlyph() +{ + +} + + +float FTTextureGlyph::Render( const FT_Vector& pen) +{ + // This could be really ugly!! + if( activeTextureID != glTextureID) + { + glEnd(); + glBindTexture( GL_TEXTURE_2D, (GLuint)glTextureID); + activeTextureID = glTextureID; + glBegin( GL_QUADS); + } + + glTexCoord2f( uv[0].x, uv[0].y); glVertex2f( pen.x + pos.x, pen.y + pos.y); + glTexCoord2f( uv[1].x, uv[0].y); glVertex2f( pen.x + destWidth + pos.x, pen.y + pos.y); + glTexCoord2f( uv[1].x, uv[1].y); glVertex2f( pen.x + destWidth + pos.x, pen.y + pos.y - destHeight); + glTexCoord2f( uv[0].x, uv[1].y); glVertex2f( pen.x + pos.x, pen.y + pos.y - destHeight); + + return advance; +} diff --git a/src/osgText/FTVectoriser.cpp b/src/osgText/FTVectoriser.cpp new file mode 100644 index 000000000..54c66ac15 --- /dev/null +++ b/src/osgText/FTVectoriser.cpp @@ -0,0 +1,227 @@ +#include "FTVectoriser.h" +#include "FTGL.h" + + +FTContour::FTContour() +: kMAXPOINTS( 1000) +{ + pointList.reserve( kMAXPOINTS); +} + + +FTContour::~FTContour() +{ + pointList.clear(); +} + + +void FTContour::AddPoint( const float x, const float y) +{ + ftPoint point( x, y, 0.0); + + // Eliminate duplicate points. + if( pointList.empty() || ( pointList[pointList.size() - 1] != point && pointList[0] != point)) + { + pointList.push_back( point); + } +} + + +FTVectoriser::FTVectoriser( const FT_Glyph glyph) +: contourFlag(0), + contour(0), + kBSTEPSIZE( 0.2) +{ + FT_OutlineGlyph outline = (FT_OutlineGlyph)glyph; + ftOutline = outline->outline; + + contourList.reserve( ftOutline.n_contours); +} + + +FTVectoriser::~FTVectoriser() +{ + for( int c = 0; c < contours(); ++c) + { + delete contourList[c]; + } + + contourList.clear(); +} + + +int FTVectoriser::points() +{ + int s = 0; + for( int c = 0; c < contours(); ++c) + { + s += contourList[c]->size(); + } + + return s; +} + + +bool FTVectoriser::Ingest() +{ + short first = 0; + short last; + const short cont = ftOutline.n_contours; + + for( short c = 0; c < cont; ++c) + { + contour = new FTContour; + contourFlag = ftOutline.flags; + last = ftOutline.contours[c]; + + for( short p = first; p <= last; ++p) + { + switch( ftOutline.tags[p]) + { + case FT_Curve_Tag_Conic: + p += Conic( p, first, last); + break; + case FT_Curve_Tag_Cubic: + p += Cubic( p, first, last); + break; + case FT_Curve_Tag_On: + default: + contour->AddPoint( ftOutline.points[p].x, ftOutline.points[p].y); + } + } + + contourList.push_back( contour); + first = last + 1; + } + + return true; +} + + +int FTVectoriser::Conic( const int index, const int first, const int last) +{ + int next = index + 1; + int prev = index - 1; + + if( index == last) + next = first; + + if( index == first) + prev = last; + + if( ftOutline.tags[next] != FT_Curve_Tag_Conic) + { + ctrlPtArray[0][0] = ftOutline.points[prev].x; ctrlPtArray[0][1] = ftOutline.points[prev].y; + ctrlPtArray[1][0] = ftOutline.points[index].x; ctrlPtArray[1][1] = ftOutline.points[index].y; + ctrlPtArray[2][0] = ftOutline.points[next].x; ctrlPtArray[2][1] = ftOutline.points[next].y; + + evaluateCurve( 2); + return 1; + } + else + { + int next2 = next + 1; + if( next == last) + next2 = first; + + //create a phantom point + float x = ( ftOutline.points[index].x + ftOutline.points[next].x) / 2; + float y = ( ftOutline.points[index].y + ftOutline.points[next].y) / 2; + + // process first curve + ctrlPtArray[0][0] = ftOutline.points[prev].x; ctrlPtArray[0][1] = ftOutline.points[prev].y; + ctrlPtArray[1][0] = ftOutline.points[index].x; ctrlPtArray[1][1] = ftOutline.points[index].y; + ctrlPtArray[2][0] = x; ctrlPtArray[2][1] = y; + + evaluateCurve( 2); + + // process second curve + ctrlPtArray[0][0] = x; ctrlPtArray[0][1] = y; + ctrlPtArray[1][0] = ftOutline.points[next].x; ctrlPtArray[1][1] = ftOutline.points[next].y; + ctrlPtArray[2][0] = ftOutline.points[next2].x; ctrlPtArray[2][1] = ftOutline.points[next2].y; + evaluateCurve( 2); + + return 2; + } +} + + +int FTVectoriser::Cubic( const int index, const int first, const int last) +{ + int next = index + 1; + int prev = index - 1; + + if( index == last) + next = first; + + int next2 = next + 1; + + if( next == last) + next2 = first; + + if( index == first) + prev = last; + + ctrlPtArray[0][0] = ftOutline.points[prev].x; ctrlPtArray[0][1] = ftOutline.points[prev].y; + ctrlPtArray[1][0] = ftOutline.points[index].x; ctrlPtArray[1][1] = ftOutline.points[index].y; + ctrlPtArray[2][0] = ftOutline.points[next].x; ctrlPtArray[2][1] = ftOutline.points[next].y; + ctrlPtArray[3][0] = ftOutline.points[next2].x; ctrlPtArray[3][1] = ftOutline.points[next2].y; + + evaluateCurve( 3); + return 2; +} + + +// De Casteljau algorithm contributed by Jed Soane +void FTVectoriser::deCasteljau( const float t, const int n) +{ + //Calculating successive b(i)'s using de Casteljau algorithm. + for( int i = 1; i <= n; i++) + for( int k = 0; k <= (n - i); k++) + { + bValues[i][k][0] = (1 - t) * bValues[i - 1][k][0] + t * bValues[i - 1][k + 1][0]; + bValues[i][k][1] = (1 - t) * bValues[i - 1][k][1] + t * bValues[i - 1][k + 1][1]; + } + + //Specify next vertex to be included on curve + contour->AddPoint( bValues[n][0][0], bValues[n][0][1]); +} + + +// De Casteljau algorithm contributed by Jed Soane +void FTVectoriser::evaluateCurve( const int n) +{ + // setting the b(0) equal to the control points + for( int i = 0; i <= n; i++) + { + bValues[0][i][0] = ctrlPtArray[i][0]; + bValues[0][i][1] = ctrlPtArray[i][1]; + } + + float t; //parameter for curve point calc. [0.0, 1.0] + + for( int m = 0; m <= ( 1 / kBSTEPSIZE); m++) + { + t = m * kBSTEPSIZE; + deCasteljau( t, n); //calls to evaluate point on curve att. + } +} + + +void FTVectoriser::Output( double* data) +{ + int i = 0; + + for( int c= 0; c < contours(); ++c) + { + const FTContour* contour = contourList[c]; + + for( int p = 0; p < contour->size(); ++p) + { + data[i] = static_cast(contour->pointList[p].x / 64.0f); // is 64 correct? + data[i + 1] = static_cast(contour->pointList[p].y / 64.0f); + data[i + 2] = 0.0; // static_cast(contour->pointList[p].z / 64.0f); + i += 3; + } + } +}