http://www.cppblog.com/shly/archive/2013/07/05/201476.html
打算抽时间写个RichText,初步想法是直接使用 freetype 。
Android IOS 似乎都支持 freetype,暂未实践,只是在 win32 上尝试一下。
richtext::Line line(480);
int color[] = {0xFFFFFF, 0xFF0000, 0xFFFFFF, 0x00FFFF, 0xFFFFFF};
char* text[] = {"恭喜", "[王尼玛]", "同学获取", "[草泥马]", " x 1" };
int effect[] = {0, 3, 0, 2, 0};
for ( int i = 0; i < 5; ++i)
{
line.AddElement( new richtext::TextElement(font, text[i], color[i], richtext::TextElement::Effect(effect[i])));
}
addChild(line.ToSprite());
效果如图:


目前完成了基本的简析功能,还差图片,表情以及动画的部分,以及后期优化工作,最最后的代码整理。- -。
接下来就分享下整个实现:
1.绘制采用freetype库实现,支持加粗,斜体,渐变(这个自己算下就可以了)以及描边,其中描边估计是最麻烦的,其它都很简单,网上也有很多实现方式,这里我就主要说说描边了,
描边方式也很多,但是大多效果都不太好,所以最后还是决定用freetype api来实现而不用自己去处理,整个实现可以参考: http://blog.sina.com.cn/s/blog_69a2aeff0100ol7e.html
2 {
3 if (!ok())
4 {
5 return false;
6 }
7
8 FT_Face face = m_size->face;
9
10 FT_UInt index = FT_Get_Char_Index(face, FT_ULong(word.m_code));
11 if (!index)
12 {
13 return false;
14 }
15
16 if (FT_Load_Glyph(face, index, FT_LOAD_NO_BITMAP))
17 {
18 return false;
19 }
20
21 FT_Glyph glyph;
22 if (FT_Get_Glyph(face->glyph, &glyph))
23 {
24 return false;
25 }
26
27 FT_Stroker stroker;
28 if (FT_Stroker_New(s_library, &stroker))
29 {
30 return false;
31 }
32
33 FT_Stroker_Set(stroker, ( int)(border * 64), FT_STROKER_LINECAP_ROUND, FT_STROKER_LINEJOIN_ROUND, 0);
34
35 if (FT_Glyph_StrokeBorder(&glyph, stroker, 0, 1))
36 {
37 return false;
38 }
39
40 FT_Outline *outline = &reinterpret_cast<FT_OutlineGlyph>(glyph)->outline;
41
42 FT_BBox bbox;
43 FT_Glyph_Get_CBox(glyph, FT_GLYPH_BBOX_GRIDFIT, &bbox);
44
45 int width = (bbox.xMax - bbox.xMin) >> 6;
46 int rows = (bbox.yMax - bbox.yMin) >> 6;
47
48 FT_Bitmap *bitmap = &face->glyph->bitmap;
49
50 word.m_width = width;
51 word.m_height = rows;
52 word.m_drawX = face->glyph->metrics.horiBearingX >> 6;
53 word.m_drawY = face->glyph->metrics.horiBearingY >> 6;
54 word.m_advanceX = face->glyph->metrics.horiAdvance >> 6;
55 word.m_buffer = new unsigned char[word.m_width * word.m_height * 4];
56 memset(word.m_buffer, 0, word.m_width * word.m_height * 4);
57
58 unsigned char* buffer = word.m_buffer;
59
60 FT_Raster_Params params;
61 FT_Bitmap bmp;
62
63 bmp.buffer = new unsigned char[width * rows];
64 memset(bmp.buffer, 0, width * rows);
65 bmp.width = width;
66 bmp.rows = rows;
67 bmp.pitch = width;
68 bmp.pixel_mode = FT_PIXEL_MODE_GRAY;
69 bmp.num_grays = 256;
70
71 memset(& params, 0, sizeof ( params));
72 params.source = outline;
73 params.target = &bmp;
74 params.flags = FT_RASTER_FLAG_AA;
75 FT_Outline_Translate(outline,-bbox.xMin,-bbox.yMin);
76 FT_Outline_Render(s_library, outline, & params);
77 unsigned char* buffer1 = bmp.buffer;
78
79 FT_BBox bbox_in;
80 FT_Glyph glyph_fg;
81 FT_Get_Glyph(face->glyph, &glyph_fg);
82 FT_Glyph_Get_CBox(glyph_fg, FT_GLYPH_BBOX_GRIDFIT,&bbox_in);
83
84 bmp.buffer = new unsigned char[width * rows];
85 memset(bmp.buffer, 0, width * rows);
86 bmp.width = width;
87 bmp.rows = rows;
88 bmp.pitch = width;
89 bmp.pixel_mode = FT_PIXEL_MODE_GRAY;
90 bmp.num_grays = 256;
91 outline = &reinterpret_cast<FT_OutlineGlyph>(glyph_fg)->outline;
92 memset(& params, 0, sizeof ( params));
93 params.source = outline;
94 params.target = &bmp;
95 params.flags = FT_RASTER_FLAG_AA;
96 FT_Outline_Translate(outline,-bbox.xMin,-bbox.yMin);
97 FT_Outline_Render(s_library, outline, & params);
98 unsigned char* buffer2 = bmp.buffer;
99
100 int pitch = width;
101 for ( int yy = 0; yy < rows; ++yy)
102 {
103 for ( int xx = 0; xx < width; ++xx)
104 {
105 int si = yy * word.m_width * 4 + xx * 4;
106 int alpha1 = buffer1[yy * pitch + xx];
107
108 unsigned char sr = (color1 & 0xFF0000) >> 16,
109 sg = (color1 & 0xFF00 ) >> 8,
110 sb = (color1 & 0xFF );
111
112 unsigned char dr = (color2 & 0xFF0000) >> 16,
113 dg = (color2 & 0xFF00 ) >> 8,
114 db = (color2 & 0xFF );
115
116 if (alpha1)
117 {
118 buffer[si + 0] = dr;
119 buffer[si + 1] = dg;
120 buffer[si + 2] = db;
121 buffer[si + 3] = alpha1;
122 }
123
124 int alpha2 = buffer2[yy * pitch + xx];
125 if (alpha2)
126 {
127 buffer[si + 0] = dr + ( sr - dr) * alpha2 / 255.0f;
128 buffer[si + 1] = dg + ( sg - dg) * alpha2 / 255.0f;
129 buffer[si + 2] = db + ( sb - db) * alpha2 / 255.0f;
130 buffer[si + 3] = min(255, alpha1 + alpha2);
131 }
132 }
133 }
134
135 delete [] buffer1;
136 delete [] buffer2;
137
138 FT_Stroker_Done(stroker);
139
140 return true;
141 }
2.字体缓存,缓存可以参考 FTC_Manager 缓存子系统,可以参看 ft_cache.h 的说明。
3.布局,目前我的布局方案:
IElement 接口:用于获取每个元素的大小,以及保存元素的位置信息,后面可以用于处理 Hittest。
ElementCollection :具体说来就是一行,由多个 Element 组成,绘制前线设置x,y值,然后调用本身layout函数来布局,最后父节点可以获取高度和宽度用于计算。
RowCollection :由多个ElementCollection构成,主要用于缓存所有结点信息,提供layout接口后计算出整个布局的包围盒,然后生成纹理在绘制。
RichDoc : 内部有一个 RowCollection,主要用于将字符串转换成IElement,并处理换行等。
TextElement :文本元素,如果是汉字就一个字对应一个TextElement,单词则由多个构成,这样方便布局。
后期可能还有 ImageElement 等等。
补充:ElementCollection 中元素,即通常的一行,如果未满行而剩余空间又小于一个固定值,我这里大概设置的30,那么就应该吧这30的空隙填充到每个元素之间,这样布局出来的效果行尾基本都是对齐的,如果空隙太大就不应该插入。
4.字符串简析
这个看自己的爱好,可以自由发挥,这里贴出效果图的布局文本。
普通的#{effect="italic" color1="ff00ff" value="25"}家用微波炉#{}可以说是非常不智能的产品,买回来不仅时间需要人工设定,使用过程中,我们也很少会根据事物的不同,选择对应的设置,只要能热食物就行。
所以,当我看到开发者 Nathan Broadbent 跟他的微波炉说话,语音控制微波炉,并且能够自动设置时间,甚至,你只需要扫描一下产品的条形码,微波炉便能自动识别对应的模式以及分钟数时,我感觉我快要窒息了。
这款微波炉被命名为 #{effect="border" color1="00ff00"}"Raspberry Picrowave"#{},顾名思义,是一台同树莓派相连的微波炉设备。
经过调试之后,这款微波炉具备以下功能:
#{effect="gradient" color1="220022" color2="ff0000"}通过网络,自动调节时间。#{}
#{effect="gradient" color1="220022" color2="ff0000"}通过将对应食物的数据录入自建的在线数据库,只需扫描条形码,微波炉便会自动开始运作。#{}
#{effect="gradient" color1="220022" color2="ff0000"}可自定义声效。#{}
你可以使用手机或者 #{effect="bold" color1="00ffff"}iPad#{} 来控制微波炉。适用场景包括:你可以提前放置食物,然后通过手机等设备来操控微波炉,或者你也可以用这项功能吓吓你的小伙伴。
当食物热好之后,它还能发推!
让微波炉更加智能化,本该是大型的微波炉厂商自己该做的事情,但市面上一直没有智能和人性化的微波炉出现,直到今天,我们人需要人工设置时间

