热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

cocos2dxv3.2Label的一些坑

这是篇旧文了,原文请猛戳:http:galoisplusplus.coding.cocos2d-xv3.2的Label实现bug真是不少ÿ

这是篇旧文了,原文请猛戳:
http://galoisplusplus.coding....

cocos2d-x v3.2的Label实现bug真是不少,前段时间恰好排查了几个与之相关的问题,在此记录一下。

文字换行

文字换行是一个困扰我们挺长时间的问题:之前就常常有文字超过指定长度却没有换行的情况出现,后来加入韩文、泰文等“奇葩”文字后问题就更严重了。cocos2d-x引擎在v3.2后大改了这部分的实现,但由于涉及的改动太多,无法作为一个独立的patch单独apply过来,而且更新引擎版本对我们上线的游戏代价太大,也不可行。好在本渣不久前终于从各种游戏系统开发中抽出时间,完整地把这部分代码review了一遍,结果发现全是LabelTextFormatter::multilineText中几行代码惹的祸,缩小了排查范围便不难fix了:

bool LabelTextFormatter::multilineText(Label *theLabel)
{auto limit = theLabel->_limitShowCount;auto strWhole = theLabel->_currentUTF16String;std::vector multiline_string;multiline_string.reserve( limit );std::vector last_word;last_word.reserve( 25 );bool isStartOfLine = false, isStartOfWord = false;float startOfLine = -1, startOfWord = -1;int skip = 0;int tIndex = 0;float scalsX = theLabel->getScaleX();float lineWidth = theLabel->_maxLineWidth;bool breakLineWithoutSpace = theLabel->_lineBreakWithoutSpaces;Label::LetterInfo* info = nullptr;for (int j = 0; j+skip _lettersInfo.at(j+skip);unsigned int justSkipped = 0;while (info->def.validDefinition == false){justSkipped++;tIndex = j+skip+justSkipped;if (strWhole[tIndex-1] == '\n'){StringUtils::trimUTF16Vector(last_word);last_word.push_back('\n');multiline_string.insert(multiline_string.end(), last_word.begin(), last_word.end());last_word.clear();isStartOfWord = false;isStartOfLine = false;startOfWord = -1;startOfLine = -1;}if(tIndex _lettersInfo.at( tIndex );}elsebreak;}skip += justSkipped;tIndex = j + skip;if (tIndex >= limit)break;char16_t character = strWhole[tIndex];if (!isStartOfWord){startOfWord = info->position.x * scalsX;isStartOfWord = true;}if (!isStartOfLine){startOfLine = startOfWord;isStartOfLine = true;}// 1) Whitespace.// 2) This character is non-CJK, but the last character is CJKbool isspace = StringUtils::isUnicodeSpace(character);bool isCJK = false;if(!isspace){isCJK = StringUtils::isCJKUnicode(character);}if (isspace ||(!last_word.empty() && StringUtils::isCJKUnicode(last_word.back()) && !isCJK)){// if current character is white space, put it into the current wordif (isspace) last_word.push_back(character);multiline_string.insert(multiline_string.end(), last_word.begin(), last_word.end());last_word.clear();isStartOfWord = false;startOfWord = -1;// put the CJK character in the last word// and put the non-CJK(ASCII) character in the current wordif (!isspace) last_word.push_back(character);continue;}float posRight = (info->position.x + info->def.xAdvance) * scalsX;// Out of bounds.if (posRight - startOfLine > lineWidth){if (!breakLineWithoutSpace && !isCJK){last_word.push_back(character);int found = StringUtils::getIndexOfLastNotChar16(multiline_string, ' ');if (found != -1)StringUtils::trimUTF16Vector(multiline_string);elsemultiline_string.clear();if (multiline_string.size() > 0)multiline_string.push_back('\n');isStartOfLine = false;startOfLine = -1;}else{StringUtils::trimUTF16Vector(last_word);//issue #8492:endless loop if not using system font, and constrained length is less than one character widthif (isStartOfLine && startOfWord == startOfLine && last_word.size() == 0)last_word.push_back(character);else--j;last_word.push_back('\n');multiline_string.insert(multiline_string.end(), last_word.begin(), last_word.end());last_word.clear();isStartOfWord = false;isStartOfLine = false;startOfWord = -1;startOfLine = -1;}}else{// Character is normal.last_word.push_back(character);}}multiline_string.insert(multiline_string.end(), last_word.begin(), last_word.end());std::u16string strNew(multiline_string.begin(), multiline_string.end());theLabel->_currentUTF16String = strNew;theLabel->computeStringNumLines();theLabel->computeHorizontalKernings(theLabel->_currentUTF16String);return true;
}
描边显示不均匀

这是我们之前常常被美术大大们吐槽的地方:文字加描边后有的地方粗有的地方细,好蓝看啊...
后来本渣在网上看到大神的patch,又自我扫盲了FreeType的基础概念,总算看懂了。cocos2d-x引擎在FontFreeType::getGlyphBitmap函数中会把不带描边的文字字形(glyph)和描边文字字形的bitmap都存到同一个数组里,在FontFreeType::renderCharAt中渲染。而描边文字字形是调用FreeType API生成的,其轮廓和不带描边的文字字形轮廓的间距并不能确保一定是我们所指定的描边大小,这个patch便是记下该间距和描边大小的offset,在拷贝bitmap时根据offset作调整。
其实cocos2d-x引擎在v3.2之后也改了这部分代码,但其实现思路却不如上述patch清晰,于是本渣便用了后者,并做了一点微小改动。

unsigned char* FontFreeType::getGlyphBitmap(unsigned short theChar, long &outWidth, long &outHeight, Rect &outRect,int &xAdvance)
{bool invalidChar = true;unsigned char * ret = nullptr;do {if (!_fontRef)break;auto glyphIndex = FT_Get_Char_Index(_fontRef, theChar);if(!glyphIndex)break;if (_distanceFieldEnabled){if (FT_Load_Glyph(_fontRef,glyphIndex,FT_LOAD_RENDER | FT_LOAD_NO_HINTING | FT_LOAD_NO_AUTOHINT))break;}else{if (FT_Load_Glyph(_fontRef,glyphIndex,FT_LOAD_RENDER | FT_LOAD_NO_AUTOHINT))break;}outRect.origin.x = _fontRef->glyph->metrics.horiBearingX >> 6;outRect.origin.y = - (_fontRef->glyph->metrics.horiBearingY >> 6);outRect.size.width = (_fontRef->glyph->metrics.width >> 6);outRect.size.height = (_fontRef->glyph->metrics.height >> 6);xAdvance = (static_cast(_fontRef->glyph->metrics.horiAdvance >> 6));outWidth = _fontRef->glyph->bitmap.width;outHeight = _fontRef->glyph->bitmap.rows;ret = _fontRef->glyph->bitmap.buffer;// apply patch from: http://my.oschina.net/u/1414326/blog/279456?fromerr=xX53o9Rqif (_outlineSize > 0){auto copyBitmap = new unsigned char[outWidth * outHeight];memcpy(copyBitmap, ret, outWidth * outHeight * sizeof(unsigned char));long bitmapWidth;long bitmapHeight;FT_BBox bbox;auto outlineBitmap = getGlyphBitmapWithOutline(theChar, bbox);if(outlineBitmap == nullptr){ret = nullptr;delete [] copyBitmap;break;}long glyphMinX = outRect.origin.x;long glyphMaxX = outRect.origin.x + outWidth;long glyphMinY = -outHeight - outRect.origin.y;long glyphMaxY = -outRect.origin.y;auto outlineMinX = bbox.xMin >> 6;auto outlineMaxX = bbox.xMax >> 6;auto outlineMinY = bbox.yMin >> 6;auto outlineMaxY = bbox.yMax >> 6;auto outlineWidth = outlineMaxX - outlineMinX;auto outlineHeight = outlineMaxY - outlineMinY;bitmapWidth = outlineMaxX - outlineMinX;bitmapHeight = outlineMaxY - outlineMinY;int offsetWidth = 0;int offsetHeight = 0;if(glyphMinX - outlineMinX != _outlineSize) {offsetWidth = glyphMinX - outlineMinX - _outlineSize;}if(outlineMaxY - glyphMaxY != _outlineSize) {offsetHeight = outlineMaxY - glyphMaxY - _outlineSize;}long index;auto blendImage = new unsigned char[bitmapWidth * bitmapHeight * 2];memset(blendImage, 0, bitmapWidth * bitmapHeight * 2);for (int x = 0; x > 6;outRect.origin.y = - (bbox.yMax >> 6);xAdvance += bitmapWidth - outRect.size.width;outRect.size.width = bitmapWidth;outRect.size.height = bitmapHeight;outWidth = bitmapWidth;outHeight = bitmapHeight;delete [] outlineBitmap;delete [] copyBitmap;ret = blendImage;}invalidChar = false;} while (0);if (invalidChar){outRect.size.width = 0;outRect.size.height = 0;xAdvance = 0;return nullptr;}else{return ret;}
}



推荐阅读
  • IOS开发之短信发送与拨打电话的方法详解
    本文详细介绍了在IOS开发中实现短信发送和拨打电话的两种方式,一种是使用系统底层发送,虽然无法自定义短信内容和返回原应用,但是简单方便;另一种是使用第三方框架发送,需要导入MessageUI头文件,并遵守MFMessageComposeViewControllerDelegate协议,可以实现自定义短信内容和返回原应用的功能。 ... [详细]
  • Go GUIlxn/walk 学习3.菜单栏和工具栏的具体实现
    本文介绍了使用Go语言的GUI库lxn/walk实现菜单栏和工具栏的具体方法,包括消息窗口的产生、文件放置动作响应和提示框的应用。部分代码来自上一篇博客和lxn/walk官方示例。文章提供了学习GUI开发的实际案例和代码示例。 ... [详细]
  • 本文介绍了在MFC下利用C++和MFC的特性动态创建窗口的方法,包括继承现有的MFC类并加以改造、插入工具栏和状态栏对象的声明等。同时还提到了窗口销毁的处理方法。本文详细介绍了实现方法并给出了相关注意事项。 ... [详细]
  • [echarts] 同指标对比柱状图相关的知识介绍及应用示例
    本文由编程笔记小编为大家整理,主要介绍了echarts同指标对比柱状图相关的知识,包括对比课程通过率最高的8个课程和最低的8个课程以及全校的平均通过率。文章提供了一个应用示例,展示了如何使用echarts制作同指标对比柱状图,并对代码进行了详细解释和说明。该示例可以帮助读者更好地理解和应用echarts。 ... [详细]
  • 本文由编程笔记#小编为大家整理,主要介绍了logistic回归(线性和非线性)相关的知识,包括线性logistic回归的代码和数据集的分布情况。希望对你有一定的参考价值。 ... [详细]
  • Iamtryingtomakeaclassthatwillreadatextfileofnamesintoanarray,thenreturnthatarra ... [详细]
  • 在Android开发中,使用Picasso库可以实现对网络图片的等比例缩放。本文介绍了使用Picasso库进行图片缩放的方法,并提供了具体的代码实现。通过获取图片的宽高,计算目标宽度和高度,并创建新图实现等比例缩放。 ... [详细]
  • 向QTextEdit拖放文件的方法及实现步骤
    本文介绍了在使用QTextEdit时如何实现拖放文件的功能,包括相关的方法和实现步骤。通过重写dragEnterEvent和dropEvent函数,并结合QMimeData和QUrl等类,可以轻松实现向QTextEdit拖放文件的功能。详细的代码实现和说明可以参考本文提供的示例代码。 ... [详细]
  • 本文分享了一个关于在C#中使用异步代码的问题,作者在控制台中运行时代码正常工作,但在Windows窗体中却无法正常工作。作者尝试搜索局域网上的主机,但在窗体中计数器没有减少。文章提供了相关的代码和解决思路。 ... [详细]
  • 后台获取视图对应的字符串
    1.帮助类后台获取视图对应的字符串publicclassViewHelper{将View输出为字符串(注:不会执行对应的ac ... [详细]
  • 拥抱Android Design Support Library新变化(导航视图、悬浮ActionBar)
    转载请注明明桑AndroidAndroid5.0Loollipop作为Android最重要的版本之一,为我们带来了全新的界面风格和设计语言。看起来很受欢迎࿰ ... [详细]
  • 本文详细介绍了Java中vector的使用方法和相关知识,包括vector类的功能、构造方法和使用注意事项。通过使用vector类,可以方便地实现动态数组的功能,并且可以随意插入不同类型的对象,进行查找、插入和删除操作。这篇文章对于需要频繁进行查找、插入和删除操作的情况下,使用vector类是一个很好的选择。 ... [详细]
  • [大整数乘法] java代码实现
    本文介绍了使用java代码实现大整数乘法的过程,同时也涉及到大整数加法和大整数减法的计算方法。通过分治算法来提高计算效率,并对算法的时间复杂度进行了研究。详细代码实现请参考文章链接。 ... [详细]
  • 前景:当UI一个查询条件为多项选择,或录入多个条件的时候,比如查询所有名称里面包含以下动态条件,需要模糊查询里面每一项时比如是这样一个数组条件:newstring[]{兴业银行, ... [详细]
  • 先看官方文档TheJavaTutorialshavebeenwrittenforJDK8.Examplesandpracticesdescribedinthispagedontta ... [详细]
author-avatar
HE-KILL-MY-EGO
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有