问题背景
因为项目的特殊性,需要根据web配置好的组件数据来初始化引擎中的组件,配置人员会根据web中看到的场景对组件位置进行摆放。发现使用同样的ttf字体在相同字号的情况下,web展示的文本长度和cocos渲染出来的不一致。特别遇到图文混排的时候,明明在web摆的好好的,cocos却看着比较乱。
经过排查发现,web中ttf字符宽度是准确到小数点以后的,而cocos引擎内部实现是会对ttf字符宽度进行取整。也就是每个字符宽度都会差零点几,字符数量多的情况下就比较明显了。
源码分析
CCFontAtlas.h和CCFontAtlas.cpp中的scaleFontLetterDefinition方法中可以看到letterDefinition.xAdvance是一个int类型,这个属性就是文本排布的时候,当前字符占据的宽度,也就是下一个字符需要隔xAdvance的长度后渲染。
//CCFontAtlas.h
struct FontLetterDefinition
{
float U;
float V;
float width;
float height;
float offsetX;
float offsetY;
int textureID;
bool validDefinition;
int xAdvance;
bool rotated;
};
//CCFontAtlas.cpp
void FontAtlas::scaleFontLetterDefinition(float scaleFactor)
{
for (auto&& fontDefinition : _letterDefinitions) {
auto& letterDefinition = fontDefinition.second;
letterDefinition.width *= scaleFactor;
letterDefinition.height *= scaleFactor;
letterDefinition.offsetX *= scaleFactor;
letterDefinition.offsetY *= scaleFactor;
letterDefinition.xAdvance = (int)(letterDefinition.xAdvance * scaleFactor);
}
}
CCFontFreeType.cpp中的getGlyphBitmap方法中可以找到对xAdvance的赋值
unsigned char* FontFreeType::getGlyphBitmap(uint64_t theChar, long &outWidth, long &outHeight, Rect &outRect,int &xAdvance)
{
bool invalidChar = true;
unsigned char* ret = nullptr;
do
{
if (_fontRef == nullptr)
break;
if (_distanceFieldEnabled)
{
if (FT_Load_Char(_fontRef, static_cast<FT_ULong>(theChar), FT_LOAD_RENDER | FT_LOAD_NO_HINTING | FT_LOAD_NO_AUTOHINT))
break;
}
else
{
if (FT_Load_Char(_fontRef, static_cast<FT_ULong>(theChar), FT_LOAD_RENDER | FT_LOAD_NO_AUTOHINT))
break;
}
auto& metrics = _fontRef->glyph->metrics;
outRect.origin.x = static_cast<float>(metrics.horiBearingX >> 6);
outRect.origin.y = static_cast<float>(-(metrics.horiBearingY >> 6));
outRect.size.width = static_cast<float>((metrics.width >> 6));
outRect.size.height = static_cast<float>((metrics.height >> 6));
//这里对xAdvance进行赋值
xAdvance = (static_cast<int>(_fontRef->glyph->metrics.horiAdvance >> 6));
outWidth = _fontRef->glyph->bitmap.width;
outHeight = _fontRef->glyph->bitmap.rows;
ret = _fontRef->glyph->bitmap.buffer;
if (_outlineSize > 0 && outWidth > 0 && outHeight > 0)
{
auto copyBitmap = new (std::nothrow) unsigned char[outWidth * outHeight];
memcpy(copyBitmap,ret,outWidth * outHeight * sizeof(unsigned char));
FT_BBox bbox;
auto outlineBitmap = getGlyphBitmapWithOutline(theChar,bbox);
if(outlineBitmap == nullptr)
{
ret = nullptr;
delete [] copyBitmap;
break;
}
int glyphMinX = (int)outRect.origin.x;
int glyphMaxX = (int)(outRect.origin.x + outWidth);
int glyphMinY = (int)(-outHeight - outRect.origin.y);
int glyphMaxY = (int)-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;
auto blendImageMinX = MIN(outlineMinX, glyphMinX);
auto blendImageMaxY = MAX(outlineMaxY, glyphMaxY);
auto blendWidth = MAX(outlineMaxX, glyphMaxX) - blendImageMinX;
auto blendHeight = blendImageMaxY - MIN(outlineMinY, glyphMinY);
outRect.origin.x = (float)blendImageMinX;
outRect.origin.y = -blendImageMaxY + _outlineSize;
unsigned char *blendImage = nullptr;
if (blendWidth > 0 && blendHeight > 0)
{
FT_Pos index, index2;
auto imageSize = blendWidth * blendHeight * 2;
blendImage = new (std::nothrow) unsigned char[imageSize];
memset(blendImage, 0, imageSize);
auto px = outlineMinX - blendImageMinX;
auto py = blendImageMaxY - outlineMaxY;
for (int x = 0; x < outlineWidth; ++x)
{
for (int y = 0; y < outlineHeight; ++y)
{
index = px + x + ((py + y) * blendWidth);
index2 = x + (y * outlineWidth);
blendImage[2 * index] = outlineBitmap[index2];
}
}
px = glyphMinX - blendImageMinX;
py = blendImageMaxY - glyphMaxY;
for (int x = 0; x < outWidth; ++x)
{
for (int y = 0; y < outHeight; ++y)
{
index = px + x + ((y + py) * blendWidth);
index2 = x + (y * outWidth);
blendImage[2 * index + 1] = copyBitmap[index2];
}
}
}
outRect.size.width = (float)blendWidth;
outRect.size.height = (float)blendHeight;
outWidth = blendWidth;
outHeight = blendHeight;
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;
}
}
解决方案
为了和web表现一致,需要把xAdvance改成float类型,并且修改xAdvance的赋值代码。
修改CCFontAtlas.h
struct FontLetterDefinition
{
float U;
float V;
float width;
float height;
float offsetX;
float offsetY;
int textureID;
bool validDefinition;
//这里改为float
float xAdvance;
bool rotated;
};
修改CCFontAtlas.cpp
void FontAtlas::scaleFontLetterDefinition(float scaleFactor)
{
for (auto&& fontDefinition : _letterDefinitions) {
auto& letterDefinition = fontDefinition.second;
letterDefinition.width *= scaleFactor;
letterDefinition.height *= scaleFactor;
letterDefinition.offsetX *= scaleFactor;
letterDefinition.offsetY *= scaleFactor;
//这里把(int)去掉
letterDefinition.xAdvance = letterDefinition.xAdvance * scaleFactor;
}
}
修改CCFontFreeType.h
//把int &xAdvance改为float &xAdvance
unsigned char* getGlyphBitmap(uint64_t theChar, long &outWidth, long &outHeight, Rect &outRect,float &xAdvance);
修改CCFontFreeType.cpp
//这里把int &xAdvance改为float &xAdvance
unsigned char* FontFreeType::getGlyphBitmap(uint64_t theChar, long &outWidth, long &outHeight, Rect &outRect,float &xAdvance)
{
bool invalidChar = true;
unsigned char* ret = nullptr;
do
{
if (_fontRef == nullptr)
break;
// @remark: glyph_index=0 means
auto glyph_index = FT_Get_Char_Index(_fontRef, static_cast<FT_ULong>(theChar));
if (FT_Load_Glyph(_fontRef, glyph_index, _distanceFieldEnabled ?
(FT_LOAD_RENDER | FT_LOAD_NO_HINTING | FT_LOAD_NO_AUTOHINT) :
(FT_LOAD_RENDER | FT_LOAD_NO_AUTOHINT)))
break;
#if defined(_DEBUG)
if (glyph_index == 0) {
std::u32string charUTF32(1, theChar);
std::string charUTF8;
cocos2d::StringUtils::UTF32ToUTF8(charUTF32, charUTF8);
if (charUTF8 == "\n") charUTF8 = "\\n";
cocos2d::log("The font face: %s doesn't contains char: <%s>", _fontRef->charmap->face->family_name, charUTF8.c_str());
}
#endif
auto& metrics = _fontRef->glyph->metrics;
outRect.origin.x = static_cast<float>(metrics.horiBearingX >> 6);
outRect.origin.y = static_cast<float>(-(metrics.horiBearingY >> 6));
outRect.size.width = static_cast<float>((metrics.width >> 6));
outRect.size.height = static_cast<float>((metrics.height >> 6));
//这里修改xAdvance的赋值方式
xAdvance = static_cast<float>(_fontRef->glyph->linearHoriAdvance / 65536.0f);
outWidth = _fontRef->glyph->bitmap.width;
outHeight = _fontRef->glyph->bitmap.rows;
ret = _fontRef->glyph->bitmap.buffer;
if (_outlineSize > 0 && outWidth > 0 && outHeight > 0)
{
auto copyBitmap = new (std::nothrow) unsigned char[outWidth * outHeight];
memcpy(copyBitmap,ret,outWidth * outHeight * sizeof(unsigned char));
FT_BBox bbox;
auto outlineBitmap = getGlyphBitmapWithOutline(theChar,bbox);
if(outlineBitmap == nullptr)
{
ret = nullptr;
delete [] copyBitmap;
break;
}
int glyphMinX = (int)outRect.origin.x;
int glyphMaxX = (int)(outRect.origin.x + outWidth);
int glyphMinY = (int)(-outHeight - outRect.origin.y);
int glyphMaxY = (int)-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;
auto blendImageMinX = MIN(outlineMinX, glyphMinX);
auto blendImageMaxY = MAX(outlineMaxY, glyphMaxY);
auto blendWidth = MAX(outlineMaxX, glyphMaxX) - blendImageMinX;
auto blendHeight = blendImageMaxY - MIN(outlineMinY, glyphMinY);
outRect.origin.x = (float)blendImageMinX;
outRect.origin.y = -blendImageMaxY + _outlineSize;
unsigned char *blendImage = nullptr;
if (blendWidth > 0 && blendHeight > 0)
{
FT_Pos index, index2;
auto imageSize = blendWidth * blendHeight * 2;
blendImage = new (std::nothrow) unsigned char[imageSize];
memset(blendImage, 0, imageSize);
auto px = outlineMinX - blendImageMinX;
auto py = blendImageMaxY - outlineMaxY;
for (int x = 0; x < outlineWidth; ++x)
{
for (int y = 0; y < outlineHeight; ++y)
{
index = px + x + ((py + y) * blendWidth);
index2 = x + (y * outlineWidth);
blendImage[2 * index] = outlineBitmap[index2];
}
}
px = glyphMinX - blendImageMinX;
py = blendImageMaxY - glyphMaxY;
for (int x = 0; x < outWidth; ++x)
{
for (int y = 0; y < outHeight; ++y)
{
index = px + x + ((y + py) * blendWidth);
index2 = x + (y * outWidth);
blendImage[2 * index + 1] = copyBitmap[index2];
}
}
}
outRect.size.width = (float)blendWidth;
outRect.size.height = (float)blendHeight;
outWidth = blendWidth;
outHeight = blendHeight;
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;
}
}
最后重新编译引擎就搞定了
858

被折叠的 条评论
为什么被折叠?



