CGUITTFFont reloaded

Post those lines of code you feel like sharing or find what you require for your project here; or simply use them as tutorials.
Akabane87
Posts: 50
Joined: Sat May 05, 2012 6:11 pm

CGUITTFFont reloaded

Post by Akabane87 »

Hey guys,

some of you probably remember an old snippet provided there long time ago and then rewritten a bit by CuteAlien.

For my latest project I reused it and gave it a bit a refacto and a big speed optimization using a dynamic cache textures system. Finally I added an optionnal nice precomputed Gauss effect easily usable.

For complete files :
http://www.mediafire.com/view/2ggvcx4cw ... UITTFont.h
http://www.mediafire.com/view/q59u8djyu ... TTFont.cpp
for utils.cpp functions : see here http://irrlicht.sourceforge.net/forum/v ... 98#p287646

CGUITTFFont.h :

Code: Select all

 
#ifndef _GUI_FREETYPE_FONT_H
#define _GUI_FREETYPE_FONT_H
 
//! freetype support enabled with 1 and disabled with 0
#define COMPILE_WITH_FREETYPE 1
 
#define FONT_TEX_SIZE 512
#define DEFAULT_GAUSS_SIZE 10
 
 
#if COMPILE_WITH_FREETYPE
 
#include <ft2build.h>
#include <freetype/freetype.h>
#include <irrlicht.h>
 
class CGUITTFace : public irr::IReferenceCounted
{
public:
    CGUITTFace();
    virtual ~CGUITTFace();
 
    bool load(const irr::io::path& filename);
 
    FT_Face     face;               // handle to face
 
private:
    static int          countClassObjects;
    static FT_Library   library;    // handle to library
};
 
class CGUIFreetypeFont;
 
class CGUITTGlyph : public irr::IReferenceCounted
{
public:
    CGUITTGlyph();
    virtual ~CGUITTGlyph();
 
    bool cached;
    void cache(irr::u32 idx_, const CGUIFreetypeFont * freetypeFont, bool usePremultipliedAlpha);
 
    irr::u32 size;
    irr::s32 top;
    irr::s32 left;
    irr::u32 texw;
    irr::u32 texh;
    irr::u32 decalX;
    irr::u32 texIdx;
    irr::core::recti srcRect;
    irr::u32 texIdxGauss;
    irr::core::recti srcRectGauss;
 
private:
    
};
 
class CGUIFreetypeFont : public irr::gui::IGUIFont
{
    friend class CGUITTGlyph;
 
public:
 
    struct ColorElement
    {
        ColorElement(irr::video::SColor color, irr::u32 size) : Color(color), Size(size) {}
        ~ColorElement() {}
 
        irr::video::SColor Color;
        irr::u32 Size;
    };
 
    //! constructor
    CGUIFreetypeFont(irr::video::IVideoDriver* Driver);
 
    //! destructor
    virtual ~CGUIFreetypeFont();
 
    //! loads a truetype font file
    bool attach(CGUITTFace *Face,irr::u32 size,bool usePremultipliedAlpha = false,irr::u32 gaussLevel = 0);
 
    //! set the size of the font
    bool setSize(irr::u32 size, irr::u32 gaussLevel = 0);
 
    //! set the size of the font
    bool setGaussLevel(irr::u32 gaussLevel);
 
    //! get the size of the font
    irr::u32 getSize() const {return Size;}
 
    //! draws an text and clips it to the specified rectangle if wanted
    virtual void draw(const irr::core::stringw& textstring, const irr::core::rect<irr::s32>& position, 
        irr::video::SColor color, bool hcenter=false, bool vcenter=false, const irr::core::rect<irr::s32>* clip=0);
    
    void draw(const irr::core::stringw& textstring, const irr::core::rect<irr::s32>& position, 
        irr::video::SColor color, float scale = 1.0f, bool hcenter=false, bool vcenter=false, 
        irr::video::SColor backgroundColor = irr::video::SColor(0,0,0,0), const irr::core::rect<irr::s32>* clip=0);
 
    void draw(const irr::core::stringw& textstring, const irr::core::rect<irr::s32>& position, 
        const irr::core::array<ColorElement>& colorElements, float scale = 1.0f, bool hcenter=false, bool vcenter=false, 
        irr::video::SColor backgroundColor = irr::video::SColor(0,0,0,0), const irr::core::rect<irr::s32>* clip=0);
    
    void drawGauss(const irr::core::stringw& textstring, const irr::core::rect<irr::s32>& position, 
        irr::video::SColor color, bool hcenter=false, bool vcenter=false, const irr::core::rect<irr::s32>* clip=0);
    
    void drawGauss(const irr::core::stringw& textstring, const irr::core::rect<irr::s32>& position, 
        irr::video::SColor color, float scale = 1.0f, bool hcenter=false, bool vcenter=false, 
        irr::video::SColor backgroundColor = irr::video::SColor(0,0,0,0), const irr::core::rect<irr::s32>* clip=0);
 
    //! returns the dimension of a text
    virtual irr::core::dimension2d<irr::u32> getDimension(const wchar_t* text) const;
    irr::core::dimension2d<irr::u32> getDimension(const wchar_t* text, irr::core::array<irr::u32>& outLinesWidth) const;
 
    //! Calculates the index of the character in the text which is on a specific position.
    virtual irr::s32 getCharacterFromPos(const wchar_t* text, irr::s32 pixel_x) const;
 
    //! Not yet supported
    virtual void setKerningWidth (irr::s32 kerning) {}
 
    //! Not yet supported
    virtual void setKerningHeight (irr::s32 kerning) {}
 
    //! Not yet supported
    virtual irr::s32 getKerningWidth(const wchar_t* thisLetter=0, const wchar_t* previousLetter=0)  const { return 0; }
 
    //! Not yet supported
    virtual irr::s32 getKerningHeight() const { return 0; }
 
    //! Not yet supported
    virtual void setInvisibleCharacters( const wchar_t *s ) {}
 
    const irr::core::array<irr::video::ITexture*>& GetTexturePool() const {return TexturePool;}
    const irr::core::array<irr::video::ITexture*>& GetTexturePoolGauss() const {return TexturePoolGauss;}
 
    irr::u32 getWidthFromCharacter(wchar_t c) const;
 
 
    bool AntiAlias;
    bool Transparency;
 
protected:
    void clearGlyphs();
 
private:
    irr::u32 getGlyphByChar(wchar_t c) const;
    irr::video::IVideoDriver* Driver;
    irr::core::array< CGUITTGlyph* > Glyphs;
    CGUITTFace * TrueTypeFace;
    mutable irr::core::dimension2d<irr::u32> LargestGlyph;
 
    irr::u32 cacheNewGlyph(const irr::u32* const bits, irr::u32 width, irr::u32 height, irr::core::recti& outSrcRect) const;
    irr::u32 cacheNewGlyphGauss(const irr::u32* const bits, irr::u32 width, irr::u32 height, irr::core::recti& outSrcRect) const;
    mutable irr::core::array<irr::video::ITexture*> TexturePool;
    mutable irr::core::vector2di CurrentTextureOffset;
    mutable irr::u32 CurrentTextureLineHeight;
 
    mutable irr::core::array<irr::video::ITexture*> TexturePoolGauss;
    mutable irr::core::vector2di CurrentTextureGaussOffset;
    mutable irr::u32 CurrentTextureGaussLineHeight;
    mutable irr::core::array< irr::core::array<float> > GaussMatrix;
 
    irr::u32 Size;
 
    bool UsePremultipliedAlpha;
    irr::u32 GaussLevel;
 
    void setGlyphTextureFlags() const;
    void restoreTextureFlags() const;
 
    static bool mTexFlag16;
    static bool mTexFlag32;
    static bool mTexFlagMip;
};
 
#endif // #if COMPILE_WITH_FREETYPE
 
#endif  // _GUI_FREETYPE_FONT_H
 
 
CEGUITTFFont.cpp :

Code: Select all

 
#include "CGUITTFont.h"
#include "utils.h"
 
#if COMPILE_WITH_FREETYPE
 
#include <cassert>
 
using namespace irr;
using namespace gui;
 
#ifdef _MSC_VER
#pragma warning(disable: 4996)
#endif
 
 
// --------------------------------------------------------
CGUITTGlyph::CGUITTGlyph()
: IReferenceCounted()
,cached(false)
,size(0)
,top(0)
,left(0)
,texw(0)
,texh(0)
,texIdx(0xffffffff)
,srcRect(0,0,0,0)
{
}
 
CGUITTGlyph::~CGUITTGlyph()
{
}
 
