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
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