l

成都手游码农一枚
随笔 - 32, 文章 - 0, 评论 - 117, 引用 - 0
数据加载中……

[cocos2d-x] RichText 杂记二。

废话不多说,先上成果图。




目前完成了基本的简析功能,还差图片,表情以及动画的部分,以及后期优化工作,最最后的代码整理。- -。

接下来就分享下整个实现:
1.绘制采用freetype库实现,支持加粗,斜体,渐变(这个自己算下就可以了)以及描边,其中描边估计是最麻烦的,其它都很简单,网上也有很多实现方式,这里我就主要说说描边了,
  描边方式也很多,但是大多效果都不太好,所以最后还是决定用freetype api来实现而不用自己去处理,整个实现可以参考:http://blog.sina.com.cn/s/blog_69a2aeff0100ol7e.html

  1 bool Font::border(Word& word, unsigned int color1, unsigned int color2, unsigned int border)
  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="border" color1="ffffff"}#{effect="border" color1="ff0000"}英文原文:Developer Hacks His Microwave Into The Microwave Of The Future#{}

普通的#{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#{} 来控制微波炉。适用场景包括:你可以提前放置食物,然后通过手机等设备来操控微波炉,或者你也可以用这项功能吓吓你的小伙伴。
当食物热好之后,它还能发推!
让微波炉更加智能化,本该是大型的微波炉厂商自己该做的事情,但市面上一直没有智能和人性化的微波炉出现,直到今天,我们人需要人工设置时间. 好的是,相信这款 Raspberry Picrowave 的出现,可能会推动微波炉智能化的进程,将物联网的生活往前又推进了一步,不过这款微波炉只是极客们自己的玩具,并没有量产,普及到普通消费者的家中还需要一段时间。#{}

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

posted on 2013-07-14 16:57 l1989 阅读(9090) 评论(19)  编辑 收藏 引用 所属分类: C++游戏

评论

# re: [cocos2d-x] RichText 杂记二。  回复  更多评论   

不错。希望能看到成品。
2013-07-14 19:51 | 小笨象

# re: [cocos2d-x] RichText 杂记二。  回复  更多评论   

挺不错, 谢谢博主分享
2013-07-14 23:30 | weibo

# re: [cocos2d-x] RichText 杂记二。  回复  更多评论   

东西很好,可是具体调用该怎么用?我调用了没显示。Word那个struct 应该怎么填充,怎么显示在cocos2d-x的层上。厚颜讨教。
2013-07-15 21:10 | alsky

# re: [cocos2d-x] RichText 杂记二。  回复  更多评论   

@alsky
class Word
{
public:
Word(unsigned long code);
~Word();

unsigned long m_code; // 字符 unicode 编码
int m_height; // 字符高度
int m_width; // 字符宽度
int m_drawX; // 字符绘制的水平偏移
int m_drawY; // 字符绘制基线上方偏移
int m_advanceX; // 字符步进
unsigned char* m_buffer; // 字符像素信息缓存
};

转换为 CCSprite
CCTexture2D* texture = new CCTexture2D();
if (!texture->initWithData(m_paBuffer, kCCTexture2DPixelFormat_RGBA8888,
m_nWidth, m_nHeight, CCSizeMake(m_nWidth, m_nHeight)))
{
return NULL;
}
return CCSprite::createWithTexture(texture);
2013-07-15 21:38 | AZL

# re: [cocos2d-x] RichText 杂记二。  回复  更多评论   

