先吐个槽,刚准备发这条博客的时候居然发现账号被盗了,真是有人闲得蛋疼,顺便赞一个csdn的客服,处理的很快。
好了,言归正转。看了一下,自动换行的实现在CCFreeType这个类里,这个类的实现只是针对英文的,它采用空格断句的方式来进行操作,一个word一个word的加,如果发现超过规定的范围就会换行,但是对于中文来说,这个实现简直弱爆了,所以就会出现不能自动换行的情况。参考它的实现,做一点小的修改,基本原理如下
1、读一行文本出来,参考它的实现,算出这个文本的宽度
2、如果这个宽度没有超过,则直接显示
3、如果这个宽度走过了,则按等宽字体处理,根据宽度算出来总共应该是几行,然后按长度进行分隔换行。
注意这个实现有几个问题:
1、性能问题,其实是在计算出了所有的字之后才发现超过了,这个本来可以发现的更早,但是为了尽可能利用原有的代码结构,只用先这样了。
2、是以等宽字体的实现的,这个不一定正确,所以可能会出现某一些还是超过了的情况。
最后附上修改的diff,我使用的是2.2.4版本。
diff --git a/cocos2d-x-2.2.4/cocos2dx/platform/winrt/CCFreeTypeFont.cpp b/cocos2d-x-2.2.4/cocos2dx/platform/winrt/CCFreeTypeFont.cpp index 9886d0a..46489af 100644 --- a/cocos2d-x-2.2.4/cocos2dx/platform/winrt/CCFreeTypeFont.cpp +++ b/cocos2d-x-2.2.4/cocos2dx/platform/winrt/CCFreeTypeFont.cpp @@ -368,13 +368,67 @@ FT_Error CCFreeTypeFont::addWord(const std::string& word) return error; } +FT_Error CCFreeTypeFont::addLine(wchar_t* line, int size, bool wrap) +{ + std::vector<TGlyph> glyphs; // glyphs for the word + FT_BBox bbox; // bounding box containing all of the glyphs in the word + int maxWidth = m_inWidth ? m_inWidth : m_windowWidth; + + FT_Vector pen = m_currentLine->pen; + FT_Error error = initWordGlyphs(glyphs, line, size, pen); + if (!error) + { + compute_bbox(glyphs, &bbox); + if (!wrap || bbox.xMax <= maxWidth) + { + m_currentLine->pen = pen; + m_currentLine->glyphs.insert(m_currentLine->glyphs.end(), glyphs.begin(), glyphs.end()); + if (m_currentLine->width == 0) + { + m_currentLine->bbox = bbox; + } + else + { + m_currentLine->bbox.xMax = bbox.xMax; + } + m_currentLine->width = m_currentLine->bbox.xMax - m_currentLine->bbox.xMin; + } + else + { + int width = bbox.xMax; + int lines = width / maxWidth + 1; + float fline = float(width) / float(maxWidth); + int lineWidth = int(size / fline); + int offset = 0; + for (int i = 0; i < lines; i++) + { + if (i) + { + newLine(); + } + wchar_t* buffer = line + offset; + int buffer_len = size - offset; + if (buffer_len > lineWidth) + { + buffer_len = lineWidth; + } + offset += buffer_len; + addLine(buffer, buffer_len, false); + if (i < lines - 1) + { + endLine(); + } + } + } + } + return error; +} + FT_Error CCFreeTypeFont::initGlyphs(const char* text) { FT_Error error = 0; std::stringstream stringStream(text); std::string line; - vector<std::string> lines; - vector<std::string> words; m_textWidth = 0; m_textHeight = 0; @@ -387,19 +441,21 @@ FT_Error CCFreeTypeFont::initGlyphs(const char* text) { newLine(); - std::size_t prev = 0, pos; - while ((pos = line.find_first_of(" ", prev)) != std::string::npos) - { - if (pos > prev) - { - addWord(line.substr(prev, pos-prev)); - } - prev = pos + 1; - } - if (prev < line.length()) - { - addWord(line.substr(prev, std::string::npos)); - } + wchar_t * pwszBuffer = nullptr; + + int num_chars = line.size(); + int nBufLen = num_chars + 1; + pwszBuffer = new wchar_t[nBufLen]; + if (!pwszBuffer) + { + return -1; + } + + memset(pwszBuffer, 0, nBufLen); + num_chars = MultiByteToWideChar(CP_UTF8, 0, line.c_str(), num_chars, pwszBuffer, nBufLen); + pwszBuffer[num_chars] = '\0'; + error = addLine(pwszBuffer, num_chars, true); + CC_SAFE_DELETE_ARRAY(pwszBuffer); endLine(); } @@ -513,6 +569,64 @@ FT_Error CCFreeTypeFont::initWordGlyphs(std::vector<TGlyph>& glyphs, const std:: return error; } +FT_Error CCFreeTypeFont::initWordGlyphs(std::vector<TGlyph>& glyphs, wchar_t* word, int size, FT_Vector& pen) +{ + FT_GlyphSlot slot = m_face->glyph; + FT_UInt glyph_index; + FT_UInt previous = 0; + FT_Error error = 0; + PGlyph glyph; + unsigned int numGlyphs = 0; + + glyphs.clear(); + glyphs.resize(size); + FT_Bool useKerning = FT_HAS_KERNING(m_face); + + for (int n = 0; n < size; n++) + { + glyph = &glyphs[numGlyphs]; + + /* convert character code to glyph index */ + FT_ULong c = word[n]; + glyph_index = FT_Get_Char_Index(m_face, c); + + if (useKerning && previous && glyph_index) + { + FT_Vector delta; + FT_Get_Kerning(m_face, previous, glyph_index, + FT_KERNING_DEFAULT, &delta); + pen.x += delta.x >> 6; + } + + /* store current pen position */ + glyph->pos = pen; + glyph->index = glyph_index; + + /* load glyph image into the slot without rendering */ + error = FT_Load_Glyph(m_face, glyph_index, FT_LOAD_DEFAULT); + if (error) + continue; /* ignore errors, jump to next glyph */ + + /* extract glyph image and store it in our table */ + error = FT_Get_Glyph(m_face->glyph, &glyph->image); + if (error) + continue; /* ignore errors, jump to next glyph */ + + /* translate the glyph image now */ + FT_Glyph_Transform(glyph->image, 0, &glyph->pos); + + /* increment pen position */ + pen.x += slot->advance.x >> 6; + + /* record current glyph index */ + previous = glyph_index; + + numGlyphs++; + } + + return error; +} + void CCFreeTypeFont::compute_bbox(std::vector<TGlyph>& glyphs, FT_BBox *abbox) { FT_BBox bbox; diff --git a/cocos2d-x-2.2.4/cocos2dx/platform/winrt/CCFreeTypeFont.h b/cocos2d-x-2.2.4/cocos2dx/platform/winrt/CCFreeTypeFont.h index 4c1cc3e..a325071 100644 --- a/cocos2d-x-2.2.4/cocos2dx/platform/winrt/CCFreeTypeFont.h +++ b/cocos2d-x-2.2.4/cocos2dx/platform/winrt/CCFreeTypeFont.h @@ -100,6 +100,7 @@ private: FT_Error CCFreeTypeFont::initGlyphs(const char* text); FT_Error CCFreeTypeFont::initWordGlyphs(std::vector<TGlyph>& glyphs, const std::string& text, FT_Vector& pen); + FT_Error CCFreeTypeFont::initWordGlyphs(std::vector<TGlyph>& glyphs, wchar_t* word, int size, FT_Vector& pen); void compute_bbox(std::vector<TGlyph>& glyphs, FT_BBox *abbox); @@ -114,6 +115,7 @@ private: FT_Vector getPenForAlignment(FTLineInfo* pInfo, CCImage::ETextAlign eAlignMask, int lineNumber, int totalLines); FT_Error addWord(const std::string& word); + FT_Error addLine(wchar_t* line, int size, bool wrap); void newLine(); void endLine();