From fab6f24f4e617b1c3a9a4c292cf1cefdc5423451 Mon Sep 17 00:00:00 2001 From: Robert Osfield Date: Thu, 6 Mar 2003 17:11:24 +0000 Subject: [PATCH] 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. --- examples/osgtext/osgtext.cpp | 4 + include/osg/ref_ptr | 2 +- include/osgText/Font | 107 +++++++++++++++++--- src/osgPlugins/freetype/FreeTypeFont.cpp | 62 ++++++++---- src/osgPlugins/freetype/FreeTypeFont.h | 2 +- src/osgPlugins/freetype/FreeTypeLibrary.cpp | 13 +-- src/osgPlugins/freetype/FreeTypeLibrary.h | 2 +- src/osgText/DefaultFont.cpp | 26 ++++- src/osgText/Font.cpp | 94 ++++++++++++----- src/osgText/Text.cpp | 12 +-- 10 files changed, 240 insertions(+), 84 deletions(-) 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) {