root/OpenSceneGraph/trunk/src/osgText/Font.cpp @ 3153

Revision 3153, 20.3 kB (checked in by robert, 10 years ago)

Added handling of the case when texture objects are released from underneath
the osgText::Font::GlyphTexture? - something which requires a full rebuild
of the texture object.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
Line 
1
2/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2003 Robert Osfield
3 *
4 * This library is open source and may be redistributed and/or modified under 
5 * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or
6 * (at your option) any later version.  The full license is in LICENSE file
7 * included with this distribution, and on the openscenegraph.org website.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12 * OpenSceneGraph Public License for more details.
13*/
14
15#include <osgText/Font>
16#include <osgText/Text>
17
18#include <osg/State>
19#include <osg/Notify>
20#include <osg/TexEnv>
21
22#include <osgDB/ReadFile>
23#include <osgDB/FileUtils>
24#include <osg/GLU>
25
26using namespace osgText;
27using namespace std;
28
29std::string findFontFile(const std::string& str)
30{
31    // try looking in OSGFILEPATH etc first for fonts.
32    std::string filename = osgDB::findDataFile(str);
33    if (!filename.empty()) return std::string(filename);
34
35
36    static osgDB::FilePathList s_FontFilePath;
37    static bool initialized = false;
38    if (!initialized)
39    {
40        initialized = true;
41    #if defined(WIN32)
42        osgDB::Registry::convertStringPathIntoFilePathList(
43            ".;C:/winnt/fonts;C:/windows/fonts",
44            s_FontFilePath);
45
46        char *ptr;
47        if ((ptr = getenv( "windir" )))
48        {
49            s_FontFilePath.push_back(ptr);
50        }
51    #else
52        osgDB::Registry::convertStringPathIntoFilePathList(
53            ".:/usr/share/fonts/ttf:/usr/share/fonts/ttf/western:/usr/share/fonts/ttf/decoratives",
54            s_FontFilePath);
55    #endif
56    }
57
58    filename = osgDB::findFileInPath(str,s_FontFilePath);
59    if (!filename.empty()) return filename;
60
61    osg::notify(osg::WARN)<<"Warning: font file \""<<str<<"\" not found."<<std::endl;   
62    return std::string();
63}
64
65osgText::Font* osgText::readFontFile(const std::string& filename)
66{
67    std::string foundFile = findFontFile(filename);
68    if (foundFile.empty()) return 0;
69
70    osg::Object* object = osgDB::readObjectFile(foundFile);
71
72    // if the object is a font then return it.
73    osgText::Font* font = dynamic_cast<osgText::Font*>(object);
74    if (font) return font;
75
76    // otherwise if the object has zero references then delete it by doing another unref().
77    if (object && object->referenceCount()==0) object->unref();
78    return 0;
79}
80
81
82Font::Font(FontImplementation* implementation):
83    _width(16),
84    _height(16),
85    _margin(2),
86    _textureWidthHint(256),
87    _textureHeightHint(256),
88    _minFilterHint(osg::Texture::LINEAR_MIPMAP_LINEAR),
89    _magFilterHint(osg::Texture::LINEAR)
90{
91    setImplementation(implementation);
92    _texEnv = new osg::TexEnv(osg::TexEnv::BLEND);
93}
94
95Font::~Font()
96{
97    if (_implementation.valid()) _implementation->_facade = 0;
98}
99
100void Font::setImplementation(FontImplementation* implementation)
101{
102    if (_implementation.valid()) _implementation->_facade = 0;
103    _implementation = implementation;
104    if (_implementation.valid()) _implementation->_facade = this;
105}
106
107Font::FontImplementation* Font::getImplementation()
108{
109    return _implementation.get();
110}
111
112const Font::FontImplementation* Font::getImplementation() const
113{
114    return _implementation.get();
115}
116
117std::string Font::getFileName() const
118{
119    if (_implementation.valid()) return _implementation->getFileName();
120    return "";
121}
122
123void Font::setSize(unsigned int width, unsigned int height)
124{
125    if (_implementation.valid()) _implementation->setSize(width, height);
126}
127
128unsigned int Font::getWidth() const
129{
130    return _width;
131}
132
133unsigned int Font::getHeight() const
134{
135    return _height;
136}
137
138void Font::setGlyphImageMargin(unsigned int margin)
139{
140    _margin = margin;
141}
142
143unsigned int Font::getGlyphImageMargin() const
144{
145    return _margin;
146}
147
148void Font::setTextureSizeHint(unsigned int width,unsigned int height)
149{
150    _textureWidthHint = width;
151    _textureHeightHint = height;
152}
153
154unsigned int Font::getTextureWidthHint() const
155{
156    return _textureWidthHint;
157}
158
159unsigned int Font::getTextureHeightHint() const
160{
161    return _textureHeightHint;
162}   
163
164
165void Font::setMinFilterHint(osg::Texture::FilterMode mode)
166{
167    _minFilterHint = mode;
168}
169
170osg::Texture::FilterMode Font::getMinFilterHint() const
171{
172    return _minFilterHint;
173}
174
175/** Set the magnification texture filter to use when creating the texture to store the glyph images when rendering.
176  * Note, this doesn't affect already created Texture Glhph's.*/
177void Font::setMagFilterHint(osg::Texture::FilterMode mode)
178{
179    _magFilterHint = mode;
180}
181
182osg::Texture::FilterMode Font::getMagFilterHint() const
183{
184    return _magFilterHint;
185}
186
187
188Font::Glyph* Font::getGlyph(unsigned int charcode)
189{
190    SizeGlyphMap::iterator itr = _sizeGlyphMap.find(SizePair(_width,_height));
191    if (itr!=_sizeGlyphMap.end())
192    {
193        GlyphMap& glyphmap = itr->second;   
194        GlyphMap::iterator gitr = glyphmap.find(charcode);
195        if (gitr!=glyphmap.end()) return gitr->second.get();
196    }
197
198    if (_implementation.valid()) return _implementation->getGlyph(charcode);
199    else return 0;
200}
201
202osg::Vec2 Font::getKerning(unsigned int leftcharcode,unsigned int rightcharcode, KerningType kerningType)
203{
204    if (_implementation.valid()) return _implementation->getKerning(leftcharcode,rightcharcode,kerningType);
205    else return osg::Vec2(0.0f,0.0f);
206}
207
208bool Font::hasVertical() const
209{
210    if (_implementation.valid()) return _implementation->hasVertical();
211    else return false;
212}
213
214
215
216void Font::addGlyph(unsigned int width, unsigned int height, unsigned int charcode, Glyph* glyph)
217{
218
219    //cout << "charcode "<<(char)charcode<<"  "<<&_glyphTextureList<<endl;
220
221    _sizeGlyphMap[SizePair(width,height)][charcode]=glyph;
222   
223   
224    int posX=0,posY=0;
225   
226    GlyphTexture* glyphTexture = 0;
227    for(GlyphTextureList::iterator itr=_glyphTextureList.begin();
228        itr!=_glyphTextureList.end() && !glyphTexture;
229        ++itr)
230    {
231        if ((*itr)->getSpaceForGlyph(glyph,posX,posY)) glyphTexture = itr->get();
232    }
233   
234    if (glyphTexture)
235    {
236        //cout << "    found space for texture "<<glyphTexture<<" posX="<<posX<<" posY="<<posY<<endl;
237    }
238   
239    if (!glyphTexture)
240    {
241       
242        osg::StateSet* stateset = new osg::StateSet;
243        _stateSetList.push_back(stateset);
244
245        glyphTexture = new GlyphTexture;
246       
247        //std::cout<<"    Creating new GlyphTexture "<<glyphTexture<<"& StateSet "<<stateset<<std::endl;
248
249        static int numberOfTexturesAllocated = 0;
250        ++numberOfTexturesAllocated;
251        //std::cout << "    " << this<< "  numberOfTexturesAllocated "<<numberOfTexturesAllocated<<std::endl;
252
253        // reserve enough space for the glyphs.
254        glyphTexture->setGlyphImageMargin(_margin);
255        glyphTexture->setTextureSize(_textureWidthHint,_textureHeightHint);
256        glyphTexture->setFilter(osg::Texture::MIN_FILTER,_minFilterHint);
257        glyphTexture->setFilter(osg::Texture::MAG_FILTER,_magFilterHint);
258        glyphTexture->setMaxAnisotropy(8);
259       
260        _glyphTextureList.push_back(glyphTexture);
261       
262        glyphTexture->setStateSet(stateset);
263        stateset->setMode(GL_BLEND,osg::StateAttribute::ON);
264        stateset->setTextureAttributeAndModes(0,glyphTexture,osg::StateAttribute::ON);
265        if (_texEnv.valid()) stateset->setTextureAttribute(0,_texEnv.get());
266        stateset->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);
267
268        if (!glyphTexture->getSpaceForGlyph(glyph,posX,posY))
269        {
270            osg::notify(osg::WARN)<<"Warning: unable to allocate texture big enough for glyph"<<std::endl;
271            return;
272        }
273
274    }   
275   
276    // add the glyph into the texture.
277    glyphTexture->addGlyph(glyph,posX,posY);
278   
279}
280
281
282Font::GlyphTexture::GlyphTexture():
283    _stateset(0),
284    _margin(2),
285    _usedY(0),
286    _partUsedX(0),
287    _partUsedY(0)
288{
289}
290
291Font::GlyphTexture::~GlyphTexture()
292{
293}
294
295// return -1 if *this < *rhs, 0 if *this==*rhs, 1 if *this>*rhs.
296int Font::GlyphTexture::compare(const osg::StateAttribute& rhs) const
297{
298    if (this<&rhs) return -1;
299    else if (this>&rhs) return 1;
300    return 0;
301}
302
303
304bool Font::GlyphTexture::getSpaceForGlyph(Glyph* glyph, int& posX, int& posY)
305{
306       
307    int width = glyph->s()+2*_margin;
308    int height = glyph->t()+2*_margin;
309
310    // first check box (_partUsedX,_usedY) to (width,height)
311    if (width <= (getTextureWidth()-_partUsedX) &&
312        height <= (getTextureHeight()-_usedY))
313    {
314        // can fit in existing row.
315
316        // record the position in which the texture will be stored.
317        posX = _partUsedX+_margin;
318        posY = _usedY+_margin;       
319
320        // move used markers on.
321        _partUsedX += width;
322        if (_usedY+height>_partUsedY) _partUsedY = _usedY+height;
323       
324        return true;
325    }
326   
327    // start an new row.
328    if (width <= getTextureWidth() &&
329        height <= (getTextureHeight()-_partUsedY))
330    {
331        // can fit next row.
332        _partUsedX = 0;
333        _usedY = _partUsedY;
334
335        posX = _partUsedX+_margin;
336        posY = _usedY+_margin;       
337
338        // move used markers on.
339        _partUsedX += width;
340        if (_usedY+height>_partUsedY) _partUsedY = _usedY+height;
341       
342        return true;
343    }
344
345    // doesn't fit into glyph.
346    return false;
347}
348
349void Font::GlyphTexture::addGlyph(Glyph* glyph, int posX, int posY)
350{
351
352    _glyphs.push_back(glyph);
353    for(unsigned int i=0;i<_glyphsToSubload.size();++i)
354    {
355        _glyphsToSubload[i].push_back(glyph);
356    }
357
358    // set up the details of where to place glyph's image in the texture.
359    glyph->setTexture(this);
360    glyph->setTexturePosition(posX,posY);
361    unsigned int sizeAdjustment = 0; // was 1.
362    glyph->setMinTexCoord(osg::Vec2((float)(posX+_margin)/(float)(getTextureWidth()-sizeAdjustment),(float)(posY+_margin)/(float)(getTextureHeight()-sizeAdjustment)));
363    glyph->setMaxTexCoord(osg::Vec2((float)(posX+glyph->s()-_margin)/(float)(getTextureWidth()-sizeAdjustment),(float)(posY+glyph->t()-_margin)/(float)(getTextureHeight()-sizeAdjustment)));
364}
365
366void Font::GlyphTexture::apply(osg::State& state) const
367{
368    // get the contextID (user defined ID of 0 upwards) for the
369    // current OpenGL context.
370    const unsigned int contextID = state.getContextID();
371
372    if (contextID>=_glyphsToSubload.size())
373    {
374        // graphics context is beyond the number of glyphsToSubloads, so
375        // we must now copy the glyph list across, this is a potential
376        // threading issue though is multiple applies are happening the
377        // same time on this object - to avoid this condition number of
378        // graphics contexts should be set before create text.
379        for(unsigned int i=_glyphsToSubload.size();i<=contextID;++i)
380        {
381            GlyphPtrList& glyphPtrs = _glyphsToSubload[i];
382            for(GlyphRefList::const_iterator itr=_glyphs.begin();
383                itr!=_glyphs.end();
384                ++itr)
385            {
386                glyphPtrs.push_back(itr->get());
387            }
388        }
389    }
390
391
392    const Extensions* extensions = getExtensions(contextID,true);
393    bool generateMipMapSupported = extensions->isGenerateMipMapSupported();
394
395    // get the texture object for the current contextID.
396    TextureObject* textureObject = getTextureObject(contextID);
397   
398    bool newTextureObject = (textureObject == 0);
399
400    if (newTextureObject)
401    {
402       
403        // being bound for the first time, need to allocate the texture
404
405        _textureObjectBuffer[contextID] = textureObject = getTextureObjectManager()->reuseOrGenerateTextureObject(
406                contextID,GL_TEXTURE_2D,1,GL_ALPHA,getTextureWidth(), getTextureHeight(),1,0);
407
408        textureObject->bind();
409
410
411        applyTexParameters(GL_TEXTURE_2D,state);
412
413       
414        // need to look at generate mip map extension if mip mapping required.
415        switch(_min_filter)
416        {
417        case NEAREST_MIPMAP_NEAREST:
418        case NEAREST_MIPMAP_LINEAR:
419        case LINEAR_MIPMAP_NEAREST:
420        case LINEAR_MIPMAP_LINEAR:
421            if (generateMipMapSupported)
422            {
423                glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP_SGIS,GL_TRUE);
424            }
425            else glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, LINEAR);
426            break;
427        default:
428            // not mip mapping so no problems.
429            break;
430        }
431               
432        // allocate the texture memory.
433        glTexImage2D( GL_TEXTURE_2D, 0, GL_ALPHA,
434                getTextureWidth(), getTextureHeight(), 0,
435                GL_ALPHA,
436                GL_UNSIGNED_BYTE,
437                0 );
438   
439    }
440    else
441    {
442        // reuse texture by binding.
443        textureObject->bind();
444       
445        if (getTextureParameterDirty(contextID))
446        {
447            applyTexParameters(GL_TEXTURE_2D,state);
448        }
449
450
451    }
452   
453    static const GLubyte* s_renderer = 0;
454    static bool s_subloadAllGlyphsTogether = false;
455    if (!s_renderer)
456    {
457        s_renderer = glGetString(GL_RENDERER);
458        osg::notify(osg::INFO)<<"glGetString(GL_RENDERER)=="<<s_renderer<<std::endl;
459        if (strstr((const char*)s_renderer,"IMPACT")!=0)
460        {
461            // we're running on an Octane, so need to work around its
462            // subloading bugs by loading all at once.
463            s_subloadAllGlyphsTogether = true;
464        }
465    }
466
467
468    // now subload the glyphs that are outstanding for this graphics context.
469    GlyphPtrList& glyphsWereSubloading = _glyphsToSubload[contextID];
470
471    if (!glyphsWereSubloading.empty() || newTextureObject)
472    {
473   
474        if (!s_subloadAllGlyphsTogether)
475        {
476            if (newTextureObject)
477            {
478                for(GlyphRefList::const_iterator itr=_glyphs.begin();
479                    itr!=_glyphs.end();
480                    ++itr)
481                {
482                    (*itr)->subload();
483                }
484            }
485            else // just subload the new entries.
486            {           
487                // default way of subloading as required.
488                //std::cout<<"subloading"<<std::endl;
489                for(GlyphPtrList::iterator itr=glyphsWereSubloading.begin();
490                    itr!=glyphsWereSubloading.end();
491                    ++itr)
492                {
493                    (*itr)->subload();
494                }
495            }
496           
497            // clear the list since we have now subloaded them.
498            glyphsWereSubloading.clear();
499           
500        }
501        else
502        {
503            //std::cout<<"all loading"<<std::endl;
504
505            // Octane has bugs in OGL driver which mean that subloads smaller
506            // than 32x32 produce errors, and also cannot handle general alignment,
507            // so to get round this copy all glyphs into a temporary image and
508            // then subload the whole lot in one go.
509
510            int tsize = getTextureHeight() * getTextureWidth();
511            unsigned char *local_data = new unsigned char[tsize];
512            memset( local_data, 0L, tsize);
513
514            for(GlyphRefList::const_iterator itr=_glyphs.begin();
515                itr!=_glyphs.end();
516                ++itr)
517            {
518                //(*itr)->subload();
519
520                // Rather than subloading to graphics, we'll write the values
521                // of the glyphs into some intermediate data and subload the
522                // whole thing at the end
523                for( int t = 0; t < (*itr)->t(); t++ )
524                {
525                    for( int s = 0; s < (*itr)->s(); s++ )
526                    {
527                        int sindex = (t*(*itr)->s()+s);
528                        int dindex = 
529                            ((((*itr)->getTexturePositionY()+t) * getTextureWidth()) +
530                            ((*itr)->getTexturePositionX()+s));
531
532                        const unsigned char *sptr = &(*itr)->data()[sindex];
533                        unsigned char *dptr       = &local_data[dindex];
534
535                        (*dptr)   = (*sptr);
536                    }
537                }
538            }
539
540            // clear the list since we have now subloaded them.
541            glyphsWereSubloading.clear();
542
543            // Subload the image once
544            glTexSubImage2D( GL_TEXTURE_2D, 0, 0, 0,
545                    getTextureWidth(),
546                    getTextureHeight(),
547                    GL_ALPHA, GL_UNSIGNED_BYTE, local_data );
548
549            delete [] local_data;
550
551        }
552    }
553    else
554    {
555        //std::cout << "no need to subload "<<std::endl;
556    }
557
558
559
560//     if (generateMipMapTurnedOn)
561//     {
562//         glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP_SGIS,GL_FALSE);
563//     }
564
565
566}
567
568// all the methods in Font::Glyph have been made non inline because VisualStudio6.0 is STUPID, STUPID, STUPID PILE OF JUNK.
569Font::Glyph::Glyph() {}
570Font::Glyph::~Glyph() {}
571
572unsigned int Font::Glyph::getGlyphCode() const { return _glyphCode; }
573
574// void Font::Glyph::setFont(Font* font) { _font = font; }
575// Font* Font::Glyph::getFont() const { return _font; }
576
577void Font::Glyph::setHorizontalBearing(const osg::Vec2& bearing) {  _horizontalBearing=bearing; }
578const osg::Vec2& Font::Glyph::getHorizontalBearing() const { return _horizontalBearing; }
579
580void Font::Glyph::setHorizontalAdvance(float advance) { _horizontalAdvance=advance; }
581float Font::Glyph::getHorizontalAdvance() const { return _horizontalAdvance; }
582
583void Font::Glyph::setVerticalBearing(const osg::Vec2& bearing) {  _verticalBearing=bearing; }
584const osg::Vec2& Font::Glyph::getVerticalBearing() const { return _verticalBearing; }
585
586void Font::Glyph::setVerticalAdvance(float advance) {  _verticalAdvance=advance; }
587float Font::Glyph::getVerticalAdvance() const { return _verticalAdvance; }
588
589void Font::Glyph::setTexture(GlyphTexture* texture) { _texture = texture; }
590Font::GlyphTexture* Font::Glyph::getTexture() { return _texture; }
591const Font::GlyphTexture* Font::Glyph::getTexture() const { return _texture; }
592
593osg::StateSet* Font::Glyph::getStateSet() { return _texture?_texture->getStateSet():0; }
594const osg::StateSet* Font::Glyph::getStateSet() const { return _texture?_texture->getStateSet():0; }
595
596void Font::Glyph::setTexturePosition(int posX,int posY) { _texturePosX = posX; _texturePosY = posY; }
597int Font::Glyph::getTexturePositionX() const { return _texturePosX; }
598int Font::Glyph::getTexturePositionY() const { return _texturePosY; }
599
600void Font::Glyph::setMinTexCoord(const osg::Vec2& coord) { _minTexCoord=coord; }
601const osg::Vec2& Font::Glyph::getMinTexCoord() const { return _minTexCoord; }
602
603void Font::Glyph::setMaxTexCoord(const osg::Vec2& coord) { _maxTexCoord=coord; }
604const osg::Vec2& Font::Glyph::getMaxTexCoord() const { return _maxTexCoord; }
605
606void Font::Glyph::subload() const
607{
608    GLenum errorNo = glGetError();
609    if (errorNo!=GL_NO_ERROR)
610    {
611        osg::notify(osg::WARN)<<"before Font::Glyph::subload(): detected OpenGL error '"<<gluErrorString(errorNo)<<std::endl;
612    }
613
614    glPixelStorei(GL_UNPACK_ALIGNMENT,getPacking());
615
616    glTexSubImage2D(GL_TEXTURE_2D,0,
617                    _texturePosX,_texturePosY,
618                    s(),t(),
619                    (GLenum)getPixelFormat(),
620                    (GLenum)getDataType(),
621                    data());
622                   
623    errorNo = glGetError();
624    if (errorNo!=GL_NO_ERROR)
625    {
626
627        osg::notify(osg::WARN)<<"after Font::Glyph::subload() : detected OpenGL error '"<<gluErrorString(errorNo)<<"'"<<std::endl;
628        osg::notify(osg::WARN)<< "\tglTexSubImage2D(0x"<<hex<<GL_TEXTURE_2D<<dec<<" ,"<<0<<"\t"<<std::endl<<
629                                 "\t                "<<_texturePosX<<" ,"<<_texturePosY<<std::endl<<
630                                 "\t                "<<s()<<" ,"<<t()<<std::endl<<hex<<
631                                 "\t                0x"<<(GLenum)getPixelFormat()<<std::endl<<
632                                 "\t                0x"<<(GLenum)getDataType()<<std::endl<<
633                                 "\t                0x"<<(unsigned long)data()<<");"<<dec<<std::endl;
634    }                   
635}
636
637void Font::Glyph::draw(osg::State& state) const
638{
639    GLuint& globj = _globjList[state.getContextID()];
640
641    // call the globj if already set otherwise compile and execute.
642    if( globj != 0 )
643    {
644        glCallList( globj );
645    }
646    else 
647    {
648        globj = glGenLists( 1 );
649        glNewList( globj, GL_COMPILE_AND_EXECUTE );
650
651        glPixelStorei(GL_UNPACK_ALIGNMENT,getPacking());
652        glDrawPixels(s(), t(),
653                     (GLenum)getPixelFormat(),
654                     (GLenum)getDataType(),
655                     data() );
656
657        glEndList();
658    }
659}
Note: See TracBrowser for help on using the browser.