打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
C#+OpenGL+FreeType显示3D文字(1)

C#+OpenGL+FreeType显示3D文字(1) - 从TTF文件导出字形贴图

最近需要用OpenGL绘制文字,这是个很费时费力的事。一般的思路就是解析TTF文件从而得到字形的贴图,然后通过OpenGL绘制贴图的方式显示文字。

本篇记录了解析TTF文件并把所有字形安排到一张大贴图上的过程。

使用FreeType

想从零开始解析TTF文件是一个比较大的工程,所以目前就借助FreeType。FreeType是一个开源的跨平台的TTF文件解析器。当然,它还支持解析OpenType等格式的文件。

预备工作

找一个TTF文件

你可以从C:\Windows\Fonts里找到很多TTF文件,我就不提供了。TTF文件里包含了文字的字形。比如下面这两个就是本篇开头的两张贴图展示的TTF:

下载FreeType源码

你可以在Github上搜索到FreeType项目,也可以到别的什么地方下载某个版本的FreeType。比如我下载的是2.6版。

源码下载解压后如上图所示。

下载CMake

我想用visual studio打开并编译它,但它没有sln工程文件。所以我们先要用CMake来自动生成一个freetype.sln文件。CMake你可以自行百度找到,由于博客园提供的空间有限,我这里也不保留CMake的安装包了。

安装完CMake后桌面上会有这样一个图标。这就是CMake。

 

创建FreeType.sln

有了CMake,就可以创建freetype.sln了。

打开CMake,按下图所示指定所需的参数,即可生成freetype.sln。

 

下面这一步指定Visual Studio。

 

看到"Generating Done"就说明成功了。

 

打开文件夹,可以看到出现了freetype.sln。

 

编译生成freetype.dll

有了freetype.sln,用visual studio打开,即可编译生成freetype.lib。但我们需要的是DLL,怎么办?按照下述步骤即可实现。

修改宏定义

找到ftoption.h文件,如下图所示,添加两个宏定义。

1 #define FT_EXPORT(x) __declspec(dllexport) x2 #define FT_BASE(x) __declspec(dllexport) x

修改项目配置

如下图所示,在freetype属性页,把配置类型和目标文件扩展名改为.dll。

生成freetype.DLL

现在重新编译项目,就会生成freetype.dll了。

C#调用FreeType.dll

效果图

拿到了freetype.dll,下一步就是在C#里调用它。本篇期望用它得到的效果图已经贴到本文开头,这里再贴一下:

字形(Glyph)信息

英文单词Glyph的意思是"字形;图象字符;纵沟纹"。这就是字形。

一个字形有3个重要的信息:宽度、高度、基线高度。

如上图所示,从f到k,这是6个字形。

每个字形都用红色矩形围了起来。这些红色的矩形高度对每个字形都是相同的,只有宽度不同。

每个字形还用黄色矩形围了起来,这些黄矩形的宽度、高度就是字形的宽度、高度。

图中还有一个蓝色的矩形,它的上边沿与红色矩形是重合的(为了方便看,我没有画重合),它的下边沿就是"基线"。对于字母"g",它有一部分在基线上方,一部分在基线下方,在基线上方那部分的高度就称为"基线高度"。

所以,基线高度就是一个字形的黄色矩形框上边沿到蓝色矩形框下边沿的距离。有了这个基线高度,各个字形才能整齐地排列到贴图上。

如果没有基线高度的概念,你看的到贴图就会是这个样子:

与上面的效果图对比一下,你就知道它们的区别了。

封装freetype.dll

有了上面的基础,就可以开始干活了。首先要封装一些freetype相关的类型。这一步比较枯燥且冗长,我把核心类型放在这里,不想看直接跳过即可。

封装freetype的核心类型

 

使用freetype时,首先要初始化库

1             // 初始化FreeType库:创建FreeType库指针2             FreeTypeLibrary library = new FreeTypeLibrary();3 4             // 初始化字体库5             FreeTypeFace face = new FreeTypeFace(library, this.fontFullname);

 

之后需要获取一个字形时,就

1                 char c = '&';2                 int fontHeight = 48;3                 FreeTypeBitmapGlyph glyph = new FreeTypeBitmapGlyph(face, c, fontHeight);

