root/OpenSceneGraph/trunk/src/osgText/Glyph.cpp @ 12407

Revision 12407, 20.6 kB (checked in by robert, 4 years ago)

Fixed handling of Font implementations that don't handle multiple font resolutions.

  • Property svn:eol-style set to native
Line 
1/* -*-c++-*- OpenSceneGraph - Copyright (C) 1998-2006 Robert Osfield
2 *
3 * This library is open source and may be redistributed and/or modified under 
4 * the terms of the OpenSceneGraph Public License (OSGPL) version 0.0 or
5 * (at your option) any later version.  The full license is in LICENSE file
6 * included with this distribution, and on the openscenegraph.org website.
7 *
8 * This library is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11 * OpenSceneGraph Public License for more details.
12*/
13
14#include <osgText/Font>
15#include <osgText/Text>
16
17#include <osg/State>
18#include <osg/Notify>
19#include <osg/GLU>
20
21#include <osgUtil/SmoothingVisitor>
22
23#include <string.h>
24#include <stdlib.h>
25
26#include "GlyphGeometry.h"
27
28using namespace osgText;
29using namespace std;
30
31GlyphTexture::GlyphTexture():
32    _margin(1),
33    _marginRatio(0.02f),
34    _usedY(0),
35    _partUsedX(0),
36    _partUsedY(0)
37{
38    setWrap(WRAP_S, CLAMP_TO_EDGE);
39    setWrap(WRAP_T, CLAMP_TO_EDGE);
40}
41
42GlyphTexture::~GlyphTexture()
43{
44}
45
46// return -1 if *this < *rhs, 0 if *this==*rhs, 1 if *this>*rhs.
47int GlyphTexture::compare(const osg::StateAttribute& rhs) const
48{
49    if (this<&rhs) return -1;
50    else if (this>&rhs) return 1;
51    return 0;
52}
53
54
55bool GlyphTexture::getSpaceForGlyph(Glyph* glyph, int& posX, int& posY)
56{
57    int maxAxis = std::max(glyph->s(), glyph->t());
58    int margin = _margin + (int)((float)maxAxis * _marginRatio);
59   
60    int width = glyph->s()+2*margin;
61    int height = glyph->t()+2*margin;
62
63    // first check box (_partUsedX,_usedY) to (width,height)
64    if (width <= (getTextureWidth()-_partUsedX) &&
65        height <= (getTextureHeight()-_usedY))
66    {
67        // can fit in existing row.
68
69        // record the position in which the texture will be stored.
70        posX = _partUsedX+margin;
71        posY = _usedY+margin;       
72
73        // move used markers on.
74        _partUsedX += width;
75        if (_usedY+height>_partUsedY) _partUsedY = _usedY+height;
76
77        return true;
78    }
79   
80    // start an new row.
81    if (width <= getTextureWidth() &&
82        height <= (getTextureHeight()-_partUsedY))
83    {
84        // can fit next row.
85        _partUsedX = 0;
86        _usedY = _partUsedY;
87
88        posX = _partUsedX+margin;
89        posY = _usedY+margin;       
90
91        // move used markers on.
92        _partUsedX += width;
93        if (_usedY+height>_partUsedY) _partUsedY = _usedY+height;
94
95        return true;
96    }
97
98    // doesn't fit into glyph.
99    return false;
100}
101
102void GlyphTexture::addGlyph(Glyph* glyph, int posX, int posY)
103{
104    OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_mutex);
105
106    _glyphs.push_back(glyph);
107    for(unsigned int i=0;i<_glyphsToSubload.size();++i)
108    {
109        _glyphsToSubload[i].push_back(glyph);
110    }
111
112    // set up the details of where to place glyph's image in the texture.
113    glyph->setTexture(this);
114    glyph->setTexturePosition(posX,posY);
115
116    glyph->setMinTexCoord( osg::Vec2( static_cast<float>(posX)/static_cast<float>(getTextureWidth()),
117                                      static_cast<float>(posY)/static_cast<float>(getTextureHeight()) ) );
118    glyph->setMaxTexCoord( osg::Vec2( static_cast<float>(posX+glyph->s())/static_cast<float>(getTextureWidth()),
119                                      static_cast<float>(posY+glyph->t())/static_cast<float>(getTextureHeight()) ) );
120}
121
122void GlyphTexture::apply(osg::State& state) const
123{
124    // get the contextID (user defined ID of 0 upwards) for the
125    // current OpenGL context.
126    const unsigned int contextID = state.getContextID();
127
128    if (contextID>=_glyphsToSubload.size())
129    {
130        OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_mutex);
131
132        // graphics context is beyond the number of glyphsToSubloads, so
133        // we must now copy the glyph list across, this is a potential
134        // threading issue though is multiple applies are happening the
135        // same time on this object - to avoid this condition number of
136        // graphics contexts should be set before create text.
137        for(unsigned int i=_glyphsToSubload.size();i<=contextID;++i)
138        {
139            GlyphPtrList& glyphPtrs = _glyphsToSubload[i];
140            for(GlyphRefList::const_iterator itr=_glyphs.begin();
141                itr!=_glyphs.end();
142                ++itr)
143            {
144                glyphPtrs.push_back(itr->get());
145            }
146        }
147    }
148
149
150    const Extensions* extensions = getExtensions(contextID,true);
151    bool generateMipMapSupported = extensions->isGenerateMipMapSupported();
152
153    // get the texture object for the current contextID.
154    TextureObject* textureObject = getTextureObject(contextID);
155   
156    bool newTextureObject = (textureObject == 0);
157
158    if (newTextureObject)
159    {
160        GLint maxTextureSize = 256;
161        glGetIntegerv(GL_MAX_TEXTURE_SIZE, &maxTextureSize);
162        if (maxTextureSize < getTextureWidth() || maxTextureSize < getTextureHeight())
163        {
164            OSG_WARN<<"Warning: osgText::Font texture size of ("<<getTextureWidth()<<", "<<getTextureHeight()<<") too large, unable to create font texture."<<std::endl;
165            OSG_WARN<<"         Maximum supported by hardward by native OpenGL implementation is ("<<maxTextureSize<<","<<maxTextureSize<<")."<<std::endl;
166            OSG_WARN<<"         Please set OSG_MAX_TEXTURE_SIZE lenvironment variable to "<<maxTextureSize<<" and re-run application."<<std::endl;
167            return;
168        }
169       
170        // being bound for the first time, need to allocate the texture
171
172        _textureObjectBuffer[contextID] = textureObject = osg::Texture::generateTextureObject(
173                this, contextID,GL_TEXTURE_2D,1,GL_ALPHA,getTextureWidth(), getTextureHeight(),1,0);
174
175        textureObject->bind();
176
177
178        applyTexParameters(GL_TEXTURE_2D,state);
179
180       
181        // need to look at generate mip map extension if mip mapping required.
182        switch(_min_filter)
183        {
184        case NEAREST_MIPMAP_NEAREST:
185        case NEAREST_MIPMAP_LINEAR:
186        case LINEAR_MIPMAP_NEAREST:
187        case LINEAR_MIPMAP_LINEAR:
188            if (generateMipMapSupported)
189            {
190                glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP_SGIS,GL_TRUE);
191            }
192            else glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, LINEAR);
193            break;
194        default:
195            // not mip mapping so no problems.
196            break;
197        }
198       
199        unsigned int imageDataSize = getTextureHeight()*getTextureWidth();
200        unsigned char* imageData = new unsigned char[imageDataSize];
201        for(unsigned int i=0; i<imageDataSize; ++i)
202        {
203            imageData[i] = 0;
204        }
205       
206
207        // allocate the texture memory.
208        glTexImage2D( GL_TEXTURE_2D, 0, GL_ALPHA,
209                getTextureWidth(), getTextureHeight(), 0,
210                GL_ALPHA,
211                GL_UNSIGNED_BYTE,
212                imageData );
213               
214        delete [] imageData;
215   
216    }
217    else
218    {
219        // reuse texture by binding.
220        textureObject->bind();
221       
222        if (getTextureParameterDirty(contextID))
223        {
224            applyTexParameters(GL_TEXTURE_2D,state);
225        }
226
227
228    }
229   
230    static const GLubyte* s_renderer = 0;
231    static bool s_subloadAllGlyphsTogether = false;
232    if (!s_renderer)
233    {
234        OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_mutex);
235
236        s_renderer = glGetString(GL_RENDERER);
237        OSG_INFO<<"glGetString(GL_RENDERER)=="<<s_renderer<<std::endl;
238        if (s_renderer && strstr((const char*)s_renderer,"IMPACT")!=0)
239        {
240            // we're running on an Octane, so need to work around its
241            // subloading bugs by loading all at once.
242            s_subloadAllGlyphsTogether = true;
243        }
244       
245        if (s_renderer &&
246            ((strstr((const char*)s_renderer,"Radeon")!=0) ||
247            (strstr((const char*)s_renderer,"RADEON")!=0) ||
248            (strstr((const char*)s_renderer,"ALL-IN-WONDER")!=0)))
249        {
250            // we're running on an ATI, so need to work around its
251            // subloading bugs by loading all at once.
252            s_subloadAllGlyphsTogether = true;
253        }
254
255        if (s_renderer && strstr((const char*)s_renderer,"Sun")!=0)
256        {
257            // we're running on an solaris x server, so need to work around its
258            // subloading bugs by loading all at once.
259            s_subloadAllGlyphsTogether = true;
260        }
261
262        const char* str = getenv("OSG_TEXT_INCREMENTAL_SUBLOADING");
263        if (str)
264        {
265            s_subloadAllGlyphsTogether = strcmp(str,"OFF")==0 || strcmp(str,"Off")==0 || strcmp(str,"off")==0;
266        }
267    }
268
269
270    // now subload the glyphs that are outstanding for this graphics context.
271    GlyphPtrList& glyphsWereSubloading = _glyphsToSubload[contextID];
272
273    if (!glyphsWereSubloading.empty() || newTextureObject)
274    {
275        OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_mutex);
276
277        if (!s_subloadAllGlyphsTogether)
278        {
279            if (newTextureObject)
280            {
281                for(GlyphRefList::const_iterator itr=_glyphs.begin();
282                    itr!=_glyphs.end();
283                    ++itr)
284                {
285                    (*itr)->subload();
286                }
287            }
288            else // just subload the new entries.
289            {           
290                // default way of subloading as required.
291                //std::cout<<"subloading"<<std::endl;
292                for(GlyphPtrList::iterator itr=glyphsWereSubloading.begin();
293                    itr!=glyphsWereSubloading.end();
294                    ++itr)
295                {
296                    (*itr)->subload();
297                }
298            }
299           
300            // clear the list since we have now subloaded them.
301            glyphsWereSubloading.clear();
302           
303        }
304        else
305        {
306            OSG_INFO<<"osgText::Font loading all glyphs as a single subload."<<std::endl;
307
308            // Octane has bugs in OGL driver which mean that subloads smaller
309            // than 32x32 produce errors, and also cannot handle general alignment,
310            // so to get round this copy all glyphs into a temporary image and
311            // then subload the whole lot in one go.
312
313            int tsize = getTextureHeight() * getTextureWidth();
314            unsigned char *local_data = new unsigned char[tsize];
315            memset( local_data, 0L, tsize);
316
317            for(GlyphRefList::const_iterator itr=_glyphs.begin();
318                itr!=_glyphs.end();
319                ++itr)
320            {
321                //(*itr)->subload();
322
323                // Rather than subloading to graphics, we'll write the values
324                // of the glyphs into some intermediate data and subload the
325                // whole thing at the end
326                for( int t = 0; t < (*itr)->t(); t++ )
327                {
328                    for( int s = 0; s < (*itr)->s(); s++ )
329                    {
330                        int sindex = (t*(*itr)->s()+s);
331                        int dindex = 
332                            ((((*itr)->getTexturePositionY()+t) * getTextureWidth()) +
333                            ((*itr)->getTexturePositionX()+s));
334
335                        const unsigned char *sptr = &(*itr)->data()[sindex];
336                        unsigned char *dptr       = &local_data[dindex];
337
338                        (*dptr)   = (*sptr);
339                    }
340                }
341            }
342
343            // clear the list since we have now subloaded them.
344            glyphsWereSubloading.clear();
345
346            // Subload the image once
347            glTexSubImage2D( GL_TEXTURE_2D, 0, 0, 0,
348                    getTextureWidth(),
349                    getTextureHeight(),
350                    GL_ALPHA, GL_UNSIGNED_BYTE, local_data );
351
352            delete [] local_data;
353
354        }
355    }
356    else
357    {
358//        OSG_INFO << "no need to subload "<<std::endl;
359    }
360
361
362
363//     if (generateMipMapTurnedOn)
364//     {
365//         glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP_SGIS,GL_FALSE);
366//     }
367
368
369}
370
371void GlyphTexture::setThreadSafeRefUnref(bool threadSafe)
372{
373    osg::Texture2D::setThreadSafeRefUnref(threadSafe);
374}
375
376void GlyphTexture::resizeGLObjectBuffers(unsigned int maxSize)
377{
378    osg::Texture2D::resizeGLObjectBuffers(maxSize);
379
380    unsigned int initialSize = _glyphsToSubload.size();
381    _glyphsToSubload.resize(maxSize);
382   
383    for(unsigned i=initialSize; i<_glyphsToSubload.size(); ++i)
384    {
385        for(GlyphRefList::iterator itr = _glyphs.begin();
386            itr != _glyphs.end();
387            ++itr)
388        {
389            _glyphsToSubload[i].push_back(itr->get());
390        }
391    }
392}
393
394osg::Image* GlyphTexture::createImage()
395{
396    osg::ref_ptr<osg::Image> image = new osg::Image;
397    image->allocateImage(getTextureWidth(), getTextureHeight(), 1, GL_ALPHA, GL_UNSIGNED_BYTE);
398    memset(image->data(), 0, image->getTotalSizeInBytes());
399
400    for(GlyphRefList::iterator itr = _glyphs.begin();
401        itr != _glyphs.end();
402        ++itr)
403    {
404        Glyph* glyph = itr->get();
405        image->copySubImage(glyph->getTexturePositionX(), glyph->getTexturePositionY(), 0, glyph);
406    }
407
408    return image.release();
409}
410
411// all the methods in Font::Glyph have been made non inline because VisualStudio6.0 is STUPID, STUPID, STUPID PILE OF JUNK.
412Glyph::Glyph(Font* font, unsigned int glyphCode):
413    _font(font),
414    _glyphCode(glyphCode),
415    _width(1.0f),
416    _height(1.0f),
417    _horizontalBearing(0.0f,0.f),
418    _horizontalAdvance(0.f),
419    _verticalBearing(0.0f,0.f),
420    _verticalAdvance(0.f),
421    _texture(0),
422    _texturePosX(0),
423    _texturePosY(0),
424    _minTexCoord(0.0f,0.0f),
425    _maxTexCoord(0.0f,0.0f)
426{
427    setThreadSafeRefUnref(true);
428}
429
430Glyph::~Glyph()
431{
432}
433
434void Glyph::setHorizontalBearing(const osg::Vec2& bearing) {  _horizontalBearing=bearing; }
435const osg::Vec2& Glyph::getHorizontalBearing() const { return _horizontalBearing; }
436
437void Glyph::setHorizontalAdvance(float advance) { _horizontalAdvance=advance; }
438float Glyph::getHorizontalAdvance() const { return _horizontalAdvance; }
439
440void Glyph::setVerticalBearing(const osg::Vec2& bearing) {  _verticalBearing=bearing; }
441const osg::Vec2& Glyph::getVerticalBearing() const { return _verticalBearing; }
442
443void Glyph::setVerticalAdvance(float advance) {  _verticalAdvance=advance; }
444float Glyph::getVerticalAdvance() const { return _verticalAdvance; }
445
446void Glyph::setTexture(GlyphTexture* texture) { _texture = texture; }
447GlyphTexture* Glyph::getTexture() { return _texture; }
448const GlyphTexture* Glyph::getTexture() const { return _texture; }
449
450void Glyph::setTexturePosition(int posX,int posY) { _texturePosX = posX; _texturePosY = posY; }
451int Glyph::getTexturePositionX() const { return _texturePosX; }
452int Glyph::getTexturePositionY() const { return _texturePosY; }
453
454void Glyph::setMinTexCoord(const osg::Vec2& coord) { _minTexCoord=coord; }
455const osg::Vec2& Glyph::getMinTexCoord() const { return _minTexCoord; }
456
457void Glyph::setMaxTexCoord(const osg::Vec2& coord) { _maxTexCoord=coord; }
458const osg::Vec2& Glyph::getMaxTexCoord() const { return _maxTexCoord; }
459
460void Glyph::subload() const
461{
462    GLenum errorNo = glGetError();
463    if (errorNo!=GL_NO_ERROR)
464    {
465        const GLubyte* msg = osg::gluErrorString(errorNo);
466        if (msg) { OSG_WARN<<"before Glyph::subload(): detected OpenGL error: "<<msg<<std::endl; }
467        else  { OSG_WARN<<"before Glyph::subload(): detected OpenGL error number: "<<errorNo<<std::endl; }
468    }
469
470    if(s() <= 0 || t() <= 0)
471    {
472        OSG_INFO<<"Glyph::subload(): texture sub-image width and/or height of 0, ignoring operation."<<std::endl;
473        return;
474    }
475
476    glPixelStorei(GL_UNPACK_ALIGNMENT,getPacking());
477
478    glTexSubImage2D(GL_TEXTURE_2D,0,
479                    _texturePosX,_texturePosY,
480                    s(),t(),
481                    (GLenum)getPixelFormat(),
482                    (GLenum)getDataType(),
483                    data());
484                   
485    errorNo = glGetError();
486    if (errorNo!=GL_NO_ERROR)
487    {
488
489
490        const GLubyte* msg = osg::gluErrorString(errorNo);
491        if (msg) { OSG_WARN<<"after Glyph::subload() : detected OpenGL error: "<<msg<<std::endl; }
492        else { OSG_WARN<<"after Glyph::subload() : detected OpenGL error number: "<<errorNo<<std::endl; }
493
494        OSG_WARN<< "\tglTexSubImage2D(0x"<<hex<<GL_TEXTURE_2D<<dec<<" ,"<<0<<"\t"<<std::endl<<
495                                 "\t                "<<_texturePosX<<" ,"<<_texturePosY<<std::endl<<
496                                 "\t                "<<s()<<" ,"<<t()<<std::endl<<hex<<
497                                 "\t                0x"<<(GLenum)getPixelFormat()<<std::endl<<
498                                 "\t                0x"<<(GLenum)getDataType()<<std::endl<<
499                                 "\t                0x"<<(unsigned long)data()<<");"<<dec<<std::endl;
500    }                   
501}
502
503Glyph3D::Glyph3D(Font* font, unsigned int glyphCode):
504    osg::Referenced(true),
505    _font(font),
506    _glyphCode(glyphCode),
507    _width(1.0f),
508    _height(1.0f),
509    _horizontalBearing(0,0),
510    _horizontalAdvance(0),
511    _verticalBearing(0,0),
512    _verticalAdvance(0)
513    {}
514
515void Glyph3D::setThreadSafeRefUnref(bool threadSafe)
516{
517    GlyphGeometries _glyphGeometries;
518    for(GlyphGeometries::iterator itr = _glyphGeometries.begin();
519        itr != _glyphGeometries.end();
520        ++itr)
521    {
522        (*itr)->setThreadSafeRefUnref(threadSafe);
523    }
524}
525
526GlyphGeometry* Glyph3D::getGlyphGeometry(const Style* style)
527{
528
529    for(GlyphGeometries::iterator itr = _glyphGeometries.begin();
530        itr != _glyphGeometries.end();
531        ++itr)
532    {
533        GlyphGeometry* glyphGeometry = itr->get();
534        if (glyphGeometry->match(style))
535        {
536            OSG_INFO<<"Glyph3D::getGlyphGeometry(Style* style) found matching GlyphGeometry."<<std::endl;
537            return glyphGeometry;
538        }
539    }
540
541    OSG_INFO<<"Glyph3D::getGlyphGeometry(Style* style) could not find matching GlyphGeometry, creating a new one."<<std::endl;
542
543    osg::ref_ptr<GlyphGeometry> glyphGeometry = new GlyphGeometry();
544    glyphGeometry->setup(this, style);
545    _glyphGeometries.push_back(glyphGeometry);
546
547    return glyphGeometry.get();
548}
549
550
551GlyphGeometry::GlyphGeometry()
552{
553}
554
555void GlyphGeometry::setThreadSafeRefUnref(bool threadSafe)
556{
557    if (_geode.valid()) _geode->setThreadSafeRefUnref(threadSafe);
558}
559
560void GlyphGeometry::setup(const Glyph3D* glyph, const Style* style)
561{
562    float creaseAngle = 30.0f;
563    bool smooth = true;
564    osg::ref_ptr<osg::Geometry> shellGeometry;
565
566    if (!style)
567    {
568        OSG_INFO<<"GlyphGeometry::setup(const Glyph* glyph, NULL) creating default glyph geometry."<<std::endl;
569
570        float width = 0.1f;
571
572        _geometry = osgText::computeTextGeometry(glyph, width);
573    }
574    else
575    {
576        OSG_INFO<<"GlyphGeometry::setup(const Glyph* glyph, NULL) create glyph geometry with custom Style."<<std::endl;
577
578        // record the style
579        _style = dynamic_cast<Style*>(style->clone(osg::CopyOp::DEEP_COPY_ALL));
580
581        const Bevel* bevel = style ? style->getBevel() : 0;
582        bool outline = style ? style->getOutlineRatio()>0.0f : false;
583        float width = style->getThicknessRatio();
584
585        if (bevel)
586        {
587            float thickness = bevel->getBevelThickness();
588
589            osg::ref_ptr<osg::Geometry> glyphGeometry = osgText::computeGlyphGeometry(glyph, thickness, width);
590
591            _geometry = osgText::computeTextGeometry(glyphGeometry.get(), *bevel, width);
592            shellGeometry = outline ? osgText::computeShellGeometry(glyphGeometry.get(), *bevel, width) : 0;
593        }
594        else
595        {
596            _geometry = osgText::computeTextGeometry(glyph, width);
597        }
598    }
599
600    if (!_geometry)
601    {
602        OSG_INFO<<"Warning: GlyphGeometry::setup(const Glyph* glyph, const Style* style) failed."<<std::endl;
603        return;
604    }
605
606    _geode = new osg::Geode;
607    _geode->addDrawable(_geometry.get());
608    if (shellGeometry.valid()) _geode->addDrawable(shellGeometry.get());
609
610    // create the normals
611    if (smooth)
612    {
613        osgUtil::SmoothingVisitor::smooth(*_geometry, osg::DegreesToRadians(creaseAngle));
614    }
615
616    _vertices = dynamic_cast<osg::Vec3Array*>(_geometry->getVertexArray());
617    _normals = dynamic_cast<osg::Vec3Array*>(_geometry->getNormalArray());
618
619    for(osg::Geometry::PrimitiveSetList::iterator itr = _geometry->getPrimitiveSetList().begin();
620        itr != _geometry->getPrimitiveSetList().end();
621        ++itr)
622    {
623        osg::PrimitiveSet* prim = itr->get();
624        if (prim->getName()=="front") _frontPrimitiveSetList.push_back(prim);
625        else if (prim->getName()=="back") _backPrimitiveSetList.push_back(prim);
626        else if (prim->getName()=="wall") _wallPrimitiveSetList.push_back(prim);
627    }
628}
629
630bool GlyphGeometry::match(const Style* style) const
631{
632    if (_style == style) return true;
633    if (!_style || !style) return false;
634
635    return (*_style==*style);
636}
Note: See TracBrowser for help on using the browser.