diff --git a/examples/osgtext/osgtext.cpp b/examples/osgtext/osgtext.cpp index 837809b96..d6fc14723 100644 --- a/examples/osgtext/osgtext.cpp +++ b/examples/osgtext/osgtext.cpp @@ -15,6 +15,7 @@ #include #include +#include #include @@ -514,6 +515,9 @@ int main( int argc, char **argv ) // set the scene to render viewer.setSceneData(rootNode.get()); + + osgDB::Registry::instance()->closeLibrary("osgdb_freetype.so"); + // create the windows and run the threads. viewer.realize(Producer::CameraGroup::ThreadPerCamera); diff --git a/include/osg/ref_ptr b/include/osg/ref_ptr index a0decab29..3c3f3820c 100644 --- a/include/osg/ref_ptr +++ b/include/osg/ref_ptr @@ -14,7 +14,7 @@ #ifndef OSG_REF_PTR #define OSG_REF_PTR 1 -// #define AUTOMATIC_CAST_TO_POINTER +#define AUTOMATIC_CAST_TO_POINTER namespace osg { diff --git a/include/osgText/Font b/include/osgText/Font index 497d8bb69..afa8d2110 100644 --- a/include/osgText/Font +++ b/include/osgText/Font @@ -45,8 +45,9 @@ public: // forward declare nested classes. class Glyph; class GlyphTexture; + class FontImplementation; - Font(); + Font(FontImplementation* implementation=0); virtual osg::Object* cloneType() const { return 0; } // cloneType() not appropriate virtual osg::Object* clone(const osg::CopyOp&) const { return 0; } // clone() not appropriate @@ -54,22 +55,55 @@ public: virtual const char* className() const { return "Font"; } virtual const char* libraryName() const { return "osgText"; } - virtual std::string getFileName() const = 0; + virtual std::string getFileName() const; - /** Set the pixel width and height */ - virtual void setSize(unsigned int width, unsigned int height) = 0; + /** Set the pixel width and height hint.*/ + virtual void setSize(unsigned int width, unsigned int height); unsigned int getWidth() { return _width; } unsigned int getHeight() { return _height; } - virtual Glyph* getGlyph(unsigned int charcode) = 0; + /** Get a Glyph for specified charcode, and the font size nearest to the current font size hint.*/ + virtual Glyph* getGlyph(unsigned int charcode); - virtual osg::Vec2 getKerning(unsigned int leftcharcode,unsigned int rightcharcode) = 0; + /** Get a kerning (adjustment of spacing of two adjacent character) for specified charcodes, w.r.t the current font size hint.*/ + virtual osg::Vec2 getKerning(unsigned int leftcharcode,unsigned int rightcharcode); - virtual bool hasVertical() const = 0; + /** Return true if this font provides vertical alignments and spacing or glyphs.*/ + virtual bool hasVertical() const; + + + /** Set the margin around each glyph, + * to ensure that texture filtering doesn't bleed adjacent glyph's into each other. + * Default margin is 2 texels.*/ + void setGlyphImageMargin(unsigned int margin) { _margin = margin; } + unsigned int getGlyphImageMargin() const { return _margin; } + + /** Set the size of texture to create to store the glyph images when rendering. + * Note, this doesn't affect already created Texture Glhph's.*/ + void setTextureSizeHint(unsigned int width,unsigned int height); + + unsigned int getTextureWidthHint() const { return _textureWidthHint; } + unsigned int getTextureHeightHint() const { return _textureWidthHint; } + + /** Set the minification texture filter to use when creating the texture to store the glyph images when rendering. + * Note, this doesn't affect already created Texture Glhph's.*/ + void setMinFilterHint(osg::Texture::FilterMode mode); + osg::Texture::FilterMode getMinFilterHint() const { return _minFilterHint; } + + /** Set the magnification texture filter to use when creating the texture to store the glyph images when rendering. + * Note, this doesn't affect already created Texture Glhph's.*/ + void setMagFilterHint(osg::Texture::FilterMode mode); + osg::Texture::FilterMode getMagFilterHint() const { return _magFilterHint; } // make Text a friend to allow it add and remove its entry in the Font's _textList. - friend class Text; + friend class FontImplementation; + + void setImplementation(FontImplementation* implementation); + + FontImplementation* getImplementation() { return _implementation.get(); } + const FontImplementation* getImplementation() const { return _implementation.get(); } + protected: @@ -85,21 +119,57 @@ protected: typedef std::map< SizePair, GlyphMap > SizeGlyphMap; typedef std::set< Text* > TextList; - SizeGlyphMap _sizeGlyphMap; - GlyphTextureList _glyphTextureList; - StateSetList _stateSetList; - - // list of text object to contact when Font is forcebly removed. - TextList _textList; + SizeGlyphMap _sizeGlyphMap; + GlyphTextureList _glyphTextureList; + StateSetList _stateSetList; // current active size of font - unsigned int _width; - unsigned int _height; + unsigned int _width; + unsigned int _height; + unsigned int _margin; + + unsigned int _textureWidthHint; + unsigned int _textureHeightHint; + osg::Texture::FilterMode _minFilterHint; + osg::Texture::FilterMode _magFilterHint; + + osg::ref_ptr _implementation; // declare the nested classes. public: + class OSGTEXT_EXPORT FontImplementation : public osg::Referenced + { + public: + + virtual std::string getFileName() const = 0; + + /** Set the pixel width and height hint.*/ + virtual void setSize(unsigned int width, unsigned int height) = 0; + + /** Get a Glyph for specified charcode, and the font size nearest to the current font size hint.*/ + virtual Glyph* getGlyph(unsigned int charcode) = 0; + + /** Get a kerning (adjustment of spacing of two adjacent character) for specified charcodes, w.r.t the current font size hint.*/ + virtual osg::Vec2 getKerning(unsigned int leftcharcode,unsigned int rightcharcode) = 0; + + /** Return true if this font provides vertical alignments and spacing or glyphs.*/ + virtual bool hasVertical() const = 0; + + void setWidth(unsigned int width) { _facade->_width = width; } + + void setHeight(unsigned int height) { _facade->_height = height; } + + void addGlyph(unsigned int width, unsigned int height, unsigned int charcode, Glyph* glyph) + { + _facade->addGlyph(width, height, charcode, glyph); + } + + Font* _facade; + }; + + class OSGTEXT_EXPORT GlyphTexture : public osg::Texture2D { public: @@ -110,6 +180,10 @@ public: osg::StateSet* getStateSet() { return _stateset; } const osg::StateSet* getStateSet() const { return _stateset; } + /** Set the margin around each glyph, to ensure that texture filtering doesn't bleed adjacent glyph's into each other.*/ + void setGlyphImageMargin(unsigned int margin) { _margin = margin; } + unsigned int getGlyphImageMargin() const { return _margin; } + bool getSpaceForGlyph(Glyph* glyph, int& posX, int& posY); void addGlyph(Glyph* glyph,int posX, int posY); @@ -124,6 +198,7 @@ public: // parameter used to compute the size and position of empty space // in the texture which could accomodate new glyphs. + int _margin; int _usedY; int _partUsedX; int _partUsedY; diff --git a/src/osgPlugins/freetype/FreeTypeFont.cpp b/src/osgPlugins/freetype/FreeTypeFont.cpp index 1e2a61af9..469568451 100644 --- a/src/osgPlugins/freetype/FreeTypeFont.cpp +++ b/src/osgPlugins/freetype/FreeTypeFont.cpp @@ -13,7 +13,8 @@ #include "FreeTypeFont.h" #include FT_GLYPH_H - + +#include #include 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("<getTextureWidthHint()-2*_facade->getGlyphImageMargin(); + height = _facade->getTextureHeightHint()-2*_facade->getGlyphImageMargin(); + + osg::notify(osg::WARN)<<" sizes capped ("<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 = 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 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;cmetrics); @@ -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(); diff --git a/src/osgPlugins/freetype/FreeTypeFont.h b/src/osgPlugins/freetype/FreeTypeFont.h index 56d2c7596..0e7facfd6 100644 --- a/src/osgPlugins/freetype/FreeTypeFont.h +++ b/src/osgPlugins/freetype/FreeTypeFont.h @@ -19,7 +19,7 @@ #include #include FT_FREETYPE_H -class FreeTypeFont : public osgText::Font +class FreeTypeFont : public osgText::Font::FontImplementation { // declare the interface to a font. public: diff --git a/src/osgPlugins/freetype/FreeTypeLibrary.cpp b/src/osgPlugins/freetype/FreeTypeLibrary.cpp index ffd58077b..216687be2 100644 --- a/src/osgPlugins/freetype/FreeTypeLibrary.cpp +++ b/src/osgPlugins/freetype/FreeTypeLibrary.cpp @@ -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; } diff --git a/src/osgPlugins/freetype/FreeTypeLibrary.h b/src/osgPlugins/freetype/FreeTypeLibrary.h index bd4fd8992..3a2d2ddda 100644 --- a/src/osgPlugins/freetype/FreeTypeLibrary.h +++ b/src/osgPlugins/freetype/FreeTypeLibrary.h @@ -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: diff --git a/src/osgText/DefaultFont.cpp b/src/osgText/DefaultFont.cpp index 3fa0bd6e7..8e21a0dc2 100644 --- a/src/osgText/DefaultFont.cpp +++ b/src/osgText/DefaultFont.cpp @@ -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 = 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_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 diff --git a/src/osgText/Text.cpp b/src/osgText/Text.cpp index 0d80d43e3..05f2e76c0 100644 --- a/src/osgText/Text.cpp +++ b/src/osgText/Text.cpp @@ -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) {