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

Revision 13041, 14.7 kB (checked in by robert, 2 years ago)

Ran script to remove trailing spaces and tabs

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
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/ApplicationUsage>
20
21#include <osgDB/ReadFile>
22#include <osgDB/FileUtils>
23#include <osgDB/FileNameUtils>
24#include <osg/GLU>
25
26#include <string.h>
27
28#include <OpenThreads/ReentrantMutex>
29
30#include "DefaultFont.h"
31
32using namespace osgText;
33using namespace std;
34
35static osg::ApplicationUsageProxy Font_e0(osg::ApplicationUsage::ENVIRONMENTAL_VARIABLE,"OSG_TEXT_INCREMENTAL_SUBLOADING <type>","ON | OFF");
36
37
38osg::ref_ptr<Font>& Font::getDefaultFont()
39{
40    static OpenThreads::Mutex s_DefaultFontMutex;
41    OpenThreads::ScopedLock<OpenThreads::Mutex> lock(s_DefaultFontMutex);
42
43    static osg::ref_ptr<Font> s_defaultFont = new DefaultFont;
44    return s_defaultFont;
45}
46
47static OpenThreads::ReentrantMutex& getFontFileMutex()
48{
49    static OpenThreads::ReentrantMutex s_FontFileMutex;
50    return s_FontFileMutex;
51}
52
53std::string osgText::findFontFile(const std::string& str)
54{
55    // try looking in OSGFILEPATH etc first for fonts.
56    std::string filename = osgDB::findDataFile(str);
57    if (!filename.empty()) return filename;
58
59    OpenThreads::ScopedLock<OpenThreads::ReentrantMutex> lock(getFontFileMutex());
60
61    static osgDB::FilePathList s_FontFilePath;
62    static bool initialized = false;
63    if (!initialized)
64    {
65        initialized = true;
66    #if defined(WIN32)
67        osgDB::convertStringPathIntoFilePathList(
68            ".;C:/winnt/fonts;C:/windows/fonts",
69            s_FontFilePath);
70
71        char *ptr;
72        if ((ptr = getenv( "windir" )))
73        {
74            std::string winFontPath = ptr;
75            winFontPath += "\\fonts";
76            s_FontFilePath.push_back(winFontPath);
77        }
78    #elif defined(__APPLE__)
79      osgDB::convertStringPathIntoFilePathList(
80        ".:/usr/share/fonts/ttf:/usr/share/fonts/ttf/western:/usr/share/fonts/ttf/decoratives:/Library/Fonts:/System/Library/Fonts",
81        s_FontFilePath);
82    #else
83      osgDB::convertStringPathIntoFilePathList(
84        ".:/usr/share/fonts/ttf:/usr/share/fonts/ttf/western:/usr/share/fonts/ttf/decoratives",
85        s_FontFilePath);
86    #endif
87    }
88
89    filename = osgDB::findFileInPath(str,s_FontFilePath);
90    if (!filename.empty()) return filename;
91
92    // Try filename without pathname, if it has a path
93    filename = osgDB::getSimpleFileName(str);
94    if(filename!=str)
95    {
96        filename = osgDB::findFileInPath(filename,s_FontFilePath);
97        if (!filename.empty()) return filename;
98    }
99    else
100    {
101        filename = osgText::findFontFile(std::string("fonts/")+filename);
102        if (!filename.empty()) return filename;
103    }
104
105    // Not found, return empty string
106    OSG_INFO<<"Warning: font file \""<<str<<"\" not found."<<std::endl;
107    return std::string();
108}
109
110osgText::Font* osgText::readFontFile(const std::string& filename, const osgDB::ReaderWriter::Options* userOptions)
111{
112    if (filename.empty()) return 0;
113
114    std::string foundFile = findFontFile(filename);
115    if (foundFile.empty())
116        foundFile = filename;
117
118    OpenThreads::ScopedLock<OpenThreads::Mutex> lock(getFontFileMutex());
119
120    osg::ref_ptr<osgDB::ReaderWriter::Options> localOptions;
121    if (!userOptions)
122    {
123        localOptions = new osgDB::ReaderWriter::Options;
124        localOptions->setObjectCacheHint(osgDB::ReaderWriter::Options::CACHE_OBJECTS);
125    }
126
127    osg::Object* object = osgDB::readObjectFile(foundFile, userOptions ? userOptions : localOptions.get());
128
129    // if the object is a font then return it.
130    osgText::Font* font = dynamic_cast<osgText::Font*>(object);
131    if (font) return font;
132
133    // otherwise if the object has zero references then delete it by doing another unref().
134    if (object && object->referenceCount()==0) object->unref();
135    return 0;
136}
137
138osgText::Font* osgText::readFontStream(std::istream& stream, const osgDB::ReaderWriter::Options* userOptions)
139{
140    OpenThreads::ScopedLock<OpenThreads::Mutex> lock(getFontFileMutex());
141
142    osg::ref_ptr<osgDB::ReaderWriter::Options> localOptions;
143    if (!userOptions)
144    {
145        localOptions = new osgDB::ReaderWriter::Options;
146        localOptions->setObjectCacheHint(osgDB::ReaderWriter::Options::CACHE_OBJECTS);
147    }
148
149    // there should be a better way to get the FreeType ReaderWriter by name...
150    osgDB::ReaderWriter *reader = osgDB::Registry::instance()->getReaderWriterForExtension("ttf");
151    if (reader == 0) return 0;
152    osgDB::ReaderWriter::ReadResult rr = reader->readObject(stream, userOptions ? userOptions : localOptions.get());
153    if (rr.error())
154    {
155        OSG_WARN << rr.message() << std::endl;
156        return 0;
157    }
158    if (!rr.validObject()) return 0;
159
160    osg::Object *object = rr.takeObject();
161
162    // if the object is a font then return it.
163    osgText::Font* font = dynamic_cast<osgText::Font*>(object);
164    if (font) return font;
165
166    // otherwise if the object has zero references then delete it by doing another unref().
167    if (object && object->referenceCount()==0) object->unref();
168    return 0;
169}
170
171osg::ref_ptr<Font> osgText::readRefFontFile(const std::string& filename, const osgDB::ReaderWriter::Options* userOptions)
172{
173    if (filename.empty()) return 0;
174
175    std::string foundFile = findFontFile(filename);
176    if (foundFile.empty())
177        foundFile = filename;
178
179    OpenThreads::ScopedLock<OpenThreads::Mutex> lock(getFontFileMutex());
180
181    osg::ref_ptr<osgDB::ReaderWriter::Options> localOptions;
182    if (!userOptions)
183    {
184        localOptions = new osgDB::ReaderWriter::Options;
185        localOptions->setObjectCacheHint(osgDB::ReaderWriter::Options::CACHE_OBJECTS);
186    }
187
188    osg::ref_ptr<osg::Object> object = osgDB::readRefObjectFile(foundFile, userOptions ? userOptions : localOptions.get());
189
190    // if the object is a font then return it.
191    osgText::Font* font = dynamic_cast<osgText::Font*>(object.get());
192    if (font) return osg::ref_ptr<Font>(font);
193
194    return 0;
195}
196
197osg::ref_ptr<Font> osgText::readRefFontStream(std::istream& stream, const osgDB::ReaderWriter::Options* userOptions)
198{
199    OpenThreads::ScopedLock<OpenThreads::Mutex> lock(getFontFileMutex());
200
201    osg::ref_ptr<osgDB::ReaderWriter::Options> localOptions;
202    if (!userOptions)
203    {
204        localOptions = new osgDB::ReaderWriter::Options;
205        localOptions->setObjectCacheHint(osgDB::ReaderWriter::Options::CACHE_OBJECTS);
206    }
207
208    // there should be a better way to get the FreeType ReaderWriter by name...
209    osgDB::ReaderWriter *reader = osgDB::Registry::instance()->getReaderWriterForExtension("ttf");
210    if (reader == 0) return 0;
211    osgDB::ReaderWriter::ReadResult rr = reader->readObject(stream, userOptions ? userOptions : localOptions.get());
212    if (rr.error())
213    {
214        OSG_WARN << rr.message() << std::endl;
215        return 0;
216    }
217    if (!rr.validObject()) return 0;
218
219    // if the object is a font then return it.
220    osgText::Font* font = dynamic_cast<osgText::Font*>(rr.getObject());
221    if (font) return osg::ref_ptr<Font>(font);
222
223    return 0;
224}
225
226Font::Font(FontImplementation* implementation):
227    osg::Object(true),
228    _margin(1),
229    _marginRatio(0.02),
230    _textureWidthHint(1024),
231    _textureHeightHint(1024),
232    _minFilterHint(osg::Texture::LINEAR_MIPMAP_LINEAR),
233    _magFilterHint(osg::Texture::LINEAR),
234    _depth(1),
235    _numCurveSamples(10)
236{
237    setImplementation(implementation);
238
239    _texenv = new osg::TexEnv;
240    _stateset = new osg::StateSet;
241    _stateset->setRenderingHint(osg::StateSet::TRANSPARENT_BIN);
242
243    char *ptr;
244    if( (ptr = getenv("OSG_MAX_TEXTURE_SIZE")) != 0)
245    {
246        unsigned int osg_max_size = atoi(ptr);
247
248        if (osg_max_size<_textureWidthHint) _textureWidthHint = osg_max_size;
249        if (osg_max_size<_textureHeightHint) _textureHeightHint = osg_max_size;
250    }
251
252}
253
254Font::~Font()
255{
256    if (_implementation.valid()) _implementation->_facade = 0;
257}
258
259void Font::setImplementation(FontImplementation* implementation)
260{
261    if (_implementation.valid()) _implementation->_facade = 0;
262    _implementation = implementation;
263    if (_implementation.valid()) _implementation->_facade = this;
264}
265
266Font::FontImplementation* Font::getImplementation()
267{
268    return _implementation.get();
269}
270
271const Font::FontImplementation* Font::getImplementation() const
272{
273    return _implementation.get();
274}
275
276std::string Font::getFileName() const
277{
278    if (_implementation.valid()) return _implementation->getFileName();
279    return std::string();
280}
281
282void Font::setGlyphImageMargin(unsigned int margin)
283{
284    _margin = margin;
285}
286
287unsigned int Font::getGlyphImageMargin() const
288{
289    return _margin;
290}
291
292void Font::setGlyphImageMarginRatio(float ratio)
293{
294    _marginRatio = ratio;
295}
296
297float Font::getGlyphImageMarginRatio() const
298{
299    return _marginRatio;
300}
301
302void Font::setTextureSizeHint(unsigned int width,unsigned int height)
303{
304    _textureWidthHint = width;
305    _textureHeightHint = height;
306
307    char *ptr;
308    if( (ptr = getenv("OSG_MAX_TEXTURE_SIZE")) != 0)
309    {
310        unsigned int osg_max_size = atoi(ptr);
311
312        if (osg_max_size<_textureWidthHint) _textureWidthHint = osg_max_size;
313        if (osg_max_size<_textureHeightHint) _textureHeightHint = osg_max_size;
314    }
315}
316
317unsigned int Font::getTextureWidthHint() const
318{
319    return _textureWidthHint;
320}
321
322unsigned int Font::getTextureHeightHint() const
323{
324    return _textureHeightHint;
325}
326
327
328void Font::setMinFilterHint(osg::Texture::FilterMode mode)
329{
330    _minFilterHint = mode;
331}
332
333osg::Texture::FilterMode Font::getMinFilterHint() const
334{
335    return _minFilterHint;
336}
337
338/** Set the magnification texture filter to use when creating the texture to store the glyph images when rendering.
339  * Note, this doesn't affect already created Texture Glhph's.*/
340void Font::setMagFilterHint(osg::Texture::FilterMode mode)
341{
342    _magFilterHint = mode;
343}
344
345osg::Texture::FilterMode Font::getMagFilterHint() const
346{
347    return _magFilterHint;
348}
349
350
351Glyph* Font::getGlyph(const FontResolution& fontRes, unsigned int charcode)
352{
353    if (!_implementation) return 0;
354
355    FontResolution fontResUsed(0,0);
356    if (_implementation->supportsMultipleFontResolutions()) fontResUsed = fontRes;
357
358    {
359        OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_glyphMapMutex);
360        FontSizeGlyphMap::iterator itr = _sizeGlyphMap.find(fontResUsed);
361        if (itr!=_sizeGlyphMap.end())
362        {
363            GlyphMap& glyphmap = itr->second;
364            GlyphMap::iterator gitr = glyphmap.find(charcode);
365            if (gitr!=glyphmap.end()) return gitr->second.get();
366        }
367    }
368
369    Glyph* glyph = _implementation->getGlyph(fontResUsed, charcode);
370    if (glyph)
371    {
372        addGlyph(fontResUsed, charcode, glyph);
373        return glyph;
374    }
375    else return 0;
376}
377
378Glyph3D* Font::getGlyph3D(unsigned int charcode)
379{
380    {
381        OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_glyphMapMutex);
382        Glyph3DMap::iterator itr = _glyph3DMap.find(charcode);
383        if (itr!=_glyph3DMap.end()) return itr->second.get();
384    }
385
386    Glyph3D* glyph = _implementation.valid() ? _implementation->getGlyph3D(charcode) : 0;
387    if (glyph)
388    {
389        OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_glyphMapMutex);
390        _glyph3DMap[charcode] = glyph;
391        return glyph;
392    }
393    return 0;
394}
395
396void Font::setThreadSafeRefUnref(bool threadSafe)
397{
398   osg::Object::setThreadSafeRefUnref(threadSafe);
399
400    if (_texenv.valid()) _texenv->setThreadSafeRefUnref(threadSafe);
401    if (_stateset.valid()) _stateset->setThreadSafeRefUnref(threadSafe);
402
403    for(GlyphTextureList::const_iterator itr=_glyphTextureList.begin();
404        itr!=_glyphTextureList.end();
405        ++itr)
406    {
407        (*itr)->setThreadSafeRefUnref(threadSafe);
408    }
409}
410
411void Font::resizeGLObjectBuffers(unsigned int maxSize)
412{
413    if (_stateset.valid()) _stateset->resizeGLObjectBuffers(maxSize);
414
415    for(GlyphTextureList::const_iterator itr=_glyphTextureList.begin();
416        itr!=_glyphTextureList.end();
417        ++itr)
418    {
419        (*itr)->resizeGLObjectBuffers(maxSize);
420    }
421}
422
423void Font::releaseGLObjects(osg::State* state) const
424{
425    if (_stateset.valid()) _stateset->releaseGLObjects(state);
426
427    for(GlyphTextureList::const_iterator itr=_glyphTextureList.begin();
428        itr!=_glyphTextureList.end();
429        ++itr)
430    {
431        (*itr)->releaseGLObjects(state);
432    }
433
434    // const_cast<Font*>(this)->_glyphTextureList.clear();
435    // const_cast<Font*>(this)->_sizeGlyphMap.clear();
436}
437
438osg::Vec2 Font::getKerning(unsigned int leftcharcode,unsigned int rightcharcode, KerningType kerningType)
439{
440    if (_implementation.valid()) return _implementation->getKerning(leftcharcode,rightcharcode,kerningType);
441    else return osg::Vec2(0.0f,0.0f);
442}
443
444bool Font::hasVertical() const
445{
446    if (_implementation.valid()) return _implementation->hasVertical();
447    else return false;
448}
449
450
451
452void Font::addGlyph(const FontResolution& fontRes, unsigned int charcode, Glyph* glyph)
453{
454    OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_glyphMapMutex);
455
456    _sizeGlyphMap[fontRes][charcode]=glyph;
457
458    int posX=0,posY=0;
459
460    GlyphTexture* glyphTexture = 0;
461    for(GlyphTextureList::iterator itr=_glyphTextureList.begin();
462        itr!=_glyphTextureList.end() && !glyphTexture;
463        ++itr)
464    {
465        if ((*itr)->getSpaceForGlyph(glyph,posX,posY)) glyphTexture = itr->get();
466    }
467
468    if (glyphTexture)
469    {
470        //cout << "    found space for texture "<<glyphTexture<<" posX="<<posX<<" posY="<<posY<<endl;
471    }
472
473    if (!glyphTexture)
474    {
475
476        glyphTexture = new GlyphTexture;
477
478        static int numberOfTexturesAllocated = 0;
479        ++numberOfTexturesAllocated;
480
481        OSG_INFO<< "   Font " << this<< ", numberOfTexturesAllocated "<<numberOfTexturesAllocated<<std::endl;
482
483        // reserve enough space for the glyphs.
484        glyphTexture->setGlyphImageMargin(_margin);
485        glyphTexture->setGlyphImageMarginRatio(_marginRatio);
486        glyphTexture->setTextureSize(_textureWidthHint,_textureHeightHint);
487        glyphTexture->setFilter(osg::Texture::MIN_FILTER,_minFilterHint);
488        glyphTexture->setFilter(osg::Texture::MAG_FILTER,_magFilterHint);
489        glyphTexture->setMaxAnisotropy(8);
490
491        _glyphTextureList.push_back(glyphTexture);
492
493        if (!glyphTexture->getSpaceForGlyph(glyph,posX,posY))
494        {
495            OSG_WARN<<"Warning: unable to allocate texture big enough for glyph"<<std::endl;
496            return;
497        }
498
499    }
500
501    // add the glyph into the texture.
502    glyphTexture->addGlyph(glyph,posX,posY);
503
504}
Note: See TracBrowser for help on using the browser.