Planet
navi homePPSaboutscreenshotsdownloaddevelopmentforum

source: downloads/OgreMain/src/OgreFont.cpp @ 1

Last change on this file since 1 was 1, checked in by landauf, 17 years ago
File size: 17.0 KB
Line 
1/*-------------------------------------------------------------------------
2This source file is a part of OGRE
3(Object-oriented Graphics Rendering Engine)
4
5For the latest info, see http://www.ogre3d.org/
6
7Copyright (c) 2000-2006 Torus Knot Software Ltd
8Also see acknowledgements in Readme.html
9
10This library is free software; you can redistribute it and/or modify it
11under the terms of the GNU Lesser General Public License (LGPL) as
12published by the Free Software Foundation; either version 2.1 of the
13License, or (at your option) any later version.
14
15This library is distributed in the hope that it will be useful, but
16WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
17or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public
18License for more details.
19
20You should have received a copy of the GNU Lesser General Public License
21along with this library; if not, write to the Free Software Foundation,
22Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA or go to
23http://www.gnu.org/copyleft/lesser.txt
24-------------------------------------------------------------------------*/
25#include "OgreStableHeaders.h"
26
27#include "OgreFont.h"
28#include "OgreMaterialManager.h"
29#include "OgreTextureManager.h"
30#include "OgreTexture.h"
31#include "OgreResourceGroupManager.h"
32#include "OgreLogManager.h"
33#include "OgreStringConverter.h"
34#include "OgreRenderWindow.h"
35#include "OgreException.h"
36#include "OgreBlendMode.h"
37#include "OgreTextureUnitState.h"
38#include "OgreTechnique.h"
39#include "OgrePass.h"
40#include "OgreMaterial.h"
41#include "OgreBitwise.h"
42#include <ft2build.h>
43#include FT_FREETYPE_H
44#include FT_GLYPH_H
45
46
47
48namespace Ogre
49{
50    //---------------------------------------------------------------------
51        Font::CmdType Font::msTypeCmd;
52        Font::CmdSource Font::msSourceCmd;
53        Font::CmdSize Font::msSizeCmd;
54        Font::CmdResolution Font::msResolutionCmd;
55        Font::CmdCodePoints Font::msCodePointsCmd;
56
57    //---------------------------------------------------------------------
58        Font::Font(ResourceManager* creator, const String& name, ResourceHandle handle,
59                const String& group, bool isManual, ManualResourceLoader* loader)
60                :Resource (creator, name, handle, group, isManual, loader),
61                mType(FT_TRUETYPE), mTtfSize(0), mTtfResolution(0), mAntialiasColour(false)
62    {
63
64                if (createParamDictionary("Font"))
65                {
66                        ParamDictionary* dict = getParamDictionary();
67                        dict->addParameter(
68                                ParameterDef("type", "'truetype' or 'image' based font", PT_STRING),
69                                &msTypeCmd);
70                        dict->addParameter(
71                                ParameterDef("source", "Filename of the source of the font.", PT_STRING),
72                                &msSourceCmd);
73                        dict->addParameter(
74                                ParameterDef("size", "True type size", PT_REAL),
75                                &msSizeCmd);
76                        dict->addParameter(
77                                ParameterDef("resolution", "True type resolution", PT_UNSIGNED_INT),
78                                &msResolutionCmd);
79                        dict->addParameter(
80                                ParameterDef("code_points", "Add a range of code points", PT_STRING),
81                                &msCodePointsCmd);
82                }
83
84    }
85    //---------------------------------------------------------------------
86    Font::~Font()
87    {
88        // have to call this here reather than in Resource destructor
89        // since calling virtual methods in base destructors causes crash
90        unload();
91    }
92    //---------------------------------------------------------------------
93    void Font::setType(FontType ftype)
94    {
95        mType = ftype;
96    }
97    //---------------------------------------------------------------------
98    FontType Font::getType(void) const
99    {
100        return mType;
101    }
102    //---------------------------------------------------------------------
103    void Font::setSource(const String& source)
104    {
105        mSource = source;
106    }
107    //---------------------------------------------------------------------
108    void Font::setTrueTypeSize(Real ttfSize)
109    {
110        mTtfSize = ttfSize;
111    }
112    //---------------------------------------------------------------------
113    void Font::setTrueTypeResolution(uint ttfResolution)
114    {
115        mTtfResolution = ttfResolution;
116    }
117    //---------------------------------------------------------------------
118    const String& Font::getSource(void) const
119    {
120        return mSource;
121    }
122    //---------------------------------------------------------------------
123    Real Font::getTrueTypeSize(void) const
124    {
125        return mTtfSize;
126    }
127    //---------------------------------------------------------------------
128    uint Font::getTrueTypeResolution(void) const
129    {
130        return mTtfResolution;
131    }
132        //---------------------------------------------------------------------
133        const Font::GlyphInfo& Font::getGlyphInfo(CodePoint id) const
134        {
135                CodePointMap::const_iterator i = mCodePointMap.find(id);
136                if (i == mCodePointMap.end())
137                {
138                        OGRE_EXCEPT(Exception::ERR_ITEM_NOT_FOUND, 
139                                "Code point " + StringConverter::toString(id) + " not found in font "
140                                + mName, "Font::getGlyphInfo");
141                }
142                return i->second;
143        }
144    //---------------------------------------------------------------------
145    void Font::loadImpl()
146    {
147        // Create a new material
148        mpMaterial =  MaterialManager::getSingleton().create(
149                        "Fonts/" + mName,  mGroup);
150
151                if (mpMaterial.isNull())
152        {
153            OGRE_EXCEPT(Exception::ERR_INTERNAL_ERROR,
154                "Error creating new material!", "Font::load" );
155        }
156
157        TextureUnitState *texLayer;
158        bool blendByAlpha = true;
159        if (mType == FT_TRUETYPE)
160        {
161            createTextureFromFont();
162            texLayer = mpMaterial->getTechnique(0)->getPass(0)->getTextureUnitState(0);
163            // Always blend by alpha
164            blendByAlpha = true;
165        }
166        else
167        {
168                        // Manually load since we need to load to get alpha
169                        mTexture = TextureManager::getSingleton().load(mSource, mGroup, TEX_TYPE_2D, 0);
170            blendByAlpha = mTexture->hasAlpha();
171            texLayer = mpMaterial->getTechnique(0)->getPass(0)->createTextureUnitState(mSource);
172        }
173        // Clamp to avoid fuzzy edges
174        texLayer->setTextureAddressingMode( TextureUnitState::TAM_CLAMP );
175                // Allow min/mag filter, but no mip
176                texLayer->setTextureFiltering(FO_LINEAR, FO_LINEAR, FO_NONE);
177
178
179        // Set up blending
180        if (blendByAlpha)
181        {
182            mpMaterial->setSceneBlending( SBT_TRANSPARENT_ALPHA );
183        }
184        else
185        {
186            // Use add if no alpha (assume black background)
187            mpMaterial->setSceneBlending(SBT_ADD);
188        }
189    }
190    //---------------------------------------------------------------------
191    void Font::unloadImpl()
192    {
193                if (!mpMaterial.isNull())
194                {
195                        MaterialManager::getSingleton().remove(mpMaterial->getHandle());
196                        mpMaterial.setNull();
197                }
198
199                if (!mTexture.isNull())
200                {
201                        TextureManager::getSingleton().remove(mTexture->getHandle());
202                        mTexture.setNull();
203                }
204    }
205    //---------------------------------------------------------------------
206    void Font::createTextureFromFont(void)
207    {
208
209                // Just create the texture here, and point it at ourselves for when
210                // it wants to (re)load for real
211                String texName = mName + "Texture";
212                // Create, setting isManual to true and passing self as loader
213                mTexture = TextureManager::getSingleton().create(
214                        texName, mGroup, true, this);
215                mTexture->setTextureType(TEX_TYPE_2D);
216                mTexture->setNumMipmaps(0);
217                mTexture->load();
218
219                TextureUnitState* t = mpMaterial->getTechnique(0)->getPass(0)->createTextureUnitState( texName );
220                // Allow min/mag filter, but no mip
221                t->setTextureFiltering(FO_LINEAR, FO_LINEAR, FO_NONE);
222
223        }
224        //---------------------------------------------------------------------
225        void Font::loadResource(Resource* res)
226        {
227                // ManualResourceLoader implementation - load the texture
228                FT_Library ftLibrary;
229                // Init freetype
230        if( FT_Init_FreeType( &ftLibrary ) )
231            OGRE_EXCEPT( Exception::ERR_INTERNAL_ERROR, "Could not init FreeType library!",
232            "Font::Font");
233
234        FT_Face face;
235        // Add a gap between letters vert and horz
236        // prevents nasty artefacts when letters are too close together
237        uint char_spacer = 5;
238
239        // Locate ttf file, load it pre-buffered into memory by wrapping the
240                // original DataStream in a MemoryDataStream
241                DataStreamPtr dataStreamPtr =
242                        ResourceGroupManager::getSingleton().openResource(
243                                mSource, mGroup, true, this);
244                MemoryDataStream ttfchunk(dataStreamPtr);
245
246        // Load font
247        if( FT_New_Memory_Face( ftLibrary, ttfchunk.getPtr(), (FT_Long)ttfchunk.size() , 0, &face ) )
248            OGRE_EXCEPT( Exception::ERR_INTERNAL_ERROR,
249            "Could not open font face!", "Font::createTextureFromFont" );
250
251
252        // Convert our point size to freetype 26.6 fixed point format
253        FT_F26Dot6 ftSize = (FT_F26Dot6)(mTtfSize * (1 << 6));
254        if( FT_Set_Char_Size( face, ftSize, 0, mTtfResolution, mTtfResolution ) )
255            OGRE_EXCEPT( Exception::ERR_INTERNAL_ERROR,
256            "Could not set char size!", "Font::createTextureFromFont" );
257
258        //FILE *fo_def = stdout;
259
260        int max_height = 0, max_width = 0, max_bear = 0;
261
262                // Backwards compatibility - if codepoints not supplied, assume 33-166
263                if (mCodePointRangeList.empty())
264                {
265                        mCodePointRangeList.push_back(CodePointRange(33, 166));
266                }
267
268                // Calculate maximum width, height and bearing
269                size_t glyphCount = 0;
270                for (CodePointRangeList::const_iterator r = mCodePointRangeList.begin();
271                        r != mCodePointRangeList.end(); ++r)
272                {
273                        const CodePointRange& range = *r;
274                        for(CodePoint cp = range.first; cp <= range.second; ++cp, ++glyphCount)
275                        {
276                                FT_Load_Char( face, cp, FT_LOAD_RENDER );
277
278                                if( ( 2 * ( face->glyph->bitmap.rows << 6 ) - face->glyph->metrics.horiBearingY ) > max_height )
279                                        max_height = ( 2 * ( face->glyph->bitmap.rows << 6 ) - face->glyph->metrics.horiBearingY );
280                                if( face->glyph->metrics.horiBearingY > max_bear )
281                                        max_bear = face->glyph->metrics.horiBearingY;
282
283                                if( (face->glyph->advance.x >> 6 ) + ( face->glyph->metrics.horiBearingX >> 6 ) > max_width)
284                                        max_width = (face->glyph->advance.x >> 6 ) + ( face->glyph->metrics.horiBearingX >> 6 );
285                        }
286
287                }
288
289                // Now work out how big our texture needs to be
290                size_t rawSize = (max_width + char_spacer) *
291                                                        ((max_height >> 6) + char_spacer) * glyphCount;
292
293                uint32 tex_side = static_cast<uint32>(Math::Sqrt(rawSize));
294                // just in case the size might chop a glyph in half, add another glyph width/height
295                tex_side += std::max(max_width, (max_height>>6));
296                // Now round up to nearest power of two
297                uint32 roundUpSize = Bitwise::firstPO2From(tex_side);
298
299                // Would we benefit from using a non-square texture (2X width(
300                size_t finalWidth, finalHeight;
301                if (roundUpSize*roundUpSize*0.5 >= rawSize)
302                {
303                        finalHeight = roundUpSize * 0.5;
304                }
305                else
306                {
307                        finalHeight = roundUpSize;
308                }
309                finalWidth = roundUpSize;
310
311                Real textureAspect = finalWidth / finalHeight;
312
313                const size_t pixel_bytes = 2;
314                size_t data_width = finalWidth * pixel_bytes;
315                size_t data_size = finalWidth * finalHeight * pixel_bytes;
316
317                LogManager::getSingleton().logMessage("Font " + mName + "using texture size " +
318                        StringConverter::toString(finalWidth) + "x" + StringConverter::toString(finalHeight));
319
320        uchar* imageData = new uchar[data_size];
321                // Reset content (White, transparent)
322        for (size_t i = 0; i < data_size; i += pixel_bytes)
323        {
324            imageData[i + 0] = 0xFF; // luminance
325            imageData[i + 1] = 0x00; // alpha
326        }
327
328        size_t l = 0, m = 0;
329                for (CodePointRangeList::const_iterator r = mCodePointRangeList.begin();
330                        r != mCodePointRangeList.end(); ++r)
331                {
332                        const CodePointRange& range = *r;
333                        for(CodePoint cp = range.first; cp <= range.second; ++cp )
334                        {
335                                FT_Error ftResult;
336
337                                // Load & render glyph
338                                ftResult = FT_Load_Char( face, cp, FT_LOAD_RENDER );
339                                if (ftResult)
340                                {
341                                        // problem loading this glyph, continue
342                                        LogManager::getSingleton().logMessage("Info: cannot load character " +
343                                                StringConverter::toString(cp) + " in font " + mName);
344                                        continue;
345                                }
346
347                                FT_Int advance = (face->glyph->advance.x >> 6 ) + ( face->glyph->metrics.horiBearingX >> 6 );
348
349                                unsigned char* buffer = face->glyph->bitmap.buffer;
350
351                                if (!buffer)
352                                {
353                                        // Yuck, FT didn't detect this but generated a null pointer!
354                                        LogManager::getSingleton().logMessage("Info: Freetype returned null for character " +
355                                                StringConverter::toString(cp) + " in font " + mName);
356                                        continue;
357                                }
358
359                                int y_bearnig = ( max_bear >> 6 ) - ( face->glyph->metrics.horiBearingY >> 6 );
360
361                                for(int j = 0; j < face->glyph->bitmap.rows; j++ )
362                                {
363                                        size_t row = j + m + y_bearnig;
364                                        uchar* pDest = &imageData[(row * data_width) + l * pixel_bytes];
365                                        for(int k = 0; k < face->glyph->bitmap.width; k++ )
366                                        {
367                                                if (mAntialiasColour)
368                                                {
369                                                        // Use the same greyscale pixel for all components RGBA
370                                                        *pDest++= *buffer;
371                                                }
372                                                else
373                                                {
374                                                        // Always white whether 'on' or 'off' pixel, since alpha
375                                                        // will turn off
376                                                        *pDest++= 0xFF;
377                                                }
378                                                // Always use the greyscale value for alpha
379                                                *pDest++= *buffer++; 
380                                        }
381                                }
382
383                                this->setGlyphTexCoords(cp,
384                                        (Real)l / (Real)finalWidth,  // u1
385                                        (Real)m / (Real)finalHeight,  // v1
386                                        (Real)( l + ( face->glyph->advance.x >> 6 ) ) / (Real)finalWidth, // u2
387                                        ( m + ( max_height >> 6 ) ) / (Real)finalHeight, // v2
388                                        textureAspect
389                                        );
390
391                                // Advance a column
392                                l += (advance + char_spacer);
393
394                                // If at end of row
395                                if( finalWidth - 1 < l + ( advance ) )
396                                {
397                                        m += ( max_height >> 6 ) + char_spacer;
398                                        l = 0;
399                                }
400                        }
401                }
402
403        DataStreamPtr memStream(
404                        new MemoryDataStream(imageData, data_size, true));
405
406        Image img;
407                img.loadRawData( memStream, finalWidth, finalHeight, PF_BYTE_LA );
408
409                Texture* tex = static_cast<Texture*>(res);
410                // Call internal _loadImages, not loadImage since that's external and
411                // will determine load status etc again, and this is a manual loader inside load()
412                ConstImagePtrList imagePtrs;
413                imagePtrs.push_back(&img);
414                tex->_loadImages( imagePtrs );
415
416
417                FT_Done_FreeType(ftLibrary);
418    }
419        //-----------------------------------------------------------------------
420        //-----------------------------------------------------------------------
421        String Font::CmdType::doGet(const void* target) const
422        {
423                const Font* f = static_cast<const Font*>(target);
424                if (f->getType() == FT_TRUETYPE)
425                {
426                        return "truetype";
427                }
428                else
429                {
430                        return "image";
431                }
432        }
433        void Font::CmdType::doSet(void* target, const String& val)
434        {
435                Font* f = static_cast<Font*>(target);
436                if (val == "truetype")
437                {
438                        f->setType(FT_TRUETYPE);
439                }
440                else
441                {
442                        f->setType(FT_IMAGE);
443                }
444        }
445        //-----------------------------------------------------------------------
446        String Font::CmdSource::doGet(const void* target) const
447        {
448                const Font* f = static_cast<const Font*>(target);
449                return f->getSource();
450        }
451        void Font::CmdSource::doSet(void* target, const String& val)
452        {
453                Font* f = static_cast<Font*>(target);
454                f->setSource(val);
455        }
456        //-----------------------------------------------------------------------
457        String Font::CmdSize::doGet(const void* target) const
458        {
459                const Font* f = static_cast<const Font*>(target);
460                return StringConverter::toString(f->getTrueTypeSize());
461        }
462        void Font::CmdSize::doSet(void* target, const String& val)
463        {
464                Font* f = static_cast<Font*>(target);
465                f->setTrueTypeSize(StringConverter::parseReal(val));
466        }
467        //-----------------------------------------------------------------------
468        String Font::CmdResolution::doGet(const void* target) const
469        {
470                const Font* f = static_cast<const Font*>(target);
471                return StringConverter::toString(f->getTrueTypeResolution());
472        }
473        void Font::CmdResolution::doSet(void* target, const String& val)
474        {
475                Font* f = static_cast<Font*>(target);
476                f->setTrueTypeResolution(StringConverter::parseUnsignedInt(val));
477        }
478        //-----------------------------------------------------------------------
479        String Font::CmdCodePoints::doGet(const void* target) const
480        {
481                const Font* f = static_cast<const Font*>(target);
482                const CodePointRangeList& rangeList = f->getCodePointRangeList();
483                StringUtil::StrStreamType str;
484                for (CodePointRangeList::const_iterator i = rangeList.begin(); i != rangeList.end(); ++i)
485                {
486                        str << i->first << "-" << i->second << " ";
487                }
488                return str.str();
489        }
490        void Font::CmdCodePoints::doSet(void* target, const String& val)
491        {
492                // Format is "code_points start1-end1 start2-end2"
493                Font* f = static_cast<Font*>(target);
494
495                StringVector vec = StringUtil::split(val, " \t");
496                for (StringVector::iterator i = vec.begin(); i != vec.end(); ++i)
497                {
498                        String& item = *i;
499                        StringVector itemVec = StringUtil::split(item, "-");
500                        if (itemVec.size() == 2)
501                        {
502                                f->addCodePointRange(CodePointRange(
503                                        StringConverter::parseLong(itemVec[0]), 
504                                        StringConverter::parseLong(itemVec[1])));
505                        }
506                }
507        }
508
509
510}
Note: See TracBrowser for help on using the repository browser.