//void CGUITTGlyph::cache(u32 idx_, CGUITTFace& ttFace_, video::IVideoDriver* driver_, irr::core::dimension2d<irr::u32> &largestSize)
void CGUITTGlyph::cache(u32 idx_, const CGUIFreetypeFont * freetypeFont, bool usePremultipliedAlpha/* = false*/)
{
    assert(freetypeFont);
    if(!freetypeFont)
        return;
 
    FT_Face face = freetypeFont->TrueTypeFace->face;
 
    FT_Set_Pixel_Sizes(face, 0, size);
    if ( size > freetypeFont->LargestGlyph.Height )
        freetypeFont->LargestGlyph.Height = size;
 
    if ( !FT_Load_Glyph(face, idx_, FT_LOAD_NO_HINTING|FT_LOAD_NO_BITMAP) )
    {
        FT_GlyphSlot glyph = face->glyph;
        FT_Bitmap  bits;
 
        if (glyph->format == ft_glyph_format_outline )
        {
            if (!FT_Render_Glyph( glyph, FT_RENDER_MODE_NORMAL))
            {
                bits = glyph->bitmap;
                u8 *pt = bits.buffer;
                top = glyph->bitmap_top;
                left = glyph->bitmap_left;
                texw = bits.width;
                texh = bits.rows;
                decalX = glyph->advance.x >> 6;
 
 
                s32 offx = left;
                s32 offy = size - top;
                if ( offx+texw > freetypeFont->LargestGlyph.Width )
                    freetypeFont->LargestGlyph.Width = offx+texw;
                if ( offy+texh > freetypeFont->LargestGlyph.Height )
                    freetypeFont->LargestGlyph.Height = offy+texh;
 
                u32 *texd = new u32[texw*texh];
                memset(texd,0,texw*texh*sizeof(u32));
                u32 *texp = texd;
                bool cflag = (freetypeFont->Driver->getDriverType() == video::EDT_DIRECT3D8) || usePremultipliedAlpha;
                for (int i = 0; i < bits.rows; i++)
                {
                    u32 *rowp = texp;
                    for (int j = 0;j < bits.width;j++)
                    {
                        if (*pt)
                        {
                            if (cflag)
                            {
                                *rowp = *pt;
                                *rowp *= 0x01010101;
                            }
                            else
                            {
                                *rowp = *pt << 24;
                                *rowp |= 0xffffff;
                            }
                        }
                        else
                        {
                            *rowp = 0;
                        }
                        pt++;
                        rowp++;
                    }
                    texp += texw;
                }
 
                texIdx = freetypeFont->cacheNewGlyph(texd, texw, texh, srcRect);
 
                u32 gaussLevel = freetypeFont->GaussLevel;
                if(gaussLevel > 1)
                {
                    u32 gaussBorder = (gaussLevel-1);
                    u32 texwgauss = texw + (gaussBorder<<1);
                    u32 texhgauss = texh + (gaussBorder<<1);
 
                    u32 *texgauss = new u32[texwgauss*texhgauss];
                    memset(texgauss,0,texwgauss*texhgauss*sizeof(u32));
                    u32 *texp = texgauss;
                    bool cflag = (freetypeFont->Driver->getDriverType() == video::EDT_DIRECT3D8) || usePremultipliedAlpha;
                    u8 *pt = bits.buffer;
                    for (int i = 0; i < (int)texhgauss; ++i)
                    {
                        u32 *rowp = texp;
                        for (int j = 0; j < (int)texwgauss; ++j)
                        {
                            float gausspxl = 0;
                            for(int k = -(int)gaussBorder; k <= (int)gaussBorder; ++k)
                            {
                                for(int l = -(int)gaussBorder; l <= (int)gaussBorder; ++l)
                                {
                                    float gaussFactor = freetypeFont->GaussMatrix[k+gaussBorder][l+gaussBorder];
                                    int srcY = i+k - gaussBorder;
                                    int srcX = j+l - gaussBorder;
                                    u32 srcpxl = (srcY >= 0 && srcY < (int)texh && srcX >= 0 && srcX < (int)texw)? pt[srcX+srcY*texw] : 0;
                                    gausspxl += (float)srcpxl/255*gaussFactor;
                                }
                            }
                            assert(gausspxl <= 1.0f);
                            if (gausspxl > 0.0f)
                            {
                                if (cflag)
                                {
                                    *rowp = (u32)(gausspxl*255);
                                    *rowp *= 0x01010101;
                                }
                                else
                                {
                                    *rowp = (u32)(gausspxl*255) << 24;
                                    *rowp |= 0xffffff;
                                }
                            }
                            else
                            {
                                *rowp = 0;
                            }
                            ++rowp;
                        }
                        texp += texwgauss;
                    }
 
                    texIdxGauss = freetypeFont->cacheNewGlyphGauss(texgauss, texwgauss, texhgauss, srcRectGauss);
                    delete[] texgauss;// thanks to zerochen for noticing it ;)
                }
 
                delete[] texd;
                cached = true;
            }
        }
    }
}
 
// --------------------------------------------------------
FT_Library  CGUITTFace::library  = 0;
int CGUITTFace::countClassObjects = 0;
 
CGUITTFace::CGUITTFace()
: face(0)
{
    ++countClassObjects;
}
 
CGUITTFace::~CGUITTFace()
{
    if ( face )
        FT_Done_Face( face );
 
    --countClassObjects;
    assert(countClassObjects >= 0 );
    if ( !countClassObjects && library )
    {
        FT_Done_FreeType( library );
        library = 0;
    }
}
 
//! loads a font file
bool CGUITTFace::load(const irr::io::path& filename)
{
    if ( !library )
    {
        if (FT_Init_FreeType( &library ))
        {
            return  false;
        }
    }
    core::stringc ansiFilename(filename);   // path can be anything but freetype can only work with ansi-filenames
    if (FT_New_Face( library,ansiFilename.c_str(),0,&face ))
    {
        return  false;
    }
    return  true;
}
 

Code: Select all

 
// --------------------------------------------------------
//! constructor
CGUIFreetypeFont::CGUIFreetypeFont(video::IVideoDriver* driver)
: Driver(driver)
, TrueTypeFace(0)
, LargestGlyph(0,0)
, Size(0)
, CurrentTextureOffset(0,0)
, CurrentTextureLineHeight(0)
{
#ifdef _DEBUG
    setDebugName("CGUIFreetypeFont");
#endif
 
    if (Driver)
        Driver->grab();
    AntiAlias = false;
    Transparency = false;
    UsePremultipliedAlpha = false;
}
 
 
 
//! destructor
bool CGUIFreetypeFont::mTexFlag16 = false;
bool CGUIFreetypeFont::mTexFlag32 = true;
bool CGUIFreetypeFont::mTexFlagMip = false;
 
void CGUIFreetypeFont::setGlyphTextureFlags() const
{
    mTexFlag16 = Driver->getTextureCreationFlag(video::ETCF_ALWAYS_16_BIT);
    mTexFlag32 = Driver->getTextureCreationFlag(video::ETCF_ALWAYS_32_BIT);
    mTexFlagMip = Driver->getTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS);
    Driver->setTextureCreationFlag(video::ETCF_ALWAYS_16_BIT,false);
    Driver->setTextureCreationFlag(video::ETCF_ALWAYS_32_BIT,true);
    Driver->setTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS, false);
}
 
void CGUIFreetypeFont::restoreTextureFlags() const
{
    Driver->setTextureCreationFlag(video::ETCF_ALWAYS_16_BIT, mTexFlag16);
    Driver->setTextureCreationFlag(video::ETCF_ALWAYS_32_BIT, mTexFlag32);
    Driver->setTextureCreationFlag(video::ETCF_CREATE_MIP_MAPS, mTexFlagMip);
}
 
 
CGUIFreetypeFont::~CGUIFreetypeFont()
{
    if ( TrueTypeFace )
        TrueTypeFace->drop();
    if (Driver)
        Driver->drop();
    clearGlyphs();
}
 
bool CGUIFreetypeFont::attach(CGUITTFace *Face,u32 size, bool usePremultipliedAlpha/* = false*/,u32 gaussLevel/* = 0*/)
{
    if (!Driver || !Face)
        return false;
 
    UsePremultipliedAlpha = usePremultipliedAlpha;
    GaussLevel = gaussLevel;
    if(GaussLevel == 1)
        ++GaussLevel;
    GaussMatrix.clear();
    if(GaussLevel > 1)
    {
        u32 gaussBorder = (GaussLevel-1);
        float sum = 0;
        for(int k = -(int)gaussBorder; k <= (int)gaussBorder; ++k)
        {
            core::array<float> line;
            for(int l = -(int)gaussBorder; l <= (int)gaussBorder; ++l)
            {
                float gaussFactor = MathUtils::Gaussian((float)k, 0, (float)gaussBorder/2) * 
                    MathUtils::Gaussian((float)l, 0, (float)gaussBorder/2);
                sum += gaussFactor;
                line.push_back(gaussFactor);
            }
            GaussMatrix.push_back(line);
        }
        for(u32 k = 0; k < GaussMatrix.size(); ++k)
            for(u32 l = 0; l < GaussMatrix[k].size(); ++l)
                GaussMatrix[k][l] /= sum;
    }
 
    Face->grab();
    if ( TrueTypeFace )
        TrueTypeFace->drop();
    TrueTypeFace = Face;
    if ( !TrueTypeFace || !TrueTypeFace->face )
        return false;
 
    Size = size;
 
    clearGlyphs();
    Glyphs.reallocate(TrueTypeFace->face->num_glyphs);
    Glyphs.set_used(TrueTypeFace->face->num_glyphs);
    for (int i = 0;i < TrueTypeFace->face->num_glyphs;i++)
    {
        CGUITTGlyph * glyph = new CGUITTGlyph();
 
        glyph->size = size;
 
        Glyphs[i] = glyph;
    }
 
    // TODO: this is a workaround to get a probably ok height for getDimensions. So we check a few extreme characters which usually make trouble.
    getGlyphByChar(L'A');
    getGlyphByChar(L'g');
    getGlyphByChar(L'.');
    getGlyphByChar(L'(');
 
    return  true;
}
 
