From 59393c2fef5b53782bbdf6adb3e3cd75e44e233d Mon Sep 17 00:00:00 2001 From: James Chen Date: Thu, 22 Mar 2012 09:46:07 +0800 Subject: [PATCH] issue #1056: Update CCLabelBMFont. --- cocos2dx/include/CCLabelBMFont.h | 329 +++--- cocos2dx/label_nodes/CCLabelBMFont.cpp | 1376 ++++++++++++++++-------- 2 files changed, 1096 insertions(+), 609 deletions(-) diff --git a/cocos2dx/include/CCLabelBMFont.h b/cocos2dx/include/CCLabelBMFont.h index d424eedaa0..521fcc74a2 100755 --- a/cocos2dx/include/CCLabelBMFont.h +++ b/cocos2dx/include/CCLabelBMFont.h @@ -23,175 +23,198 @@ 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. -Use any of these editors to generate BMFonts: - http://glyphdesigner.71squared.com/ (Commercial, Mac OS X) - http://www.n4te.com/hiero/hiero.jnlp (Free, Java) - http://slick.cokeandcode.com/demos/hiero.jnlp (Free, Java) +Use any of these editors to generate BMFonts: + http://glyphdesigner.71squared.com/ (Commercial, Mac OS X) + http://www.n4te.com/hiero/hiero.jnlp (Free, Java) + http://slick.cokeandcode.com/demos/hiero.jnlp (Free, Java) http://www.angelcode.com/products/bmfont/ (Free, Windows only) ****************************************************************************/ #ifndef __CCBITMAP_FONT_ATLAS_H__ #define __CCBITMAP_FONT_ATLAS_H__ + #include "CCSpriteBatchNode.h" #include +#include +#include +#include -namespace cocos2d{ +NS_CC_BEGIN - struct _KerningHashElement; +enum { + kCCLabelAutomaticWidth = -1, +}; - /** - @struct ccBMFontDef - BMFont definition +struct _KerningHashElement; +struct _FontDefHashElement; + +/** +@struct ccBMFontDef +BMFont definition +*/ +typedef struct _BMFontDef { + //! ID of the character + unsigned int charID; + //! origin and size of the font + CCRect rect; + //! The X amount the image should be offset when drawing the image (in pixels) + short xOffset; + //! The Y amount the image should be offset when drawing the image (in pixels) + short yOffset; + //! The amount to move the current position after drawing the character (in pixels) + short xAdvance; +} ccBMFontDef; + +/** @struct ccBMFontPadding +BMFont padding +@since v0.8.2 +*/ +typedef struct _BMFontPadding { + /// padding left + int left; + /// padding top + int top; + /// padding right + int right; + /// padding bottom + int bottom; +} ccBMFontPadding; + + +/** @brief CCBMFontConfiguration has parsed configuration of the the .fnt file +@since v0.8 +*/ +class CC_DLL CCBMFontConfiguration : public CCObject +{ + // XXX: Creating a public interface so that the bitmapFontArray[] is accesible +public://@public + // BMFont definitions + struct _FontDefHashElement* m_pFontDefDictionary; + + //! FNTConfig: Common Height + unsigned int m_uCommonHeight; + //! Padding + ccBMFontPadding m_tPadding; + //! atlas name + std::string m_sAtlasName; + //! values for kerning + struct _KerningHashElement *m_pKerningDictionary; +public: + CCBMFontConfiguration(); + virtual ~CCBMFontConfiguration(); + const char * description(); + /** allocates a CCBMFontConfiguration with a FNT file */ + static CCBMFontConfiguration * configurationWithFNTFile(const char *FNTfile); + /** initializes a BitmapFontConfiguration with a FNT file */ + bool initWithFNTfile(const char *FNTfile); +private: + void parseConfigFile(const char *controlFile); + void parseCharacterDefinition(std::string line, ccBMFontDef *characterDefinition); + void parseInfoArguments(std::string line); + void parseCommonArguments(std::string line); + void parseImageFileName(std::string line, const char *fntFile); + void parseKerningCapacity(std::string line); + void parseKerningEntry(std::string line); + void purgeKerningDictionary(); + void purgeFontDefDictionary(); +}; + +/** @brief CCLabelBMFont is a subclass of CCSpriteSheet. + +Features: +- Treats each character like a CCSprite. This means that each individual character can be: +- rotated +- scaled +- translated +- tinted +- chage the opacity +- It can be used as part of a menu item. +- anchorPoint can be used to align the "label" +- Supports AngelCode text format + +Limitations: +- All inner characters are using an anchorPoint of (0.5f, 0.5f) and it is not recommend to change it +because it might affect the rendering + +CCLabelBMFont implements the protocol CCLabelProtocol, like CCLabel and CCLabelAtlas. +CCLabelBMFont has the flexibility of CCLabel, the speed of CCLabelAtlas and all the features of CCSprite. +If in doubt, use CCLabelBMFont instead of CCLabelAtlas / CCLabel. + +Supported editors: +http://glyphdesigner.71squared.com/ (Commercial, Mac OS X) +http://www.n4te.com/hiero/hiero.jnlp (Free, Java) +http://slick.cokeandcode.com/demos/hiero.jnlp (Free, Java) +http://www.angelcode.com/products/bmfont/ (Free, Windows only) + +@since v0.8 +*/ + +class CC_DLL CCLabelBMFont : public CCSpriteBatchNode, public CCLabelProtocol, public CCRGBAProtocol +{ + /** conforms to CCRGBAProtocol protocol */ + CC_PROPERTY(GLubyte, m_cOpacity, Opacity) + /** conforms to CCRGBAProtocol protocol */ + CC_PROPERTY_PASS_BY_REF(ccColor3B, m_tColor, Color) + /** conforms to CCRGBAProtocol protocol */ + CC_PROPERTY(bool, m_bIsOpacityModifyRGB, IsOpacityModifyRGB) +protected: + // string to render + unsigned short* m_sString; + std::string m_sString_initial; + CCBMFontConfiguration *m_pConfiguration; + CCTextAlignment m_pAlignment; + float m_fWidth; + bool m_bLineBreakWithoutSpaces; + // offset of the texture atlas + CCPoint m_tImageOffset; +public: + CCLabelBMFont(); + + virtual ~CCLabelBMFont(); + /** Purges the cached data. + Removes from memory the cached configurations and the atlas name dictionary. + @since v0.99.3 */ - typedef struct _BMFontDef { - //! ID of the character - unsigned int charID; - //! origin and size of the font - CCRect rect; - //! The X amount the image should be offset when drawing the image (in pixels) - int xOffset; - //! The Y amount the image should be offset when drawing the image (in pixels) - int yOffset; - //! The amount to move the current position after drawing the character (in pixels) - int xAdvance; - } ccBMFontDef; + static void purgeCachedData(); + /** creates a bitmap font altas with an initial string and the FNT file */ + static CCLabelBMFont * labelWithString(const char *str, const char *fntFile); + static CCLabelBMFont * labelWithString(const char *str, const char *fntFile, float width, CCTextAlignment alignment); + static CCLabelBMFont * labelWithString(const char *str, const char *fntFile, float width, CCTextAlignment alignment, CCPoint imageOffset); - /** @struct ccBMFontPadding - BMFont padding - @since v0.8.2 - */ - typedef struct _BMFontPadding { - /// padding left - int left; - /// padding top - int top; - /// padding right - int right; - /// padding bottom - int bottom; - } ccBMFontPadding; - - - /** @brief CCBMFontConfiguration has parsed configuration of the the .fnt file - @since v0.8 - */ - class CC_DLL CCBMFontConfiguration : public CCObject - { - // XXX: Creating a public interface so that the bitmapFontArray[] is accesible - public://@public - //! The characters building up the font - std::map* m_pBitmapFontArray; - - //! FNTConfig: Common Height - unsigned int m_uCommonHeight; - //! Padding - ccBMFontPadding m_tPadding; - //! atlas name - std::string m_sAtlasName; - //! values for kerning - struct _KerningHashElement *m_pKerningDictionary; - public: - CCBMFontConfiguration(); - virtual ~CCBMFontConfiguration(); - char * description(); - /** allocates a CCBMFontConfiguration with a FNT file */ - static CCBMFontConfiguration * configurationWithFNTFile(const char *FNTfile); - /** initializes a BitmapFontConfiguration with a FNT file */ - bool initWithFNTfile(const char *FNTfile); - private: - void parseConfigFile(const char *controlFile); - void parseCharacterDefinition(std::string line, ccBMFontDef *characterDefinition); - void parseInfoArguments(std::string line); - void parseCommonArguments(std::string line); - void parseImageFileName(std::string line, const char *fntFile); - void parseKerningCapacity(std::string line); - void parseKerningEntry(std::string line); - void purgeKerningDictionary(); - }; - - /** @brief CCLabelBMFont is a subclass of CCSpriteSheet. - - Features: - - Treats each character like a CCSprite. This means that each individual character can be: - - rotated - - scaled - - translated - - tinted - - chage the opacity - - It can be used as part of a menu item. - - anchorPoint can be used to align the "label" - - Supports AngelCode text format - - Limitations: - - All inner characters are using an anchorPoint of (0.5f, 0.5f) and it is not recommend to change it - because it might affect the rendering - - CCLabelBMFont implements the protocol CCLabelProtocol, like CCLabel and CCLabelAtlas. - CCLabelBMFont has the flexibility of CCLabel, the speed of CCLabelAtlas and all the features of CCSprite. - If in doubt, use CCLabelBMFont instead of CCLabelAtlas / CCLabel. - - Supported editors: - http://glyphdesigner.71squared.com/ (Commercial, Mac OS X) - http://www.n4te.com/hiero/hiero.jnlp (Free, Java) - http://slick.cokeandcode.com/demos/hiero.jnlp (Free, Java) - http://www.angelcode.com/products/bmfont/ (Free, Windows only) - - @since v0.8 - */ - - class CC_DLL CCLabelBMFont : public CCSpriteBatchNode, public CCLabelProtocol, public CCRGBAProtocol - { - /** conforms to CCRGBAProtocol protocol */ - CC_PROPERTY(GLubyte, m_cOpacity, Opacity) - /** conforms to CCRGBAProtocol protocol */ - CC_PROPERTY_PASS_BY_REF(ccColor3B, m_tColor, Color) - /** conforms to CCRGBAProtocol protocol */ - CC_PROPERTY(bool, m_bIsOpacityModifyRGB, IsOpacityModifyRGB) - protected: - // string to render - std::string m_sString; - CCBMFontConfiguration *m_pConfiguration; - public: - CCLabelBMFont() - : m_cOpacity(0) - , m_bIsOpacityModifyRGB(false) - , m_sString("") - , m_pConfiguration(NULL) - {} - virtual ~CCLabelBMFont(); - /** Purges the cached data. - Removes from memory the cached configurations and the atlas name dictionary. - @since v0.99.3 - */ - static void purgeCachedData(); - /** creates a bitmap font altas with an initial string and the FNT file */ - static CCLabelBMFont * labelWithString(const char *str, const char *fntFile); - - /** init a bitmap font altas with an initial string and the FNT file */ - bool initWithString(const char *str, const char *fntFile); - /** updates the font chars based on the string to render */ - void createFontChars(); - // super method - virtual void setString(const char *label); - virtual const char* getString(void); - virtual void setCString(const char *label); - virtual void setAnchorPoint(const CCPoint& var); + /** init a bitmap font altas with an initial string and the FNT file */ + bool initWithString(const char *str, const char *fntFile, float width, CCTextAlignment alignment, CCPoint imageOffset); + bool initWithString(const char *str, const char *fntFile, float width, CCTextAlignment alignment); + bool initWithString(const char *str, const char *fntFile); + /** updates the font chars based on the string to render */ + void createFontChars(); + // super method + virtual void setString(const char *label); + virtual void setString(const char *label, bool fromUpdate); + virtual void updateString(bool fromUpdate); + virtual const char* getString(void); + virtual void setCString(const char *label); + virtual void setAnchorPoint(const CCPoint& var); + virtual void updateLabel(); + virtual void setAlignment(CCTextAlignment alignment); + virtual void setWidth(float width); + virtual void setLineBreakWithoutSpace(bool breakWithoutSpace); #if CC_LABELBMFONT_DEBUG_DRAW - virtual void draw(); + virtual void draw(); #endif // CC_LABELBMFONT_DEBUG_DRAW - private: - char * atlasNameFromFntFile(const char *fntFile); - int kerningAmountForFirst(unsigned short first, unsigned short second); +private: + char * atlasNameFromFntFile(const char *fntFile); + int kerningAmountForFirst(unsigned short first, unsigned short second); - }; +}; - /** Free function that parses a FNT file a place it on the cache - */ - CC_DLL CCBMFontConfiguration * FNTConfigLoadFile( const char *file ); - /** Purges the FNT config cache - */ - CC_DLL void FNTConfigRemoveCache( void ); -}// namespace cocos2d +/** Free function that parses a FNT file a place it on the cache +*/ +CC_DLL CCBMFontConfiguration * FNTConfigLoadFile( const char *file ); +/** Purges the FNT config cache +*/ +CC_DLL void FNTConfigRemoveCache( void ); + +NS_CC_END #endif //__CCBITMAP_FONT_ATLAS_H__ diff --git a/cocos2dx/label_nodes/CCLabelBMFont.cpp b/cocos2dx/label_nodes/CCLabelBMFont.cpp index 7bbacf32e4..d860440b01 100644 --- a/cocos2dx/label_nodes/CCLabelBMFont.cpp +++ b/cocos2dx/label_nodes/CCLabelBMFont.cpp @@ -31,419 +31,25 @@ http://www.angelcode.com/products/bmfont/ (Free, Windows only) ****************************************************************************/ #include "CCLabelBMFont.h" -#include "CCDirector.h" +#include "CCString.h" #include "platform/platform.h" #include "CCDictionary.h" #include "CCConfiguration.h" #include "CCDrawingPrimitives.h" #include "CCSprite.h" #include "CCPointExtension.h" - #include "CCFileUtils.h" #include "support/data_support/uthash.h" +#include "CCDirector.h" NS_CC_BEGIN - -// -//FNTConfig Cache - free functions -// -CCDictionary* configurations = NULL; -CCBMFontConfiguration* FNTConfigLoadFile( const char *fntFile) -{ - CCBMFontConfiguration* pRet = NULL; - if( configurations == NULL ) - { - configurations = new CCDictionary(); - } - - pRet = (CCBMFontConfiguration*)configurations->objectForKey(fntFile); - if( pRet == NULL ) - { - pRet = CCBMFontConfiguration::configurationWithFNTFile(fntFile); - configurations->setObject(pRet, fntFile); - } - - return pRet; -} - -void FNTConfigRemoveCache( void ) -{ - if (configurations) - { - configurations->removeAllObjects(); - CC_SAFE_RELEASE_NULL(configurations); - } -} -// -//Hash Element -// -// Equal function for targetSet. -typedef struct _KerningHashElement -{ - int key; // key for the hash. 16-bit for 1st element, 16-bit for 2nd element - int amount; - UT_hash_handle hh; -} tKerningHashElement; -// -//BitmapFontConfiguration -// - -CCBMFontConfiguration * CCBMFontConfiguration::configurationWithFNTFile(const char *FNTfile) -{ - CCBMFontConfiguration * pRet = new CCBMFontConfiguration(); - if (pRet->initWithFNTfile(FNTfile)) - { - pRet->autorelease(); - return pRet; - } - CC_SAFE_DELETE(pRet); - return NULL; -} - -bool CCBMFontConfiguration::initWithFNTfile(const char *FNTfile) -{ - CCAssert(FNTfile != NULL && strlen(FNTfile)!=0, ""); - m_pKerningDictionary = NULL; - this->parseConfigFile(FNTfile); - return true; -} - -CCBMFontConfiguration::CCBMFontConfiguration() - : m_pBitmapFontArray(new std::map) - , m_uCommonHeight(0) - , m_pKerningDictionary(NULL) -{ - -} - -CCBMFontConfiguration::~CCBMFontConfiguration() -{ - CCLOGINFO( "cocos2d: deallocing CCBMFontConfiguration" ); - CC_SAFE_DELETE(m_pBitmapFontArray); - this->purgeKerningDictionary(); - m_sAtlasName.clear(); -} -char * CCBMFontConfiguration::description(void) -{ - char *ret = new char[100]; - sprintf(ret, "", HASH_COUNT(m_pKerningDictionary), m_sAtlasName.c_str()); - return ret; -} -void CCBMFontConfiguration::purgeKerningDictionary() -{ - tKerningHashElement *current; - while(m_pKerningDictionary) - { - current = m_pKerningDictionary; - HASH_DEL(m_pKerningDictionary,current); - free(current); - } -} -void CCBMFontConfiguration::parseConfigFile(const char *controlFile) -{ - std::string fullpath = CCFileUtils::fullPathFromRelativePath(controlFile); - - CCFileData data(fullpath.c_str(), "rb"); - unsigned long nBufSize = data.getSize(); - char* pBuffer = (char*) data.getBuffer(); - - CCAssert(pBuffer, "CCBMFontConfiguration::parseConfigFile | Open file error."); - - if (!pBuffer) - { - return; - } - - // parse spacing / padding - std::string line; - std::string strLeft(pBuffer, nBufSize); - while (strLeft.length() > 0) - { - int pos = strLeft.find('\n'); - - if (pos != (int)std::string::npos) - { - // the data is more than a line.get one line - line = strLeft.substr(0, pos); - strLeft = strLeft.substr(pos + 1); - } - else - { - // get the left data - line = strLeft; - strLeft.erase(); - } - - if(line.substr(0,strlen("info face")) == "info face") - { - // XXX: info parsing is incomplete - // Not needed for the Hiero editors, but needed for the AngelCode editor - // [self parseInfoArguments:line]; - this->parseInfoArguments(line); - } - // Check to see if the start of the line is something we are interested in - else if(line.substr(0,strlen("common lineHeight")) == "common lineHeight") - { - this->parseCommonArguments(line); - } - else if(line.substr(0,strlen("page id")) == "page id") - { - this->parseImageFileName(line, controlFile); - } - else if(line.substr(0,strlen("chars c")) == "chars c") - { - // Ignore this line - } - else if(line.substr(0,strlen("char")) == "char") - { - // Parse the current line and create a new CharDef - ccBMFontDef characterDefinition; - this->parseCharacterDefinition(line, &characterDefinition); - - // Add the CharDef returned to the charArray - (*m_pBitmapFontArray)[ characterDefinition.charID ] = characterDefinition; - } - else if(line.substr(0,strlen("kernings count")) == "kernings count") - { - this->parseKerningCapacity(line); - } - else if(line.substr(0,strlen("kerning first")) == "kerning first") - { - this->parseKerningEntry(line); - } - } -} -void CCBMFontConfiguration::parseImageFileName(std::string line, const char *fntFile) -{ - ////////////////////////////////////////////////////////////////////////// - // line to parse: - // page id=0 file="bitmapFontTest.png" - ////////////////////////////////////////////////////////////////////////// - - // page ID. Sanity check - int index = line.find('=')+1; - int index2 = line.find(' ', index); - std::string value = line.substr(index, index2-index); - CCAssert(atoi(value.c_str()) == 0, "LabelBMFont file could not be found"); - // file - index = line.find('"')+1; - index2 = line.find('"', index); - value = line.substr(index, index2-index); - - m_sAtlasName = CCFileUtils::fullPathFromRelativeFile(value.c_str(), fntFile); -} -void CCBMFontConfiguration::parseInfoArguments(std::string line) -{ - ////////////////////////////////////////////////////////////////////////// - // possible lines to parse: - // info face="Script" size=32 bold=0 italic=0 charset="" unicode=1 stretchH=100 smooth=1 aa=1 padding=1,4,3,2 spacing=0,0 outline=0 - // info face="Cracked" size=36 bold=0 italic=0 charset="" unicode=0 stretchH=100 smooth=1 aa=1 padding=0,0,0,0 spacing=1,1 - ////////////////////////////////////////////////////////////////////////// - - // padding - int index = line.find("padding="); - int index2 = line.find(' ', index); - std::string value = line.substr(index, index2-index); - sscanf(value.c_str(), "padding=%d,%d,%d,%d", &m_tPadding.top, &m_tPadding.right, &m_tPadding.bottom, &m_tPadding.left); - CCLOG("cocos2d: padding: %d,%d,%d,%d", m_tPadding.left, m_tPadding.top, m_tPadding.right, m_tPadding.bottom); -} -void CCBMFontConfiguration::parseCommonArguments(std::string line) -{ - ////////////////////////////////////////////////////////////////////////// - // line to parse: - // common lineHeight=104 base=26 scaleW=1024 scaleH=512 pages=1 packed=0 - ////////////////////////////////////////////////////////////////////////// - - // Height - int index = line.find("lineHeight="); - int index2 = line.find(' ', index); - std::string value = line.substr(index, index2-index); - sscanf(value.c_str(), "lineHeight=%u", &m_uCommonHeight); - // scaleW. sanity check - index = line.find("scaleW=") + strlen("scaleW="); - index2 = line.find(' ', index); - value = line.substr(index, index2-index); - CCAssert(atoi(value.c_str()) <= CCConfiguration::sharedConfiguration()->getMaxTextureSize(), "CCLabelBMFont: page can't be larger than supported"); - // scaleH. sanity check - index = line.find("scaleH=") + strlen("scaleH="); - index2 = line.find(' ', index); - value = line.substr(index, index2-index); - CCAssert(atoi(value.c_str()) <= CCConfiguration::sharedConfiguration()->getMaxTextureSize(), "CCLabelBMFont: page can't be larger than supported"); - // pages. sanity check - index = line.find("pages=") + strlen("pages="); - index2 = line.find(' ', index); - value = line.substr(index, index2-index); - CCAssert(atoi(value.c_str()) == 1, "CCBitfontAtlas: only supports 1 page"); - - // packed (ignore) What does this mean ?? -} -void CCBMFontConfiguration::parseCharacterDefinition(std::string line, ccBMFontDef *characterDefinition) -{ - ////////////////////////////////////////////////////////////////////////// - // line to parse: - // char id=32 x=0 y=0 width=0 height=0 xoffset=0 yoffset=44 xadvance=14 page=0 chnl=0 - ////////////////////////////////////////////////////////////////////////// - - // Character ID - int index = line.find("id="); - int index2 = line.find(' ', index); - std::string value = line.substr(index, index2-index); - sscanf(value.c_str(), "id=%u", &characterDefinition->charID); - - // Character x - index = line.find("x="); - index2 = line.find(' ', index); - value = line.substr(index, index2-index); - sscanf(value.c_str(), "x=%f", &characterDefinition->rect.origin.x); - // Character y - index = line.find("y="); - index2 = line.find(' ', index); - value = line.substr(index, index2-index); - sscanf(value.c_str(), "y=%f", &characterDefinition->rect.origin.y); - // Character width - index = line.find("width="); - index2 = line.find(' ', index); - value = line.substr(index, index2-index); - sscanf(value.c_str(), "width=%f", &characterDefinition->rect.size.width); - // Character height - index = line.find("height="); - index2 = line.find(' ', index); - value = line.substr(index, index2-index); - sscanf(value.c_str(), "height=%f", &characterDefinition->rect.size.height); - // Character xoffset - index = line.find("xoffset="); - index2 = line.find(' ', index); - value = line.substr(index, index2-index); - sscanf(value.c_str(), "xoffset=%d", &characterDefinition->xOffset); - // Character yoffset - index = line.find("yoffset="); - index2 = line.find(' ', index); - value = line.substr(index, index2-index); - sscanf(value.c_str(), "yoffset=%d", &characterDefinition->yOffset); - // Character xadvance - index = line.find("xadvance="); - index2 = line.find(' ', index); - value = line.substr(index, index2-index); - sscanf(value.c_str(), "xadvance=%d", &characterDefinition->xAdvance); -} -void CCBMFontConfiguration::parseKerningCapacity(std::string line) -{ - // When using uthash there is not need to parse the capacity. - - // CCAssert(!kerningDictionary, @"dictionary already initialized"); - // - // // Break the values for this line up using = - // CCMutableArray *values = [line componentsSeparatedByString:@"="]; - // NSEnumerator *nse = [values objectEnumerator]; - // CCString *propertyValue; - // - // // We need to move past the first entry in the array before we start assigning values - // [nse nextObject]; - // - // // count - // propertyValue = [nse nextObject]; - // int capacity = [propertyValue intValue]; - // - // if( capacity != -1 ) - // kerningDictionary = ccHashSetNew(capacity, targetSetEql); -} -void CCBMFontConfiguration::parseKerningEntry(std::string line) -{ - ////////////////////////////////////////////////////////////////////////// - // line to parse: - // kerning first=121 second=44 amount=-7 - ////////////////////////////////////////////////////////////////////////// - - // first - int first; - int index = line.find("first="); - int index2 = line.find(' ', index); - std::string value = line.substr(index, index2-index); - sscanf(value.c_str(), "first=%d", &first); - - // second - int second; - index = line.find("second="); - index2 = line.find(' ', index); - value = line.substr(index, index2-index); - sscanf(value.c_str(), "second=%d", &second); - - // amount - int amount; - index = line.find("amount="); - index2 = line.find(' ', index); - value = line.substr(index, index2-index); - sscanf(value.c_str(), "amount=%d", &amount); - - tKerningHashElement *element = (tKerningHashElement *)calloc( sizeof( *element ), 1 ); - element->amount = amount; - element->key = (first<<16) | (second&0xffff); - HASH_ADD_INT(m_pKerningDictionary,key, element); -} -// -//CCLabelBMFont -// - -//LabelBMFont - Purge Cache -void CCLabelBMFont::purgeCachedData() -{ - FNTConfigRemoveCache(); -} - -//LabelBMFont - Creation & Init -CCLabelBMFont *CCLabelBMFont::labelWithString(const char *str, const char *fntFile) -{ - CCLabelBMFont *pRet = new CCLabelBMFont(); - if(pRet && pRet->initWithString(str, fntFile)) - { - pRet->autorelease(); - return pRet; - } - CC_SAFE_DELETE(pRet); - return NULL; -} - -bool CCLabelBMFont::initWithString(const char *theString, const char *fntFile) -{ - CCAssert(theString != NULL, ""); - CC_SAFE_RELEASE(m_pConfiguration);// allow re-init - m_pConfiguration = FNTConfigLoadFile(fntFile); - m_pConfiguration->retain(); - CCAssert( m_pConfiguration, "Error creating config for LabelBMFont"); - - if (CCSpriteBatchNode::initWithFile(m_pConfiguration->m_sAtlasName.c_str(), strlen(theString))) - { - m_cOpacity = 255; - m_tColor = ccWHITE; - m_tContentSize = CCSizeZero; - m_bIsOpacityModifyRGB = m_pobTextureAtlas->getTexture()->getHasPremultipliedAlpha(); - setAnchorPoint(ccp(0.5f, 0.5f)); - this->setString(theString); - return true; - } - return false; -} -CCLabelBMFont::~CCLabelBMFont() -{ - m_sString.clear(); - CC_SAFE_RELEASE(m_pConfiguration); -} - -// LabelBMFont - Atlas generation -int CCLabelBMFont::kerningAmountForFirst(unsigned short first, unsigned short second) -{ - int ret = 0; - unsigned int key = (first<<16) | (second & 0xffff); - - if( m_pConfiguration->m_pKerningDictionary ) { - tKerningHashElement *element = NULL; - HASH_FIND_INT(m_pConfiguration->m_pKerningDictionary, &key, element); - if(element) - ret = element->amount; - } - return ret; -} +typedef struct _FontDefHashElement +{ + unsigned int key; // key. Font Unicode value + ccBMFontDef fontDef; // font definition + UT_hash_handle hh; +} tFontDefHashElement; static int cc_wcslen(const unsigned short* str) { @@ -539,12 +145,103 @@ static const char *const g_utf8_skip = utf8_skip_data; #define cc_utf8_next_char(p) (char *)((p) + g_utf8_skip[*(unsigned char *)(p)]) +/* + * @str: the string to search through. + * @c: the character to find. + * + * Returns the index of the first occurrence of the character, if found. Otherwise -1 is returned. + * + * Return value: the index of the first occurrence of the character if found or -1 otherwise. + * */ +static unsigned int cc_utf8_find_char(std::vector str, unsigned short c) +{ + unsigned int len = str.size(); + + for (unsigned int i = 0; i < len; ++i) + if (str[i] == c) return i; + + return -1; +} + +/* + * @str: the string to search through. + * @c: the character to not look for. + * + * Return value: the index of the last character that is not c. + * */ +static unsigned int cc_utf8_find_last_not_char(std::vector str, unsigned short c) +{ + int len = str.size(); + + int i = len - 1; + for (; i >= 0; --i) + if (str[i] != c) return i; + + return i; +} + +/* + * @str: the string to trim + * @index: the index to start trimming from. + * + * Trims str st str=[0, index) after the operation. + * + * Return value: the trimmed string. + * */ +static void cc_utf8_trim_from(std::vector* str, int index) +{ + int size = str->size(); + if (index >= size || index < 0) + return; + + str->erase(str->begin() + index, str->begin() + size); +} + +/* + * @ch is the unicode character whitespace? + * + * Reference: http://en.wikipedia.org/wiki/Whitespace_character#Unicode + * + * Return value: weather the character is a whitespace character. + * */ +static bool isspace_unicode(unsigned short ch) +{ + return ch >= 0x0009 && ch <= 0x000D || ch == 0x0020 || ch == 0x0085 || ch == 0x00A0 || ch == 0x1680 + || (ch >= 0x2000 && ch <= 0x200A) || ch == 0x2028 || ch == 0x2029 || ch == 0x202F + || ch == 0x205F || ch == 0x3000; +} + +static void cc_utf8_trim_ws(std::vector* str) +{ + using namespace std; + int len = str->size(); + + if ( len <= 0 ) + return; + + int last_index = len - 1; + + // Only start trimming if the last character is whitespace.. + if (isspace_unicode((*str)[last_index])) + { + for (int i = last_index - 1; i >= 0; --i) + { + if (isspace_unicode((*str)[i])) + last_index = i; + else + break; + } + + cc_utf8_trim_from(str, last_index); + } +} + /* * g_utf8_strlen: * @p: pointer to the start of a UTF-8 encoded string. * @max: the maximum number of bytes to examine. If @max * is less than 0, then the string is assumed to be - * nul-terminated. If @max is 0, @p will not be examined and + * null-terminated. If @max is 0, @p will not be examined and * may be %NULL. * * Returns the length of the string in characters. @@ -620,6 +317,519 @@ cc_utf8_get_char (const char * p) return result; } +/* + * cc_utf8_from_cstr: + * @str_old: pointer to the start of a C string. + * + * Creates a utf8 string from a cstring. + * + * Return value: the newly created utf8 string. + * */ +static unsigned short* cc_utf8_from_cstr(const char* str_old) +{ + int len = cc_utf8_strlen(str_old, -1); + + unsigned short* str_new = new unsigned short[len + 1]; + str_new[len] = 0; + + for (int i = 0; i < len; ++i) + { + str_new[i] = cc_utf8_get_char(str_old); + str_old = cc_utf8_next_char(str_old); + } + + return str_new; +} + +static std::vector cc_utf8_vec_from_cstr(const unsigned short* str) +{ + int len = cc_wcslen(str); + std::vector str_new; + + for (int i = 0; i < len; ++i) + str_new.push_back(str[i]); + + return str_new; +} + +// +//FNTConfig Cache - free functions +// +CCDictionary *configurations = NULL; +CCBMFontConfiguration* FNTConfigLoadFile( const char *fntFile) +{ + CCBMFontConfiguration* pRet = NULL; + + if( configurations == NULL ) + { + configurations = new CCDictionary(); + } + + pRet = (CCBMFontConfiguration*)configurations->objectForKey(fntFile); + if( pRet == NULL ) + { + pRet = CCBMFontConfiguration::configurationWithFNTFile(fntFile); + configurations->setObject(pRet, fntFile); + } + + return pRet; +} + +void FNTConfigRemoveCache( void ) +{ + if (configurations) + { + configurations->removeAllObjects(); + CC_SAFE_RELEASE_NULL(configurations); + } +} + +// +//Hash Element +// +// Equal function for targetSet. +typedef struct _KerningHashElement +{ + int key; // key for the hash. 16-bit for 1st element, 16-bit for 2nd element + int amount; + UT_hash_handle hh; +} tKerningHashElement; +// +//BitmapFontConfiguration +// + +CCBMFontConfiguration * CCBMFontConfiguration::configurationWithFNTFile(const char *FNTfile) +{ + CCBMFontConfiguration * pRet = new CCBMFontConfiguration(); + if (pRet->initWithFNTfile(FNTfile)) + { + pRet->autorelease(); + return pRet; + } + CC_SAFE_DELETE(pRet); + return NULL; +} + +bool CCBMFontConfiguration::initWithFNTfile(const char *FNTfile) +{ + CCAssert(FNTfile != NULL && strlen(FNTfile)!=0, ""); + m_pKerningDictionary = NULL; + this->parseConfigFile(FNTfile); + return true; +} + +CCBMFontConfiguration::CCBMFontConfiguration() + : m_pFontDefDictionary(NULL) + , m_uCommonHeight(0) + , m_pKerningDictionary(NULL) +{ + +} + +CCBMFontConfiguration::~CCBMFontConfiguration() +{ + CCLOGINFO( "cocos2d: deallocing CCBMFontConfiguration" ); + this->purgeFontDefDictionary(); + this->purgeKerningDictionary(); + m_sAtlasName.clear(); +} + +const char* CCBMFontConfiguration::description(void) +{ + char* pBuf = new char[100]; + sprintf(pBuf, "", this, + HASH_COUNT(m_pFontDefDictionary), + HASH_COUNT(m_pKerningDictionary), + m_sAtlasName.c_str()); + CCString* pRet = new CCString(pBuf); + pRet->autorelease(); + CC_SAFE_DELETE_ARRAY(pBuf); + return pRet->c_str(); +} + +void CCBMFontConfiguration::purgeKerningDictionary() +{ + tKerningHashElement *current; + while(m_pKerningDictionary) + { + current = m_pKerningDictionary; + HASH_DEL(m_pKerningDictionary,current); + free(current); + } +} + +void CCBMFontConfiguration::purgeFontDefDictionary() +{ + tFontDefHashElement *current, *tmp; + + HASH_ITER(hh, m_pFontDefDictionary, current, tmp) { + HASH_DEL(m_pFontDefDictionary, current); + free(current); + } +} + + +void CCBMFontConfiguration::parseConfigFile(const char *controlFile) +{ + std::string fullpath = CCFileUtils::fullPathFromRelativePath(controlFile); + + CCFileData data(fullpath.c_str(), "rb"); + unsigned long nBufSize = data.getSize(); + char* pBuffer = (char*) data.getBuffer(); + + CCAssert(pBuffer, "CCBMFontConfiguration::parseConfigFile | Open file error."); + + if (!pBuffer) + { + return; + } + + // parse spacing / padding + std::string line; + std::string strLeft(pBuffer, nBufSize); + while (strLeft.length() > 0) + { + int pos = strLeft.find('\n'); + + if (pos != (int)std::string::npos) + { + // the data is more than a line.get one line + line = strLeft.substr(0, pos); + strLeft = strLeft.substr(pos + 1); + } + else + { + // get the left data + line = strLeft; + strLeft.erase(); + } + + if(line.substr(0,strlen("info face")) == "info face") + { + // XXX: info parsing is incomplete + // Not needed for the Hiero editors, but needed for the AngelCode editor + // [self parseInfoArguments:line]; + this->parseInfoArguments(line); + } + // Check to see if the start of the line is something we are interested in + else if(line.substr(0,strlen("common lineHeight")) == "common lineHeight") + { + this->parseCommonArguments(line); + } + else if(line.substr(0,strlen("page id")) == "page id") + { + this->parseImageFileName(line, controlFile); + } + else if(line.substr(0,strlen("chars c")) == "chars c") + { + // Ignore this line + } + else if(line.substr(0,strlen("char")) == "char") + { + // Parse the current line and create a new CharDef + tFontDefHashElement* element = (tFontDefHashElement*)malloc( sizeof(*element) ); + this->parseCharacterDefinition(line, &element->fontDef); + + element->key = element->fontDef.charID; + HASH_ADD_INT(m_pFontDefDictionary, key, element); + } + else if(line.substr(0,strlen("kernings count")) == "kernings count") + { + this->parseKerningCapacity(line); + } + else if(line.substr(0,strlen("kerning first")) == "kerning first") + { + this->parseKerningEntry(line); + } + } +} + +void CCBMFontConfiguration::parseImageFileName(std::string line, const char *fntFile) +{ + ////////////////////////////////////////////////////////////////////////// + // line to parse: + // page id=0 file="bitmapFontTest.png" + ////////////////////////////////////////////////////////////////////////// + + // page ID. Sanity check + int index = line.find('=')+1; + int index2 = line.find(' ', index); + std::string value = line.substr(index, index2-index); + CCAssert(atoi(value.c_str()) == 0, "LabelBMFont file could not be found"); + // file + index = line.find('"')+1; + index2 = line.find('"', index); + value = line.substr(index, index2-index); + + m_sAtlasName = CCFileUtils::fullPathFromRelativeFile(value.c_str(), fntFile); +} + +void CCBMFontConfiguration::parseInfoArguments(std::string line) +{ + ////////////////////////////////////////////////////////////////////////// + // possible lines to parse: + // info face="Script" size=32 bold=0 italic=0 charset="" unicode=1 stretchH=100 smooth=1 aa=1 padding=1,4,3,2 spacing=0,0 outline=0 + // info face="Cracked" size=36 bold=0 italic=0 charset="" unicode=0 stretchH=100 smooth=1 aa=1 padding=0,0,0,0 spacing=1,1 + ////////////////////////////////////////////////////////////////////////// + + // padding + int index = line.find("padding="); + int index2 = line.find(' ', index); + std::string value = line.substr(index, index2-index); + sscanf(value.c_str(), "padding=%d,%d,%d,%d", &m_tPadding.top, &m_tPadding.right, &m_tPadding.bottom, &m_tPadding.left); + CCLOG("cocos2d: padding: %d,%d,%d,%d", m_tPadding.left, m_tPadding.top, m_tPadding.right, m_tPadding.bottom); +} + +void CCBMFontConfiguration::parseCommonArguments(std::string line) +{ + ////////////////////////////////////////////////////////////////////////// + // line to parse: + // common lineHeight=104 base=26 scaleW=1024 scaleH=512 pages=1 packed=0 + ////////////////////////////////////////////////////////////////////////// + + // Height + int index = line.find("lineHeight="); + int index2 = line.find(' ', index); + std::string value = line.substr(index, index2-index); + sscanf(value.c_str(), "lineHeight=%u", &m_uCommonHeight); + // scaleW. sanity check + index = line.find("scaleW=") + strlen("scaleW="); + index2 = line.find(' ', index); + value = line.substr(index, index2-index); + CCAssert(atoi(value.c_str()) <= CCConfiguration::sharedConfiguration()->getMaxTextureSize(), "CCLabelBMFont: page can't be larger than supported"); + // scaleH. sanity check + index = line.find("scaleH=") + strlen("scaleH="); + index2 = line.find(' ', index); + value = line.substr(index, index2-index); + CCAssert(atoi(value.c_str()) <= CCConfiguration::sharedConfiguration()->getMaxTextureSize(), "CCLabelBMFont: page can't be larger than supported"); + // pages. sanity check + index = line.find("pages=") + strlen("pages="); + index2 = line.find(' ', index); + value = line.substr(index, index2-index); + CCAssert(atoi(value.c_str()) == 1, "CCBitfontAtlas: only supports 1 page"); + + // packed (ignore) What does this mean ?? +} + +void CCBMFontConfiguration::parseCharacterDefinition(std::string line, ccBMFontDef *characterDefinition) +{ + ////////////////////////////////////////////////////////////////////////// + // line to parse: + // char id=32 x=0 y=0 width=0 height=0 xoffset=0 yoffset=44 xadvance=14 page=0 chnl=0 + ////////////////////////////////////////////////////////////////////////// + + // Character ID + int index = line.find("id="); + int index2 = line.find(' ', index); + std::string value = line.substr(index, index2-index); + sscanf(value.c_str(), "id=%u", &characterDefinition->charID); + + // Character x + index = line.find("x="); + index2 = line.find(' ', index); + value = line.substr(index, index2-index); + sscanf(value.c_str(), "x=%f", &characterDefinition->rect.origin.x); + // Character y + index = line.find("y="); + index2 = line.find(' ', index); + value = line.substr(index, index2-index); + sscanf(value.c_str(), "y=%f", &characterDefinition->rect.origin.y); + // Character width + index = line.find("width="); + index2 = line.find(' ', index); + value = line.substr(index, index2-index); + sscanf(value.c_str(), "width=%f", &characterDefinition->rect.size.width); + // Character height + index = line.find("height="); + index2 = line.find(' ', index); + value = line.substr(index, index2-index); + sscanf(value.c_str(), "height=%f", &characterDefinition->rect.size.height); + // Character xoffset + index = line.find("xoffset="); + index2 = line.find(' ', index); + value = line.substr(index, index2-index); + sscanf(value.c_str(), "xoffset=%d", &characterDefinition->xOffset); + // Character yoffset + index = line.find("yoffset="); + index2 = line.find(' ', index); + value = line.substr(index, index2-index); + sscanf(value.c_str(), "yoffset=%d", &characterDefinition->yOffset); + // Character xadvance + index = line.find("xadvance="); + index2 = line.find(' ', index); + value = line.substr(index, index2-index); + sscanf(value.c_str(), "xadvance=%d", &characterDefinition->xAdvance); +} + +void CCBMFontConfiguration::parseKerningCapacity(std::string line) +{ + // When using uthash there is not need to parse the capacity. + + // CCAssert(!kerningDictionary, @"dictionary already initialized"); + // + // // Break the values for this line up using = + // CCMutableArray *values = [line componentsSeparatedByString:@"="]; + // NSEnumerator *nse = [values objectEnumerator]; + // CCString *propertyValue; + // + // // We need to move past the first entry in the array before we start assigning values + // [nse nextObject]; + // + // // count + // propertyValue = [nse nextObject]; + // int capacity = [propertyValue intValue]; + // + // if( capacity != -1 ) + // kerningDictionary = ccHashSetNew(capacity, targetSetEql); +} + +void CCBMFontConfiguration::parseKerningEntry(std::string line) +{ + ////////////////////////////////////////////////////////////////////////// + // line to parse: + // kerning first=121 second=44 amount=-7 + ////////////////////////////////////////////////////////////////////////// + + // first + int first; + int index = line.find("first="); + int index2 = line.find(' ', index); + std::string value = line.substr(index, index2-index); + sscanf(value.c_str(), "first=%d", &first); + + // second + int second; + index = line.find("second="); + index2 = line.find(' ', index); + value = line.substr(index, index2-index); + sscanf(value.c_str(), "second=%d", &second); + + // amount + int amount; + index = line.find("amount="); + index2 = line.find(' ', index); + value = line.substr(index, index2-index); + sscanf(value.c_str(), "amount=%d", &amount); + + tKerningHashElement *element = (tKerningHashElement *)calloc( sizeof( *element ), 1 ); + element->amount = amount; + element->key = (first<<16) | (second&0xffff); + HASH_ADD_INT(m_pKerningDictionary,key, element); +} +// +//CCLabelBMFont +// + +//LabelBMFont - Purge Cache +void CCLabelBMFont::purgeCachedData() +{ + FNTConfigRemoveCache(); +} + +//LabelBMFont - Creation & Init +CCLabelBMFont *CCLabelBMFont::labelWithString(const char *str, const char *fntFile) +{ + CCLabelBMFont *pRet = new CCLabelBMFont(); + if(pRet && pRet->initWithString(str, fntFile)) + { + pRet->autorelease(); + return pRet; + } + CC_SAFE_DELETE(pRet); + return NULL; +} + +//LabelBMFont - Creation & Init +CCLabelBMFont *CCLabelBMFont::labelWithString(const char *str, const char *fntFile, float width, CCTextAlignment alignment) +{ + CCLabelBMFont *pRet = new CCLabelBMFont(); + if(pRet && pRet->initWithString(str, fntFile, width, alignment)) + { + pRet->autorelease(); + return pRet; + } + CC_SAFE_DELETE(pRet); + return NULL; +} + +//LabelBMFont - Creation & Init +CCLabelBMFont *CCLabelBMFont::labelWithString(const char *str, const char *fntFile, float width, CCTextAlignment alignment, CCPoint imageOffset) +{ + CCLabelBMFont *pRet = new CCLabelBMFont(); + if(pRet && pRet->initWithString(str, fntFile, width, alignment, imageOffset)) + { + pRet->autorelease(); + return pRet; + } + CC_SAFE_DELETE(pRet); + return NULL; +} + +bool CCLabelBMFont::initWithString(const char *theString, const char *fntFile) +{ + return initWithString(theString, fntFile, kCCLabelAutomaticWidth, CCTextAlignmentLeft, CCPointZero); +} + +bool CCLabelBMFont::initWithString(const char *theString, const char *fntFile, float width, CCTextAlignment alignment) +{ + return initWithString(theString, fntFile, width, alignment, CCPointZero); +} + +bool CCLabelBMFont::initWithString(const char *theString, const char *fntFile, float width, CCTextAlignment alignment, CCPoint imageOffset) +{ + CCAssert(theString != NULL, ""); + CC_SAFE_RELEASE(m_pConfiguration);// allow re-init + m_pConfiguration = FNTConfigLoadFile(fntFile); + m_pConfiguration->retain(); + CCAssert( m_pConfiguration, "Error creating config for LabelBMFont"); + + if (CCSpriteBatchNode::initWithFile(m_pConfiguration->m_sAtlasName.c_str(), strlen(theString))) + { + m_pAlignment = alignment; + m_tImageOffset = imageOffset; + m_fWidth = width; + m_sString = cc_utf8_from_cstr(theString); + m_cOpacity = 255; + m_tColor = ccWHITE; + m_tContentSize = CCSizeZero; + m_bIsOpacityModifyRGB = m_pobTextureAtlas->getTexture()->getHasPremultipliedAlpha(); + this->setString(theString); + setAnchorPoint(ccp(0.5f, 0.5f)); + return true; + } + return false; +} + +CCLabelBMFont::CCLabelBMFont() +: m_cOpacity(0) +, m_bIsOpacityModifyRGB(false) +, m_pConfiguration(NULL) +, m_bLineBreakWithoutSpaces(false) +, m_tImageOffset(CCPointZero) +{ + +} + +CCLabelBMFont::~CCLabelBMFont() +{ + CC_SAFE_DELETE(m_sString); + CC_SAFE_RELEASE(m_pConfiguration); +} + +// LabelBMFont - Atlas generation +int CCLabelBMFont::kerningAmountForFirst(unsigned short first, unsigned short second) +{ + int ret = 0; + unsigned int key = (first<<16) | (second & 0xffff); + + if( m_pConfiguration->m_pKerningDictionary ) { + tKerningHashElement *element = NULL; + HASH_FIND_INT(m_pConfiguration->m_pKerningDictionary, &key, element); + if(element) + ret = element->amount; + } + return ret; +} void CCLabelBMFont::createFontChars() { @@ -635,33 +845,15 @@ void CCLabelBMFont::createFontChars() unsigned int quantityOfLines = 1; - if (0 == m_sString.length()) - { - return; - } - - int utf8len = cc_utf8_strlen(m_sString.c_str(), -1); - if (utf8len == 0) - { - return; - } - - unsigned short* pUniStr = new unsigned short[utf8len+1]; - pUniStr[utf8len] = 0; - - const char* p = m_sString.c_str(); - - for (int i = 0; i < utf8len; ++i) - { - pUniStr[i] = cc_utf8_get_char(p); - p = cc_utf8_next_char (p); - } - - unsigned int stringLen = cc_wcslen(pUniStr); + unsigned int stringLen = cc_wcslen(m_sString); + if (stringLen == 0) + { + return; + } for (unsigned int i = 0; i < stringLen - 1; ++i) { - unsigned short c = pUniStr[i]; + unsigned short c = m_sString[i]; if (c == '\n') { quantityOfLines++; @@ -673,7 +865,7 @@ void CCLabelBMFont::createFontChars() for (unsigned int i= 0; i < stringLen; i++) { - unsigned short c = pUniStr[i]; + unsigned short c = m_sString[i]; if (c == '\n') { @@ -682,14 +874,20 @@ void CCLabelBMFont::createFontChars() continue; } - std::map::iterator it = m_pConfiguration->m_pBitmapFontArray->find(c); - CCAssert(it != m_pConfiguration->m_pBitmapFontArray->end(), "LabelBMFont: character is not supported"); - - kerningAmount = this->kerningAmountForFirst(prev, c); - - const ccBMFontDef& fontDef = (*(m_pConfiguration->m_pBitmapFontArray))[c]; - - CCRect rect = fontDef.rect; + tFontDefHashElement *element = NULL; + + // unichar is a short, and an int is needed on HASH_FIND_INT + unsigned int key = c; + HASH_FIND_INT(m_pConfiguration->m_pFontDefDictionary, &key, element); + CCAssert(element, "FontDefinition could not be found!"); + + ccBMFontDef fontDef = element->fontDef; + + CCRect rect = fontDef.rect; + rect = CC_RECT_PIXELS_TO_POINTS(rect); + + rect.origin.x += m_tImageOffset.x; + rect.origin.y += m_tImageOffset.y; CCSprite *fontChar; @@ -712,13 +910,12 @@ void CCLabelBMFont::createFontChars() } float yOffset = (float)(m_pConfiguration->m_uCommonHeight) - fontDef.yOffset; - CCPoint fontPos = ccp( (float)nextFontPositionX + fontDef.xOffset + fontDef.rect.size.width*0.5f + kerningAmount, (float)nextFontPositionY + yOffset - rect.size.height*0.5f ); + CCPoint fontPos = ccp( (float)nextFontPositionX + fontDef.xOffset + fontDef.rect.size.width*0.5f + kerningAmount, + (float)nextFontPositionY + yOffset - rect.size.height*0.5f * CC_CONTENT_SCALE_FACTOR() ); fontChar->setPosition(CC_POINT_PIXELS_TO_POINTS(fontPos)); - // NSLog(@"position.y: %f", fontChar.position.y); - // update kerning - nextFontPositionX += (*(m_pConfiguration->m_pBitmapFontArray))[c].xAdvance + kerningAmount; + nextFontPositionX += fontDef.xAdvance + kerningAmount; prev = c; // Apply label properties @@ -742,35 +939,54 @@ void CCLabelBMFont::createFontChars() tmpSize.width = (float) longestLine; tmpSize.height = (float) totalHeight; - this->setContentSize(tmpSize); + this->setContentSize(CC_SIZE_PIXELS_TO_POINTS(tmpSize)); - CC_SAFE_DELETE_ARRAY(pUniStr); } //LabelBMFont - CCLabelProtocol protocol void CCLabelBMFont::setString(const char *newString) -{ - m_sString.clear(); - m_sString = newString; +{ + this->setString(newString, false); +} + +void CCLabelBMFont::setString(const char *newString, bool fromUpdate) +{ + if (fromUpdate) + { + m_sString = cc_utf8_from_cstr(newString); + } + else + { + m_sString_initial = std::string(newString); + } + + updateString(fromUpdate); +} + +void CCLabelBMFont::updateString(bool fromUpdate) +{ if (m_pChildren && m_pChildren->count() != 0) { - CCObject* child; - CCARRAY_FOREACH(m_pChildren, child) - { - CCNode* pNode = (CCNode*) child; - if (pNode) - { - pNode->setIsVisible(false); - } - } + CCObject* child; + CCARRAY_FOREACH(m_pChildren, child) + { + CCNode* pNode = (CCNode*) child; + if (pNode) + { + pNode->setIsVisible(false); + } + } } this->createFontChars(); + + if (!fromUpdate) + updateLabel(); } const char* CCLabelBMFont::getString(void) { - return m_sString.c_str(); + return m_sString_initial.c_str(); } void CCLabelBMFont::setCString(const char *label) @@ -855,10 +1071,257 @@ void CCLabelBMFont::setAnchorPoint(const CCPoint& point) if( ! CCPoint::CCPointEqualToPoint(point, m_tAnchorPoint) ) { CCSpriteBatchNode::setAnchorPoint(point); - this->createFontChars(); + updateLabel(); } } +// LabelBMFont - Alignment +void CCLabelBMFont::updateLabel() +{ + using namespace std; + + this->setString(m_sString_initial.c_str(), true); + + if (m_fWidth > 0) + { + // Step 1: Make multiline + vector str_whole = cc_utf8_vec_from_cstr(m_sString); + unsigned int stringLength = str_whole.size(); + vector multiline_string; + vector last_word; + int line = 1, i = 0; + bool start_line = false, start_word = false; + float startOfLine = -1, startOfWord = -1; + int skip = 0; + + CCArray* children = getChildren(); + for (unsigned int j = 0; j < children->count(); j++) + { + CCSprite* characterSprite; + + while (!(characterSprite = (CCSprite*)this->getChildByTag(j + skip))) + skip++; + + if (!characterSprite->getIsVisible()) continue; + + if (i >= stringLength || i < 0) + break; + + unsigned short character = str_whole[i]; + + if (!start_word) + { + startOfWord = characterSprite->getPosition().x - characterSprite->getContentSize().width/2.0f; + start_word = true; + } + if (!start_line) + { + startOfLine = startOfWord; + start_line = true; + } + + // Newline. + if (character == '\n') + { + cc_utf8_trim_ws(&last_word); + +// int found = cc_utf8_find_char(last_word, ' '); +// if (found != -1) +// cc_utf8_trim_from(&last_word, found + 1); + + multiline_string.insert(multiline_string.end(), last_word.begin(), last_word.end()); + last_word.clear(); + start_word = false; + start_line = false; + startOfWord = -1; + startOfLine = -1; + i++; + line++; + + if (i >= stringLength || i < 0) + break; + + character = str_whole[i]; + + if (!startOfWord) + { + startOfWord = characterSprite->getPosition().x - characterSprite->getContentSize().width/2.0f; + start_word = true; + } + if (!startOfLine) + { + startOfLine = startOfWord; + start_line = true; + } + } + + // Whitespace. + if (isspace_unicode(character)) + { + last_word.push_back(character); + multiline_string.insert(multiline_string.end(), last_word.begin(), last_word.end()); + last_word.clear(); + start_word = false; + startOfWord = -1; + i++; + continue; + } + + // Out of bounds. + if (characterSprite->getPosition().x + characterSprite->getContentSize().width / 2.0f - startOfLine + > m_fWidth ) + { + if (!m_bLineBreakWithoutSpaces) + { + last_word.push_back(character); + + int found = cc_utf8_find_last_not_char(multiline_string, ' '); + if (found != -1) + cc_utf8_trim_ws(&multiline_string); + else + multiline_string.clear(); + + if (multiline_string.size() > 0) + multiline_string.push_back('\n'); + + line++; + start_line = false; + startOfLine = -1; + i++; + } + else + { + cc_utf8_trim_ws(&last_word); + + last_word.push_back('\n'); + multiline_string.insert(multiline_string.end(), last_word.begin(), last_word.end()); + last_word.clear(); + start_word = false; + start_line = false; + startOfWord = -1; + startOfLine = -1; + line++; + + if (i >= stringLength || i < 0) + break; + + if (!startOfWord) + { + startOfWord = characterSprite->getPosition().x - characterSprite->getContentSize().width/2.0f; + start_word = true; + } + if (!startOfLine) + { + startOfLine = startOfWord; + start_line = true; + } + + j--; + } + + continue; + } + else + { + // Character is normal. + last_word.push_back(character); + i++; + continue; + } + } + + multiline_string.insert(multiline_string.end(), last_word.begin(), last_word.end()); + + int size = multiline_string.size(); + unsigned short* str_new = new unsigned short[size + 1]; + + for (int i = 0; i < size; ++i) + str_new[i] = multiline_string[i]; + + str_new[size] = 0; + + delete[] m_sString; + m_sString = str_new; + updateString(true); + } + + // Step 2: Make alignment + if (m_pAlignment != CCTextAlignmentLeft) + { + int i = 0; + + int lineNumber = 0; + int str_len = cc_wcslen(m_sString); + vector last_line; + for (int ctr = 0; ctr <= str_len; ++ctr) + { + if (m_sString[ctr] == '\n' || m_sString[ctr] == 0) + { + float lineWidth = 0.0f; + int line_length = last_line.size(); + int index = i + line_length - 1 + lineNumber; + if (index < 0) continue; + + CCSprite* lastChar = (CCSprite*)getChildByTag(index); + + lineWidth = lastChar->getPosition().x + lastChar->getContentSize().width/2.0f; + + float shift = 0; + switch (m_pAlignment) + { + case CCTextAlignmentCenter: + shift = getContentSize().width/2.0f - lineWidth/2.0f; + break; + case CCTextAlignmentRight: + shift = getContentSize().width - lineWidth; + default: + break; + } + + if (shift != 0) + { + int j = 0; + for (unsigned j = 0; j < line_length; j++) + { + index = i + j + lineNumber; + if (index < 0) continue; + + CCSprite* characterSprite = (CCSprite*)getChildByTag(index); + characterSprite->setPosition(ccpAdd(characterSprite->getPosition(), ccp(shift, 0.0f))); + } + } + + i += line_length; + lineNumber++; + + last_line.clear(); + continue; + } + + last_line.push_back(m_sString[ctr]); + } + } +} + +// LabelBMFont - Alignment +void CCLabelBMFont::setAlignment(CCTextAlignment alignment) +{ + this->m_pAlignment = alignment; + updateLabel(); +} + +void CCLabelBMFont::setWidth(float width) +{ + this->m_fWidth = width; + updateLabel(); +} + +void CCLabelBMFont::setLineBreakWithoutSpace( bool breakWithoutSpace ) +{ + m_bLineBreakWithoutSpaces = breakWithoutSpace; + updateLabel(); +} + //LabelBMFont - Debug draw #if CC_LABELBMFONT_DEBUG_DRAW void CCLabelBMFont::draw() @@ -871,6 +1334,7 @@ void CCLabelBMFont::draw() }; ccDrawPoly(vertices, 4, true); } + #endif // CC_LABELBMFONT_DEBUG_DRAW NS_CC_END