Changed the osgText::Font implementation so it used a facade us abstract away
the actual implemention. This has been done so that when a freetype font is created the implementation can unloaded when the freetype plugin is unloaded without breaking the main font. Also add image margin around founds to prevent any image boundaries appearing.
This commit is contained in:
@@ -13,7 +13,8 @@
|
||||
|
||||
#include "FreeTypeFont.h"
|
||||
#include FT_GLYPH_H
|
||||
|
||||
|
||||
#include <osg/Notify>
|
||||
#include <osgDB/WriteFile>
|
||||
|
||||
FreeTypeFont::FreeTypeFont(const std::string& filename, FT_Face face):
|
||||
@@ -28,32 +29,35 @@ FreeTypeFont::~FreeTypeFont()
|
||||
|
||||
void FreeTypeFont::setSize(unsigned int width, unsigned int height)
|
||||
{
|
||||
if (width+2*_facade->getGlyphImageMargin()>_facade->getTextureWidthHint() ||
|
||||
height+2*_facade->getGlyphImageMargin()>_facade->getTextureHeightHint())
|
||||
{
|
||||
osg::notify(osg::WARN)<<"Warning: FreeTypeFont::setSize("<<width<<","<<height<<") sizes too large,"<<std::endl;
|
||||
|
||||
width = _facade->getTextureWidthHint()-2*_facade->getGlyphImageMargin();
|
||||
height = _facade->getTextureHeightHint()-2*_facade->getGlyphImageMargin();
|
||||
|
||||
osg::notify(osg::WARN)<<" sizes capped ("<<width<<","<<height<<") to fit int current glyph texture size."<<std::endl;
|
||||
}
|
||||
|
||||
FT_Error error = FT_Set_Pixel_Sizes( _face, /* handle to face object */
|
||||
width, /* pixel_width */
|
||||
height ); /* pixel_height */
|
||||
|
||||
if (error)
|
||||
{
|
||||
std::cout<<"FT_Set_Pixel_Sizes() - error "<<error<<std::endl;
|
||||
osg::notify(osg::WARN)<<"FT_Set_Pixel_Sizes() - error "<<error<<std::endl;
|
||||
}
|
||||
else
|
||||
{
|
||||
_width = width;
|
||||
_height = height;
|
||||
setWidth(width);
|
||||
setHeight(height);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
osgText::Font::Glyph* FreeTypeFont::getGlyph(unsigned int charcode)
|
||||
{
|
||||
SizeGlyphMap::iterator itr = _sizeGlyphMap.find(SizePair(_width,_height));
|
||||
if (itr!=_sizeGlyphMap.end())
|
||||
{
|
||||
GlyphMap& glyphmap = itr->second;
|
||||
GlyphMap::iterator gitr = glyphmap.find(charcode);
|
||||
if (gitr!=glyphmap.end()) return gitr->second.get();
|
||||
}
|
||||
|
||||
FT_Error error = FT_Load_Char( _face, charcode, FT_LOAD_RENDER|FT_LOAD_NO_BITMAP );
|
||||
if (error)
|
||||
{
|
||||
@@ -64,29 +68,47 @@ osgText::Font::Glyph* FreeTypeFont::getGlyph(unsigned int charcode)
|
||||
|
||||
FT_GlyphSlot glyphslot = _face->glyph;
|
||||
|
||||
int rows = glyphslot->bitmap.rows;
|
||||
int width = glyphslot->bitmap.width;
|
||||
int pitch = glyphslot->bitmap.pitch;
|
||||
unsigned char* buffer = glyphslot->bitmap.buffer;
|
||||
|
||||
osg::ref_ptr<Glyph> glyph = new Glyph;
|
||||
unsigned char* data = new unsigned char[width*rows*2];
|
||||
glyph->setImage(width,rows,1,
|
||||
unsigned int sourceWidth = glyphslot->bitmap.width;;
|
||||
unsigned int sourceHeight = glyphslot->bitmap.rows;
|
||||
|
||||
unsigned int margin = _facade->getGlyphImageMargin();
|
||||
unsigned int width = sourceWidth+2*margin;
|
||||
unsigned int height = sourceHeight+2*margin;
|
||||
|
||||
osg::ref_ptr<osgText::Font::Glyph> glyph = new osgText::Font::Glyph;
|
||||
|
||||
unsigned int dataSize = width*height*2;
|
||||
unsigned char* data = new unsigned char[dataSize];
|
||||
|
||||
|
||||
// clear the image to zeros.
|
||||
for(unsigned char* p=data;p!=data+dataSize;++p) *p = 0;
|
||||
|
||||
glyph->setImage(width,height,1,
|
||||
GL_LUMINANCE_ALPHA,
|
||||
GL_LUMINANCE_ALPHA,GL_UNSIGNED_BYTE,
|
||||
data,
|
||||
osg::Image::USE_NEW_DELETE,
|
||||
1);
|
||||
|
||||
// skip the top margin
|
||||
data += (margin*width)*2;
|
||||
|
||||
// copy image across to osgText::Glyph image.
|
||||
for(int r=rows-1;r>=0;--r)
|
||||
for(int r=sourceHeight-1;r>=0;--r)
|
||||
{
|
||||
data+=2*margin; // skip the left margin
|
||||
|
||||
unsigned char* ptr = buffer+r*pitch;
|
||||
for(int c=0;c<width;++c,++ptr)
|
||||
for(unsigned int c=0;c<sourceWidth;++c,++ptr)
|
||||
{
|
||||
(*data++)=255;
|
||||
(*data++)=*ptr;
|
||||
}
|
||||
data+=2*margin; // skip the right margin.
|
||||
}
|
||||
|
||||
FT_Glyph_Metrics* metrics = &(glyphslot->metrics);
|
||||
@@ -96,7 +118,7 @@ osgText::Font::Glyph* FreeTypeFont::getGlyph(unsigned int charcode)
|
||||
glyph->setVerticalBearing(osg::Vec2((float)metrics->vertBearingX/64.0f,(float)(metrics->vertBearingY-metrics->height)/64.0f)); // top middle.
|
||||
glyph->setVerticalAdvance((float)metrics->vertAdvance/64.0f);
|
||||
|
||||
addGlyph(_width,_height,charcode,glyph.get());
|
||||
addGlyph(_facade->getWidth(),_facade->getHeight(),charcode,glyph.get());
|
||||
|
||||
return glyph.get();
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
#include <ft2build.h>
|
||||
#include FT_FREETYPE_H
|
||||
|
||||
class FreeTypeFont : public osgText::Font
|
||||
class FreeTypeFont : public osgText::Font::FontImplementation
|
||||
{
|
||||
// declare the interface to a font.
|
||||
public:
|
||||
|
||||
@@ -26,7 +26,6 @@ FreeTypeLibrary::FreeTypeLibrary()
|
||||
|
||||
FreeTypeLibrary::~FreeTypeLibrary()
|
||||
{
|
||||
|
||||
for(FontMap::iterator itr=_fontMap.begin();
|
||||
itr!=_fontMap.end();
|
||||
++itr)
|
||||
@@ -37,7 +36,7 @@ FreeTypeLibrary::~FreeTypeLibrary()
|
||||
// external references must exist...
|
||||
itr->second = 0;
|
||||
|
||||
delete freetypefont;
|
||||
freetypefont->_facade->setImplementation(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -55,11 +54,11 @@ FreeTypeLibrary* FreeTypeLibrary::instance()
|
||||
return &s_library;
|
||||
}
|
||||
|
||||
FreeTypeFont* FreeTypeLibrary::getFont(const std::string& fontfile,unsigned int index)
|
||||
osgText::Font* FreeTypeLibrary::getFont(const std::string& fontfile,unsigned int index)
|
||||
{
|
||||
|
||||
FontMap::iterator itr = _fontMap.find(fontfile);
|
||||
if (itr!=_fontMap.end()) return itr->second.get();
|
||||
if (itr!=_fontMap.end()) return itr->second->_facade;
|
||||
|
||||
FT_Face face; /* handle to face object */
|
||||
FT_Error error = FT_New_Face( _ftlibrary, fontfile.c_str(), index, &face );
|
||||
@@ -76,8 +75,10 @@ FreeTypeFont* FreeTypeLibrary::getFont(const std::string& fontfile,unsigned int
|
||||
return 0;
|
||||
}
|
||||
|
||||
FreeTypeFont* font = new FreeTypeFont(fontfile,face);
|
||||
_fontMap[fontfile]=font;
|
||||
FreeTypeFont* fontImp = new FreeTypeFont(fontfile,face);
|
||||
_fontMap[fontfile]=fontImp;
|
||||
|
||||
osgText::Font* font = new osgText::Font(fontImp);
|
||||
return font;
|
||||
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ public:
|
||||
/** get the singleton instance.*/
|
||||
static FreeTypeLibrary* instance();
|
||||
|
||||
FreeTypeFont* getFont(const std::string& fontfile,unsigned int index=0);
|
||||
osgText::Font* getFont(const std::string& fontfile,unsigned int index=0);
|
||||
|
||||
protected:
|
||||
|
||||
|
||||
@@ -22,6 +22,8 @@ using namespace osgText;
|
||||
|
||||
DefaultFont::DefaultFont()
|
||||
{
|
||||
_minFilterHint = osg::Texture::LINEAR_MIPMAP_LINEAR;
|
||||
_magFilterHint = osg::Texture::NEAREST;
|
||||
constructGlyphs();
|
||||
}
|
||||
|
||||
@@ -190,15 +192,25 @@ void DefaultFont::constructGlyphs()
|
||||
{0x00, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x1c, 0x00, 0x00}
|
||||
};
|
||||
|
||||
_width = 8;
|
||||
_height = 12;
|
||||
unsigned int sourceWidth = 8;
|
||||
unsigned int sourceHeight = 12;
|
||||
|
||||
_width = sourceWidth+2*_margin;
|
||||
_height = sourceHeight+2*_margin;
|
||||
|
||||
|
||||
|
||||
// populate the glyph mp
|
||||
for(unsigned int i=32;i<127;i++)
|
||||
{
|
||||
osg::ref_ptr<Glyph> glyph = new Glyph;
|
||||
|
||||
unsigned char* data = new unsigned char[8*12*2];
|
||||
unsigned int dataSize = _width*_height*2;
|
||||
unsigned char* data = new unsigned char[dataSize];
|
||||
|
||||
// clear the image to zeros.
|
||||
for(unsigned char* p=data;p!=data+dataSize;++p) *p = 0;
|
||||
|
||||
glyph->setImage(_width,_height,1,
|
||||
GL_LUMINANCE_ALPHA,
|
||||
GL_LUMINANCE_ALPHA,GL_UNSIGNED_BYTE,
|
||||
@@ -210,8 +222,13 @@ void DefaultFont::constructGlyphs()
|
||||
unsigned char* ptr = rasters[i-32];
|
||||
unsigned char value_on = 255;
|
||||
unsigned char value_off = 0;
|
||||
for(unsigned int row=0;row<_height;++row,++ptr)
|
||||
|
||||
// skip the top margin
|
||||
data += (_margin*_width)*2;
|
||||
|
||||
for(unsigned int row=0;row<sourceHeight;++row,++ptr)
|
||||
{
|
||||
data+=2*_margin; // skip the left margin
|
||||
(*data++)=((*ptr)&128)?value_on:value_off;
|
||||
(*data++)=((*ptr)&128)?value_on:value_off;
|
||||
(*data++)=((*ptr)&64)?value_on:value_off;
|
||||
@@ -228,6 +245,7 @@ void DefaultFont::constructGlyphs()
|
||||
(*data++)=((*ptr)&2)?value_on:value_off;
|
||||
(*data++)=((*ptr)&1)?value_on:value_off;
|
||||
(*data++)=((*ptr)&1)?value_on:value_off;
|
||||
data+=2*_margin; // skip the right margin.
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -75,23 +75,69 @@ osgText::Font* osgText::readFontFile(const std::string& filename)
|
||||
}
|
||||
|
||||
|
||||
Font::Font():
|
||||
Font::Font(FontImplementation* implementation):
|
||||
_width(16),
|
||||
_height(16)
|
||||
_height(16),
|
||||
_margin(2),
|
||||
_textureWidthHint(256),
|
||||
_textureHeightHint(256),
|
||||
_minFilterHint(osg::Texture::LINEAR_MIPMAP_LINEAR),
|
||||
_magFilterHint(osg::Texture::LINEAR)
|
||||
{
|
||||
setImplementation(implementation);
|
||||
}
|
||||
|
||||
Font::~Font()
|
||||
{
|
||||
for(TextList::iterator itr=_textList.begin();
|
||||
itr!=_textList.end();
|
||||
++itr)
|
||||
{
|
||||
Text* text = *itr;
|
||||
text->_font.release();
|
||||
}
|
||||
if (_implementation.valid()) _implementation->_facade = 0;
|
||||
}
|
||||
|
||||
void Font::setImplementation(FontImplementation* implementation)
|
||||
{
|
||||
if (_implementation.valid()) _implementation->_facade = 0;
|
||||
_implementation = implementation;
|
||||
if (_implementation.valid()) _implementation->_facade = this;
|
||||
}
|
||||
|
||||
std::string Font::getFileName() const
|
||||
{
|
||||
if (_implementation.valid()) return _implementation->getFileName();
|
||||
return "";
|
||||
}
|
||||
|
||||
void Font::setSize(unsigned int width, unsigned int height)
|
||||
{
|
||||
if (_implementation.valid()) return _implementation->setSize(width, height);
|
||||
}
|
||||
|
||||
Font::Glyph* Font::getGlyph(unsigned int charcode)
|
||||
{
|
||||
SizeGlyphMap::iterator itr = _sizeGlyphMap.find(SizePair(_width,_height));
|
||||
if (itr!=_sizeGlyphMap.end())
|
||||
{
|
||||
GlyphMap& glyphmap = itr->second;
|
||||
GlyphMap::iterator gitr = glyphmap.find(charcode);
|
||||
if (gitr!=glyphmap.end()) return gitr->second.get();
|
||||
}
|
||||
|
||||
if (_implementation.valid()) return _implementation->getGlyph(charcode);
|
||||
else return 0;
|
||||
}
|
||||
|
||||
osg::Vec2 Font::getKerning(unsigned int leftcharcode,unsigned int rightcharcode)
|
||||
{
|
||||
if (_implementation.valid()) return _implementation->getKerning(leftcharcode,rightcharcode);
|
||||
else return osg::Vec2(0.0f,0.0f);
|
||||
}
|
||||
|
||||
bool Font::hasVertical() const
|
||||
{
|
||||
if (_implementation.valid()) return _implementation->hasVertical();
|
||||
else return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void Font::addGlyph(unsigned int width, unsigned int height, unsigned int charcode, Glyph* glyph)
|
||||
{
|
||||
_sizeGlyphMap[SizePair(width,height)][charcode]=glyph;
|
||||
@@ -115,13 +161,12 @@ void Font::addGlyph(unsigned int width, unsigned int height, unsigned int charco
|
||||
_stateSetList.push_back(stateset);
|
||||
|
||||
glyphTexture = new GlyphTexture;
|
||||
|
||||
|
||||
// reserve enough space for the glyphs.
|
||||
glyphTexture->setTextureSize(256,256);
|
||||
glyphTexture->setFilter(osg::Texture::MIN_FILTER,osg::Texture::LINEAR);
|
||||
glyphTexture->setFilter(osg::Texture::MIN_FILTER,osg::Texture::LINEAR_MIPMAP_LINEAR);
|
||||
//glyphTexture->setFilter(osg::Texture::MAG_FILTER,osg::Texture::NEAREST);
|
||||
glyphTexture->setFilter(osg::Texture::MAG_FILTER,osg::Texture::LINEAR);
|
||||
glyphTexture->setGlyphImageMargin(_margin);
|
||||
glyphTexture->setTextureSize(_textureWidthHint,_textureHeightHint);
|
||||
glyphTexture->setFilter(osg::Texture::MIN_FILTER,_minFilterHint);
|
||||
glyphTexture->setFilter(osg::Texture::MAG_FILTER,_magFilterHint);
|
||||
glyphTexture->setMaxAnisotropy(8);
|
||||
|
||||
_glyphTextureList.push_back(glyphTexture);
|
||||
@@ -147,6 +192,7 @@ void Font::addGlyph(unsigned int width, unsigned int height, unsigned int charco
|
||||
|
||||
Font::GlyphTexture::GlyphTexture():
|
||||
_stateset(0),
|
||||
_margin(2),
|
||||
_usedY(0),
|
||||
_partUsedX(0),
|
||||
_partUsedY(0)
|
||||
@@ -159,11 +205,9 @@ Font::GlyphTexture::~GlyphTexture()
|
||||
|
||||
bool Font::GlyphTexture::getSpaceForGlyph(Glyph* glyph, int& posX, int& posY)
|
||||
{
|
||||
|
||||
int margin = 2;
|
||||
|
||||
int width = glyph->s()+2*margin;
|
||||
int height = glyph->t()+2*margin;
|
||||
int width = glyph->s()+2*_margin;
|
||||
int height = glyph->t()+2*_margin;
|
||||
|
||||
// first check box (_partUsedX,_usedY) to (width,height)
|
||||
if (width <= (getTextureWidth()-_partUsedX) &&
|
||||
@@ -172,8 +216,8 @@ bool Font::GlyphTexture::getSpaceForGlyph(Glyph* glyph, int& posX, int& posY)
|
||||
// can fit in existing row.
|
||||
|
||||
// record the position in which the texture will be stored.
|
||||
posX = _partUsedX+margin;
|
||||
posY = _usedY+margin;
|
||||
posX = _partUsedX+_margin;
|
||||
posY = _usedY+_margin;
|
||||
|
||||
// move used markers on.
|
||||
_partUsedX += width;
|
||||
@@ -190,8 +234,8 @@ bool Font::GlyphTexture::getSpaceForGlyph(Glyph* glyph, int& posX, int& posY)
|
||||
_partUsedX = 0;
|
||||
_usedY = _partUsedY;
|
||||
|
||||
posX = _partUsedX+margin;
|
||||
posY = _usedY+margin;
|
||||
posX = _partUsedX+_margin;
|
||||
posY = _usedY+_margin;
|
||||
|
||||
// move used markers on.
|
||||
_partUsedX += width;
|
||||
@@ -216,8 +260,8 @@ void Font::GlyphTexture::addGlyph(Glyph* glyph, int posX, int posY)
|
||||
// set up the details of where to place glyph's image in the texture.
|
||||
glyph->setTexture(this);
|
||||
glyph->setTexturePosition(posX,posY);
|
||||
glyph->setMinTexCoord(osg::Vec2(((float)posX-1.0f)/((float)getTextureWidth()-1.0f),((float)posY-1)/((float)getTextureHeight()-1.0f)));
|
||||
glyph->setMaxTexCoord(osg::Vec2((float)(posX+glyph->s())/((float)getTextureWidth()-1.0f),(float)(posY+glyph->t())/((float)getTextureHeight()-1.0f)));
|
||||
glyph->setMinTexCoord(osg::Vec2((float)(posX+_margin)/(float)(getTextureWidth()-1),(float)(posY+_margin)/(float)(getTextureHeight()-1)));
|
||||
glyph->setMaxTexCoord(osg::Vec2((float)(posX+glyph->s()-_margin)/(float)(getTextureWidth()-1),(float)(posY+glyph->t()-_margin)/(float)(getTextureHeight()-1)));
|
||||
}
|
||||
|
||||
void Font::GlyphTexture::apply(osg::State& state) const
|
||||
|
||||
@@ -51,25 +51,17 @@ Text::Text(const Text& text,const osg::CopyOp& copyop):
|
||||
_color(text._color),
|
||||
_drawMode(text._drawMode)
|
||||
{
|
||||
if (_font.valid()) _font->_textList.insert(this);
|
||||
}
|
||||
|
||||
Text::~Text()
|
||||
{
|
||||
if (_font.valid()) _font->_textList.erase(this);
|
||||
}
|
||||
|
||||
void Text::setFont(Font* font)
|
||||
{
|
||||
if (_font==font) return;
|
||||
|
||||
// unregister from the old font.
|
||||
if (_font.valid()) _font->_textList.erase(this);
|
||||
|
||||
_font = font;
|
||||
|
||||
// register with the new font.
|
||||
if (_font.valid()) _font->_textList.insert(this);
|
||||
|
||||
computeGlyphRepresentation();
|
||||
}
|
||||
@@ -244,8 +236,8 @@ void Text::computeGlyphRepresentation()
|
||||
if (glyph)
|
||||
{
|
||||
|
||||
float width = (float)glyph->s() * wr;
|
||||
float height = (float)glyph->t() * hr;
|
||||
float width = (float)(glyph->s()-2*activefont->getGlyphImageMargin()) * wr;
|
||||
float height = (float)(glyph->t()-2*activefont->getGlyphImageMargin()) * hr;
|
||||
|
||||
if (_layout==RIGHT_TO_LEFT)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user