热门标签 | 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;}
}



推荐阅读
  • 在洛谷 P1344 的坏牛奶追踪问题中,第一问要求计算最小割,而第二问则需要找到割边数量最少的最小割。通过为每条边附加一个单位权值,可以在求解最小割时优先选择边数较少的方案,从而同时解决两个问题。这种策略不仅简化了问题的求解过程,还确保了结果的最优性。 ... [详细]
  • 2012年9月12日优酷土豆校园招聘笔试题目解析与备考指南
    2012年9月12日,优酷土豆校园招聘笔试题目解析与备考指南。在选择题部分,有一道题目涉及中国人的血型分布情况,具体为A型30%、B型20%、O型40%、AB型10%。若需确保在随机选取的样本中,至少有一人为B型血的概率不低于90%,则需要选取的最少人数是多少?该问题不仅考察了概率统计的基本知识,还要求考生具备一定的逻辑推理能力。 ... [详细]
  • Netty框架中运用Protobuf实现高效通信协议
    在Netty框架中,通过引入Protobuf来实现高效的通信协议。为了使用Protobuf,需要先准备好环境,包括下载并安装Protobuf的代码生成器`protoc`以及相应的源码包。具体资源可从官方下载页面获取,确保版本兼容性以充分发挥其性能优势。此外,配置好开发环境后,可以通过定义`.proto`文件来自动生成Java类,从而简化数据序列化和反序列化的操作,提高通信效率。 ... [详细]
  • 本文深入探讨了Ajax的工作机制及其在现代Web开发中的应用。Ajax作为一种异步通信技术,改变了传统的客户端与服务器直接交互的模式。通过引入Ajax,客户端与服务器之间的通信变得更加高效和灵活。文章详细分析了Ajax的核心原理,包括XMLHttpRequest对象的使用、数据传输格式(如JSON和XML)以及事件处理机制。此外,还介绍了Ajax在提升用户体验、实现动态页面更新等方面的具体应用,并讨论了其在当前Web开发中的重要性和未来发展趋势。 ... [详细]
  • ButterKnife 是一款用于 Android 开发的注解库,主要用于简化视图和事件绑定。本文详细介绍了 ButterKnife 的基础用法,包括如何通过注解实现字段和方法的绑定,以及在实际项目中的应用示例。此外,文章还提到了截至 2016 年 4 月 29 日,ButterKnife 的最新版本为 8.0.1,为开发者提供了最新的功能和性能优化。 ... [详细]
  • 本文探讨了如何利用 jQuery 的 JSONP 技术实现跨域调用外部 Web 服务。通过详细解析 JSONP 的工作原理及其在 jQuery 中的应用,本文提供了实用的代码示例和最佳实践,帮助开发者解决跨域请求中的常见问题。 ... [详细]
  • 手指触控|Android电容屏幕驱动调试指南
    手指触控|Android电容屏幕驱动调试指南 ... [详细]
  • 本文介绍了如何在iOS平台上使用GLSL着色器将YV12格式的视频帧数据转换为RGB格式,并展示了转换后的图像效果。通过详细的技术实现步骤和代码示例,读者可以轻松掌握这一过程,适用于需要进行视频处理的应用开发。 ... [详细]
  • DRF框架中Serializer反序列化验证机制详解:深入探讨Validators的应用与优化
    在DRF框架的反序列化验证机制中,除了基本的字段类型和长度校验外,还常常需要进行更为复杂的条件限制校验。通过引入`validators`模块,可以实现自定义校验逻辑,如唯一字段校验等。本文将详细探讨`validators`的使用方法及其优化策略,帮助开发者更好地理解和应用这一重要功能。 ... [详细]
  • 本文探讨了资源访问的学习路径与方法,旨在帮助学习者更高效地获取和利用各类资源。通过分析不同资源的特点和应用场景,提出了多种实用的学习策略和技术手段,为学习者提供了系统的指导和建议。 ... [详细]
  • 深入理解 Java 控制结构的全面指南 ... [详细]
  • 本文介绍了UUID(通用唯一标识符)的概念及其在JavaScript中生成Java兼容UUID的代码实现与优化技巧。UUID是一个128位的唯一标识符,广泛应用于分布式系统中以确保唯一性。文章详细探讨了如何利用JavaScript生成符合Java标准的UUID,并提供了多种优化方法,以提高生成效率和兼容性。 ... [详细]
  • 题目链接:http://codeforces.com/gym/101190/attachments题意:在一个共享三轮车站点,某些用户需要租用车辆。该问题涉及如何通过离线查询和排序优化策略来高效地管理和分配车辆资源。具体来说,需要设计一种算法,在满足所有用户需求的同时,最小化总等待时间和资源浪费。通过合理的数据结构和算法优化,可以显著提高系统的整体性能和用户体验。 ... [详细]
  • 在处理遗留数据库的映射时,反向工程是一个重要的初始步骤。由于实体模式已经在数据库系统中存在,Hibernate 提供了自动化工具来简化这一过程,帮助开发人员快速生成持久化类和映射文件。通过反向工程,可以显著提高开发效率并减少手动配置的错误。此外,该工具还支持对现有数据库结构进行分析,自动生成符合 Hibernate 规范的配置文件,从而加速项目的启动和开发周期。 ... [详细]
  • 基址获取与驱动开发:内核中提取ntoskrnl模块的基地址方法解析
    基址获取与驱动开发:内核中提取ntoskrnl模块的基地址方法解析 ... [详细]
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社区 版权所有