diff --git a/cocos2dx/platform/emscripten/CCImage.cpp b/cocos2dx/platform/emscripten/CCImage.cpp index 4b22c2813a..36d140ed89 100644 --- a/cocos2dx/platform/emscripten/CCImage.cpp +++ b/cocos2dx/platform/emscripten/CCImage.cpp @@ -89,33 +89,58 @@ public: } bool divideString(TTF_Font *face, const char* sText, int iMaxWidth, int iMaxHeight) { - const char* pText = sText; - - //init stringstream std::stringstream ss; int w, h; - while (*pText != '\0') { - if (*pText == '\n') { - buildLine(ss, face); + // if there is no maximum width specified, just slam the whole input + // string into a single line, thereby avoiding many expensive calls to + // compute font metrics. + if(iMaxWidth == 0) + { + std::string text = sText; + ss << text; + buildLine(ss, face); + return true; + } - pText++; + std::vector words; + std::string text = sText; + std::istringstream iss(text); + copy(std::istream_iterator(iss), + std::istream_iterator(), + std::back_inserter< vector >(words)); + + for(std::vector::iterator i = words.begin(); i != words.end(); ++i) + { + // Specially handle the case where a single word exceeds the + // available width. + TTF_SizeText(face, i->c_str(), &w, &h); + if(w > iMaxWidth) + { + buildLine(ss, face); + for(std::string::iterator c = i->begin(); c != i->end(); ++c) + { + ss << *c; + TTF_SizeText(face, ss.str().c_str(), &w, &h); + if(w > iMaxWidth) + { + buildLine(ss, face); + } + } continue; } - - //check its width - //divide it when exceeding - std::string s = ss.str(); - s.push_back(*pText); - TTF_SizeText(face, s.c_str(), &w, &h); - if (iMaxWidth > 0 && (w > iMaxWidth)) { + std::string tmp = ss.str() + std::string(" ") + *i; + TTF_SizeText(face, tmp.c_str(), &w, &h); + if(w > iMaxWidth) + { buildLine(ss, face); + ss << *i; + } + else + { + ss << " " << *i; } - - ss << *pText; - pText++; - } buildLine(ss, face); @@ -130,54 +155,55 @@ public: * while -1 means fail * */ - int computeLineStart(TTF_Font *face, Image::TextAlign eAlignMask, char cText, - int iLineIndex) { - return 0; - /* - int iRet; - int iError = FT_Load_Glyph(face, FT_Get_Char_Index(face, cText), - FT_LOAD_DEFAULT); - if (iError) { - return -1; - } - - if (eAlignMask == Image::kAlignCenter) { - iRet = (iMaxLineWidth - vLines[iLineIndex].iLineWidth) / 2 - - RSHIFT6(face->glyph->metrics.horiBearingX ); - - } else if (eAlignMask == Image::kAlignRight) { - iRet = (iMaxLineWidth - vLines[iLineIndex].iLineWidth) - - RSHIFT6(face->glyph->metrics.horiBearingX ); - } else { - // left or other situation - iRet = -RSHIFT6(face->glyph->metrics.horiBearingX ); - } - return iRet; - */ - } - - int computeLineStartY( TTF_Font *face, Image::TextAlign eAlignMask, int txtHeight, int borderHeight ){ - return 0; - /* - int iRet; - if (eAlignMask == Image::kAlignCenter || eAlignMask == Image::kAlignLeft || - eAlignMask == Image::kAlignRight ) { - //vertical center - iRet = (borderHeight - txtHeight)/2 + RSHIFT6(face->size->metrics.ascender); + int computeLineStart(Image::TextAlign eAlignMask, int lineWidth, int maxLineWidth) + { + int result = 0; + if( eAlignMask == Image::TextAlign::CENTER || + eAlignMask == Image::TextAlign::TOP || + eAlignMask == Image::TextAlign::BOTTOM) + { + result = (maxLineWidth / 2) - (lineWidth / 2); + } + else if(eAlignMask == Image::TextAlign::RIGHT || + eAlignMask == Image::TextAlign::TOP_RIGHT || + eAlignMask == Image::TextAlign::BOTTOM_RIGHT) + { + result = maxLineWidth - lineWidth; + } + // In all other cases (left alignment, most likely), return 0. + + // Attempt to ensure that we don't produce any completely invalid reslts. + if(result < 0) + { + result = 0; + } + return result; + } + + int computeLineStartY(Image::TextAlign eAlignMask, int lineHeight, int maxLineHeight) + { + int result = 0; + if( eAlignMask == Image::TextAlign::CENTER || + eAlignMask == Image::TextAlign::RIGHT || + eAlignMask == Image::TextAlign::LEFT) + { + result = (maxLineHeight / 2) - (lineHeight / 2); + } + else if(eAlignMask == Image::TextAlign::BOTTOM || + eAlignMask == Image::TextAlign::BOTTOM_RIGHT || + eAlignMask == Image::TextAlign::BOTTOM_LEFT) + { + result = maxLineHeight - lineHeight; + } + // In all other cases (top alignment, most likely), return 0; + + if(result < 0) + { + result = 0; + } + return result; + } - } else if (eAlignMask == Image::kAlignBottomRight || - eAlignMask == Image::kAlignBottom || - eAlignMask == Image::kAlignBottomLeft ) { - //vertical bottom - iRet = borderHeight - txtHeight + RSHIFT6(face->size->metrics.ascender); - } else { - // left or other situation - iRet = RSHIFT6(face->size->metrics.ascender); - } - return iRet; - */ - } - bool getBitmap(const char *text, int nWidth, int nHeight, Image::TextAlign eAlignMask, const char * pFontName, float fontSize) { const char* pText = text; int pxSize = (int)fontSize; @@ -213,7 +239,10 @@ public: // pass back SDL's buffer then, though would need additional logic to // call SDL_FreeSurface appropriately. - // FIXME: handle alignment, etc. + // Y offset for vertical alignment remains constant throughout, as it + // is the entire block of text that must be vertically aligned. + int yOffset = computeLineStartY(eAlignMask, vLines.size() * pxSize, iMaxLineHeight); + for (size_t l = 0; l < vLines.size(); l++) { pText = vLines[l].sLineStr.c_str(); if(!strlen(pText)) @@ -229,6 +258,10 @@ public: return false; } + // The lock/unlock pair is required since Emscripten's SDL + // implementation copies pixel data from its off-screen canvas to + // the pixels array in the unlock operation. Without this, we would + // be reading uninitialized memory. SDL_LockSurface(tSurf); SDL_UnlockSurface(tSurf); @@ -237,17 +270,20 @@ public: int *pixels = (int*)tSurf->pixels; int *out = (int*)_data; + // Compute offset to produce horizontal alignment. + int xOffset = computeLineStart(eAlignMask, tSurf->w, iMaxLineWidth); + // (i, j) should be treated as (x, y) coordinates in the source // bitmap. This loop maps those locations to the target bitmap. // Need to ensure that those values do not exceed the allocated // memory. int minWidth = MIN(tSurf->w, iMaxLineWidth); - for(int i = 0; i < tSurf->h && (i + l * pxSize) < iMaxLineHeight; ++i) + for(int j = 0; j < tSurf->h && (j + l * pxSize) < iMaxLineHeight; ++j) { - for(int j = 0; j < minWidth; ++j) + for(int i = 0; i < minWidth; ++i) { - int sourceOffset = i * tSurf->w + j; - int targetOffset = (l * pxSize + i) * iMaxLineWidth + j; + int sourceOffset = j * tSurf->w + i; + int targetOffset = (l * pxSize + j + yOffset) * iMaxLineWidth + i + xOffset; // HTML5 canvas is non-pre-alpha-multiplied, so alpha-multiply here. unsigned char *p = (unsigned char*) &pixels[sourceOffset]; @@ -257,7 +293,7 @@ public: SDL_FreeSurface(tSurf); } - //clear all lines + // clear all lines vLines.clear(); TTF_CloseFont(face); @@ -300,7 +336,7 @@ bool Image::initWithString( CC_BREAK_IF(! dc.getBitmap(pText, nWidth, nHeight, eAlignMask, fullFontName.c_str(), nSize)); - // assign the dc._data to _data in order to save time + // assign the dc.m_pData to m_pData in order to save time _data = dc._data; CC_BREAK_IF(! _data);