bool CGUIFreetypeFont::setSize(u32 size, u32 gaussLevel/* = 0*/)
{
    if(Size == size && (gaussLevel == 0 || gaussLevel == GaussLevel))
        return true;
 
    if ( !TrueTypeFace )
        return false;
 
    Size = size;
    if(gaussLevel > 0 && gaussLevel != GaussLevel)
    {
        GaussLevel = gaussLevel;
        if(GaussLevel == 1)
            ++GaussLevel;
        GaussMatrix.clear();
        if(GaussLevel > 1)
        {
            u32 gaussBorder = (GaussLevel-1);
            float sum = 0;
            for(int k = -(int)gaussBorder; k <= (int)gaussBorder; ++k)
            {
                core::array<float> line;
                for(int l = -(int)gaussBorder; l <= (int)gaussBorder; ++l)
                {
                    float gaussFactor = MathUtils::Gaussian((float)k, 0, (float)gaussBorder/2) * 
                        MathUtils::Gaussian((float)l, 0, (float)gaussBorder/2);
                    sum += gaussFactor;
                    line.push_back(gaussFactor);
                }
                GaussMatrix.push_back(line);
            }
            for(u32 k = 0; k < GaussMatrix.size(); ++k)
                for(u32 l = 0; l < GaussMatrix[k].size(); ++l)
                    GaussMatrix[k][l] /= sum;
        }
    }
 
    clearGlyphs();
    LargestGlyph = irr::core::dimension2d<irr::u32>(0,0);
    Glyphs.reallocate(TrueTypeFace->face->num_glyphs);
    Glyphs.set_used(TrueTypeFace->face->num_glyphs);
    for (int i = 0;i < TrueTypeFace->face->num_glyphs;i++)
    {
        CGUITTGlyph * glyph = new CGUITTGlyph();
 
        glyph->size = size;
 
        Glyphs[i] = glyph;
    }
 
    // TODO: this is a workaround to get a probably ok height for getDimensions. So we check a few extreme characters which usually make trouble.
    getGlyphByChar(L'A');
    getGlyphByChar(L'g');
    getGlyphByChar(L'.');
    getGlyphByChar(L'(');
 
    return  true;
}
 
void CGUIFreetypeFont::clearGlyphs()
{
    for ( unsigned int i=0; i < Glyphs.size(); ++i )
    {
        if ( Glyphs[i] )
        {
            Glyphs[i]->drop();
        }
        Glyphs[i] = 0;
    }
 
    for(u32 i = 0; i < TexturePool.size(); ++i)
        TexturePool[i]->drop();
    TexturePool.clear();
    CurrentTextureLineHeight = 0;
    CurrentTextureOffset = irr::core::vector2di(0,0);
 
    for(u32 i = 0; i < TexturePoolGauss.size(); ++i)
        TexturePoolGauss[i]->drop();
    TexturePoolGauss.clear();
    CurrentTextureGaussLineHeight = 0;
    CurrentTextureGaussOffset = irr::core::vector2di(0,0);
}
 
u32 CGUIFreetypeFont::getGlyphByChar(wchar_t c) const
{
    u32 idx = FT_Get_Char_Index( TrueTypeFace->face, c );
    if ( idx && !Glyphs[idx - 1]->cached )
        Glyphs[idx - 1]->cache(idx, this, UsePremultipliedAlpha);
    return  idx;
}
 
irr::u32 CGUIFreetypeFont::cacheNewGlyph(const irr::u32* const bits, irr::u32 width, irr::u32 height, irr::core::recti& outSrcRect) const
{
    if(!bits)
        return 0xffffffff;
 
    if(CurrentTextureOffset.X + width >= FONT_TEX_SIZE)// need new line
    {
        CurrentTextureOffset.Y += CurrentTextureLineHeight;
        CurrentTextureOffset.X = 0;
        CurrentTextureLineHeight = 0;
    }
    if(CurrentTextureOffset.Y + height >= FONT_TEX_SIZE)// need new texture
    {
        CurrentTextureOffset = irr::core::vector2di(0,0);
        CurrentTextureLineHeight = 0;
    }
 
    if(CurrentTextureOffset == irr::core::vector2di(0,0))// create new empty texture
    {
        c8 name[128];
        sprintf(name,"ttf%d_%d_%p", Size, 0, this);
        video::IImage *img = Driver->createImage(video::ECF_A8R8G8B8,core::dimension2d<u32>(FONT_TEX_SIZE,FONT_TEX_SIZE));
        setGlyphTextureFlags();
        irr::video::ITexture* tex = Driver->addTexture(name,img);
        tex->grab();
        Driver->removeTexture(tex);
        TexturePool.push_back(tex);
        img->drop();
        restoreTextureFlags();
    }
 
    outSrcRect = irr::core::recti(CurrentTextureOffset, core::dimension2di(width, height));
 
    // write the pixels at the right place
    irr::u32 idx = TexturePool.size()-1;
    irr::video::ITexture* tex = TexturePool[idx];
    u32* dst = (u32*)tex->lock();
    dst += CurrentTextureOffset.Y*FONT_TEX_SIZE + CurrentTextureOffset.X;
    const u32* src = bits;
    for(u32 i = 0; i < height; ++i)
    {
        memcpy(dst, src, width*4);
        src += width;
        dst += FONT_TEX_SIZE;
    }
 
    tex->unlock();
 
    if(height > CurrentTextureLineHeight)
        CurrentTextureLineHeight = height;
    CurrentTextureOffset.X += width;
 
    return idx;
}
 

Code: Select all

 
irr::u32 CGUIFreetypeFont::cacheNewGlyphGauss(const irr::u32* const bits, irr::u32 width, irr::u32 height, irr::core::recti& outSrcRect) const
{
    if(!bits)
        return 0xffffffff;
 
    if(CurrentTextureGaussOffset.X + width >= FONT_TEX_SIZE)// need new line
    {
        CurrentTextureGaussOffset.Y += CurrentTextureGaussLineHeight;
        CurrentTextureGaussOffset.X = 0;
        CurrentTextureGaussLineHeight = 0;
    }
    if(CurrentTextureGaussOffset.Y + height >= FONT_TEX_SIZE)// need new texture
    {
        CurrentTextureGaussOffset = irr::core::vector2di(0,0);
        CurrentTextureGaussLineHeight = 0;
    }
 
    if(CurrentTextureGaussOffset == irr::core::vector2di(0,0))// create new empty texture
    {
        c8 name[128];
        sprintf(name,"ttf_gauss%d_%d_%p", Size, 0, this);
        video::IImage *img = Driver->createImage(video::ECF_A8R8G8B8,core::dimension2d<u32>(FONT_TEX_SIZE,FONT_TEX_SIZE));
        setGlyphTextureFlags();
        irr::video::ITexture* tex = Driver->addTexture(name,img);
        tex->grab();
        Driver->removeTexture(tex);
        TexturePoolGauss.push_back(tex);
        img->drop();
        restoreTextureFlags();
    }
 
    outSrcRect = irr::core::recti(CurrentTextureGaussOffset, core::dimension2di(width, height));
 
    // write the pixels at the right place
    irr::u32 idx = TexturePoolGauss.size()-1;
    irr::video::ITexture* tex = TexturePoolGauss[idx];
    u32* dst = (u32*)tex->lock();
    dst += CurrentTextureGaussOffset.Y*FONT_TEX_SIZE + CurrentTextureGaussOffset.X;
    const u32* src = bits;
    for(u32 i = 0; i < height; ++i)
    {
        memcpy(dst, src, width*4);
        src += width;
        dst += FONT_TEX_SIZE;
    }
 
    tex->unlock();
 
    if(height > CurrentTextureGaussLineHeight)
        CurrentTextureGaussLineHeight = height;
    CurrentTextureGaussOffset.X += width;
 
    return idx;
}
 
//! returns the dimension of a text
core::dimension2d<u32> CGUIFreetypeFont::getDimension(const wchar_t* text) const
{
    core::array<u32> outLinesWidth;
    return getDimension(text, outLinesWidth);
}
 
