Port my variant of font-rendering over to dev branch -- adds correct alignment behavior.

This commit is contained in:
James Gregory 2013-08-08 18:39:42 -07:00
parent a8ac892c0b
commit 0287f273e0
1 changed files with 108 additions and 72 deletions

View File

@ -89,33 +89,58 @@ public:
} }
bool divideString(TTF_Font *face, const char* sText, int iMaxWidth, int iMaxHeight) { bool divideString(TTF_Font *face, const char* sText, int iMaxWidth, int iMaxHeight) {
const char* pText = sText;
//init stringstream
std::stringstream ss; std::stringstream ss;
int w, h; int w, h;
while (*pText != '\0') { // if there is no maximum width specified, just slam the whole input
if (*pText == '\n') { // string into a single line, thereby avoiding many expensive calls to
buildLine(ss, face); // compute font metrics.
if(iMaxWidth == 0)
{
std::string text = sText;
ss << text;
buildLine(ss, face);
return true;
}
pText++; std::vector<std::string> words;
std::string text = sText;
std::istringstream iss(text);
copy(std::istream_iterator<std::string>(iss),
std::istream_iterator<std::string>(),
std::back_inserter< vector<string> >(words));
for(std::vector<string>::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; 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); buildLine(ss, face);
ss << *i;
}
else
{
ss << " " << *i;
} }
ss << *pText;
pText++;
} }
buildLine(ss, face); buildLine(ss, face);
@ -130,54 +155,55 @@ public:
* while -1 means fail * while -1 means fail
* *
*/ */
int computeLineStart(TTF_Font *face, Image::TextAlign eAlignMask, char cText, int computeLineStart(Image::TextAlign eAlignMask, int lineWidth, int maxLineWidth)
int iLineIndex) { {
return 0; int result = 0;
/* if( eAlignMask == Image::TextAlign::CENTER ||
int iRet; eAlignMask == Image::TextAlign::TOP ||
int iError = FT_Load_Glyph(face, FT_Get_Char_Index(face, cText), eAlignMask == Image::TextAlign::BOTTOM)
FT_LOAD_DEFAULT); {
if (iError) { result = (maxLineWidth / 2) - (lineWidth / 2);
return -1; }
} else if(eAlignMask == Image::TextAlign::RIGHT ||
eAlignMask == Image::TextAlign::TOP_RIGHT ||
if (eAlignMask == Image::kAlignCenter) { eAlignMask == Image::TextAlign::BOTTOM_RIGHT)
iRet = (iMaxLineWidth - vLines[iLineIndex].iLineWidth) / 2 {
- RSHIFT6(face->glyph->metrics.horiBearingX ); result = maxLineWidth - lineWidth;
}
} else if (eAlignMask == Image::kAlignRight) { // In all other cases (left alignment, most likely), return 0.
iRet = (iMaxLineWidth - vLines[iLineIndex].iLineWidth)
- RSHIFT6(face->glyph->metrics.horiBearingX ); // Attempt to ensure that we don't produce any completely invalid reslts.
} else { if(result < 0)
// left or other situation {
iRet = -RSHIFT6(face->glyph->metrics.horiBearingX ); result = 0;
} }
return iRet; return result;
*/ }
}
int computeLineStartY(Image::TextAlign eAlignMask, int lineHeight, int maxLineHeight)
int computeLineStartY( TTF_Font *face, Image::TextAlign eAlignMask, int txtHeight, int borderHeight ){ {
return 0; int result = 0;
/* if( eAlignMask == Image::TextAlign::CENTER ||
int iRet; eAlignMask == Image::TextAlign::RIGHT ||
if (eAlignMask == Image::kAlignCenter || eAlignMask == Image::kAlignLeft || eAlignMask == Image::TextAlign::LEFT)
eAlignMask == Image::kAlignRight ) { {
//vertical center result = (maxLineHeight / 2) - (lineHeight / 2);
iRet = (borderHeight - txtHeight)/2 + RSHIFT6(face->size->metrics.ascender); }
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) { bool getBitmap(const char *text, int nWidth, int nHeight, Image::TextAlign eAlignMask, const char * pFontName, float fontSize) {
const char* pText = text; const char* pText = text;
int pxSize = (int)fontSize; int pxSize = (int)fontSize;
@ -213,7 +239,10 @@ public:
// pass back SDL's buffer then, though would need additional logic to // pass back SDL's buffer then, though would need additional logic to
// call SDL_FreeSurface appropriately. // 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++) { for (size_t l = 0; l < vLines.size(); l++) {
pText = vLines[l].sLineStr.c_str(); pText = vLines[l].sLineStr.c_str();
if(!strlen(pText)) if(!strlen(pText))
@ -229,6 +258,10 @@ public:
return false; 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_LockSurface(tSurf);
SDL_UnlockSurface(tSurf); SDL_UnlockSurface(tSurf);
@ -237,17 +270,20 @@ public:
int *pixels = (int*)tSurf->pixels; int *pixels = (int*)tSurf->pixels;
int *out = (int*)_data; 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 // (i, j) should be treated as (x, y) coordinates in the source
// bitmap. This loop maps those locations to the target bitmap. // bitmap. This loop maps those locations to the target bitmap.
// Need to ensure that those values do not exceed the allocated // Need to ensure that those values do not exceed the allocated
// memory. // memory.
int minWidth = MIN(tSurf->w, iMaxLineWidth); 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 sourceOffset = j * tSurf->w + i;
int targetOffset = (l * pxSize + i) * iMaxLineWidth + j; int targetOffset = (l * pxSize + j + yOffset) * iMaxLineWidth + i + xOffset;
// HTML5 canvas is non-pre-alpha-multiplied, so alpha-multiply here. // HTML5 canvas is non-pre-alpha-multiplied, so alpha-multiply here.
unsigned char *p = (unsigned char*) &pixels[sourceOffset]; unsigned char *p = (unsigned char*) &pixels[sourceOffset];
@ -257,7 +293,7 @@ public:
SDL_FreeSurface(tSurf); SDL_FreeSurface(tSurf);
} }
//clear all lines // clear all lines
vLines.clear(); vLines.clear();
TTF_CloseFont(face); TTF_CloseFont(face);
@ -300,7 +336,7 @@ bool Image::initWithString(
CC_BREAK_IF(! dc.getBitmap(pText, nWidth, nHeight, eAlignMask, fullFontName.c_str(), nSize)); 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; _data = dc._data;
CC_BREAK_IF(! _data); CC_BREAK_IF(! _data);