标签:
因为系统字体已经没办法满足项目的需求,需要用一个新的字体,但由于担心字体版权等问题,因为改用通用做法,做一套全字体的BMFont ,全字体9千多个汉字和其他符号,还好2048X2048堆下来了,只用了19号字体,而且万幸经过压缩后,图片也没有多大了。
但是问题来了,在安卓上,首次引用到这个字体时,字体解析居然花了10多秒。
打开.fnt文件
info face="STHeitiSC-Light" size=19 bold=0 italic=0 charset="" unicode=0 stretchH=100 smooth=1 aa=1 padding=0,0,0,0 spacing=1,1 common lineHeight=26 base=19 scaleW=2048 scaleH=2048 pages=1 packed=0 page id=0 file="youyuan.png" chars count=9237 char id=12298 x=1 y=1 width=11 height=21 xoffset=10 yoffset=1 xadvance=20 page=0 chnl=0 letter="《" char id=12299 x=13 y=1 width=11 height=21 xoffset=0 yoffset=1 xadvance=20 page=0 chnl=0 letter="》" char id=12300 x=25 y=1 width=6 height=21 xoffset=12 yoffset=1 xadvance=20 page=0 chnl=0 letter="「" char id=12301 x=32 y=1 width=6 height=21 xoffset=2 yoffset=1 xadvance=20 page=0 chnl=0 letter="」" char id=65288 x=39 y=1 width=6 height=21 xoffset=12 yoffset=1 xadvance=20 page=0 chnl=0 letter="(" char id=65289 x=46 y=1 width=6 height=21 xoffset=2 yoffset=1 xadvance=20 page=0 chnl=0 letter=")" char id=124 x=53 y=1 width=2 height=21 xoffset=4 yoffset=3 xadvance=9 page=0 chnl=0 letter="|" char id=30306 x=77 y=1 width=20 height=20 xoffset=0 yoffset=1 xadvance=19 page=0 chnl=0 letter="癢" char id=30221 x=98 y=1 width=20 height=20 xoffset=0 yoffset=1 xadvance=19 page=0 chnl=0 letter="瘍" char id=23518 x=119 y=1 width=19 height=20 xoffset=1 yoffset=1 xadvance=19 page=0 chnl=0 letter="寞" char id=22815 x=139 y=1 width=19 height=20 xoffset=1 yoffset=1 xadvance=19 page=0 chnl=0 letter="够"
查看CCLabelBMFont.cpp,查看其解析函数
1 std::set<unsigned int>* CCBMFontConfiguration::parseConfigFile(const char *controlFile) 2 { 3 std::string fullpath = CCFileUtils::sharedFileUtils()->fullPathForFilename(controlFile); 4 CCString *contents = CCString::createWithContentsOfFile(fullpath.c_str()); 5 6 CCAssert(contents, "CCBMFontConfiguration::parseConfigFile | Open file error."); 7 8 set<unsigned int> *validCharsString = new set<unsigned int>(); 9 10 if (!contents) 11 { 12 CCLOG("cocos2d: Error parsing FNTfile %s", controlFile); 13 return NULL; 14 } 15 16 // parse spacing / padding 17 std::string line; 18 std::string strLeft = contents->getCString(); 19 while (strLeft.length() > 0) 20 { 21 int pos = strLeft.find(‘\n‘); 22 23 if (pos != (int)std::string::npos) 24 { 25 // the data is more than a line.get one line 26 line = strLeft.substr(0, pos); 27 strLeft = strLeft.substr(pos + 1); 28 } 29 else 30 { 31 // get the left data 32 line = strLeft; 33 strLeft.erase(); 34 } 35 36 if(line.substr(0,strlen("info face")) == "info face") 37 { 38 // XXX: info parsing is incomplete 39 // Not needed for the Hiero editors, but needed for the AngelCode editor 40 // [self parseInfoArguments:line]; 41 this->parseInfoArguments(line); 42 } 43 // Check to see if the start of the line is something we are interested in 44 else if(line.substr(0,strlen("common lineHeight")) == "common lineHeight") 45 { 46 this->parseCommonArguments(line); 47 } 48 else if(line.substr(0,strlen("page id")) == "page id") 49 { 50 this->parseImageFileName(line, controlFile); 51 } 52 else if(line.substr(0,strlen("chars c")) == "chars c") 53 { 54 // Ignore this line 55 } 56 else if(line.substr(0,strlen("char")) == "char") 57 { 58 // Parse the current line and create a new CharDef 59 tCCFontDefHashElement* element = (tCCFontDefHashElement*)malloc( sizeof(*element) ); 60 this->parseCharacterDefinition(line, &element->fontDef); 61 62 element->key = element->fontDef.charID; 63 HASH_ADD_INT(m_pFontDefDictionary, key, element); 64 65 validCharsString->insert(element->fontDef.charID); 66 } 67 // else if(line.substr(0,strlen("kernings count")) == "kernings count") 68 // { 69 // this->parseKerningCapacity(line); 70 // } 71 else if(line.substr(0,strlen("kerning first")) == "kerning first") 72 { 73 this->parseKerningEntry(line); 74 } 75 } 76 77 return validCharsString; 78 }
执行最多次数的应该是这里:
1 else if(line.substr(0,strlen("char")) == "char") 2 { 3 // Parse the current line and create a new CharDef 4 tCCFontDefHashElement* element = (tCCFontDefHashElement*)malloc( sizeof(*element) ); 5 this->parseCharacterDefinition(line, &element->fontDef); 6 7 element->key = element->fontDef.charID; 8 HASH_ADD_INT(m_pFontDefDictionary, key, element); 9 10 validCharsString->insert(element->fontDef.charID); 11 }
继续跟踪:parseCharacterDefinition() 这个函数
1 void CCBMFontConfiguration::parseCharacterDefinition(std::string line, ccBMFontDef *characterDefinition) 2 { 3 ////////////////////////////////////////////////////////////////////////// 4 // line to parse: 5 // char id=32 x=0 y=0 width=0 height=0 xoffset=0 yoffset=44 xadvance=14 page=0 chnl=0 6 ////////////////////////////////////////////////////////////////////////// 7 8 // Character ID 9 int index = line.find("id="); 10 int index2 = line.find(‘ ‘, index); 11 std::string value = line.substr(index, index2-index); 12 sscanf(value.c_str(), "id=%u", &characterDefinition->charID); 13 14 // Character x 15 index = line.find("x="); 16 index2 = line.find(‘ ‘, index); 17 value = line.substr(index, index2-index); 18 sscanf(value.c_str(), "x=%f", &characterDefinition->rect.origin.x); 19 // Character y 20 index = line.find("y="); 21 index2 = line.find(‘ ‘, index); 22 value = line.substr(index, index2-index); 23 sscanf(value.c_str(), "y=%f", &characterDefinition->rect.origin.y); 24 // Character width 25 index = line.find("width="); 26 index2 = line.find(‘ ‘, index); 27 value = line.substr(index, index2-index); 28 sscanf(value.c_str(), "width=%f", &characterDefinition->rect.size.width); 29 // Character height 30 index = line.find("height="); 31 index2 = line.find(‘ ‘, index); 32 value = line.substr(index, index2-index); 33 sscanf(value.c_str(), "height=%f", &characterDefinition->rect.size.height); 34 // Character xoffset 35 index = line.find("xoffset="); 36 index2 = line.find(‘ ‘, index); 37 value = line.substr(index, index2-index); 38 sscanf(value.c_str(), "xoffset=%hd", &characterDefinition->xOffset); 39 // Character yoffset 40 index = line.find("yoffset="); 41 index2 = line.find(‘ ‘, index); 42 value = line.substr(index, index2-index); 43 sscanf(value.c_str(), "yoffset=%hd", &characterDefinition->yOffset); 44 // Character xadvance 45 index = line.find("xadvance="); 46 index2 = line.find(‘ ‘, index); 47 value = line.substr(index, index2-index); 48 sscanf(value.c_str(), "xadvance=%hd", &characterDefinition->xAdvance); 49 }
居然全部为字符串一行一行的解析,因为肯定是会很慢的。
查看.fnt文件 ,其char 解析部分,全部都是固定格式的。因此思路很简单,修改.fnt文件 ,改为二进制解析,这样肯定可以提升很高的效率。
当然首先要做的是一个转换工具,将.fnt 文件转化为二进制文件。这里,我做的并不是所有的数据都转为二进制,因为时间关系,也没过多的去研究其他的字符。因此,我做的是保留其他所有的符为原本的内容,只修改char id = 这些项的格式
为了确保原本的.fnt 文件还继续能读,新生成的.fnt 文件中加入了标记“type=sxbmfont”
修改fnt文件代码,这里只贴出关键代码,其他部分我用的是MFC简单写的,就不贴出来了
结构体格式定义:
1 struct BMFontCharNode 2 { 3 int id; 4 int x,y; 5 int width,height; 6 int xoffset,yoffset; 7 int xadvance; 8 }; 9 typedef struct BMFontCharNode BMFontNode;
这是我的MFC点击编码时:
1 void CtxtToBinDlg::OnBnClickedEncode() 2 { 3 // TODO: 在此添加控件通知处理程序代码 4 if (mOutContent == "") 5 { 6 MessageBox("请先选择输出目录"); 7 return; 8 } 9 CString path = mOutContent + "/" + m_fileName; 10 fstream fin(m_filePath); 11 const int LINE_LENGTH = 150; 12 char str[LINE_LENGTH]; 13 memset(str,0,LINE_LENGTH); 14 fstream fout(path,ios::out); 15 fout.write("type=sxbmfont\r\n",strlen("type=sxbmfont\r\n")); 16 for ( int i=0; i < 4 ; i ++) 17 { 18 if(fin.getline(str,LINE_LENGTH) ) 19 { 20 for (int j=0;j<LINE_LENGTH;j++) 21 if (str[j] < 0) 22 str[j] = 0; 23 CString str1 = CString(str) + "\r\n"; 24 if (str1.Left(strlen("char id=")) != "char id=") 25 { 26 fout.write(str1.GetBuffer(),str1.GetLength()); 27 } 28 else 29 { 30 break; 31 } 32 memset(str,0,LINE_LENGTH); 33 } 34 } 35 fout.close(); 36 37 38 fstream binary_file(path,ios::out|ios::binary|ios::app); 39 while( fin.getline(str,LINE_LENGTH) ) 40 { 41 for (int j=0;j<LINE_LENGTH;j++) 42 if (str[j] < 0) 43 str[j] = 0; 44 CString str1 = str; 45 if (str1.Left(strlen("char id=")) == "char id=") 46 { 47 BMFontNode t; 48 sscanf_s(str1.GetBuffer(), "char id=%d x=%d y=%d width=%d height=%d xoffset=%d yoffset=%d xadvance=%d", 49 &t.id,&t.x,&t.y,&t.width,&t.height,&t.xoffset,&t.yoffset,&t.xadvance); 50 // int pase = 1; 51 binary_file.write(reinterpret_cast<char *>(&t),sizeof(t)); 52 } 53 memset(str,0,LINE_LENGTH); 54 } 55 binary_file.close(); 56 if(!isContent) 57 MessageBox("生成成功!"); 58 }
生成新的.fnt文件事,需要修改CCLabelBMFont 来读取
修改的部分如下:
在头文件加入新的结构体定义:(与保存时的结构一致)
CCLabelBMFont.h中加入结构定义
1 struct BMFontCharNode 2 { 3 int id; 4 int x,y; 5 int width,height; 6 int xoffset,yoffset; 7 int xadvance; 8 }; 9 typedef struct BMFontCharNode BMFontNode;
修改CCLabelBMFont.cpp
添加重载函数(重载char id = 行的解析)
1 void CCBMFontConfiguration::parseCharacterDefinition(BMFontNode *node, ccBMFontDef *characterDefinition) 2 { 3 characterDefinition->charID = node->id; 4 characterDefinition->rect.origin.x = node->x; 5 characterDefinition->rect.origin.y = node->y; 6 characterDefinition->rect.size.width = node->width; 7 characterDefinition->rect.size.height = node->height; 8 characterDefinition->xOffset = node->xoffset; 9 characterDefinition->yOffset = node->yoffset; 10 characterDefinition->xAdvance = node->xadvance; 11 }
修改解析函数:
1 std::set<unsigned int>* CCBMFontConfiguration::parseConfigFile(const char *controlFile) 2 { 3 /* 4 * 修改数据解析方式 5 * 6 */ 7 std::string fullpath = CCFileUtils::sharedFileUtils()->fullPathForFilename(controlFile); 8 9 set<unsigned int> *validCharsString = new set<unsigned int>(); 10 11 12 CCString *contents = CCString::createWithContentsOfFile(fullpath.c_str()); 13 char contentstr[20]; 14 memcpy(contentstr,contents->getCString(),strlen("type=sxbmfont")); 15 contentstr[strlen("type=sxbmfont")] = 0; 16 if (strcmp(contentstr,"type=sxbmfont")) 17 { 18 CCAssert(contents, "CCBMFontConfiguration::parseConfigFile | Open file error."); 19 // parse spacing / padding 20 std::string line; 21 std::string strLeft = contents->getCString(); 22 while (strLeft.length() > 0) 23 { 24 int pos = strLeft.find(‘\n‘); 25 26 if (pos != (int)std::string::npos) 27 { 28 // the data is more than a line.get one line 29 line = strLeft.substr(0, pos); 30 strLeft = strLeft.substr(pos + 1); 31 } 32 else 33 { 34 // get the left data 35 line = strLeft; 36 strLeft.erase(); 37 } 38 if(line.substr(0,strlen("info face")) == "info face") 39 { 40 // XXX: info parsing is incomplete 41 // Not needed for the Hiero editors, but needed for the AngelCode editor 42 // [self parseInfoArguments:line]; 43 this->parseInfoArguments(line); 44 } 45 // Check to see if the start of the line is something we are interested in 46 else if(line.substr(0,strlen("common lineHeight")) == "common lineHeight") 47 { 48 this->parseCommonArguments(line); 49 } 50 else if(line.substr(0,strlen("page id")) == "page id") 51 { 52 this->parseImageFileName(line, controlFile); 53 } 54 else if(line.substr(0,strlen("chars c")) == "chars c") 55 { 56 // Ignore this line 57 } 58 else if(line.substr(0,strlen("char")) == "char") 59 { 60 // Parse the current line and create a new CharDef 61 tCCFontDefHashElement* element = (tCCFontDefHashElement*)malloc( sizeof(*element) ); 62 this->parseCharacterDefinition(line, &element->fontDef); 63 64 element->key = element->fontDef.charID; 65 HASH_ADD_INT(m_pFontDefDictionary, key, element); 66 67 validCharsString->insert(element->fontDef.charID); 68 } 69 70 else if(line.substr(0,strlen("kerning first")) == "kerning first") 71 { 72 this->parseKerningEntry(line); 73 } 74 } 75 76 } 77 else 78 { 79 unsigned long len = 0; 80 unsigned char* data = CCFileUtils::sharedFileUtils()->getFileData(fullpath.c_str(), "rb", &len); 81 const int LINE_LENGTH = 150; 82 char str[LINE_LENGTH]; 83 int nodeSize = sizeof(BMFontNode); 84 int index = 0; 85 int count = 0; 86 for(int i=0 ; i < len ; i++) 87 { 88 if(data[i] == ‘\n‘) 89 { 90 memcpy(str,data+index,i-index+1); 91 str[i-index+1] = 0; 92 std::string line = str; 93 if(line.substr(0,strlen("info face")) == "info face") 94 { 95 this->parseInfoArguments(line); 96 } 97 // Check to see if the start of the line is something we are interested in 98 else if(line.substr(0,strlen("common lineHeight")) == "common lineHeight") 99 { 100 this->parseCommonArguments(line); 101 } 102 else if(line.substr(0,strlen("page id")) == "page id") 103 { 104 this->parseImageFileName(line, controlFile); 105 } 106 else if(line.substr(0,strlen("chars c")) == "chars c") 107 { 108 // Ignore this line 109 } 110 else if(line.substr(0,strlen("char")) == "char") 111 { 112 // Parse the current line and create a new CharDef 113 tCCFontDefHashElement* element = (tCCFontDefHashElement*)malloc( sizeof(*element) ); 114 this->parseCharacterDefinition(line, &element->fontDef); 115 116 element->key = element->fontDef.charID; 117 HASH_ADD_INT(m_pFontDefDictionary, key, element); 118 119 validCharsString->insert(element->fontDef.charID); 120 } 121 else if(line.substr(0,strlen("kerning first")) == "kerning first") 122 { 123 this->parseKerningEntry(line); 124 } 125 index = i+1; 126 count ++; 127 if(count == 5) 128 break; 129 } 130 131 } 132 while(index < len) 133 { 134 BMFontNode t; 135 memcpy(&t,data+index,nodeSize); 136 index = index + nodeSize; 137 tCCFontDefHashElement* element = (tCCFontDefHashElement*)malloc( sizeof(*element) ); 138 this->parseCharacterDefinition(&t, &element->fontDef); 139 140 element->key = element->fontDef.charID; 141 HASH_ADD_INT(m_pFontDefDictionary, key, element); 142 143 validCharsString->insert(element->fontDef.charID); 144 } 145 } 146 147 148 149 return validCharsString; 150 }
至此优化完成。修改后,这样解析过程应该基本上不怎么消耗时间了,因为没有过多的字符解析
关于cocos2dx 2.x CCLabelBMFont的解析优化
标签:
原文地址:http://www.cnblogs.com/JhonKing/p/5141668.html