root/OpenSceneGraph/trunk/src/osgPlugins/freetype/FreeTypeFont.cpp @ 13041

Revision 13041, 19.0 kB (checked in by robert, 3 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 "FreeTypeFont.h"
15#include "FreeTypeLibrary.h"
16
17#include <freetype/ftoutln.h>
18#include <freetype/ftbbox.h>
19
20#include <osg/Notify>
21#include <osg/io_utils>
22#include <osgDB/WriteFile>
23
24namespace FreeType
25{
26
27struct Char3DInfo
28{
29    Char3DInfo(int numSteps):
30        _verts( new osg::Vec3Array ),
31        _geometry( new osg::Geometry ),
32        _numSteps(numSteps),
33        _maxY(-FLT_MAX),
34        _maxX(-FLT_MAX),
35        _minX(FLT_MAX),
36        _minY(FLT_MAX),
37        _coord_scale(1.0/64.0)
38    {
39        _geometry->setVertexArray(_verts.get());
40    }
41
42    ~Char3DInfo()
43    {
44    }
45
46    void completeCurrentPrimitiveSet()
47    {
48        if (_currentPrimitiveSet.valid() && _currentPrimitiveSet->size()>1)
49        {
50            _geometry->addPrimitiveSet( _currentPrimitiveSet.get() );
51        }
52        _currentPrimitiveSet = 0;
53    }
54
55    osg::Geometry* get()
56    {
57        completeCurrentPrimitiveSet();
58
59        return _geometry.get();
60    }
61
62    void addVertex(osg::Vec3 pos)
63    {
64        _previous = pos;
65
66        pos *= _coord_scale;
67
68        if (!_verts->empty() && _verts->back()==pos)
69        {
70            // OSG_NOTICE<<"addVertex("<<pos<<") duplicate, ignoring"<<std::endl;
71            return;
72        }
73
74        if (!_currentPrimitiveSet)
75        {
76            _currentPrimitiveSet = new osg::DrawElementsUShort( osg::PrimitiveSet::POLYGON);
77            _currentPrimitiveSet->setName("boundary");
78        }
79
80        if (!(_currentPrimitiveSet->empty()) &&
81            (*_verts)[(*_currentPrimitiveSet)[0]] == pos)
82        {
83            _currentPrimitiveSet->push_back( (*_currentPrimitiveSet)[0] );
84        }
85        else
86        {
87            _currentPrimitiveSet->push_back( _verts->size() );
88
89            _verts->push_back( pos );
90
91            setMinMax(pos);
92        }
93    }
94
95    void moveTo(const osg::Vec2& pos)
96    {
97        completeCurrentPrimitiveSet();
98
99        addVertex( osg::Vec3(pos.x(),pos.y(),0) );
100
101    }
102    void lineTo(const osg::Vec2& pos)
103    {
104        addVertex( osg::Vec3(pos.x(),pos.y(),0) );
105    }
106
107    void conicTo(const osg::Vec2& control, const osg::Vec2& pos)
108    {
109        osg::Vec3 p0 = _previous;
110        osg::Vec3 p1 = osg::Vec3(control.x(),control.y(),0);
111        osg::Vec3 p2 = osg::Vec3(pos.x(),pos.y(),0);
112
113        double dt = 1.0/_numSteps;
114        double u=0;
115        for (int i=0; i<=_numSteps; ++i)
116        {
117            double w = 1;
118            double bs = 1.0/( (1-u)*(1-u)+2*(1-u)*u*w +u*u );
119            osg::Vec3 p = (p0*((1-u)*(1-u)) + p1*(2*(1-u)*u*w) + p2*(u*u))*bs;
120            addVertex( p );
121
122            u += dt;
123        }
124    }
125
126    void cubicTo(const osg::Vec2& control1, const osg::Vec2& control2, const osg::Vec2& pos)
127    {
128        osg::Vec3 p0 = _previous;
129        osg::Vec3 p1 = osg::Vec3(control1.x(),control1.y(),0);
130        osg::Vec3 p2 = osg::Vec3(control2.x(),control2.y(),0);
131        osg::Vec3 p3 = osg::Vec3(pos.x(),pos.y(),0);
132
133        double cx = 3*(p1.x() - p0.x());
134        double bx = 3*(p2.x() - p1.x()) - cx;
135        double ax = p3.x() - p0.x() - cx - bx;
136        double cy = 3*(p1.y() - p0.y());
137        double by = 3*(p2.y() - p1.y()) - cy;
138        double ay = p3.y() - p0.y() - cy - by;
139
140        double dt = 1.0/_numSteps;
141        double u=0;
142        for (int i=0; i<=_numSteps; ++i)
143        {
144            osg::Vec3 p = osg::Vec3( ax*u*u*u + bx*u*u  + cx*u + p0.x(),ay*u*u*u + by*u*u  + cy*u + p0.y(),0 );
145            addVertex( p );
146
147            u += dt;
148        }
149    }
150
151    void setMinMax(const osg::Vec3& pos)
152    {
153        _maxY = std::max(_maxY, (double) pos.y());
154        _minY = std::min(_minY, (double) pos.y());
155        _maxX = std::max(_maxX, (double) pos.x());
156        _minX = std::min(_minX, (double) pos.x());
157    }
158
159    osg::ref_ptr<osg::Vec3Array>    _verts;
160    osg::ref_ptr<osg::DrawElementsUShort> _currentPrimitiveSet;
161    osg::ref_ptr<osg::Geometry>     _geometry;
162    osg::Vec3                       _previous;
163    int                             _numSteps;
164    double                          _maxY;
165    double                          _maxX;
166    double                          _minX;
167    double                          _minY;
168    double                          _coord_scale;
169
170};
171
172
173int moveTo( const FT_Vector* to, void* user )
174{
175    Char3DInfo* char3d = (Char3DInfo*)user;
176    char3d->moveTo( osg::Vec2(to->x,to->y) );
177    return 0;
178}
179int lineTo( const FT_Vector* to, void* user )
180{
181    Char3DInfo* char3d = (Char3DInfo*)user;
182    char3d->lineTo( osg::Vec2(to->x,to->y) );
183    return 0;
184}
185int conicTo( const FT_Vector* control,const FT_Vector* to, void* user )
186{
187    Char3DInfo* char3d = (Char3DInfo*)user;
188    char3d->conicTo( osg::Vec2(control->x,control->y), osg::Vec2(to->x,to->y) );
189    return 0;
190}
191int cubicTo( const FT_Vector* control1,const FT_Vector* control2,const FT_Vector* to, void* user )
192{
193    Char3DInfo* char3d = (Char3DInfo*)user;
194    char3d->cubicTo(
195        osg::Vec2(control1->x,control1->y),
196        osg::Vec2(control2->x,control2->y),
197        osg::Vec2(to->x,to->y) );
198    return 0;
199}
200
201}
202
203FreeTypeFont::FreeTypeFont(const std::string& filename, FT_Face face, unsigned int flags):
204    _currentRes(osgText::FontResolution(0,0)),
205    _filename(filename),
206    _buffer(0),
207    _face(face),
208    _flags(flags)
209{
210    init();
211}
212
213FreeTypeFont::FreeTypeFont(FT_Byte* buffer, FT_Face face, unsigned int flags):
214    _currentRes(osgText::FontResolution(0,0)),
215    _filename(""),
216    _buffer(buffer),
217    _face(face),
218    _flags(flags)
219{
220    init();
221}
222
223FreeTypeFont::~FreeTypeFont()
224{
225    if(_face)
226    {
227        FreeTypeLibrary* freeTypeLibrary = FreeTypeLibrary::instance();
228        if (freeTypeLibrary)
229        {
230            // remove myself from the local registry to ensure that
231            // not dangling pointers remain
232            freeTypeLibrary->removeFontImplmentation(this);
233
234            // free the freetype font face itself
235            FT_Done_Face(_face);
236            _face = 0;
237
238            // release memory held for FT_Face to work
239            if (_buffer)
240            {
241                delete [] _buffer;
242                _buffer = 0;
243            }
244        }
245    }
246}
247
248void FreeTypeFont::init()
249{
250    FT_Error _error;
251    _error = FT_Set_Pixel_Sizes(_face, 32, 32);
252    if (_error)
253    {
254        OSG_NOTICE << "FreeTypeFont3D: set pixel sizes failed ..." << std::endl;
255        return;
256    }
257    _currentRes.first = 32;
258    _currentRes.second = 32;
259}
260
261void FreeTypeFont::setFontResolution(const osgText::FontResolution& fontSize)
262{
263    if (fontSize==_currentRes) return;
264
265    int width = fontSize.first;
266    int height = fontSize.second;
267    int maxAxis = std::max(width, height);
268    int margin = _facade->getGlyphImageMargin() + (int)((float)maxAxis * _facade->getGlyphImageMarginRatio());
269
270    if ((unsigned int)(width+2*margin) > _facade->getTextureWidthHint() ||
271        (unsigned int)(width+2*margin) > _facade->getTextureHeightHint())
272    {
273        OSG_WARN<<"Warning: FreeTypeFont::setSize("<<width<<","<<height<<") sizes too large,"<<std::endl;
274
275        width = _facade->getTextureWidthHint()-2*margin;
276        height = _facade->getTextureHeightHint()-2*margin;
277
278        OSG_WARN<<"         sizes capped ("<<width<<","<<height<<") to fit int current glyph texture size."<<std::endl;
279    }
280
281    FT_Error error = FT_Set_Pixel_Sizes( _face,      /* handle to face object  */
282                                         width,      /* pixel_width            */
283                                         height );   /* pixel_height            */
284
285    if (error)
286    {
287        OSG_WARN<<"FT_Set_Pixel_Sizes() - error 0x"<<std::hex<<error<<std::dec<<std::endl;
288    }
289    else
290    {
291        _currentRes = fontSize;
292    }
293
294}
295
296osgText::Glyph* FreeTypeFont::getGlyph(const osgText::FontResolution& fontRes, unsigned int charcode)
297{
298    OpenThreads::ScopedLock<OpenThreads::Mutex> lock(FreeTypeLibrary::instance()->getMutex());
299
300    setFontResolution(fontRes);
301
302    float coord_scale = getCoordScale();
303
304    //
305    // GT: fix for symbol fonts (i.e. the Webdings font) as the wrong character are being
306    // returned, for symbol fonts in windows (FT_ENCONDING_MS_SYMBOL in freetype) the correct
307    // values are from 0xF000 to 0xF0FF not from 0x000 to 0x00FF (0 to 255) as you would expect.
308    // Microsoft uses a private field for its symbol fonts
309    //
310    unsigned int charindex = charcode;
311    if (_face->charmap != NULL)
312    {
313        if (_face->charmap->encoding == FT_ENCODING_MS_SYMBOL)
314        {
315            charindex |= 0xF000;
316        }
317    }
318
319    FT_Error error = FT_Load_Char( _face, charindex, FT_LOAD_RENDER|FT_LOAD_NO_BITMAP|_flags );
320    if (error)
321    {
322        OSG_WARN << "FT_Load_Char(...) error 0x"<<std::hex<<error<<std::dec<<std::endl;
323        return 0;
324    }
325
326
327    FT_GlyphSlot glyphslot = _face->glyph;
328
329    int pitch = glyphslot->bitmap.pitch;
330    unsigned char* buffer = glyphslot->bitmap.buffer;
331
332    unsigned int sourceWidth = glyphslot->bitmap.width;;
333    unsigned int sourceHeight = glyphslot->bitmap.rows;
334
335    unsigned int width = sourceWidth;
336    unsigned int height = sourceHeight;
337
338    osg::ref_ptr<osgText::Glyph> glyph = new osgText::Glyph(_facade, charcode);
339
340    unsigned int dataSize = width*height;
341    unsigned char* data = new unsigned char[dataSize];
342
343
344    // clear the image to zeros.
345    for(unsigned char* p=data;p<data+dataSize;) { *p++ = 0; }
346
347    glyph->setImage(width,height,1,
348                    GL_ALPHA,
349                    GL_ALPHA,GL_UNSIGNED_BYTE,
350                    data,
351                    osg::Image::USE_NEW_DELETE,
352                    1);
353
354    glyph->setInternalTextureFormat(GL_ALPHA);
355
356    // copy image across to osgText::Glyph image.
357    switch(glyphslot->bitmap.pixel_mode)
358    {
359        case FT_PIXEL_MODE_MONO:
360            for(int r=sourceHeight-1;r>=0;--r)
361            {
362                unsigned char* ptr = buffer+r*pitch;
363                for(unsigned int c=0;c<sourceWidth;++c)
364                {
365                    (*data++)= (ptr[c >> 3] & (1 << (~c & 7))) ? 255 : 0;
366                }
367            }
368            break;
369
370
371        case FT_PIXEL_MODE_GRAY:
372            for(int r=sourceHeight-1;r>=0;--r)
373            {
374                unsigned char* ptr = buffer+r*pitch;
375                for(unsigned int c=0;c<sourceWidth;++c,++ptr)
376                {
377                    (*data++)=*ptr;
378                }
379            }
380            break;
381
382        default:
383            OSG_WARN << "FT_Load_Char(...) returned bitmap with unknown pixel_mode " << glyphslot->bitmap.pixel_mode << std::endl;
384    }
385
386
387    FT_Glyph_Metrics* metrics = &(_face->glyph->metrics);
388
389    glyph->setWidth((float)metrics->width * coord_scale);
390    glyph->setHeight((float)metrics->height * coord_scale);
391    glyph->setHorizontalBearing(osg::Vec2((float)metrics->horiBearingX * coord_scale,(float)(metrics->horiBearingY-metrics->height) * coord_scale)); // bottom left.
392    glyph->setHorizontalAdvance((float)metrics->horiAdvance * coord_scale);
393    glyph->setVerticalBearing(osg::Vec2((float)metrics->vertBearingX * coord_scale,(float)(metrics->vertBearingY-metrics->height) * coord_scale)); // top middle.
394    glyph->setVerticalAdvance((float)metrics->vertAdvance * coord_scale);
395
396#if 0
397    OSG_NOTICE<<"getGlyph("<<charcode<<", "<<char(charcode)<<") _face="<<_face<<", _filename="<<_filename<<std::endl;
398    OSG_NOTICE<<"   height="<<glyph->getHeight()<<std::endl;
399    OSG_NOTICE<<"   width="<<glyph->getWidth()<<std::endl;
400    OSG_NOTICE<<"   horizontalBearing="<<glyph->getHorizontalBearing()<<std::endl;
401    OSG_NOTICE<<"   horizontalAdvance="<<glyph->getHorizontalAdvance()<<std::endl;
402    OSG_NOTICE<<"   verticalBearing="<<glyph->getHorizontalBearing()<<std::endl;
403    OSG_NOTICE<<"   verticalAdvance="<<glyph->getVerticalAdvance()<<std::endl;
404    OSG_NOTICE<<"   coord_scale = "<<coord_scale<<std::endl;
405    OSG_NOTICE<<"   _face->units_per_EM = "<<_face->units_per_EM<<", scale="<<1.0f/float(_face->units_per_EM)<<std::endl;
406#endif
407
408//    cout << "      in getGlyph() implementation="<<this<<"  "<<_filename<<"  facade="<<_facade<<endl;
409
410    return glyph.release();
411
412}
413
414osgText::Glyph3D * FreeTypeFont::getGlyph3D(unsigned int charcode)
415{
416    OpenThreads::ScopedLock<OpenThreads::Mutex> lock(FreeTypeLibrary::instance()->getMutex());
417
418    //
419    // GT: fix for symbol fonts (i.e. the Webdings font) as the wrong character are being
420    // returned, for symbol fonts in windows (FT_ENCONDING_MS_SYMBOL in freetype) the correct
421    // values are from 0xF000 to 0xF0FF not from 0x000 to 0x00FF (0 to 255) as you would expect.
422    // Microsoft uses a private field for its symbol fonts
423    //
424    unsigned int charindex = charcode;
425    if (_face->charmap != NULL)
426    {
427        if (_face->charmap->encoding == FT_ENCODING_MS_SYMBOL)
428        {
429            charindex |= 0xF000;
430        }
431    }
432
433    FT_Error error = FT_Load_Char( _face, charindex, FT_LOAD_DEFAULT|_flags );
434    if (error)
435    {
436        OSG_WARN << "FT_Load_Char(...) error 0x"<<std::hex<<error<<std::dec<<std::endl;
437        return 0;
438    }
439    if (_face->glyph->format != FT_GLYPH_FORMAT_OUTLINE)
440    {
441        OSG_WARN << "FreeTypeFont3D::getGlyph : not a vector font" << std::endl;
442        return 0;
443    }
444
445    float coord_scale = getCoordScale();
446
447    // ** init FreeType to describe the glyph
448    FreeType::Char3DInfo char3d(_facade->getNumberCurveSamples());
449    char3d._coord_scale = coord_scale;
450
451    FT_Outline outline = _face->glyph->outline;
452    FT_Outline_Funcs funcs;
453    funcs.conic_to = (FT_Outline_ConicToFunc)&FreeType::conicTo;
454    funcs.line_to = (FT_Outline_LineToFunc)&FreeType::lineTo;
455    funcs.cubic_to = (FT_Outline_CubicToFunc)&FreeType::cubicTo;
456    funcs.move_to = (FT_Outline_MoveToFunc)&FreeType::moveTo;
457    funcs.shift = 0;
458    funcs.delta = 0;
459
460    // ** record description
461    FT_Error _error = FT_Outline_Decompose(&outline, &funcs, &char3d);
462    if (_error)
463    {
464        OSG_WARN << "FreeTypeFont3D::getGlyph : - outline decompose failed ..." << std::endl;
465        return 0;
466    }
467
468    // ** create geometry for each part of the glyph
469    osg::ref_ptr<osg::Geometry> frontGeo(new osg::Geometry);
470
471    osg::ref_ptr<osg::Vec3Array> rawVertices = new osg::Vec3Array(*(char3d._verts));
472    osg::Geometry::PrimitiveSetList rawPrimitives;
473    for(osg::Geometry::PrimitiveSetList::iterator itr = char3d.get()->getPrimitiveSetList().begin();
474        itr != char3d.get()->getPrimitiveSetList().end();
475        ++itr)
476    {
477        rawPrimitives.push_back(dynamic_cast<osg::PrimitiveSet*>((*itr)->clone(osg::CopyOp::DEEP_COPY_ALL)));
478    }
479
480    // ** save vertices and PrimitiveSetList of each face in the Glyph3D PrimitiveSet face list
481    osg::ref_ptr<osgText::Glyph3D> glyph = new osgText::Glyph3D(_facade, charcode);
482
483    // copy the raw primitive set list before we tessellate it.
484    glyph->getRawFacePrimitiveSetList() = rawPrimitives;
485    glyph->setRawVertexArray(rawVertices.get());
486
487
488    FT_Glyph_Metrics* metrics = &(_face->glyph->metrics);
489
490    glyph->setWidth((float)metrics->width * coord_scale);
491    glyph->setHeight((float)metrics->height * coord_scale);
492    glyph->setHorizontalBearing(osg::Vec2((float)metrics->horiBearingX * coord_scale,(float)(metrics->horiBearingY-metrics->height) * coord_scale)); // bottom left.
493    glyph->setHorizontalAdvance((float)metrics->horiAdvance * coord_scale);
494    glyph->setVerticalBearing(osg::Vec2((float)metrics->vertBearingX * coord_scale,(float)(metrics->vertBearingY-metrics->height) * coord_scale)); // top middle.
495    glyph->setVerticalAdvance((float)metrics->vertAdvance * coord_scale);
496
497#if 0
498    OSG_NOTICE<<"getGlyph3D("<<charcode<<", "<<char(charcode)<<")"<<std::endl;
499    OSG_NOTICE<<"   height="<<glyph->getHeight()<<std::endl;
500    OSG_NOTICE<<"   width="<<glyph->getWidth()<<std::endl;
501    OSG_NOTICE<<"   horizontalBearing="<<glyph->getHorizontalBearing()<<std::endl;
502    OSG_NOTICE<<"   horizontalAdvance="<<glyph->getHorizontalAdvance()<<std::endl;
503    OSG_NOTICE<<"   verticalBearing="<<glyph->getHorizontalBearing()<<std::endl;
504    OSG_NOTICE<<"   verticalAdvance="<<glyph->getVerticalAdvance()<<std::endl;
505#endif
506
507    FT_BBox ftbb;
508    FT_Outline_Get_BBox(&outline, &ftbb);
509    osg::BoundingBox bb(float(ftbb.xMin) * coord_scale, float(ftbb.yMin) * coord_scale, 0.0f, float(ftbb.xMax) * coord_scale, float(ftbb.yMax) * coord_scale, 0.0f);
510
511    glyph->setBoundingBox(bb);
512
513    return glyph.release();
514}
515
516osg::Vec2 FreeTypeFont::getKerning(unsigned int leftcharcode,unsigned int rightcharcode, osgText::KerningType kerningType)
517{
518    OpenThreads::ScopedLock<OpenThreads::Mutex> lock(FreeTypeLibrary::instance()->getMutex());
519
520    if (!FT_HAS_KERNING(_face) || (kerningType == osgText::KERNING_NONE)) return osg::Vec2(0.0f,0.0f);
521
522    FT_Kerning_Mode mode = (kerningType==osgText::KERNING_DEFAULT) ? ft_kerning_default : ft_kerning_unfitted;
523
524    // convert character code to glyph index
525    FT_UInt left = FT_Get_Char_Index( _face, leftcharcode );
526    FT_UInt right = FT_Get_Char_Index( _face, rightcharcode );
527
528    // get the kerning distances.
529    FT_Vector  kerning;
530
531    FT_Error error = FT_Get_Kerning( _face,                     // handle to face object
532                                     left,                      // left glyph index
533                                     right,                     // right glyph index
534                                     mode,                      // kerning mode
535                                     &kerning );                // target vector
536
537    if (error)
538    {
539        OSG_WARN << "FT_Get_Kerning(...) returned error code " <<std::hex<<error<<std::dec<< std::endl;
540        return osg::Vec2(0.0f,0.0f);
541    }
542
543    float coord_scale = getCoordScale();
544
545    return osg::Vec2((float)kerning.x*coord_scale,(float)kerning.y*coord_scale);
546}
547
548bool FreeTypeFont::hasVertical() const
549{
550    OpenThreads::ScopedLock<OpenThreads::Mutex> lock(FreeTypeLibrary::instance()->getMutex());
551    return FT_HAS_VERTICAL(_face)!=0;
552}
553
554bool FreeTypeFont::getVerticalSize(float & ascender, float & descender) const
555{
556    OpenThreads::ScopedLock<OpenThreads::Mutex> lock(FreeTypeLibrary::instance()->getMutex());
557#if 0
558    if(_face->units_per_EM != 0)
559    {
560        float coord_scale = 1.0f/static_cast<float>(_face->units_per_EM);
561        ascender = static_cast<float>(_face->ascender) * coord_scale;
562        descender = static_cast<float>(_face->descender) * coord_scale;
563        return true;
564    }
565    else
566    {
567        return false;
568    }
569#else
570    float coord_scale = getCoordScale();
571    ascender = static_cast<float>(_face->ascender) * coord_scale;
572    descender = static_cast<float>(_face->descender) * coord_scale;
573    return true;
574#endif
575}
576
577float FreeTypeFont::getCoordScale() const
578{
579    //float coord_scale = _freetype_scale/64.0f;
580    //float coord_scale = 1.0f/64.0f;
581    float coord_scale = 1.0f/(float(_currentRes.second)*64.0f);
582    return coord_scale;
583}
Note: See TracBrowser for help on using the browser.