实现了简陋的Freetype2在DirectX下显示字体
上个月开始和同学一块在做类似CS的射击游戏demo,要自己实现游戏里的简单2DOverlay和文本显示(不用具体GUI了,菜单什么的再用CEGUI),由于我这人做事慢,所以就去实现字体这种小模块~0~(学末总是很难静下心编程唉~~),本来以为网上这类资源挺多的,搜到的无非是NeHe的openGL+ft2,还有一位仁兄模仿Ogre写的一个代码(里面有些问题),多不是很系统,自己认真花了3,4天模仿OgreFont实现了一个简单的英文字体在dx 9下使用ID3DXSprite接口进行渲染,然后照着clayman和hyzboy的提示修改成了支持中文的动态调频写入纹理那样(不过没做测试哦~~下下周得去上海2K面试,我就将就用了~)问题应该还有很多,而且应该做成考虑时间那样(LRU),以后再说吧。。。
整个流程:
最后把代码贴下面吧,希望对大家有帮助。
整个流程:
最后把代码贴下面吧,希望对大家有帮助。
/*
***********************************************************************\
This is a fucking
______ ___ _ _
| ___| / _ \ | | | |
| |_ / /_\ \| |__ | | ___
| _| | _ || '_ \ | | / _ \
| | | | | || |_) || || __/
\_| \_| |_/|_.__/ |_| \___| 's free file
filename: Font.h
created: 2010/07/30
creator: 承天一
purpose: Freetype字体类
************************************************************************ */
#ifndef __FONT_H__
#define __FONT_H__
#include < map >
#include < ft2build.h >
#include FT_FREETYPE_H
#include FT_GLYPH_H
using namespace std;
namespace Fable
{
class Font : public NedAlloc
{
public :
typedef unsigned int CodePoint;
typedef FloatRect UVRect;
/// Information about the position and size of a glyph in a texture
struct GlyphInfo
{
public :
CodePoint codePoint; // 字符的unicode码
UVRect uvRect; // 纹理区域
float aspectRatio;
USHORT useCount; // 字符的使用次数
UINT l;
UINT m;
public :
GlyphInfo(CodePoint _code, const UVRect & _rect, float _aspect, UINT _l, UINT _m)
: codePoint(_code), uvRect(_rect), aspectRatio(_aspect), useCount( 0 ), l(_l), m(_m)
{
}
};
/// A range of code points, inclusive on both ends
typedef std::pair < CodePoint, CodePoint > CodePointRange;
typedef vector < CodePointRange > CodePointRangeList;
protected :
/// Map from unicode code point to texture coordinates
typedef map < CodePoint, GlyphInfo > CodePointMap;
CodePointMap mCodePointMap;
/// Range of code points to generate glyphs for (truetype only)
CodePointRangeList mCodePointRangeList;
public :
Font();
~ Font();
void load( const std:: string & name);
void unLoad();
void addCodePointRange( const CodePointRange & range)
{
mCodePointRangeList.push_back(range);
}
/* * Clear the list of code point ranges.
*/
void clearCodePointRanges()
{
mCodePointRangeList.clear();
}
/* * Get a const reference to the list of code point ranges to be used to
generate glyphs from a truetype font.
*/
const CodePointRangeList & getCodePointRangeList() const
{
return mCodePointRangeList;
}
protected :
/// Size of the truetype font, in points
float mTtfSize;
/// Resolution (dpi) of truetype font
unsigned int mTtfResolution;
/// Max distance to baseline of this (truetype) font
int mTtfMaxBearingY;
/// for TRUE_TYPE font only
bool mAntialiasColour;
IDirect3DTexture9 * mTexture;
UINT mWidth;
UINT mHeight;
std:: string mFontName;
/// 纹理上使用区域还剩的个数
UINT mLeftBlankNum;
UINT mMaxCharNum;
uchar * mImageData;
FT_Library mFtLibrary;
FT_Face mFtFace;
UINT mPixelBytes;
UINT mCharDataWidth;
UINT mMaxCharSize;
UINT mDataSize;
int mMaxHeight;
int mMaxWidth;
float mTextureAspect;
UINT mCharSpacer;
UINT mImage_m;
UINT mImage_l;
public :
UINT getTextureWidth() const { return mWidth;}
UINT getTextureHeight() const { return mHeight;}
inline const UVRect & getGlyphTexCoords(CodePoint id) const
{
CodePointMap::const_iterator i = mCodePointMap.find(id);
if (i != mCodePointMap.end())
{
return i -> second.uvRect;
}
else
{
static UVRect nullRect( 0.0 , 0.0 , 0.0 , 0.0 );
return nullRect;
}
}
/* * Sets the texture coordinates of a glyph.
@remarks
You only need to call this if you're setting up a font loaded from a texture manually.
@note
Also sets the aspect ratio (width / height) of this character. textureAspect
is the width/height of the texture (may be non-square)
*/
inline void setGlyphTexCoords(CodePoint id, UINT u1Pixel, UINT v1Pixel, UINT u2Pixel, UINT v2Pixel, float textureAspect)
{
float u1 = ( float )u1Pixel / ( float )mWidth, v1 = ( float )v1Pixel / ( float )mHeight, u2 = ( float )u2Pixel / ( float )mWidth, v2 = ( float )v2Pixel / ( float )mWidth;
CodePointMap::iterator i = mCodePointMap.find(id);
if (i != mCodePointMap.end())
{
i -> second.uvRect.left = u1;
i -> second.uvRect.top = v1;
i -> second.uvRect.right = u2;
i -> second.uvRect.bottom = v2;
i -> second.aspectRatio = textureAspect * (u2 - u1) / (v2 - v1);
i -> second.l = u1Pixel;
i -> second.m = v1Pixel;
}
else
{
mCodePointMap.insert(
CodePointMap::value_type(id,
GlyphInfo(id, UVRect(u1, v1, u2, v2),
textureAspect * (u2 - u1) / (v2 - v1), u1Pixel, v1Pixel)));
}
}
/* * Gets the aspect ratio (width / height) of this character. */
inline float getGlyphAspectRatio(CodePoint id) const
{
CodePointMap::const_iterator i = mCodePointMap.find(id);
if (i != mCodePointMap.end())
{
return i -> second.aspectRatio;
}
else
{
return 1.0 ;
}
}
/* * Sets the aspect ratio (width / height) of this character.
@remarks
You only need to call this if you're setting up a font loaded from a
texture manually.
*/
inline void setGlyphAspectRatio(CodePoint id, float ratio)
{
CodePointMap::iterator i = mCodePointMap.find(id);
if (i != mCodePointMap.end())
{
i -> second.aspectRatio = ratio;
}
}
/* * Gets the information available for a glyph corresponding to a
given code point, or throws an exception if it doesn't exist;
*/
const GlyphInfo * getGlyphInfo(CodePoint id) const ;
LPDIRECT3DTEXTURE9 getFontTexture() const { return mTexture; }
void insertGlyphInfo(CodePoint id);
bool hasBlankInTexture() const
{
return mLeftBlankNum > 0 ;
}
void renderGlyphIntoTexture(CodePoint id);
CodePoint getLessUseChar();
void removeGlyph(CodePoint id);
};
}
#endif
/* ***********************************************************************\
This is a fucking
______ ___ _ _
| ___| / _ \ | | | |
| |_ / /_\ \| |__ | | ___
| _| | _ || '_ \ | | / _ \
| | | | | || |_) || || __/
\_| \_| |_/|_.__/ |_| \___| 's free file
filename: Font.cpp
created: 2010/07/30
creator: 承天一
purpose: Freetype字体类
************************************************************************ */
#include " stdafx.h "
#include " RenderCore.h "
#include " Font.h "
#include " TextureManager.h "
#include < d3dx9tex.h >
#undef max
#undef min
namespace Fable
{
Font::Font()
:mTtfMaxBearingY( 0 ), mTtfResolution( 0 ), mAntialiasColour( false ),
mTexture( 0 ), mLeftBlankNum( 0 ),
mImageData(nullptr), mImage_m( 0 ),mImage_l( 0 )
{
mWidth = 1024 ;
mHeight = 1024 ;
mTtfSize = 20 ;
mTtfResolution = 96 ;
}
Font:: ~ Font()
{
unLoad();
}
void Font::load( const std:: string & name)
{
mFontName = name;
FT_Library ftLibrary;
// 初始化库
if (FT_Init_FreeType( & ftLibrary))
FA_EXCEPT(ERR_FONT, " FreeType初始化失败 " );
mCharSpacer = 5 ;
if (FT_New_Face(ftLibrary, name.c_str(), 0 , & mFtFace))
FA_EXCEPT(ERR_FONT, " FreeType无法打开ttf文件 " );
UINT maxFaceNum = mFtFace -> num_faces;
FT_F26Dot6 ftSize = (FT_F26Dot6)(mTtfSize * ( 1 << 6 ));
if (FT_Set_Char_Size( mFtFace, ftSize, 0 , mTtfResolution, mTtfResolution))
FA_EXCEPT(ERR_FONT, " Could not set char size! " );
mMaxHeight = 0 , mMaxWidth = 0 ;
if (mCodePointRangeList.empty())
{
mCodePointRangeList.push_back(CodePointRange( 33 , 166 ));
mCodePointRangeList.push_back(CodePointRange( 19968 , 40869 ));
}
// Calculate maximum width, height and bearing
for (CodePointRangeList::const_iterator r = mCodePointRangeList.begin();
r != mCodePointRangeList.end(); ++ r)
{
const CodePointRange & range = * r;
for (CodePoint cp = range.first; cp <= range.second; ++ cp)
{
FT_Load_Char( mFtFace, cp, FT_LOAD_RENDER );
if ( ( 2 * ( mFtFace -> glyph -> bitmap.rows << 6 ) - mFtFace -> glyph -> metrics.horiBearingY ) > mMaxHeight )
mMaxHeight = ( 2 * ( mFtFace -> glyph -> bitmap.rows << 6 ) - mFtFace -> glyph -> metrics.horiBearingY );
if ( mFtFace -> glyph -> metrics.horiBearingY > mTtfMaxBearingY )
mTtfMaxBearingY = mFtFace -> glyph -> metrics.horiBearingY;
if ( (mFtFace -> glyph -> advance.x >> 6 ) + ( mFtFace -> glyph -> metrics.horiBearingX >> 6 ) > mMaxWidth)
mMaxWidth = (mFtFace -> glyph -> advance.x >> 6 ) + ( mFtFace -> glyph -> metrics.horiBearingX >> 6 );
}
}
// We just make a 1024 * 1024 texture, it's enough
mTextureAspect = 1.0f ;
mPixelBytes = 2 ;
mCharDataWidth = (mMaxWidth + mCharSpacer) * mPixelBytes;
mDataSize = mWidth * mHeight * mPixelBytes;
mMaxCharSize = ((mMaxHeight >> 6 ) + mCharSpacer) * mCharDataWidth;
mMaxCharNum = mDataSize / mMaxCharSize;
mLeftBlankNum = mMaxCharNum;
CON_INFO( " Font texture size %d * %d " , mWidth, mHeight);
mImageData = FA_NEW_ARRAY_T(uchar, mDataSize);
// Reset content (transparent, white)
for (size_t i = 0 ; i < mDataSize; i += mPixelBytes)
{
mImageData[i + 0 ] = 0xFF ; // luminance
mImageData[i + 1 ] = 0x00 ; // alpha
}
HRESULT hr = 0 ;
hr = D3DXCreateTexture(
RenderCore::getInstancePtr() -> getDevice(),
mWidth,
mHeight,
1 ,
0 ,
D3DFMT_A8L8,
D3DPOOL_MANAGED,
& mTexture);
if (FAILED(hr))
{
string msg = DXGetErrorDescriptionA(hr);
FA_EXCEPT(ERR_FONT, " Create font Texture failed: " + msg);
}
}
void Font::unLoad()
{
FA_DELETE_ARRAY_T(mImageData, uchar, mDataSize);
SAFE_RELEASE(mTexture);
FT_Done_FreeType(mFtLibrary);
}
const Font::GlyphInfo * Font::getGlyphInfo(CodePoint id) const
{
CodePointMap::const_iterator i = mCodePointMap.find(id);
if (i == mCodePointMap.end())
{
return nullptr;
}
return & i -> second;
}
void Font::renderGlyphIntoTexture(CodePoint id)
{
FT_Error ftResult;
// Load & render glyph
ftResult = FT_Load_Char( mFtFace, id, FT_LOAD_RENDER );
if (ftResult)
{
// problem loading this glyph, continue
CON_INFO( " Info: cannot load CodePoint %d " , id);
}
FT_Int advance = mFtFace -> glyph -> advance.x >> 6 ;
unsigned char * buffer = mFtFace -> glyph -> bitmap.buffer;
if ( ! buffer)
{
// Yuck, FT didn't detect this but generated a null pointer!
CON_INFO( " Info: Freetype returned null for character %d " , id);
}
int y_bearnig = ( mTtfMaxBearingY >> 6 ) - ( mFtFace -> glyph -> metrics.horiBearingY >> 6 );
int x_bearing = mFtFace -> glyph -> metrics.horiBearingX >> 6 ;
for ( int j = 0 ; j < mFtFace -> glyph -> bitmap.rows; ++ j )
{
size_t row = j + mImage_m + y_bearnig;
UCHAR * pDest = & mImageData[(row * mWidth * mPixelBytes) + (mImage_l + x_bearing) * mPixelBytes];
for ( int k = 0 ; k < mFtFace -> glyph -> bitmap.width; ++ k )
{
if (mAntialiasColour)
{
// Use the same greyscale pixel for all components RGBA
* pDest ++= * buffer;
}
else
{
// Always white whether 'on' or 'off' pixel, since alpha
// will turn off
* pDest ++= 0xFF ;
}
// Always use the greyscale value for alpha
* pDest ++= * buffer ++ ;
}
}
this -> setGlyphTexCoords(id,
mImage_l, // u1
mImage_m, // v1
mImage_l + ( mFtFace -> glyph -> advance.x >> 6 ), // u2
mImage_m + ( mMaxHeight >> 6 ), // v2
mTextureAspect
);
// Advance a column
mImage_l += (advance + mCharSpacer);
// If at end of row
if ( mWidth - 1 < mImage_l + ( advance ) )
{
mImage_m += ( mMaxHeight >> 6 ) + mCharSpacer;
mImage_l = 0 ;
}
-- mLeftBlankNum;
D3DLOCKED_RECT lockedRect;
mTexture -> LockRect( 0 , & lockedRect, 0 , 0 );
// 使用类型注意
uchar * TexData = (uchar * )lockedRect.pBits;
for (UINT i = 0 ; i < mHeight; ++ i)
{
for (UINT j = 0 ; j < mWidth; ++ j)
{
// Pitch数据的总长度
int index = i * lockedRect.Pitch / mPixelBytes + j;
TexData[index] = mImageData[index];
}
}
mTexture -> UnlockRect( 0 );
// for test
// #ifdef _DEBUG
// D3DXSaveTextureToFileA(".. // media // test.png",D3DXIFF_PNG, mTexture, 0);
// #endif
}
void Font::insertGlyphInfo(CodePoint id)
{
if ( ! hasBlankInTexture()) // has no space left in texture
{
removeGlyph(getLessUseChar());
}
renderGlyphIntoTexture(id);
}
Font::CodePoint Font::getLessUseChar()
{
CodePointMap::iterator i = mCodePointMap.begin(), iend = mCodePointMap.end(), iless = mCodePointMap.begin();
while (i != iend)
{
if (i -> second.useCount < iless -> second.useCount)
iless = i;
++ i;
}
return iless -> second.codePoint;
}
void Font::removeGlyph(CodePoint id)
{
CodePointMap::iterator it = mCodePointMap.find(id);
if (it != mCodePointMap.end())
{
mImage_l = it -> second.l;
mImage_m = it -> second.m;
mCodePointMap.erase(it);
++ mLeftBlankNum;
}
else
{
FA_EXCEPT(ERR_FONT, " Can not find CodePoint to remove in void Font::removeGlyph(CodePoint id) " );
}
}
}
This is a fucking
______ ___ _ _
| ___| / _ \ | | | |
| |_ / /_\ \| |__ | | ___
| _| | _ || '_ \ | | / _ \
| | | | | || |_) || || __/
\_| \_| |_/|_.__/ |_| \___| 's free file
filename: Font.h
created: 2010/07/30
creator: 承天一
purpose: Freetype字体类
************************************************************************ */
#ifndef __FONT_H__
#define __FONT_H__
#include < map >
#include < ft2build.h >
#include FT_FREETYPE_H
#include FT_GLYPH_H
using namespace std;
namespace Fable
{
class Font : public NedAlloc
{
public :
typedef unsigned int CodePoint;
typedef FloatRect UVRect;
/// Information about the position and size of a glyph in a texture
struct GlyphInfo
{
public :
CodePoint codePoint; // 字符的unicode码
UVRect uvRect; // 纹理区域
float aspectRatio;
USHORT useCount; // 字符的使用次数
UINT l;
UINT m;
public :
GlyphInfo(CodePoint _code, const UVRect & _rect, float _aspect, UINT _l, UINT _m)
: codePoint(_code), uvRect(_rect), aspectRatio(_aspect), useCount( 0 ), l(_l), m(_m)
{
}
};
/// A range of code points, inclusive on both ends
typedef std::pair < CodePoint, CodePoint > CodePointRange;
typedef vector < CodePointRange > CodePointRangeList;
protected :
/// Map from unicode code point to texture coordinates
typedef map < CodePoint, GlyphInfo > CodePointMap;
CodePointMap mCodePointMap;
/// Range of code points to generate glyphs for (truetype only)
CodePointRangeList mCodePointRangeList;
public :
Font();
~ Font();
void load( const std:: string & name);
void unLoad();
void addCodePointRange( const CodePointRange & range)
{
mCodePointRangeList.push_back(range);
}
/* * Clear the list of code point ranges.
*/
void clearCodePointRanges()
{
mCodePointRangeList.clear();
}
/* * Get a const reference to the list of code point ranges to be used to
generate glyphs from a truetype font.
*/
const CodePointRangeList & getCodePointRangeList() const
{
return mCodePointRangeList;
}
protected :
/// Size of the truetype font, in points
float mTtfSize;
/// Resolution (dpi) of truetype font
unsigned int mTtfResolution;
/// Max distance to baseline of this (truetype) font
int mTtfMaxBearingY;
/// for TRUE_TYPE font only
bool mAntialiasColour;
IDirect3DTexture9 * mTexture;
UINT mWidth;
UINT mHeight;
std:: string mFontName;
/// 纹理上使用区域还剩的个数
UINT mLeftBlankNum;
UINT mMaxCharNum;
uchar * mImageData;
FT_Library mFtLibrary;
FT_Face mFtFace;
UINT mPixelBytes;
UINT mCharDataWidth;
UINT mMaxCharSize;
UINT mDataSize;
int mMaxHeight;
int mMaxWidth;
float mTextureAspect;
UINT mCharSpacer;
UINT mImage_m;
UINT mImage_l;
public :
UINT getTextureWidth() const { return mWidth;}
UINT getTextureHeight() const { return mHeight;}
inline const UVRect & getGlyphTexCoords(CodePoint id) const
{
CodePointMap::const_iterator i = mCodePointMap.find(id);
if (i != mCodePointMap.end())
{
return i -> second.uvRect;
}
else
{
static UVRect nullRect( 0.0 , 0.0 , 0.0 , 0.0 );
return nullRect;
}
}
/* * Sets the texture coordinates of a glyph.
@remarks
You only need to call this if you're setting up a font loaded from a texture manually.
@note
Also sets the aspect ratio (width / height) of this character. textureAspect
is the width/height of the texture (may be non-square)
*/
inline void setGlyphTexCoords(CodePoint id, UINT u1Pixel, UINT v1Pixel, UINT u2Pixel, UINT v2Pixel, float textureAspect)
{
float u1 = ( float )u1Pixel / ( float )mWidth, v1 = ( float )v1Pixel / ( float )mHeight, u2 = ( float )u2Pixel / ( float )mWidth, v2 = ( float )v2Pixel / ( float )mWidth;
CodePointMap::iterator i = mCodePointMap.find(id);
if (i != mCodePointMap.end())
{
i -> second.uvRect.left = u1;
i -> second.uvRect.top = v1;
i -> second.uvRect.right = u2;
i -> second.uvRect.bottom = v2;
i -> second.aspectRatio = textureAspect * (u2 - u1) / (v2 - v1);
i -> second.l = u1Pixel;
i -> second.m = v1Pixel;
}
else
{
mCodePointMap.insert(
CodePointMap::value_type(id,
GlyphInfo(id, UVRect(u1, v1, u2, v2),
textureAspect * (u2 - u1) / (v2 - v1), u1Pixel, v1Pixel)));
}
}
/* * Gets the aspect ratio (width / height) of this character. */
inline float getGlyphAspectRatio(CodePoint id) const
{
CodePointMap::const_iterator i = mCodePointMap.find(id);
if (i != mCodePointMap.end())
{
return i -> second.aspectRatio;
}
else
{
return 1.0 ;
}
}
/* * Sets the aspect ratio (width / height) of this character.
@remarks
You only need to call this if you're setting up a font loaded from a
texture manually.
*/
inline void setGlyphAspectRatio(CodePoint id, float ratio)
{
CodePointMap::iterator i = mCodePointMap.find(id);
if (i != mCodePointMap.end())
{
i -> second.aspectRatio = ratio;
}
}
/* * Gets the information available for a glyph corresponding to a
given code point, or throws an exception if it doesn't exist;
*/
const GlyphInfo * getGlyphInfo(CodePoint id) const ;
LPDIRECT3DTEXTURE9 getFontTexture() const { return mTexture; }
void insertGlyphInfo(CodePoint id);
bool hasBlankInTexture() const
{
return mLeftBlankNum > 0 ;
}
void renderGlyphIntoTexture(CodePoint id);
CodePoint getLessUseChar();
void removeGlyph(CodePoint id);
};
}
#endif
/* ***********************************************************************\
This is a fucking
______ ___ _ _
| ___| / _ \ | | | |
| |_ / /_\ \| |__ | | ___
| _| | _ || '_ \ | | / _ \
| | | | | || |_) || || __/
\_| \_| |_/|_.__/ |_| \___| 's free file
filename: Font.cpp
created: 2010/07/30
creator: 承天一
purpose: Freetype字体类
************************************************************************ */
#include " stdafx.h "
#include " RenderCore.h "
#include " Font.h "
#include " TextureManager.h "
#include < d3dx9tex.h >
#undef max
#undef min
namespace Fable
{
Font::Font()
:mTtfMaxBearingY( 0 ), mTtfResolution( 0 ), mAntialiasColour( false ),
mTexture( 0 ), mLeftBlankNum( 0 ),
mImageData(nullptr), mImage_m( 0 ),mImage_l( 0 )
{
mWidth = 1024 ;
mHeight = 1024 ;
mTtfSize = 20 ;
mTtfResolution = 96 ;
}
Font:: ~ Font()
{
unLoad();
}
void Font::load( const std:: string & name)
{
mFontName = name;
FT_Library ftLibrary;
// 初始化库
if (FT_Init_FreeType( & ftLibrary))
FA_EXCEPT(ERR_FONT, " FreeType初始化失败 " );
mCharSpacer = 5 ;
if (FT_New_Face(ftLibrary, name.c_str(), 0 , & mFtFace))
FA_EXCEPT(ERR_FONT, " FreeType无法打开ttf文件 " );
UINT maxFaceNum = mFtFace -> num_faces;
FT_F26Dot6 ftSize = (FT_F26Dot6)(mTtfSize * ( 1 << 6 ));
if (FT_Set_Char_Size( mFtFace, ftSize, 0 , mTtfResolution, mTtfResolution))
FA_EXCEPT(ERR_FONT, " Could not set char size! " );
mMaxHeight = 0 , mMaxWidth = 0 ;
if (mCodePointRangeList.empty())
{
mCodePointRangeList.push_back(CodePointRange( 33 , 166 ));
mCodePointRangeList.push_back(CodePointRange( 19968 , 40869 ));
}
// Calculate maximum width, height and bearing
for (CodePointRangeList::const_iterator r = mCodePointRangeList.begin();
r != mCodePointRangeList.end(); ++ r)
{
const CodePointRange & range = * r;
for (CodePoint cp = range.first; cp <= range.second; ++ cp)
{
FT_Load_Char( mFtFace, cp, FT_LOAD_RENDER );
if ( ( 2 * ( mFtFace -> glyph -> bitmap.rows << 6 ) - mFtFace -> glyph -> metrics.horiBearingY ) > mMaxHeight )
mMaxHeight = ( 2 * ( mFtFace -> glyph -> bitmap.rows << 6 ) - mFtFace -> glyph -> metrics.horiBearingY );
if ( mFtFace -> glyph -> metrics.horiBearingY > mTtfMaxBearingY )
mTtfMaxBearingY = mFtFace -> glyph -> metrics.horiBearingY;
if ( (mFtFace -> glyph -> advance.x >> 6 ) + ( mFtFace -> glyph -> metrics.horiBearingX >> 6 ) > mMaxWidth)
mMaxWidth = (mFtFace -> glyph -> advance.x >> 6 ) + ( mFtFace -> glyph -> metrics.horiBearingX >> 6 );
}
}
// We just make a 1024 * 1024 texture, it's enough
mTextureAspect = 1.0f ;
mPixelBytes = 2 ;
mCharDataWidth = (mMaxWidth + mCharSpacer) * mPixelBytes;
mDataSize = mWidth * mHeight * mPixelBytes;
mMaxCharSize = ((mMaxHeight >> 6 ) + mCharSpacer) * mCharDataWidth;
mMaxCharNum = mDataSize / mMaxCharSize;
mLeftBlankNum = mMaxCharNum;
CON_INFO( " Font texture size %d * %d " , mWidth, mHeight);
mImageData = FA_NEW_ARRAY_T(uchar, mDataSize);
// Reset content (transparent, white)
for (size_t i = 0 ; i < mDataSize; i += mPixelBytes)
{
mImageData[i + 0 ] = 0xFF ; // luminance
mImageData[i + 1 ] = 0x00 ; // alpha
}
HRESULT hr = 0 ;
hr = D3DXCreateTexture(
RenderCore::getInstancePtr() -> getDevice(),
mWidth,
mHeight,
1 ,
0 ,
D3DFMT_A8L8,
D3DPOOL_MANAGED,
& mTexture);
if (FAILED(hr))
{
string msg = DXGetErrorDescriptionA(hr);
FA_EXCEPT(ERR_FONT, " Create font Texture failed: " + msg);
}
}
void Font::unLoad()
{
FA_DELETE_ARRAY_T(mImageData, uchar, mDataSize);
SAFE_RELEASE(mTexture);
FT_Done_FreeType(mFtLibrary);
}
const Font::GlyphInfo * Font::getGlyphInfo(CodePoint id) const
{
CodePointMap::const_iterator i = mCodePointMap.find(id);
if (i == mCodePointMap.end())
{
return nullptr;
}
return & i -> second;
}
void Font::renderGlyphIntoTexture(CodePoint id)
{
FT_Error ftResult;
// Load & render glyph
ftResult = FT_Load_Char( mFtFace, id, FT_LOAD_RENDER );
if (ftResult)
{
// problem loading this glyph, continue
CON_INFO( " Info: cannot load CodePoint %d " , id);
}
FT_Int advance = mFtFace -> glyph -> advance.x >> 6 ;
unsigned char * buffer = mFtFace -> glyph -> bitmap.buffer;
if ( ! buffer)
{
// Yuck, FT didn't detect this but generated a null pointer!
CON_INFO( " Info: Freetype returned null for character %d " , id);
}
int y_bearnig = ( mTtfMaxBearingY >> 6 ) - ( mFtFace -> glyph -> metrics.horiBearingY >> 6 );
int x_bearing = mFtFace -> glyph -> metrics.horiBearingX >> 6 ;
for ( int j = 0 ; j < mFtFace -> glyph -> bitmap.rows; ++ j )
{
size_t row = j + mImage_m + y_bearnig;
UCHAR * pDest = & mImageData[(row * mWidth * mPixelBytes) + (mImage_l + x_bearing) * mPixelBytes];
for ( int k = 0 ; k < mFtFace -> glyph -> bitmap.width; ++ k )
{
if (mAntialiasColour)
{
// Use the same greyscale pixel for all components RGBA
* pDest ++= * buffer;
}
else
{
// Always white whether 'on' or 'off' pixel, since alpha
// will turn off
* pDest ++= 0xFF ;
}
// Always use the greyscale value for alpha
* pDest ++= * buffer ++ ;
}
}
this -> setGlyphTexCoords(id,
mImage_l, // u1
mImage_m, // v1
mImage_l + ( mFtFace -> glyph -> advance.x >> 6 ), // u2
mImage_m + ( mMaxHeight >> 6 ), // v2
mTextureAspect
);
// Advance a column
mImage_l += (advance + mCharSpacer);
// If at end of row
if ( mWidth - 1 < mImage_l + ( advance ) )
{
mImage_m += ( mMaxHeight >> 6 ) + mCharSpacer;
mImage_l = 0 ;
}
-- mLeftBlankNum;
D3DLOCKED_RECT lockedRect;
mTexture -> LockRect( 0 , & lockedRect, 0 , 0 );
// 使用类型注意
uchar * TexData = (uchar * )lockedRect.pBits;
for (UINT i = 0 ; i < mHeight; ++ i)
{
for (UINT j = 0 ; j < mWidth; ++ j)
{
// Pitch数据的总长度
int index = i * lockedRect.Pitch / mPixelBytes + j;
TexData[index] = mImageData[index];
}
}
mTexture -> UnlockRect( 0 );
// for test
// #ifdef _DEBUG
// D3DXSaveTextureToFileA(".. // media // test.png",D3DXIFF_PNG, mTexture, 0);
// #endif
}
void Font::insertGlyphInfo(CodePoint id)
{
if ( ! hasBlankInTexture()) // has no space left in texture
{
removeGlyph(getLessUseChar());
}
renderGlyphIntoTexture(id);
}
Font::CodePoint Font::getLessUseChar()
{
CodePointMap::iterator i = mCodePointMap.begin(), iend = mCodePointMap.end(), iless = mCodePointMap.begin();
while (i != iend)
{
if (i -> second.useCount < iless -> second.useCount)
iless = i;
++ i;
}
return iless -> second.codePoint;
}
void Font::removeGlyph(CodePoint id)
{
CodePointMap::iterator it = mCodePointMap.find(id);
if (it != mCodePointMap.end())
{
mImage_l = it -> second.l;
mImage_m = it -> second.m;
mCodePointMap.erase(it);
++ mLeftBlankNum;
}
else
{
FA_EXCEPT(ERR_FONT, " Can not find CodePoint to remove in void Font::removeGlyph(CodePoint id) " );
}
}
}