core::dimension2d<u32> CGUIFreetypeFont::getDimension(const wchar_t* text, core::array<u32>& outLinesWidth) const
{
    core::dimension2d<u32> dim(0, Glyphs[0]->size);
 
    u32 largestLine = 0;
    u32 nbLines = 1;
    for(const wchar_t* p = text; *p; ++p)
    {
        bool lineBreak = false;
        if (*p == L'\r') // Mac or Windows breaks
        {
            lineBreak = true;
            if (p[1] == L'\n') // Windows breaks
                ++p;
        }
        else if (*p == L'\n') // Unix breaks
        {
            lineBreak = true;
        }
        if (lineBreak)
        {
            ++nbLines;
            if(dim.Width > largestLine)
                largestLine = dim.Width;
            outLinesWidth.push_back(dim.Width);
            dim.Width = 0;
            continue;
        }
 
        dim.Width += getWidthFromCharacter(*p);
    }
 
    if(dim.Width > largestLine)
        largestLine = dim.Width;
    outLinesWidth.push_back(dim.Width);
 
    // TODO: The correct solution might be working with TrueTypeFace->height but I can't figure out how to use units_per_EM
    // even if I know which FT_Render_Mode I used. I'm sure there is some way to figure that out, but I have to give up for now.
    if ( TrueTypeFace && LargestGlyph.Height > dim.Height)
        dim.Height = LargestGlyph.Height;
 
    dim.Height *= nbLines;
    dim.Width = largestLine;
 
    return dim;
}
 
 
u32 CGUIFreetypeFont::getWidthFromCharacter(wchar_t c) const
{
    u32 n = getGlyphByChar(c);
    if ( n > 0)
    {
        int w = Glyphs[n - 1]->decalX;
        if (w > 0)
            return w;
    }
    if (c >= 0x2000)
    {
        return  Glyphs[0]->size;
    }
    else
    {
        return  Glyphs[0]->size / 2;
    }
}
 
 

Code: Select all

  
//! draws a text and clips it to the specified rectangle if wanted
void CGUIFreetypeFont::draw(const irr::core::stringw& textstring, const irr::core::rect<s32>& position, video::SColor color, bool hcenter, bool vcenter, const core::rect<s32>* clip)
{
    if (!Driver)
        return;
 
    s32 posWidth = position.getWidth();
    s32 posHeight = position.getHeight();
 
    core::dimension2d<s32> textDimension;
    core::position2d<s32> offset = position.UpperLeftCorner;
 
    const wchar_t * text = textstring.c_str();
    core::array<u32> linesWidth;
    if (hcenter || vcenter)
    {
        textDimension = getDimension(text, linesWidth);
 
        if (hcenter)
            offset.X = ((position.getWidth() - textDimension.Width)>>1) + offset.X;
 
        if (vcenter)
            offset.Y = ((position.getHeight() - textDimension.Height)>>1) + offset.Y;
    }
 
    if(posWidth > 0 && offset.X < position.UpperLeftCorner.X)
        offset.X = position.UpperLeftCorner.X;
    if(posHeight > 0 && offset.Y < position.UpperLeftCorner.Y)
        offset.Y = position.UpperLeftCorner.Y;
 
    s32 originX = offset.X;
    u32 n;
 
    irr::core::array< irr::core::array<S2DImageData> > dataPool;
    for(u32 i = 0; i < TexturePool.size(); ++i)// create as many entry in the dataPool as we have entries in the TexturePool
    {
        irr::core::array<S2DImageData> data;
        dataPool.push_back(data);
    }
 
    u32 lineNb = 0;
    if(hcenter)
        offset.X += (lineNb < linesWidth.size())? (textDimension.Width - linesWidth[lineNb])>>1 : 0;
    while(*text)
    {
        bool lineBreak = false;
        if (*text == L'\r') // Mac or Windows breaks
        {
            lineBreak = true;
            if (text[1] == L'\n') // Windows breaks
                ++text;
        }
        else if (*text == L'\n') // Unix breaks
        {
            lineBreak = true;
        }
        if (lineBreak)
        {
            offset.Y += LargestGlyph.Height;
            offset.X = originX;
            ++lineNb;
            if(hcenter)
                offset.X += (lineNb < linesWidth.size())? (textDimension.Width - linesWidth[lineNb])>>1 : 0;
            ++text;
            continue;
        }
 
        n = getGlyphByChar(*text);
        s32 characWidth = getWidthFromCharacter(*text);
        if ( n > 0)
        {
            if(posWidth <= 0 || offset.X + characWidth <= position.LowerRightCorner.X)
            {
                CGUITTGlyph* glyph = Glyphs[n-1];
                if (Driver->getDriverType() != video::EDT_SOFTWARE)
                {
                    if (!Transparency)
                        color.color |= 0xff000000;
                    {
                        S2DImageData data;
                        float posX = (float)glyph->texw*0.5f+offset.X+glyph->left;
                        float posY = (float)glyph->texh*0.5f+offset.Y+glyph->size - glyph->top;
                        data.DestMatrix = core::matrix4().setTranslation(core::vector3df(posX, posY, 0.0f))/* *
                            core::matrix4().setRotationAxisRadians(0.0f, core::vector3df(0.0f, 0.0f, 1.0f)) *
                            core::matrix4().setScale(core::vector3df(scale, scale, 0.0f))*/;
                        data.SourceRect = glyph->srcRect;
                        data.VertexColor = color;
                        dataPool[glyph->texIdx].push_back(data);
 
                    }
                }
            }
        }
        offset.X += characWidth;
 
        ++text;
    }
 
    for(u32 i = 0; i < dataPool.size(); ++i)
    {
        draw2DImageBatch(Driver, TexturePool[i], dataPool[i], true, false, UsePremultipliedAlpha);
    }
}
 
void CGUIFreetypeFont::draw(const irr::core::stringw& textstring, const irr::core::rect<s32>& position, video::SColor color, float scale, bool hcenter, bool vcenter, video::SColor backgroundColor, const core::rect<s32>* clip)
{
    if (!Driver)
        return;
 
    s32 posWidth = position.getWidth();
    s32 posHeight = position.getHeight();
 
    core::dimension2d<s32> textDimension;
    core::position2d<s32> offset = position.UpperLeftCorner;
    
    const wchar_t * text = textstring.c_str();
    core::array<u32> linesWidth;
    if (hcenter || vcenter)
    {
        textDimension = getDimension(text, linesWidth);
 
        if (hcenter)
            offset.X += (position.getWidth() - (s32)((float)textDimension.Width*scale))>>1;
 
        if (vcenter)
            offset.Y += (position.getHeight() - (s32)((float)textDimension.Height*scale))>>1;
    }
 
    if(posWidth > 0 && offset.X < position.UpperLeftCorner.X)
        offset.X = position.UpperLeftCorner.X;
    if(posHeight > 0 && offset.Y < position.UpperLeftCorner.Y)
        offset.Y = position.UpperLeftCorner.Y;
 
    if(backgroundColor.getAlpha() > 0)
    {
        if(textDimension == core::dimension2d<s32>())
            textDimension = getDimension(text, linesWidth);
 
        const s32 margin = (s32)(scale*0.2f*Size);
        fill2DRect(Driver, 
            Rectangle2(offset.X-margin, offset.Y-margin, offset.X+textDimension.Width+margin, offset.Y+textDimension.Height+margin), 
            backgroundColor, backgroundColor, backgroundColor, backgroundColor, UsePremultipliedAlpha);
    }
 
    s32 originX = offset.X;
    u32 n;
 
    irr::core::array< irr::core::array<S2DImageData> > dataPool;
    for(u32 i = 0; i < TexturePool.size(); ++i)// create as many entry in the dataPool as we have entries in the TexturePool
    {
        irr::core::array<S2DImageData> data;
        dataPool.push_back(data);
    }
 
    u32 lineNb = 0;
    if(hcenter)
        offset.X += (lineNb < linesWidth.size())? (s32)(scale*0.5f*(textDimension.Width - linesWidth[lineNb])) : 0;
    while(*text)
    {
        bool lineBreak = false;
        if (*text == L'\r') // Mac or Windows breaks
        {
            lineBreak = true;
            if (text[1] == L'\n') // Windows breaks
                ++text;
        }
        else if (*text == L'\n') // Unix breaks
        {
            lineBreak = true;
        }
        if (lineBreak)
        {
            offset.Y += (s32)(scale*LargestGlyph.Height);
            offset.X = originX;
            ++lineNb;
            if(hcenter)
                offset.X += (lineNb < linesWidth.size())? (s32)(scale*0.5f*(textDimension.Width - linesWidth[lineNb])) : 0;
            ++text;
            continue;
        }
 
        n = getGlyphByChar(*text);
        s32 characWidth = (s32)((float)getWidthFromCharacter(*text)*scale);
        if ( n > 0)
        {
            if(posWidth <= 0 || offset.X + characWidth <= position.LowerRightCorner.X)
            {
                CGUITTGlyph* glyph = Glyphs[n-1];
                if (Driver->getDriverType() != video::EDT_SOFTWARE)
                {
                    if (!Transparency)
                        color.color |= 0xff000000;
                    {
                        core::vector2df(scale, scale), true, color, false, UsePremultipliedAlpha);
 
                        S2DImageData data;
                        float posX = (float)glyph->texw*0.5f*scale+offset.X+(float)glyph->left*scale;
                        float posY = (float)glyph->texh*0.5f*scale+offset.Y+(float)(glyph->size - glyph->top)*scale;
                        data.DestMatrix = core::matrix4().setTranslation(core::vector3df(posX, posY, 0.0f)) *
                            /*core::matrix4().setRotationAxisRadians(0.0f, core::vector3df(0.0f, 0.0f, 1.0f)) **/
                            core::matrix4().setScale(core::vector3df(scale, scale, 0.0f));
                        data.SourceRect = glyph->srcRect;
                        data.VertexColor = color;
                        dataPool[glyph->texIdx].push_back(data);
                    }
                }
            }
        }
        offset.X += characWidth;
 
        ++text;
    }
 
    for(u32 i = 0; i < dataPool.size(); ++i)
    {
        draw2DImageBatch(Driver, TexturePool[i], dataPool[i], true, false, UsePremultipliedAlpha);
    }
}
 

