axmol/cocos2dx/label_nodes/CCTextImage.cpp

476 lines
12 KiB
C++
Raw Normal View History

/****************************************************************************
Copyright (c) 2013 Zynga Inc.
http://www.cocos2d-x.org
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
****************************************************************************/
2013-07-11 02:59:05 +08:00
#include <stdio.h>
#include <string.h>
#include <vector>
#include <string>
2013-07-12 05:41:03 +08:00
#include "cocos2d.h"
2013-07-11 02:59:05 +08:00
#include "CCTextImage.h"
#include "CCFontFreeType.h"
2013-07-19 05:11:12 +08:00
#include "CCFontRenderFreeType.h"
2013-07-11 02:59:05 +08:00
NS_CC_BEGIN
TextLineDef::TextLineDef(float x, float y, float width, float height) :_x(x), _y(y), _width(width), _height(height)
{
}
TextPageDef::TextPageDef(int pageNum, int width, int height): _pageNum(pageNum),
_width(width),
_height(height),
_pageData(0),
_pageTexture(0)
2013-07-11 02:59:05 +08:00
{
}
TextPageDef::~TextPageDef()
{
int numLines = _lines.size();
for( int c = 0; c<numLines; ++c )
{
delete _lines[c];
}
_lines.clear();
if (_pageData)
{
delete [] _pageData;
}
if (_pageTexture)
{
_pageTexture->release();
2013-07-11 02:59:05 +08:00
}
}
bool TextPageDef::generatePageTexture(bool releasePageData)
{
if (!_pageData)
return false;
if (_pageTexture)
{
_pageTexture->release();
2013-07-11 02:59:05 +08:00
_pageTexture = 0;
}
Size imageSize = Size((float)(_width), (float)(_height));
2013-07-11 02:59:05 +08:00
if( (imageSize.width <=0) || (imageSize.height<=0) )
return false;
_pageTexture = new Texture2D();
if (!_pageTexture)
return false;
2013-08-01 04:59:08 +08:00
int dataLenght = (_width * _height * 4);
bool textureCreated = _pageTexture->initWithData(_pageData, dataLenght, Texture2D::PixelFormat::RGBA8888, _width, _height, imageSize);
2013-07-11 02:59:05 +08:00
// _pageTexture->setPremultipliedAlpha(true);
2013-07-11 02:59:05 +08:00
// release the page data if requested
if ( releasePageData && textureCreated )
{
delete [] _pageData;
_pageData = 0;
}
return textureCreated;
}
void TextPageDef::preparePageTexture(bool releaseRAWData)
{
generatePageTexture(releaseRAWData);
}
Texture2D *TextPageDef::getPageTexture()
{
if (!_pageTexture)
{
generatePageTexture();
}
return _pageTexture;
}
2013-07-20 01:33:26 +08:00
TextFontPagesDef::TextFontPagesDef()
2013-07-11 02:59:05 +08:00
{
}
TextFontPagesDef::~TextFontPagesDef()
{
int numPages = _pages.size();
for( int c = 0; c<numPages; ++c )
{
if (_pages[c])
delete _pages[c];
}
}
2013-07-19 05:11:12 +08:00
TextImage::TextImage(): _font(0), _fontRender(0), _fontPages(0)
2013-07-11 02:59:05 +08:00
{
}
TextImage::~TextImage()
{
if (_fontPages)
delete _fontPages;
if (_font)
_font->release();
2013-07-19 05:11:12 +08:00
if (_fontRender)
delete _fontRender;
2013-07-11 02:59:05 +08:00
}
bool TextImage::initWithString(const char * pText, int nWidth, int nHeight, const char * pFontName, int nSize, bool releaseRAWData)
{
// carloX
bool textIsUTF16 = false;
2013-07-20 01:33:26 +08:00
// create the reference to the font we want to use
if ( !createFontRef(pFontName, nSize) )
return false;
2013-07-20 01:33:26 +08:00
// generate the glyphs for the requested text (glyphs are latter's bounding boxes)
if ( !generateTextGlyphs(pText) )
return false;
2013-07-19 05:11:12 +08:00
Size constrainSize;
unsigned short int *strUTF16 = 0;
int stringNumChars;
if ( textIsUTF16 )
{
2013-07-20 01:33:26 +08:00
strUTF16 = (unsigned short int *)pText;
stringNumChars = cc_wcslen(strUTF16);
}
else
{
// string needs to go to unicode
strUTF16 = _font->getUTF16Text(pText, stringNumChars);
}
2013-07-20 01:33:26 +08:00
if (!strUTF16 || !stringNumChars)
return false;
2013-07-20 01:33:26 +08:00
// create all the needed pages
if (!createPageDefinitions(strUTF16, nWidth, nHeight, _font->getFontMaxHeight()))
return false;
2013-07-20 01:33:26 +08:00
// release the original string if needed
if ( !textIsUTF16 )
delete [] strUTF16;
2013-07-20 01:33:26 +08:00
// actually create the needed images
return createImageDataFromPages(_fontPages, releaseRAWData);
}
bool TextImage::createPageDefinitions(unsigned short int *inText, int imageWidth, int imageHeight, int lineHeight)
{
bool needToReleaseText = false;
int delta = 0;
int currentPage = 0;
float currentY = 0.0;
2013-07-20 01:33:26 +08:00
//
unsigned short int *strUTF16 = inText;
if (_fontPages)
delete _fontPages;
// create pages for the font
2013-07-20 01:33:26 +08:00
_fontPages = new TextFontPagesDef();
if (!_fontPages)
return false;
// create the first page (ther is going to be at least one page)
2013-07-20 01:33:26 +08:00
TextPageDef *currentPageDef = new TextPageDef(currentPage, imageWidth, imageHeight);
if ( !currentPageDef )
return false;
// add the current page
_fontPages->addPage(currentPageDef);
2013-07-20 01:33:26 +08:00
// work out creating pages
do {
// choose texture page
2013-07-20 01:33:26 +08:00
if ( ( currentY + lineHeight ) > imageHeight )
{
currentY = 0;
currentPage += 1;
// create a new page and add
2013-07-20 01:33:26 +08:00
currentPageDef = new TextPageDef(currentPage, imageWidth, imageHeight);
if ( !currentPageDef )
return false;
_fontPages->addPage(currentPageDef);
}
// get the new fitting string
Size tempSize;
2013-07-20 01:33:26 +08:00
tempSize.width = imageWidth;
tempSize.height = imageHeight;
// figure out how many glyphs fit in this line
int newLineSize = 0;
2013-07-20 01:33:26 +08:00
int numFittingChar = getNumGlyphsFittingInSize(_textGlyphs, strUTF16, _font, &tempSize, newLineSize);
// crete the temporary new string
unsigned short int *pTempString = 0;
pTempString = _font->trimUTF16Text(strUTF16, 0, (numFittingChar - 1));
// create the new line and add to the current page
TextLineDef *newLine = new TextLineDef(0.0, currentY, newLineSize, lineHeight);
if ( !newLine )
return false;
// add all the glyphs to this line
addGlyphsToLine(newLine, (const char *)pTempString, true);
// add the line the to current page
currentPageDef->addLine(newLine);
// can now release the string
delete [] pTempString;
// create the new string
int stringLenght = _font->getUTF16TextLenght(strUTF16);
delta = (stringLenght - numFittingChar);
// there is still some leftover, need to work on it
if ( delta )
{
// create the new string
unsigned short int *tempS = _font->trimUTF16Text(strUTF16, numFittingChar, (stringLenght - 1));
2013-07-20 01:33:26 +08:00
if (needToReleaseText)
delete [] strUTF16;
2013-07-20 01:33:26 +08:00
// a copy of the string has been created, so next time I'll need to release it
needToReleaseText = true;
// assign pointer
strUTF16 = tempS;
}
// go to next line
currentY += lineHeight;
} while( delta );
2013-07-20 01:33:26 +08:00
if (needToReleaseText)
delete [] strUTF16;
2013-07-20 01:33:26 +08:00
return true;
}
int TextImage::getNumGlyphsFittingInSize(std::map<unsigned short int, GlyphDef> &glyphDefs, unsigned short int *strUTF8, Font *pFont, Size *constrainSize, int &outNewSize)
{
if (!strUTF8)
return NULL;
float widthWithBBX = 0.0f;
float lastWidth = 0.0f;
// get the string to UTF8
int numChar = cc_wcslen(strUTF8);
for (int c = 0; c<numChar; ++c)
{
widthWithBBX += (glyphDefs[strUTF8[c]].getRect().size.width + glyphDefs[strUTF8[c]].getPadding());
if (widthWithBBX >= constrainSize->width)
{
outNewSize = lastWidth;
return c;
}
lastWidth = widthWithBBX;
}
outNewSize = constrainSize->width;
return numChar;
2013-07-11 02:59:05 +08:00
}
bool TextImage::createFontRef(const char *fontName, int fontSize)
{
if (_font)
{
_font->release();
2013-07-11 02:59:05 +08:00
_font = 0;
}
2013-07-19 05:11:12 +08:00
// carloX
_font = new FontFreeType();
2013-07-19 05:11:12 +08:00
if (!_font)
return false;
_font->retain();
2013-07-19 05:11:12 +08:00
if( !_font->createFontObject(fontName, fontSize))
return false;
return true;
}
2013-07-11 02:59:05 +08:00
bool TextImage::createFontRender()
{
if (!_font)
return false;
2013-07-11 02:59:05 +08:00
if (_fontRender)
{
delete _fontRender;
_fontRender = 0;
}
2013-07-19 05:11:12 +08:00
// carloX
_fontRender = new FontRenderFreeType(_font);
2013-07-11 02:59:05 +08:00
if (!_fontRender)
2013-07-11 02:59:05 +08:00
return false;
return true;
}
bool TextImage::addGlyphsToLine(TextLineDef *line, const char *lineText, bool textIsUTF16)
2013-07-11 02:59:05 +08:00
{
if (!_font)
return false;
int numLetters = 0;
unsigned short int *UTF16string = 0;
if (textIsUTF16)
{
UTF16string = (unsigned short int *) lineText;
numLetters = cc_wcslen(UTF16string);
}
else
{
UTF16string = _font->getUTF16Text(lineText, numLetters);
}
for (int c=0; c<numLetters; ++c)
{
_textGlyphs[UTF16string[c]].setCommonHeight(line->getHeight());
line->addGlyph(_textGlyphs[UTF16string[c]] );
}
if(!textIsUTF16)
delete [] UTF16string;
return true;
}
bool TextImage::generateTextGlyphs(const char * pText)
{
2013-07-20 01:33:26 +08:00
if (!_font)
return false;
int numGlyphs = 0;
2013-07-20 01:33:26 +08:00
GlyphDef *pNewGlyphs = _font->getGlyphDefintionsForText(pText, numGlyphs);
if (!pNewGlyphs)
return false;
2013-07-20 01:33:26 +08:00
if (!_textGlyphs.empty())
_textGlyphs.clear();
for (int c=0; c < numGlyphs; ++c)
_textGlyphs[pNewGlyphs[c].getUTF8Letter()] = pNewGlyphs[c];
delete [] pNewGlyphs;
return true;
}
2013-07-11 02:59:05 +08:00
bool TextImage::createImageDataFromPages(TextFontPagesDef *thePages, bool releaseRAWData)
{
int numPages = thePages->getNumPages();
if (!numPages)
return false;
2013-07-11 02:59:05 +08:00
for (int c = 0; c < numPages; ++c)
{
unsigned char *pPageData = 0;
2013-07-19 05:11:12 +08:00
pPageData = preparePageGlyphData(thePages->getPageAt(c));
2013-07-11 02:59:05 +08:00
if (pPageData)
{
// set the page data
thePages->getPageAt(c)->setPageData(pPageData);
// crete page texture and relase RAW data
thePages->getPageAt(c)->preparePageTexture(releaseRAWData);
}
else
{
return false;
}
2013-07-11 02:59:05 +08:00
}
return true;
}
2013-07-19 05:11:12 +08:00
unsigned char * TextImage::preparePageGlyphData(TextPageDef *thePage)
2013-07-11 02:59:05 +08:00
{
if ( !_fontRender )
2013-07-11 02:59:05 +08:00
{
createFontRender();
}
if (_fontRender)
{
2013-07-19 05:11:12 +08:00
return _fontRender->preparePageGlyphData(thePage);
2013-07-11 02:59:05 +08:00
}
else
{
return 0;
}
}
NS_CC_END