@AZL
感谢指导!!
关于 texture->initWithData(m_paBuffer, kCCText
m_paBuffer是什么?
是不是 运行了
border(*wordp,0xcccccc,0xdd2211,2);函数以后的
wordp->m_buffer ?

我这样做了以后,出来了一个白色图块,对颜色什么修改都没反应。
我理解的border这个函数,你最后要用的是 wordp-》m_buffer
不知道理解错了没有。
2013-07-15 23:43 | alsky

# re: [cocos2d-x] RichText 杂记二。  回复  更多评论   

@alsky
差不多吧,你可以把多个word的像素缓存考到一个大的位图结构(RGBA),然后直接initWithData初始纹理。
2013-07-16 09:02 | AZL

# re: [cocos2d-x] RichText 杂记二。  回复  更多评论   

@AZL
谢谢,
当我加了句
FT_Set_Char_Size(face,48<<6,48<<6, 76 , 76);
以后成功了,
但是这个很怪异,这个76换成别的都不行,我也不知道为什么一定要是76,
这个应该是分辨率的,但是我不知道这个应该从哪里取。
不知道你那时是怎样解决的,请一定指教啊
2013-07-16 16:07 | alsky

# re: [cocos2d-x] RichText 杂记二。  回复  更多评论   

调用:
Word *wordp = new Word(L'中');
says->border(*wordp,0xcccccc,0x12cccc,2);

函数:
bool Says::border(Word& word, unsigned int color1, unsigned int color2, unsigned int border)
{

FT_Library m_library;

FT_Face face;// = m_size->face;

//打开字库文件,创建一个字体
FT_Init_FreeType(&m_library);
FT_New_Face(m_library,"C:\\WINDOWS\\Fonts\\msyh.ttf",0,&face);


FT_UInt index = FT_Get_Char_Index(face,word.m_code);
if (!index)
{
return false;
}

FT_Set_Char_Size(face,48<<6,48<<6,76,76);
。。。
}后面完全一样省略

我现在的效果是 改变分辨率,76,76那里可以看到‘中’字,如果要看到‘国’字,又要把分辨率调成75,75,要看到英文字母又要改变。。。
我都要抓狂了。应该不是改分辨率那里啊。。。
2013-07-16 21:53 | alsky

# re: [cocos2d-x] RichText 杂记二。  回复  更多评论   

@alsky
还有,word里面的m_code
我用的wchar ,没有用//unsigned long
2013-07-16 21:55 | alsky

# re: [cocos2d-x] RichText 杂记二。  回复  更多评论   

!我找到原因了,是我
initWithData(wordp->m_buffer, kCCTexture2DPixelFormat_RGBA8888,
m_nWidth, m_nHeight, CCSizeMake(m_nWidth, m_nHeight)))
时,m_nWidth,和高指定死了,应该去读自高就对了,真是毫无技术含量的问题,浪费大大时间了。

谢谢大大了。

最后,我想看大大写个 freetype 逐字显示的 教程。。。
2013-07-16 22:09 | alsky

# re: [cocos2d-x] RichText 杂记二。  回复  更多评论   

很不错的 谢谢分享
2013-07-17 16:45 | tb

# re: [cocos2d-x] RichText 杂记二。  回复  更多评论   

如果彩色的文本遇到换行,是自动换行还是手动编辑?
看这里好像是要手动换行
#{effect="gradient" color1="220022" color2="ff0000"}通过将对应食物的数据录入自建的在线数据库,只需扫描条形码,微波炉便会自动开始运作。#{}
#{effect="gradient" color1="220022" color2="ff0000"}可自定义声效。#{}
2013-08-12 11:33 | abao2000

# re: [cocos2d-x] RichText 杂记二。  回复  更多评论   

你好能给个工程下载的么?
2013-09-09 18:39 | CQC

# re: [cocos2d-x] RichText 杂记二。  回复  更多评论   

很好,支持一下。 希望博主早日完成
2013-10-07 18:01 | hzt

# re: [cocos2d-x] RichText 杂记二。  回复  更多评论   

楼主,能分享一下代码吗
2013-12-05 23:53 | Damein

# re: [cocos2d-x] RichText 杂记二。  回复  更多评论   

LZ 我在cocos2d-x 2.x里面加的freetype 渲染的字体 有的对有的不对 这是什么原因
2015-07-06 10:36 | u_tansuo

# re: [cocos2d-x] RichText 杂记二。  回复  更多评论   

@alsky
我的跟你的情况差不多 不同字号 同一句话 渲染出来有的字符就是错的??

FT_Set_Char_Size(face,48<<6,48<<6,76,76)改变后两个参数 ,渲染出来的效果也变化,有的字符正常 有的字符错误 http://www.cocoachina.com/bbs/read.php?tid-309167.html 这是效果
2015-07-06 10:55 | u_tansuo

# re: [cocos2d-x] RichText 杂记二。[未登录]  回复  更多评论   

楼主, FT_Stroker_Set(stroker, (int)(border * 64), FT_STROKER_LINECAP_ROUND, FT_STROKER_LINEJOIN_ROUND, 0)中border的初始值是多少?
2015-11-24 11:43 | 天涯

# re: [cocos2d-x] RichText 杂记二。[未登录]  回复  更多评论   

能说说buffer1与buffer2的区别吗?谢谢
2015-11-27 10:37 | 天涯

只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   博问   Chat2DB   管理