Code: Select all

 
void CGUIFreetypeFont::draw(const irr::core::stringw& textstring, const irr::core::rect<irr::s32>& position, 
          const irr::core::array<ColorElement>& colorElements, float scale/* = 1.0f*/, bool hcenter/*=false*/, bool vcenter/*=false*/, 
          irr::video::SColor backgroundColor/* = irr::video::SColor(0,0,0,0)*/, const irr::core::rect<irr::s32>* clip/*=0*/)
{
    if (!Driver)
        return;
 
    s32 posWidth = position.getWidth();
    s32 posHeight = position.getHeight();
 
    irr::video::SColor defaultColor(255,255,255,255);
    irr::video::SColor currentColor(defaultColor);
    s32 currentLetterCount = 0;
    s32 currentColorIndex = -1;
 
    core::dimension2d<s32> textDimension;
    core::position2d<s32> offset = position.UpperLeftCorner;
 
    const wchar_t * text = textstring.c_str();
    core::array<u32> linesWidth;
    if (hcenter || vcenter)
    {
        textDimension = getDimension(text, linesWidth);
 
        if (hcenter)
            offset.X += (position.getWidth() - (s32)((float)textDimension.Width*scale))>>1;
 
        if (vcenter)
            offset.Y += (position.getHeight() - (s32)((float)textDimension.Height*scale))>>1;
    }
 
    if(posWidth > 0 && offset.X < position.UpperLeftCorner.X)
        offset.X = position.UpperLeftCorner.X;
    if(posHeight > 0 && offset.Y < position.UpperLeftCorner.Y)
        offset.Y = position.UpperLeftCorner.Y;
 
    if(backgroundColor.getAlpha() > 0)
    {
        if(textDimension == core::dimension2d<s32>())
            textDimension = getDimension(text, linesWidth);
 
        const s32 margin = (s32)(scale*0.2f*Size);
        fill2DRect(Driver, 
            Rectangle2(offset.X-margin, offset.Y-margin, offset.X+textDimension.Width+margin, offset.Y+textDimension.Height+margin), 
            backgroundColor, backgroundColor, backgroundColor, backgroundColor, UsePremultipliedAlpha);
    }
 
    s32 originX = offset.X;
    u32 n;
 
    irr::core::array< irr::core::array<S2DImageData> > dataPool;
    for(u32 i = 0; i < TexturePool.size(); ++i)// create as many entry in the dataPool as we have entries in the TexturePool
    {
        irr::core::array<S2DImageData> data;
        dataPool.push_back(data);
    }
 
    u32 lineNb = 0;
    if(hcenter)
        offset.X += (lineNb < linesWidth.size())? (s32)(scale*0.5f*(textDimension.Width - linesWidth[lineNb])) : 0;
    while(*text)
    {
        while(currentLetterCount <= 0)
        {
            if(currentColorIndex < (s32)colorElements.size())
            {
                ++currentColorIndex;
                if((u32)currentColorIndex < colorElements.size())// take the next color for the next {Size} letters
                {
                    currentColor = colorElements[currentColorIndex].Color;
                    currentLetterCount = (s32)colorElements[currentColorIndex].Size;
                }
                else// no more colors : use default one until the end of the string
                {
                    currentColor = defaultColor;
                    currentLetterCount = textstring.size();
                }
            }
            else// no more colors : use default one until the end of the string
            {
                currentColor = defaultColor;
                currentLetterCount = textstring.size();
            }
        }
 
        bool lineBreak = false;
        if (*text == L'\r') // Mac or Windows breaks
        {
            lineBreak = true;
            if (text[1] == L'\n') // Windows breaks
            {
                --currentLetterCount;
                ++text;
            }
        }
        else if (*text == L'\n') // Unix breaks
        {
            lineBreak = true;
        }
        if (lineBreak)
        {
            offset.Y += (s32)(scale*LargestGlyph.Height);
            offset.X = originX;
            ++lineNb;
            if(hcenter)
                offset.X += (lineNb < linesWidth.size())? (s32)(scale*0.5f*(textDimension.Width - linesWidth[lineNb])) : 0;
            --currentLetterCount;
            ++text;
            continue;
        }
 
        n = getGlyphByChar(*text);
        s32 characWidth = (s32)((float)getWidthFromCharacter(*text)*scale);
        if ( n > 0)
        {
            if(posWidth <= 0 || offset.X + characWidth <= position.LowerRightCorner.X)
            {
                CGUITTGlyph* glyph = Glyphs[n-1];
                if (Driver->getDriverType() != video::EDT_SOFTWARE)
                {
                    if (!Transparency)
                        currentColor.color |= 0xff000000;
                    {
                        S2DImageData data;
                        float posX = (float)glyph->texw*0.5f*scale+offset.X+(float)glyph->left*scale;
                        float posY = (float)glyph->texh*0.5f*scale+offset.Y+(float)(glyph->size - glyph->top)*scale;
                        data.DestMatrix = core::matrix4().setTranslation(core::vector3df(posX, posY, 0.0f)) *
                            /*core::matrix4().setRotationAxisRadians(0.0f, core::vector3df(0.0f, 0.0f, 1.0f)) **/
                            core::matrix4().setScale(core::vector3df(scale, scale, 0.0f));
                        data.SourceRect = glyph->srcRect;
                        data.VertexColor = currentColor;
                        dataPool[glyph->texIdx].push_back(data);
                    }
                }
            }
        }
        offset.X += characWidth;
 
        --currentLetterCount;
        ++text;
    }
 
    for(u32 i = 0; i < dataPool.size(); ++i)
    {
        draw2DImageBatch(Driver, TexturePool[i], dataPool[i], true, false, UsePremultipliedAlpha);
    }
}
 
void CGUIFreetypeFont::drawGauss(const irr::core::stringw& textstring, const irr::core::rect<s32>& position, video::SColor color, bool hcenter, bool vcenter, const core::rect<s32>* clip)
{
    if (!Driver || GaussLevel <= 1)
        return;
 
    s32 posWidth = position.getWidth();
    s32 posHeight = position.getHeight();
 
    core::dimension2d<s32> textDimension;
    core::position2d<s32> offset = position.UpperLeftCorner;
 
    const wchar_t * text = textstring.c_str();
    core::array<u32> linesWidth;
    if (hcenter || vcenter)
    {
        textDimension = getDimension(text, linesWidth);
 
        if (hcenter)
            offset.X = ((position.getWidth() - textDimension.Width)>>1) + offset.X;
 
        if (vcenter)
            offset.Y = ((position.getHeight() - textDimension.Height)>>1) + offset.Y;
    }
 
    if(posWidth > 0 && offset.X < position.UpperLeftCorner.X)
        offset.X = position.UpperLeftCorner.X;
    if(posHeight > 0 && offset.Y < position.UpperLeftCorner.Y)
        offset.Y = position.UpperLeftCorner.Y;
 
    s32 originX = offset.X;
    u32 n;
 
    irr::core::array< irr::core::array<S2DImageData> > dataPool;
    for(u32 i = 0; i < TexturePoolGauss.size(); ++i)// create as many entry in the dataPool as we have entries in the TexturePool
    {
        irr::core::array<S2DImageData> data;
        dataPool.push_back(data);
    }
 
    u32 lineNb = 0;
    if(hcenter)
        offset.X += (lineNb < linesWidth.size())? (textDimension.Width - linesWidth[lineNb])>>1 : 0;
    while(*text)
    {
        bool lineBreak = false;
        if (*text == L'\r') // Mac or Windows breaks
        {
            lineBreak = true;
            if (text[1] == L'\n') // Windows breaks
                ++text;
        }
        else if (*text == L'\n') // Unix breaks
        {
            lineBreak = true;
        }
        if (lineBreak)
        {
            offset.Y += LargestGlyph.Height;
            offset.X = originX;
            ++lineNb;
            if(hcenter)
                offset.X += (lineNb < linesWidth.size())? (textDimension.Width - linesWidth[lineNb])>>1 : 0;
            ++text;
            continue;
        }
 
        n = getGlyphByChar(*text);
        s32 characWidth = getWidthFromCharacter(*text);
        if ( n > 0)
        {
            if(posWidth <= 0 || offset.X + characWidth <= position.LowerRightCorner.X)
            {
                CGUITTGlyph* glyph = Glyphs[n-1];
                if (Driver->getDriverType() != video::EDT_SOFTWARE)
                {
                    if (!Transparency)
                        color.color |= 0xff000000;
                    {
                        S2DImageData data;
                        float posX = (float)glyph->texw*0.5f+offset.X+glyph->left;
                        float posY = (float)glyph->texh*0.5f+offset.Y+glyph->size - glyph->top;
                        data.DestMatrix = core::matrix4().setTranslation(core::vector3df(posX, posY, 0.0f))/* *
                            core::matrix4().setRotationAxisRadians(0.0f, core::vector3df(0.0f, 0.0f, 1.0f)) *
                            core::matrix4().setScale(core::vector3df(scale, scale, 0.0f))*/;
                        data.SourceRect = glyph->srcRectGauss;
                        data.VertexColor = color;
                        dataPool[glyph->texIdxGauss].push_back(data);
 
                    }
                }
            }
        }
        offset.X += characWidth;
 
        ++text;
    }
 
    for(u32 i = 0; i < dataPool.size(); ++i)
    {
        draw2DImageBatch(Driver, TexturePoolGauss[i], dataPool[i], true, false, UsePremultipliedAlpha);
    }
}
 