glyph里包含了字形的宽度、高度和用byte表示的灰度图。

字形集->贴图

得到glyph就可以生成整个贴图了,代码如下。

  1     /// <summary>  2     /// 用一个纹理绘制ASCII表上所有可见字符(具有指定的高度和字体)  3     /// </summary>  4     public class ModernSingleTextureFont  5     {  6         private string fontFullname;  7         private char firstChar;  8         private char lastChar;  9         private int maxWidth; 10         private int fontHeight; 11         private int textureWidth; 12         private int textureHeight; 13         Dictionary<char, CharacterInfo> charInfoDict = new Dictionary<char, CharacterInfo>(); 14  15         /// <summary> 16         /// 用一个纹理绘制ASCII表上所有可见字符(具有指定的高度和字体) 17         /// </summary> 18         /// <param name="fontFullname"></param> 19         /// <param name="fontHeight">此值越大,绘制文字的清晰度越高,但占用的纹理资源就越多。</param> 20         /// <param name="firstChar"></param> 21         /// <param name="lastChar"></param> 22         /// <param name="maxWidth">生成的纹理的最大宽度。</param> 23         public ModernSingleTextureFont(string fontFullname, int fontHeight, char firstChar, char lastChar, int maxWidth) 24         { 25             this.fontFullname = fontFullname; 26             this.fontHeight = fontHeight; 27             this.firstChar = firstChar; 28             this.lastChar = lastChar; 29             this.maxWidth = maxWidth; 30         } 31  32         public System.Drawing.Bitmap GetBitmap() 33         { 34             // 初始化FreeType库:创建FreeType库指针 35             FreeTypeLibrary library = new FreeTypeLibrary(); 36  37             // 初始化字体库 38             FreeTypeFace face = new FreeTypeFace(library, this.fontFullname); 39  40             GetTextureBlueprint(face, this.fontHeight, this.maxWidth, out this.textureWidth, out this.textureHeight); 41  42             System.Drawing.Bitmap bigBitmap = GetBigBitmap(face, this.maxWidth, this.textureWidth, this.textureHeight); 43  44             face.Dispose(); 45             library.Dispose(); 46  47             return bigBitmap; 48         } 49  50         private System.Drawing.Bitmap GetBigBitmap(FreeTypeFace face, int maxTextureWidth, int widthOfTexture, int heightOfTexture) 51         { 52             System.Drawing.Bitmap bigBitmap = new System.Drawing.Bitmap(widthOfTexture, heightOfTexture); 53             Graphics graphics = Graphics.FromImage(bigBitmap); 54  55             //for (int i = (int)this.firstChar; i <= (int)this.lastChar; i++) 56             for (char c = this.firstChar; c <= this.lastChar; c++) 57             { 58                 //char c = Convert.ToChar(i); 59                 FreeTypeBitmapGlyph glyph = new FreeTypeBitmapGlyph(face, c, this.fontHeight); 60                 bool zeroSize = (glyph.obj.bitmap.rows == 0 && glyph.obj.bitmap.width == 0); 61                 bool zeroBuffer = glyph.obj.bitmap.buffer == IntPtr.Zero; 62                 if (zeroSize && (!zeroBuffer)) { throw new Exception(); } 63                 if ((!zeroSize) && zeroBuffer) { throw new Exception(); } 64  65                 if (!zeroSize) 66                 { 67                     int size = glyph.obj.bitmap.width * glyph.obj.bitmap.rows; 68                     byte[] byteBitmap = new byte[size]; 69                     Marshal.Copy(glyph.obj.bitmap.buffer, byteBitmap, 0, byteBitmap.Length); 70                     CharacterInfo cInfo; 71                     if (this.charInfoDict.TryGetValue(c, out cInfo)) 72                     { 73                         if (cInfo.width > 0) 74                         { 75                             System.Drawing.Bitmap bitmap = new System.Drawing.Bitmap(cInfo.width, cInfo.height); 76                             for (int tmpRow = 0; tmpRow < cInfo.height; ++tmpRow) 77                             { 78                                 for (int tmpWidth = 0; tmpWidth < cInfo.width; ++tmpWidth) 79                                 { 80                                     byte color = byteBitmap[tmpRow * cInfo.width + tmpWidth]; 81                                     bitmap.SetPixel(tmpWidth, tmpRow, Color.FromArgb(color, color, color)); 82                                 } 83                             } 84  85                             int baseLine = this.fontHeight / 4 * 3; 86                             graphics.DrawImage(bitmap, cInfo.xoffset, 87                                 cInfo.yoffset + baseLine - glyph.obj.top); 88                         } 89                     } 90                     else 91                     { throw new Exception(string.Format("Not support for display the char [{0}]", c)); } 92                 } 93  94             } 95  96             graphics.Dispose(); 97  98             return bigBitmap; 99         }100 101         private void GetTextureBlueprint(FreeTypeFace face, int fontHeight, int maxTextureWidth, out int widthOfTexture, out int heightOfTexture)102         {103             widthOfTexture = 0;104             heightOfTexture = this.fontHeight;105 106             int glyphX = 0;107             int glyphY = 0;108 109             for (int i = (int)this.firstChar; i <= (int)this.lastChar; i++)110             {111                 char c = Convert.ToChar(i);112                 FreeTypeBitmapGlyph glyph = new FreeTypeBitmapGlyph(face, c, fontHeight);113                 bool zeroSize = (glyph.obj.bitmap.rows == 0 && glyph.obj.bitmap.width == 0);114                 bool zeroBuffer = glyph.obj.bitmap.buffer == IntPtr.Zero;115                 if (zeroSize && (!zeroBuffer)) { throw new Exception(); }116                 if ((!zeroSize) && zeroBuffer) { throw new Exception(); }117                 if (zeroSize) { continue; }118 119                 int glyphWidth = glyph.obj.bitmap.width;120                 int glyphHeight = glyph.obj.bitmap.rows;121 122                 if (glyphX + glyphWidth + 1 > maxTextureWidth)123                 {124                     heightOfTexture += this.fontHeight;125 126                     glyphX = 0;127                     glyphY = heightOfTexture - this.fontHeight;128 129                     CharacterInfo cInfo = new CharacterInfo();130                     cInfo.xoffset = glyphX; cInfo.yoffset = glyphY;131                     cInfo.width = glyphWidth; cInfo.height = glyphHeight;132                     this.charInfoDict.Add(c, cInfo);133                 }134                 else135                 {136                     widthOfTexture = Math.Max(widthOfTexture, glyphX + glyphWidth + 1);137 138                     CharacterInfo cInfo = new CharacterInfo();139                     cInfo.xoffset = glyphX; cInfo.yoffset = glyphY;140                     cInfo.width = glyphWidth; cInfo.height = glyphHeight;141                     this.charInfoDict.Add(c, cInfo);142                 }143 144                 glyphX += glyphWidth + 1;145             }146 147         }148 149     }

