/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2003 Robert Osfield * * This library is open source and may be redistributed and/or modified under * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or * (at your option) any later version. The full license is in LICENSE file * included with this distribution, and on the openscenegraph.org website. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * OpenSceneGraph Public License for more details. */ #include #include #include #include #include #include #include using namespace osgText; using namespace std; std::string findFontFile(const std::string& str) { // try looking in OSGFILEPATH etc first for fonts. std::string filename = osgDB::findDataFile(str); if (!filename.empty()) return std::string(filename); static osgDB::FilePathList s_FontFilePath; static bool initialized = false; if (!initialized) { initialized = true; #if defined(WIN32) osgDB::Registry::convertStringPathIntoFilePathList( ".;C:/winnt/fonts;C:/windows/fonts", s_FontFilePath); char *ptr; if ((ptr = getenv( "windir" ))) { s_FontFilePath.push_back(ptr); } #else osgDB::Registry::convertStringPathIntoFilePathList( ".:/usr/share/fonts/ttf:/usr/share/fonts/ttf/western:/usr/share/fonts/ttf/decoratives", s_FontFilePath); #endif } filename = osgDB::findFileInPath(str,s_FontFilePath); if (!filename.empty()) return filename; osg::notify(osg::WARN)<<"Warning: font file \""<(object); if (font) return font; // otherwise if the object has zero references then delete it by doing another unref(). if (object && object->referenceCount()==0) object->unref(); return 0; } Font::Font(FontImplementation* implementation): _width(16), _height(16), _margin(2), _textureWidthHint(256), _textureHeightHint(256), _minFilterHint(osg::Texture::LINEAR_MIPMAP_LINEAR), _magFilterHint(osg::Texture::LINEAR) { setImplementation(implementation); } Font::~Font() { 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; } Font::FontImplementation* Font::getImplementation() { return _implementation.get(); } const Font::FontImplementation* Font::getImplementation() const { return _implementation.get(); } std::string Font::getFileName() const { if (_implementation.valid()) return _implementation->getFileName(); return ""; } void Font::setSize(unsigned int width, unsigned int height) { if (_implementation.valid()) _implementation->setSize(width, height); } unsigned int Font::getWidth() { return _width; } unsigned int Font::getHeight() { return _height; } void Font::setGlyphImageMargin(unsigned int margin) { _margin = margin; } unsigned int Font::getGlyphImageMargin() const { return _margin; } void Font::setTextureSizeHint(unsigned int width,unsigned int height) { _textureWidthHint = width; _textureHeightHint = height; } unsigned int Font::getTextureWidthHint() const { return _textureWidthHint; } unsigned int Font::getTextureHeightHint() const { return _textureHeightHint; } void Font::setMinFilterHint(osg::Texture::FilterMode mode) { _minFilterHint = mode; } osg::Texture::FilterMode Font::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 Font::setMagFilterHint(osg::Texture::FilterMode mode) { _magFilterHint = mode; } osg::Texture::FilterMode Font::getMagFilterHint() const { return _magFilterHint; } 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) { //cout << "charcode "<<(char)charcode<<" "<<&_glyphTextureList<getSpaceForGlyph(glyph,posX,posY)) glyphTexture = itr->get(); } if (glyphTexture) { //cout << " found space for texture "<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); glyphTexture->setStateSet(stateset); stateset->setMode(GL_BLEND,osg::StateAttribute::ON); stateset->setTextureAttributeAndModes(0,glyphTexture,osg::StateAttribute::ON); stateset->setRenderingHint(osg::StateSet::TRANSPARENT_BIN); if (!glyphTexture->getSpaceForGlyph(glyph,posX,posY)) { osg::notify(osg::WARN)<<"Warning: unable to allocate texture big enough for glyph"<addGlyph(glyph,posX,posY); } Font::GlyphTexture::GlyphTexture(): _stateset(0), _margin(2), _usedY(0), _partUsedX(0), _partUsedY(0) { } Font::GlyphTexture::~GlyphTexture() { } // return -1 if *this < *rhs, 0 if *this==*rhs, 1 if *this>*rhs. int Font::GlyphTexture::compare(const osg::StateAttribute& rhs) const { if (this<&rhs) return -1; else if (this>&rhs) return 1; return 0; } bool Font::GlyphTexture::getSpaceForGlyph(Glyph* glyph, int& posX, int& posY) { int width = glyph->s()+2*_margin; int height = glyph->t()+2*_margin; // first check box (_partUsedX,_usedY) to (width,height) if (width <= (getTextureWidth()-_partUsedX) && height <= (getTextureHeight()-_usedY)) { // can fit in existing row. // record the position in which the texture will be stored. posX = _partUsedX+_margin; posY = _usedY+_margin; // move used markers on. _partUsedX += width; if (_usedY+height>_partUsedY) _partUsedY = _usedY+height; return true; } // start an new row. if (width <= getTextureWidth() && height <= (getTextureHeight()-_partUsedY)) { // can fit next row. _partUsedX = 0; _usedY = _partUsedY; posX = _partUsedX+_margin; posY = _usedY+_margin; // move used markers on. _partUsedX += width; if (_usedY+height>_partUsedY) _partUsedY = _usedY+height; return true; } // doesn't fit into glyph. return false; } void Font::GlyphTexture::addGlyph(Glyph* glyph, int posX, int posY) { _glyphs.push_back(glyph); for(unsigned int i=0;i<_glyphsToSubload.size();++i) { _glyphsToSubload[i].push_back(glyph); } // 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+_margin-1)/(float)(getTextureWidth()-1),(float)(posY+_margin-1)/(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 { // get the contextID (user defined ID of 0 upwards) for the // current OpenGL context. const unsigned int contextID = state.getContextID(); if (contextID>=_glyphsToSubload.size()) { // graphics context is beyond the number of glyphsToSubloads, so // we must now copy the glyph list across, this is a potential // threading issue though is multiple applies are happening the // same time on this object - to avoid this condition number of // graphics contexts should be set before create text. for(unsigned int i=_glyphsToSubload.size();i<=contextID;++i) { GlyphPtrList& glyphPtrs = _glyphsToSubload[i]; for(GlyphRefList::const_iterator itr=_glyphs.begin(); itr!=_glyphs.end(); ++itr) { glyphPtrs.push_back(itr->get()); } } } const Extensions* extensions = getExtensions(contextID,true); bool generateMipMapSupported = extensions->isGenerateMipMapSupported(); // get the globj for the current contextID. GLuint& handle = getTextureObject(contextID); bool generateMipMapOn = false; if (handle == 0) { // being bound for the first time, need to allocate the texture glGenTextures( 1L, (GLuint *)&handle ); glBindTexture( GL_TEXTURE_2D, handle ); applyTexParameters(GL_TEXTURE_2D,state); // need to look at generate mip map extension if mip mapping required. switch(_min_filter) { case NEAREST_MIPMAP_NEAREST: case NEAREST_MIPMAP_LINEAR: case LINEAR_MIPMAP_NEAREST: case LINEAR_MIPMAP_LINEAR: if (generateMipMapSupported) { glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP_SGIS,GL_TRUE); generateMipMapOn = true; } else glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, LINEAR); break; default: // not mip mapping so no problems. break; } // allocate the texture memory. glTexImage2D( GL_TEXTURE_2D, 0, GL_LUMINANCE_ALPHA, getTextureWidth(), getTextureHeight(), 0, GL_LUMINANCE_ALPHA, GL_UNSIGNED_BYTE, 0 ); } else { // reuse texture by binding. glBindTexture( GL_TEXTURE_2D, handle ); if (getTextureParameterDirty(contextID)) { applyTexParameters(GL_TEXTURE_2D,state); } bool generateMipMapOn = false; // need to look at generate mip map extension if mip mapping required. switch(_min_filter) { case NEAREST_MIPMAP_NEAREST: case NEAREST_MIPMAP_LINEAR: case LINEAR_MIPMAP_NEAREST: case LINEAR_MIPMAP_LINEAR: if (generateMipMapSupported) { glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP_SGIS,GL_TRUE); generateMipMapOn = true; } else glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, LINEAR); break; default: // not mip mapping so no problems. break; } } // now subload the glyphs that are outstanding for this graphics context. GlyphPtrList& glyphsWereSubloading = _glyphsToSubload[contextID]; if (!glyphsWereSubloading.empty()) { for(GlyphPtrList::iterator itr=glyphsWereSubloading.begin(); itr!=glyphsWereSubloading.end(); ++itr) { (*itr)->subload(); } // clear the list since we have now subloaded them. glyphsWereSubloading.clear(); } else { //std::cout << "no need to subload "<getStateSet():0; } const osg::StateSet* Font::Glyph::getStateSet() const { return _texture?_texture->getStateSet():0; } void Font::Glyph::setTexturePosition(int posX,int posY) { _texturePosX = posX; _texturePosY = posY; } int Font::Glyph::getTexturePositionX() const { return _texturePosX; } int Font::Glyph::getTexturePositionY() const { return _texturePosY; } void Font::Glyph::setMinTexCoord(const osg::Vec2& coord) { _minTexCoord=coord; } const osg::Vec2& Font::Glyph::getMinTexCoord() const { return _minTexCoord; } void Font::Glyph::setMaxTexCoord(const osg::Vec2& coord) { _maxTexCoord=coord; } const osg::Vec2& Font::Glyph::getMaxTexCoord() const { return _maxTexCoord; } void Font::Glyph::subload() const { GLenum errorNo = glGetError(); if (errorNo!=GL_NO_ERROR) { osg::notify(osg::WARN)<<"before Font::Glyph::subload(): detected OpenGL error '"<