PS:附带一张真机测试图,没想象中感觉好。
PS:造成失真是犹豫界面被拉伸导致,所以如果不拉升,效果还是一样的。

http://www.cppblog.com/shly/archive/2013/12/07/204638.html
上一篇博客尝试性在cocos2d-x中使用freetype,由于项目进度,近期没怎么研究,苦逼加班族。现在稍微闲下来了在小谈一下这个话题。
这几天稍微闲了下,于是打算把freetype集成到公司项目中,这里先总结下,代码不能上传,因为在公司没权限- -!
1.上一篇文章中的的描边其实有两处内存泄漏, Get_Glayph后没有调用Done_Glayph。
2.文字渲染可以glTexSubImage2D函数来替换,也可以一次性通过Rgb数据生成纹理,注意要设置纹理几个参数,否则拉伸了可能会出现模糊不清,而且初始化创建纹理时应该填充为透明色。同事要注意这个函数,如果你的x+w大于整个纹理宽度是会报错的,文字也就不见了,要小心。
3.android 加载字体可以直接加载,不需要从内存中读取,因为系统字体目录是可以直接访问读取的。
4.字体缓存我的实现方法是利用FTC_Manager的前提下将每个字母的RGB数据缓存,下次渲染如果发现效果大小一样的旧直接返回不再去读取,要注意的是FTC_Mananger_LookupSize 函数返回的FT_Size结构,所以不能通过它来索引缓存,而应该用实际字体Size,还有就是FT_Size在使用前一定要确保是最新的哦,这里东西是不能长时间保留的,也就是说查找一次用一次。
5.注意android纹理限制,2048*2048,所以对于很长的文本要注意了。
6.为了兼容以前的一些代码,现在我是直接将CCLabellTTF的实现替换了,所以同时要注意两个布局参数,调整好了后你会发现在PC端使用android系统字体的话整体效果应该是和在android上一样,这样就很方便在PC端查看效果了,美术也就方便不少了。
暂且就想到了这些,整体来说目前测试出的效果目前运行的还算比较流畅的。