Code: Select all

 
void CGUIFreetypeFont::drawGauss(const irr::core::stringw& textstring, const irr::core::rect<s32>& position, video::SColor color, float scale, bool hcenter, bool vcenter, video::SColor backgroundColor, const core::rect<s32>* clip)
{
    if (!Driver || GaussLevel <= 1)
        return;
 
    s32 posWidth = position.getWidth();
    s32 posHeight = position.getHeight();
 
    core::dimension2d<s32> textDimension;
    core::position2d<s32> offset = position.UpperLeftCorner;
 
    const wchar_t * text = textstring.c_str();
    core::array<u32> linesWidth;
    if (hcenter || vcenter)
    {
        textDimension = getDimension(text, linesWidth);
 
        if (hcenter)
            offset.X += (position.getWidth() - (s32)((float)textDimension.Width*scale))>>1;
 
        if (vcenter)
            offset.Y += (position.getHeight() - (s32)((float)textDimension.Height*scale))>>1;
    }
 
    if(posWidth > 0 && offset.X < position.UpperLeftCorner.X)
        offset.X = position.UpperLeftCorner.X;
    if(posHeight > 0 && offset.Y < position.UpperLeftCorner.Y)
        offset.Y = position.UpperLeftCorner.Y;
 
    if(backgroundColor.getAlpha() > 0)
    {
        if(textDimension == core::dimension2d<s32>())
            textDimension = getDimension(text, linesWidth);
 
        const s32 margin = (s32)(scale*0.2f*Size);
        fill2DRect(Driver, 
            Rectangle2(offset.X-margin, offset.Y-margin, offset.X+textDimension.Width+margin, offset.Y+textDimension.Height+margin), 
            backgroundColor, backgroundColor, backgroundColor, backgroundColor, UsePremultipliedAlpha);
    }
 
    s32 originX = offset.X;
    u32 n;
 
    irr::core::array< irr::core::array<S2DImageData> > dataPool;
    for(u32 i = 0; i < TexturePoolGauss.size(); ++i)// create as many entry in the dataPool as we have entries in the TexturePool
    {
        irr::core::array<S2DImageData> data;
        dataPool.push_back(data);
    }
 
    u32 lineNb = 0;
    if(hcenter)
        offset.X += (lineNb < linesWidth.size())? (s32)(scale*0.5f*(textDimension.Width - linesWidth[lineNb])) : 0;
    while(*text)
    {
        bool lineBreak = false;
        if (*text == L'\r') // Mac or Windows breaks
        {
            lineBreak = true;
            if (text[1] == L'\n') // Windows breaks
                ++text;
        }
        else if (*text == L'\n') // Unix breaks
        {
            lineBreak = true;
        }
        if (lineBreak)
        {
            offset.Y += (s32)(scale*LargestGlyph.Height);
            offset.X = originX;
            ++lineNb;
            if(hcenter)
                offset.X += (lineNb < linesWidth.size())? (s32)(scale*0.5f*(textDimension.Width - linesWidth[lineNb])) : 0;
            ++text;
            continue;
        }
 
        n = getGlyphByChar(*text);
        s32 characWidth = (s32)((float)getWidthFromCharacter(*text)*scale);
        if ( n > 0)
        {
            if(posWidth <= 0 || offset.X + characWidth <= position.LowerRightCorner.X)
            {
                CGUITTGlyph* glyph = Glyphs[n-1];
                if (Driver->getDriverType() != video::EDT_SOFTWARE)
                {
                    if (!Transparency)
                        color.color |= 0xff000000;
                    {
                        S2DImageData data;
                        float posX = (float)glyph->texw*0.5f*scale+offset.X+(float)glyph->left*scale;
                        float posY = (float)glyph->texh*0.5f*scale+offset.Y+(float)(glyph->size - glyph->top)*scale;
                        data.DestMatrix = core::matrix4().setTranslation(core::vector3df(posX, posY, 0.0f)) *
                            /*core::matrix4().setRotationAxisRadians(0.0f, core::vector3df(0.0f, 0.0f, 1.0f)) **/
                            core::matrix4().setScale(core::vector3df(scale, scale, 0.0f));
                        data.SourceRect = glyph->srcRectGauss;
                        data.VertexColor = color;
                        dataPool[glyph->texIdxGauss].push_back(data);
                    }
                }
            }
        }
        offset.X += characWidth;
 
        ++text;
    }
 
    for(u32 i = 0; i < dataPool.size(); ++i)
    {
        draw2DImageBatch(Driver, TexturePoolGauss[i], dataPool[i], true, false, UsePremultipliedAlpha);
    }
}
 
//! Calculates the index of the character in the text which is on a specific position.
s32 CGUIFreetypeFont::getCharacterFromPos(const wchar_t* text, s32 pixel_x) const
{
    s32 x = 0;
    s32 idx = 0;
 
    while (text[idx])
    {
        x += getWidthFromCharacter(text[idx]);
 
        if (x >= pixel_x)
            return idx;
 
        ++idx;
    }
 
    return -1;
}
 
#endif // #if COMPILE_WITH_FREETYPE
 
 
Last edited by Akabane87 on Thu Aug 21, 2014 9:03 am, edited 8 times in total.
Akabane87
Posts: 50
Joined: Sat May 05, 2012 6:11 pm

Re: CGUITTFFont reloaded

Post by Akabane87 »

and finally my custom built render functions defined in utils.h and declared in utils.cpp :

Code: Select all

 
struct S2DImageData
{
    irr::core::rect<irr::s32> SourceRect;
    irr::core::matrix4 DestMatrix;
    irr::video::SColor VertexColor;
};
 