开源TTF2Bmps下载

根据本篇记录的内容,我写了TTF2Bmps这个程序,可以将任何一个TTF文件解析为BMP图片。你可以在此下载

2015-08-08

TTF2Bmps现在能够获取所有unicode字符的字形,对输入方式也做了改进。新版的下载地址在此
下面展示了获取某些中文字符的情形。
下面展示了获取所有unicode字符的情形。由于整个unicode字符太多,所以只显示了一部分。
改进了UI,可以一次指定多个TTF文件,开始计算后有进度条展示进度。下图展示的是生成多个TTF文件的汉字部分贴图。此版下载地址在这里

2015-08-10

改进了UI,可以详细显示进度。可以生成贴图对应的Xml文件(包含贴图每个字形的位置信息)。贴图增加红色虚线标识各行的界限。可选择生成各个字形及其位置信息的列表(多个PNG)。此版下载地址在这里。

2015-08-12

特殊处理空格、tab。调整UI。修复若干bug。

总结

有了贴图,下一步就可以用OpenGL绘制文字了。下回再叙。

本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
FreeType 2 教程
Freetype字体引擎分析与指南(中文版翻译)
使用freetype2从ttf字库中提取任意大点阵字体
android文本布局引擎
OpenGL11
AGG AGG与FreeType库
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服