void draw2DImageBatch(irr::video::IVideoDriver *driver, irr::video::ITexture* texture, const irr::core::array<S2DImageData>& imageData, bool useAlphaChannel, bool useFiltering/* = true*/, bool usePremultipliedAlpha/* = false*/)
{
    if(imageData.empty())
        return;
 
    //irr::video::SMaterial material;
    irr::video::SMaterial& material = driver->getMaterial2D();
 
    // Store and clear the world matrix
    irr::core::matrix4 oldWorldMat = driver->getTransform(irr::video::ETS_WORLD);
    // Store and clear the projection matrix
    irr::core::matrix4 oldProjMat = driver->getTransform(irr::video::ETS_PROJECTION);
    // Store and clear the view matrix
    irr::core::matrix4 oldViewMat = driver->getTransform(irr::video::ETS_VIEW);
 
    core::matrix4 m;
    // this fixes some problems with pixel exact rendering, but also breaks nice texturing
    driver->setTransform(irr::video::ETS_WORLD, m);
 
    // adjust the view such that pixel center aligns with texels
    // Otherwise, subpixel artifacts will occur
    if(driver->getDriverType() == video::EDT_DIRECT3D9)
        m.setTranslation(core::vector3df(-0.5f,-0.5f,0));
    driver->setTransform(irr::video::ETS_VIEW, m);
 
    const core::dimension2d<u32>& renderTargetSize = driver->getCurrentRenderTargetSize();
    m.buildProjectionMatrixOrthoLH(f32(renderTargetSize.Width), f32(-(s32)(renderTargetSize.Height)), -1.0, 1.0);
    m.setTranslation(core::vector3df(-1,1,0));
    driver->setTransform(irr::video::ETS_PROJECTION, m);
 
    irr::core::array<irr::video::S3DVertex> vertices;
    irr::core::array<irr::u16> indices;
 
    for(u32 i = 0; i < imageData.size(); ++i)
    {
        const S2DImageData& data = imageData[i];
        // Find the positions of corners
        irr::core::vector3df corner[4];
 
        const irr::core::rect<irr::s32>& sourceRect = data.SourceRect;
        f32 halfWidth = (f32)(sourceRect.getWidth())*0.5f;
        f32 halfHeight = (f32)(sourceRect.getHeight())*0.5f;
        corner[0] = irr::core::vector3df(0.0f - halfWidth, 0.0f - halfHeight, 0.0f);
        corner[1] = irr::core::vector3df(0.0f + halfWidth, 0.0f - halfHeight, 0.0f);
        corner[2] = irr::core::vector3df(0.0f - halfWidth, 0.0f + halfHeight, 0.0f);
        corner[3] = irr::core::vector3df(0.0f + halfWidth, 0.0f + halfHeight, 0.0f);
        for (int x = 0; x < 4; x++)
        {
            data.DestMatrix.transformVect(corner[x]);
        }
 
        // Find the uv coordinates of the sourceRect
        irr::core::vector2df uvCorner[4];
        uvCorner[0] = irr::core::vector2df((f32)sourceRect.UpperLeftCorner.X,(f32)sourceRect.UpperLeftCorner.Y);
        uvCorner[1] = irr::core::vector2df((f32)sourceRect.LowerRightCorner.X,(f32)sourceRect.UpperLeftCorner.Y);
        uvCorner[2] = irr::core::vector2df((f32)sourceRect.UpperLeftCorner.X,(f32)sourceRect.LowerRightCorner.Y);
        uvCorner[3] = irr::core::vector2df((f32)sourceRect.LowerRightCorner.X,(f32)sourceRect.LowerRightCorner.Y);
        const irr::core::dimension2d<u32>& texSize = texture->getOriginalSize();
        for (int x = 0; x < 4; x++)
        {
            float uvX = uvCorner[x].X/(float)texSize.Width;
            float uvY = uvCorner[x].Y/(float)texSize.Height;
            uvCorner[x] = irr::core::vector2df(uvX,uvY);
        }
 
        // Vertices for the image
        irr::video::S3DVertex vertex;
        irr::u16 base = vertices.size();
        indices.push_back(base);
        indices.push_back(base+1);
        indices.push_back(base+2);
        indices.push_back(base+3);
        indices.push_back(base+2);
        indices.push_back(base+1);
 
        for (int x = 0; x < 4; x++)
        {
            vertex.Pos = corner[x];
            vertex.TCoords = uvCorner[x];
            vertex.Color.color = usePremultipliedAlpha? PremultiplyAlpha(data.VertexColor.color) : data.VertexColor.color;
            vertices.push_back(vertex);
        }
    }
 
    //driver->enableMaterial2D(true);
    material.Lighting = false;
    material.ZWriteEnable = false;
    material.ZBuffer = false;
    material.BackfaceCulling = false;
    //material.AntiAliasing = true;
    material.TextureLayer[0].Texture = texture;
    // Fix dark border appearance when texture scaled down with bilinear or trilinear filter on
    material.TextureLayer[0].TextureWrapU = video::ETC_CLAMP;
    material.TextureLayer[0].TextureWrapV = video::ETC_CLAMP;
 
    if(usePremultipliedAlpha)
        material.MaterialTypeParam = irr::video::pack_textureBlendFunc(irr::video::EBF_ONE, irr::video::EBF_ONE_MINUS_SRC_ALPHA, irr::video::EMFN_MODULATE_1X, irr::video::EAS_TEXTURE | irr::video::EAS_VERTEX_COLOR);
    else
        material.MaterialTypeParam = irr::video::pack_textureBlendFunc(irr::video::EBF_SRC_ALPHA, irr::video::EBF_ONE_MINUS_SRC_ALPHA, irr::video::EMFN_MODULATE_1X, irr::video::EAS_TEXTURE | irr::video::EAS_VERTEX_COLOR);
 
    if (useAlphaChannel)
        material.MaterialType = irr::video::EMT_ONETEXTURE_BLEND;
    else
        material.MaterialType = irr::video::EMT_SOLID;
 
    material.TextureLayer[0].BilinearFilter = false;
    material.TextureLayer[0].TrilinearFilter = useFiltering;
 
    driver->setMaterial(material);
    //driver->draw2DVertexPrimitiveList(&vertices[0],4,&indices[0],2);
    driver->drawIndexedTriangleList(&vertices[0],4*imageData.size(),&indices[0],2*imageData.size());
 
    //driver->enableMaterial2D(false);
 
    // Restore projection and view matrices
    driver->setTransform(irr::video::ETS_WORLD,oldWorldMat);
    driver->setTransform(irr::video::ETS_PROJECTION,oldProjMat);
    driver->setTransform(irr::video::ETS_VIEW,oldViewMat);
}
 
void fill2DRect(irr::video::IVideoDriver *driver, const irr::core::rect<s32>& position, irr::video::SColor colorLeftUp, irr::video::SColor colorRightUp, irr::video::SColor colorLeftDown, irr::video::SColor colorRightDown, bool usePremultipliedAlpha/* = false*/)
{
    //irr::video::SMaterial material;
    irr::video::SMaterial& material = driver->getMaterial2D();
 
    if(usePremultipliedAlpha)
    {
        colorLeftUp.color = PremultiplyAlpha(colorLeftUp.color);
        colorRightUp.color = PremultiplyAlpha(colorRightUp.color);
        colorLeftDown.color = PremultiplyAlpha(colorLeftDown.color);
        colorRightDown.color = PremultiplyAlpha(colorRightDown.color);
    }
 
    video::SColor colors[4] = {colorLeftUp, colorRightUp, colorLeftDown, colorRightDown};
 
    // Store and clear the world matrix
    irr::core::matrix4 oldWorldMat = driver->getTransform(irr::video::ETS_WORLD);
    // Store and clear the projection matrix
    irr::core::matrix4 oldProjMat = driver->getTransform(irr::video::ETS_PROJECTION);
    // Store and clear the view matrix
    irr::core::matrix4 oldViewMat = driver->getTransform(irr::video::ETS_VIEW);
 
    core::matrix4 m;
    // this fixes some problems with pixel exact rendering, but also breaks nice texturing
    driver->setTransform(irr::video::ETS_WORLD, m);
 
    // adjust the view such that pixel center aligns with texels
    // Otherwise, subpixel artifacts will occur
    if(driver->getDriverType() == video::EDT_DIRECT3D9)
        m.setTranslation(core::vector3df(-0.5f,-0.5f,0));
    driver->setTransform(irr::video::ETS_VIEW, m);
 
    const core::dimension2d<u32>& renderTargetSize = driver->getCurrentRenderTargetSize();
    m.buildProjectionMatrixOrthoLH(f32(renderTargetSize.Width), f32(-(s32)(renderTargetSize.Height)), -1.0, 1.0);
    m.setTranslation(core::vector3df(-1,1,0));
    driver->setTransform(irr::video::ETS_PROJECTION, m);
 
 
    // Find the positions of corners
    irr::core::vector3df corner[4];
 
    f32 halfWidth = (f32)(position.getWidth())*0.5f;
    f32 halfHeight = (f32)(position.getHeight())*0.5f;
    corner[0] = irr::core::vector3df(0.0f - halfWidth, 0.0f - halfHeight, 0.0f);
    corner[1] = irr::core::vector3df(0.0f + halfWidth, 0.0f - halfHeight, 0.0f);
    corner[2] = irr::core::vector3df(0.0f - halfWidth, 0.0f + halfHeight, 0.0f);
    corner[3] = irr::core::vector3df(0.0f + halfWidth, 0.0f + halfHeight, 0.0f);
 
    // transform corners
    for (int x = 0; x < 4; x++)
    {
        corner[x] += irr::core::vector3df(halfWidth, halfHeight, 0.0f);
        corner[x] += irr::core::vector3df((float)position.UpperLeftCorner.X, (float)position.UpperLeftCorner.Y, 0.0f);
    }
 
    // Vertices for the image
    irr::u16 indices[6] = { 0, 1, 2, 3 ,2 ,1 };
 
    irr::video::S3DVertex vertice[4];
    for (int x = 0; x < 4; ++x)
    {
 
        vertice[x].Pos = irr::core::vector3df(corner[x].X, corner[x].Y, 0.0f);
        vertice[x].Color = colors[x];
    }
 
    material.Lighting = false;
    material.ZWriteEnable = false;
    material.ZBuffer = false;
    material.BackfaceCulling = false;
    material.TextureLayer[0].Texture = NULL;
 
 
    if(usePremultipliedAlpha)
        material.MaterialTypeParam = irr::video::pack_textureBlendFunc(irr::video::EBF_ONE, irr::video::EBF_ONE_MINUS_SRC_ALPHA, irr::video::EMFN_MODULATE_1X, irr::video::EAS_TEXTURE | irr::video::EAS_VERTEX_COLOR);
    else
        material.MaterialTypeParam = irr::video::pack_textureBlendFunc(irr::video::EBF_SRC_ALPHA, irr::video::EBF_ONE_MINUS_SRC_ALPHA, irr::video::EMFN_MODULATE_1X, irr::video::EAS_TEXTURE | irr::video::EAS_VERTEX_COLOR);
 
    material.MaterialType = irr::video::EMT_ONETEXTURE_BLEND;
 
    driver->setMaterial(material);
    //driver->draw2DVertexPrimitiveList(&vertices[0],4,&indices[0],2);
    driver->drawIndexedTriangleList(&vertice[0],4,&indices[0],2);
 
    // Restore projection and view matrices
    driver->setTransform(irr::video::ETS_WORLD,oldWorldMat);
    driver->setTransform(irr::video::ETS_PROJECTION,oldProjMat);
    driver->setTransform(irr::video::ETS_VIEW,oldViewMat);
}
 

Code: Select all

 
u32 PremultiplyAlpha(u32 color)
{
    u32 a = (color>>24)/* & 0xff*/ + 1;
    u32 r = (a*((color>>16) & 0xff))>>8;
    u32 g = (a*((color>>8) & 0xff))>>8;
    u32 b = (a*(color & 0xff))>>8;
    return (--a<<24) | (r<<16) | (g<<8) | b;
}
 
void PremultiplyAlpha(irr::video::ITexture* texture)
{
    if(!texture || texture->getColorFormat() != irr::video::ECF_A8R8G8B8)
        return;
 
    u32* color = (u32*)texture->lock();
    const irr::core::dimension2d<u32>& dim = texture->getSize();
    u32 size = dim.Height*dim.Width;
    do
    {
        *color = PremultiplyAlpha(*color);
        ++color;
    }while (--size);
 
    texture->unlock();
}
 
class MathUtils
{
public:
        static float Gaussian(float x, float mu, float sigma)
    {
        return exp( -(((x-mu)/(sigma))*((x-mu)/(sigma)))/2.0f );
    }
}
 
I apparently forgot to replace some Rectangle2 by their original definition :

Code: Select all

typedef core::rect<s32> Rectangle2;
There is still place for optimisation of course but it is far better than the original one ;).

Here is a typical use of it :

Code: Select all

 
// init
u32 size = 42;
CGUIFreetypeFont *font = new CGUIFreetypeFont(driver);
CGUITTFace* face = new CGUITTFace();
face->load("Font.ttf");
font->attach(face, size, USE_PREMULTIPLIED_ALPHA, (size>>2));
face->drop();// now we attached it we can drop the reference
font->AntiAlias = true;
font->Transparency = true;
 
 
// live down size
u32 size = 32;
font->setSize(size, size>>2);
 
 
// rendering    
// (point centered) 
core::position2d<s32> pos(100, 100);
core::stringw str("Test rendering !");
font->drawGauss(str, core::rect<s32>(pos, core::dimension2d<u32>(0, 0)), video::SColor(100, 200, 200, 255), true, true);
font->draw(str, core::rect<s32>(pos, core::dimension2d<u32>(0, 0)), video::SColor(200, 255, 255, 255), true, true);
 
You can also make the text fit inside a box by simply defining a non zero dimension for the box parameter, and even specify different colors inside the same string with the function accepting an array of ColorElement (if you want keywords coloration for example).

Enjoy ;)
Last edited by Akabane87 on Wed Aug 20, 2014 3:07 pm, edited 2 times in total.
CuteAlien
Admin
Posts: 9643
Joined: Mon Mar 06, 2006 2:25 pm
Location: Tübingen, Germany
Contact:

Re: CGUITTFFont reloaded

Post by CuteAlien »

Just in the same week where I switched to the other ttf implementation ;-) I wonder which one is faster now.

Btw - for such a large armount of code it's probably worth it to create a repository on github, google-code, bitbucket or similar. Pretty easy to set-up a project there and then you have your files with full source-control. Easier than updating forum-posts. (you can see an example in my .sig below - just took me a few minutes to set that up and afterward I wondered why I hadn't done that years ago)
IRC: #irrlicht on irc.libera.chat
Code snippet repository: https://github.com/mzeilfelder/irr-playground-micha
Free racer made with Irrlicht: http://www.irrgheist.com/hcraftsource.htm
Akabane87
Posts: 50
Joined: Sat May 05, 2012 6:11 pm

Re: CGUITTFFont reloaded

Post by Akabane87 »

Yep I uploaded it to my mediafire account and put the link at the top of the 1st post ;) The links won't change if I update the files afterwards :p
chronologicaldot
Competition winner
Posts: 685
Joined: Mon Sep 10, 2012 8:51 am

Re: CGUITTFFont reloaded

Post by chronologicaldot »

I think a repository would be better. Mediafire is nice, but it limits the number of downloads/Mbs-for-download unless you pay for hosting. But considering the number of options out there for code hosting, there's usually something free to meet your needs. If you just want to post it, there's pastebin. If you plan on it being long-term editing, there's Bitbucket. etc.

Nice job on this, by the way.
I do find it peculiar that the draw function is so big. Maybe it'd speed things up if in your call to get the character code, you also return the glyph/texture.

EDIT: Specifically, n = getGlyphByChar(*text);
Akabane87
Posts: 50
Joined: Sat May 05, 2012 6:11 pm

Re: CGUITTFFont reloaded

Post by Akabane87 »

Yes I agree with you :D Actually if you take the original code, this part remained unchanged. I admit that I focused only on the cache system compared to the old code that was putting every glyph inside a single texture. Then I didn't spend more time on optimisation and only focused on features (gaussian blur, keywords coloration...). But your optimisation suggestions are perfectly relevant.

About the repository, I have and use one for my project using this stuff, but it's not an open project for now. I only shared this part of my code because it cames from a snippet found here that made me spare a lot of time. So I consider sharing my modifications in return ;).
hendu
Posts: 2600
Joined: Sat Dec 18, 2010 12:53 pm

Re: CGUITTFFont reloaded

Post by hendu »

What's the gain in doing a gaussian blur on the cpu?
Akabane87
Posts: 50
Joined: Sat May 05, 2012 6:11 pm

Re: CGUITTFFont reloaded

Post by Akabane87 »

It could have been done in a shader for sure, but here my gaussian blured font is cached the same way my font is. So all the CPU work is done once, making the render fast. Now if you reiterate your question on the texture cache generation, I would say that I prefered the simplicity and portability of cpu code compared to gpu shaders :p.

edit : my app using this code was initially designed to work on pc/mac windows/linux/osx with opengl/d3D/sofware render before I broke the sofware rendering. Additionnally I plan to port the code in c# for psvita. So I prefere keep it simple with no shader ;).
hendu
Posts: 2600
Joined: Sat Dec 18, 2010 12:53 pm

Re: CGUITTFFont reloaded

Post by hendu »

What's the use case for blurring often-changing text?
Akabane87
Posts: 50
Joined: Sat May 05, 2012 6:11 pm

Re: CGUITTFFont reloaded

Post by Akabane87 »

I personnaly use this effect for in game menus to do stuff similar that was does sony in the psp firmwares (blured font for non slected items and blured + non blured over font for the selected item). But I don't get why you say often-changing text because the point is that the text is not changing at all for menus.
hendu
Posts: 2600
Joined: Sat Dec 18, 2010 12:53 pm

Re: CGUITTFFont reloaded

Post by hendu »

Well, I asked because I don't see the point of caching blurred glyphs unless the text changed often. For the menu use case it would make much more sense to cache normal and blurred textures of one whole menu item.
Akabane87
Posts: 50
Joined: Sat May 05, 2012 6:11 pm

Re: CGUITTFFont reloaded

Post by Akabane87 »

I asked myself the same question, but here are the 3 points that made me used this cache system :

- Making something generic for menu and in game texts (I use it in game for dialogs too (not the blured font of course but blured and not blured glyphs work the same way))
- Caching letters is faster (for loading of course ; not for rendering) than caching the whole menus because letters are cached once and generally used multiple times in a same menu
- Since caching big letters can be a bit long, I prefer reducing the loading time and take more time to render (I don't really need more than 900fps in menus :p) than the opposite.

+ I prefer sharing here a generic glyph cache system than a menu entry cache system ^^.
To get back to the original purpose of the topic, I took the old CGUITTFFont class and simply improved it a little ;) (normally it should I mean :roll: ).
gerdb
Posts: 194
Joined: Wed Dec 02, 2009 8:21 pm
Location: Dresden, Germany

Re: CGUITTFFont reloaded

Post by gerdb »

Hi,

and where is that PremultiplyAlpha function my compiler is complaining about ????
Also missing MathUtils::Gaussian and Rectangle2!

Odd that your compilers dont complain.
zerochen
Posts: 273
Joined: Wed Jan 07, 2009 1:17 am
Location: Germany

Re: CGUITTFFont reloaded

Post by zerochen »

besides that isnt there a delete missing?

Code: Select all

 
delete[] texgauss; <--
delete[] texd;
 
regards
zerochen
christianclavet
Posts: 1638
Joined: Mon Apr 30, 2007 3:24 am
Location: Montreal, CANADA
Contact:

Re: CGUITTFFont reloaded

Post by christianclavet »

What is the text encoding on this font system? Can this render UTF8?
Post Reply