diff --git a/cocos/2d/CCFontAtlas.cpp b/cocos/2d/CCFontAtlas.cpp index 1524af599b..8ce578f32e 100644 --- a/cocos/2d/CCFontAtlas.cpp +++ b/cocos/2d/CCFontAtlas.cpp @@ -24,6 +24,11 @@ ****************************************************************************/ #include "2d/CCFontAtlas.h" +#if CC_TARGET_PLATFORM != CC_PLATFORM_WIN32 && CC_TARGET_PLATFORM != CC_PLATFORM_WINRT && CC_TARGET_PLATFORM != CC_PLATFORM_ANDROID +#include +#elif CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID +#include "android/jni/Java_org_cocos2dx_lib_Cocos2dxHelper.h" +#endif #include "2d/CCFontFreeType.h" #include "base/ccUTF8.h" #include "base/CCDirector.h" @@ -31,7 +36,6 @@ #include "base/CCEventDispatcher.h" #include "base/CCEventType.h" - NS_CC_BEGIN const int FontAtlas::CacheTextureWidth = 512; @@ -41,6 +45,8 @@ const char* FontAtlas::CMD_RESET_FONTATLAS = "__cc_RESET_FONTATLAS"; FontAtlas::FontAtlas(Font &theFont) : _font(&theFont) +, _fontFreeType(nullptr) +, _iconv(nullptr) , _currentPageData(nullptr) , _fontAscender(0) , _rendererRecreatedListener(nullptr) @@ -49,23 +55,23 @@ FontAtlas::FontAtlas(Font &theFont) { _font->retain(); - FontFreeType* fontTTf = dynamic_cast(_font); - if (fontTTf) + _fontFreeType = dynamic_cast(_font); + if (_fontFreeType) { _lineHeight = _font->getFontMaxHeight(); - _fontAscender = fontTTf->getFontAscender(); + _fontAscender = _fontFreeType->getFontAscender(); auto texture = new (std::nothrow) Texture2D; _currentPage = 0; _currentPageOrigX = 0; _currentPageOrigY = 0; _letterPadding = 0; - if(fontTTf->isDistanceFieldEnabled()) + if (_fontFreeType->isDistanceFieldEnabled()) { _letterPadding += 2 * FontFreeType::DistanceMapSpread; } _currentPageDataSize = CacheTextureWidth * CacheTextureHeight; - auto outlineSize = fontTTf->getOutlineSize(); + auto outlineSize = _fontFreeType->getOutlineSize(); if(outlineSize > 0) { _lineHeight += 2 * outlineSize; @@ -94,8 +100,7 @@ FontAtlas::FontAtlas(Font &theFont) FontAtlas::~FontAtlas() { #if CC_ENABLE_CACHE_TEXTURE_DATA - FontFreeType* fontTTf = dynamic_cast(_font); - if (fontTTf && _rendererRecreatedListener) + if (_fontFreeType && _rendererRecreatedListener) { auto eventDispatcher = Director::getInstance()->getEventDispatcher(); eventDispatcher->removeEventListener(_rendererRecreatedListener); @@ -107,6 +112,14 @@ FontAtlas::~FontAtlas() relaseTextures(); delete []_currentPageData; + +#if CC_TARGET_PLATFORM != CC_PLATFORM_WIN32 && CC_TARGET_PLATFORM != CC_PLATFORM_WINRT && CC_TARGET_PLATFORM != CC_PLATFORM_ANDROID + if (_iconv) + { + iconv_close(_iconv); + _iconv = nullptr; + } +#endif } void FontAtlas::relaseTextures() @@ -120,8 +133,7 @@ void FontAtlas::relaseTextures() void FontAtlas::purgeTexturesAtlas() { - FontFreeType* fontTTf = dynamic_cast(_font); - if (fontTTf && _atlasTextures.size() > 1) + if (_fontFreeType && _atlasTextures.size() > 1) { auto eventDispatcher = Director::getInstance()->getEventDispatcher(); eventDispatcher->dispatchCustomEvent(CMD_PURGE_FONTATLAS,this); @@ -131,8 +143,7 @@ void FontAtlas::purgeTexturesAtlas() void FontAtlas::listenRendererRecreated(EventCustom *event) { - FontFreeType* fontTTf = dynamic_cast(_font); - if (fontTTf) + if (_fontFreeType) { auto eventDispatcher = Director::getInstance()->getEventDispatcher(); eventDispatcher->dispatchCustomEvent(CMD_PURGE_FONTATLAS,this); @@ -160,13 +171,133 @@ bool FontAtlas::getLetterDefinitionForChar(char16_t utf16Char, FontLetterDefinit } } -bool FontAtlas::prepareLetterDefinitions(const std::u16string& utf16String) +void FontAtlas::conversionU16TOGB2312(const std::u16string& u16Text, std::unordered_map& charCodeMap) { - FontFreeType* fontTTf = dynamic_cast(_font); - if(fontTTf == nullptr) + size_t strLen = u16Text.length(); + auto gb2312StrSize = strLen * 2; + auto gb2312Text = new (std::nothrow) char[gb2312StrSize]; + memset(gb2312Text, 0, gb2312StrSize); + + switch (_fontFreeType->getEncoding()) + { + case FT_ENCODING_GB2312: + { +#if CC_TARGET_PLATFORM == CC_PLATFORM_WIN32 || CC_TARGET_PLATFORM == CC_PLATFORM_WINRT + WideCharToMultiByte(936, NULL, (LPCWCH)u16Text.c_str(), strLen, (LPSTR)gb2312Text, gb2312StrSize, NULL, NULL); +#elif CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID + conversionEncodingJNI((char*)u16Text.c_str(), gb2312StrSize, "UTF-16LE", gb2312Text, "GB2312"); +#else + if (_iconv == nullptr) + { + _iconv = iconv_open("gb2312", "utf-16le"); + } + + if (_iconv == (iconv_t)-1) + { + CCLOG("conversion from utf16 to gb2312 not available"); + } + else + { + char* pin = (char*)u16Text.c_str(); + char* pout = gb2312Text; + size_t inLen = strLen * 2; + size_t outLen = gb2312StrSize; + + iconv(_iconv, (char**)&pin, &inLen, &pout, &outLen); + } +#endif + } + break; + default: + CCLOG("Unsupported encoding:%d", _fontFreeType->getEncoding()); + break; + } + + unsigned short gb2312Code = 0; + unsigned char* dst = (unsigned char*)&gb2312Code; + unsigned short u16Code; + for (size_t index = 0, gbIndex = 0; index < strLen; ++index) + { + u16Code = u16Text[index]; + if (u16Code < 256) + { + charCodeMap[u16Code] = u16Code; + gbIndex += 1; + } + else + { + dst[0] = gb2312Text[gbIndex + 1]; + dst[1] = gb2312Text[gbIndex]; + charCodeMap[u16Code] = gb2312Code; + + gbIndex += 2; + } + } + + delete[] gb2312Text; +} + +void FontAtlas::findNewCharacters(const std::u16string& u16Text, std::unordered_map& charCodeMap) +{ + std::u16string newChars; + FT_Encoding charEncoding = _fontFreeType->getEncoding(); + + //find new characters + if (_letterDefinitions.empty()) + { + newChars = u16Text; + } + else + { + auto length = u16Text.length(); + newChars.resize(length); + for (size_t i = 0; i < length; ++i) + { + auto outIterator = _letterDefinitions.find(u16Text[i]); + if (outIterator == _letterDefinitions.end()) + { + newChars.push_back(u16Text[i]); + } + } + } + + if (!newChars.empty()) + { + switch (charEncoding) + { + case FT_ENCODING_UNICODE: + { + for (auto u16Code : newChars) + { + charCodeMap[u16Code] = u16Code; + } + break; + } + case FT_ENCODING_GB2312: + { + conversionU16TOGB2312(newChars, charCodeMap); + break; + } + default: + CCLOG("FontAtlas::findNewCharacters: Unsupported encoding:%d", charEncoding); + break; + } + } +} + +bool FontAtlas::prepareLetterDefinitions(const std::u16string& utf16Text) +{ + if (_fontFreeType == nullptr) + { return false; + } - size_t length = utf16String.length(); + std::unordered_map codeMapOfNewChar; + findNewCharacters(utf16Text, codeMapOfNewChar); + if (codeMapOfNewChar.empty()) + { + return false; + } float offsetAdjust = _letterPadding / 2; long bitmapWidth; @@ -175,117 +306,106 @@ bool FontAtlas::prepareLetterDefinitions(const std::u16string& utf16String) FontLetterDefinition tempDef; auto scaleFactor = CC_CONTENT_SCALE_FACTOR(); - auto pixelFormat = fontTTf->getOutlineSize() > 0 ? Texture2D::PixelFormat::AI88 : Texture2D::PixelFormat::A8; + auto pixelFormat = _fontFreeType->getOutlineSize() > 0 ? Texture2D::PixelFormat::AI88 : Texture2D::PixelFormat::A8; - bool existNewLetter = false; float startY = _currentPageOrigY; - for (size_t i = 0; i < length; ++i) + for (auto&& it : codeMapOfNewChar) { - auto outIterator = _letterDefinitions.find(utf16String[i]); + auto bitmap = _fontFreeType->getGlyphBitmap(it.second, bitmapWidth, bitmapHeight, tempRect, tempDef.xAdvance); + if (bitmap && bitmapWidth > 0 && bitmapHeight > 0) + { + tempDef.validDefinition = true; + tempDef.width = tempRect.size.width + _letterPadding; + tempDef.height = tempRect.size.height + _letterPadding; + tempDef.offsetX = tempRect.origin.x + offsetAdjust; + tempDef.offsetY = _fontAscender + tempRect.origin.y - offsetAdjust; - if (outIterator == _letterDefinitions.end()) - { - existNewLetter = true; - - auto bitmap = fontTTf->getGlyphBitmap(utf16String[i],bitmapWidth,bitmapHeight,tempRect,tempDef.xAdvance); - if (bitmap && bitmapWidth > 0 && bitmapHeight > 0) + if (bitmapHeight > _currLineHeight) { - tempDef.validDefinition = true; - tempDef.width = tempRect.size.width + _letterPadding; - tempDef.height = tempRect.size.height + _letterPadding; - tempDef.offsetX = tempRect.origin.x + offsetAdjust; - tempDef.offsetY = _fontAscender + tempRect.origin.y - offsetAdjust; - - if (bitmapHeight > _currLineHeight) + _currLineHeight = static_cast(bitmapHeight)+1; + } + if (_currentPageOrigX + tempDef.width > CacheTextureWidth) + { + _currentPageOrigY += _currLineHeight; + _currLineHeight = 0; + _currentPageOrigX = 0; + if (_currentPageOrigY + _lineHeight >= CacheTextureHeight) { - _currLineHeight = static_cast(bitmapHeight) + 1; - } - if (_currentPageOrigX + tempDef.width > CacheTextureWidth) - { - _currentPageOrigY += _currLineHeight; - _currLineHeight = 0; - _currentPageOrigX = 0; - if(_currentPageOrigY + _lineHeight >= CacheTextureHeight) + unsigned char *data = nullptr; + if (pixelFormat == Texture2D::PixelFormat::AI88) { - unsigned char *data = nullptr; - if(pixelFormat == Texture2D::PixelFormat::AI88) - { - data = _currentPageData + CacheTextureWidth * (int)startY * 2; - } - else - { - data = _currentPageData + CacheTextureWidth * (int)startY; - } - _atlasTextures[_currentPage]->updateWithData(data, 0, startY, - CacheTextureWidth, CacheTextureHeight - startY); + data = _currentPageData + CacheTextureWidth * (int)startY * 2; + } + else + { + data = _currentPageData + CacheTextureWidth * (int)startY; + } + _atlasTextures[_currentPage]->updateWithData(data, 0, startY, + CacheTextureWidth, CacheTextureHeight - startY); - startY = 0.0f; + startY = 0.0f; - _currentPageOrigY = 0; - memset(_currentPageData, 0, _currentPageDataSize); - _currentPage++; - auto tex = new (std::nothrow) Texture2D; - if (_antialiasEnabled) - { - tex->setAntiAliasTexParameters(); - } - else - { - tex->setAliasTexParameters(); - } - tex->initWithData(_currentPageData, _currentPageDataSize, - pixelFormat, CacheTextureWidth, CacheTextureHeight, Size(CacheTextureWidth,CacheTextureHeight) ); - addTexture(tex,_currentPage); - tex->release(); - } + _currentPageOrigY = 0; + memset(_currentPageData, 0, _currentPageDataSize); + _currentPage++; + auto tex = new (std::nothrow) Texture2D; + if (_antialiasEnabled) + { + tex->setAntiAliasTexParameters(); + } + else + { + tex->setAliasTexParameters(); + } + tex->initWithData(_currentPageData, _currentPageDataSize, + pixelFormat, CacheTextureWidth, CacheTextureHeight, Size(CacheTextureWidth, CacheTextureHeight)); + addTexture(tex, _currentPage); + tex->release(); } - fontTTf->renderCharAt(_currentPageData,_currentPageOrigX,_currentPageOrigY,bitmap,bitmapWidth,bitmapHeight); - - tempDef.U = _currentPageOrigX; - tempDef.V = _currentPageOrigY; - tempDef.textureID = _currentPage; - _currentPageOrigX += tempDef.width + 1; - // take from pixels to points - tempDef.width = tempDef.width / scaleFactor; - tempDef.height = tempDef.height / scaleFactor; - tempDef.U = tempDef.U / scaleFactor; - tempDef.V = tempDef.V / scaleFactor; } - else{ - if(tempDef.xAdvance) - tempDef.validDefinition = true; - else - tempDef.validDefinition = false; + _fontFreeType->renderCharAt(_currentPageData, _currentPageOrigX, _currentPageOrigY, bitmap, bitmapWidth, bitmapHeight); - tempDef.width = 0; - tempDef.height = 0; - tempDef.U = 0; - tempDef.V = 0; - tempDef.offsetX = 0; - tempDef.offsetY = 0; - tempDef.textureID = 0; - _currentPageOrigX += 1; - } + tempDef.U = _currentPageOrigX; + tempDef.V = _currentPageOrigY; + tempDef.textureID = _currentPage; + _currentPageOrigX += tempDef.width + 1; + // take from pixels to points + tempDef.width = tempDef.width / scaleFactor; + tempDef.height = tempDef.height / scaleFactor; + tempDef.U = tempDef.U / scaleFactor; + tempDef.V = tempDef.V / scaleFactor; + } + else{ + if (tempDef.xAdvance) + tempDef.validDefinition = true; + else + tempDef.validDefinition = false; - _letterDefinitions[utf16String[i]] = tempDef; - } + tempDef.width = 0; + tempDef.height = 0; + tempDef.U = 0; + tempDef.V = 0; + tempDef.offsetX = 0; + tempDef.offsetY = 0; + tempDef.textureID = 0; + _currentPageOrigX += 1; + } + + _letterDefinitions[it.first] = tempDef; } - if(existNewLetter) + unsigned char *data = nullptr; + if (pixelFormat == Texture2D::PixelFormat::AI88) { - unsigned char *data = nullptr; - if(pixelFormat == Texture2D::PixelFormat::AI88) - { - data = _currentPageData + CacheTextureWidth * (int)startY * 2; - } - else - { - data = _currentPageData + CacheTextureWidth * (int)startY; - } - _atlasTextures[_currentPage]->updateWithData(data, 0, startY, - CacheTextureWidth, _currentPageOrigY - startY + _lineHeight); + data = _currentPageData + CacheTextureWidth * (int)startY * 2; } + else + { + data = _currentPageData + CacheTextureWidth * (int)startY; + } + _atlasTextures[_currentPage]->updateWithData(data, 0, startY, CacheTextureWidth, _currentPageOrigY - startY + _lineHeight); + return true; } diff --git a/cocos/2d/CCFontAtlas.h b/cocos/2d/CCFontAtlas.h index 6f16de6afd..35892d14c4 100644 --- a/cocos/2d/CCFontAtlas.h +++ b/cocos/2d/CCFontAtlas.h @@ -41,6 +41,7 @@ class Font; class Texture2D; class EventCustom; class EventListenerCustom; +class FontFreeType; struct FontLetterDefinition { @@ -109,10 +110,17 @@ public: protected: void relaseTextures(); + + void findNewCharacters(const std::u16string& u16Text, std::unordered_map& charCodeMap); + + void conversionU16TOGB2312(const std::u16string& u16Text, std::unordered_map& charCodeMap); + std::unordered_map _atlasTextures; std::unordered_map _letterDefinitions; float _lineHeight; Font* _font; + FontFreeType* _fontFreeType; + void* _iconv; // Dynamic GlyphCollection related stuff int _currentPage; diff --git a/cocos/2d/CCFontFreeType.cpp b/cocos/2d/CCFontFreeType.cpp index fe859cb954..0e4c1cb98e 100644 --- a/cocos/2d/CCFontFreeType.cpp +++ b/cocos/2d/CCFontFreeType.cpp @@ -99,6 +99,7 @@ FontFreeType::FontFreeType(bool distanceFieldEnabled /* = false */,int outline / , _outlineSize(0.0f) , _lineHeight(0) , _fontAtlas(nullptr) +, _encoding(FT_ENCODING_UNICODE) { if (outline > 0) { @@ -154,7 +155,8 @@ bool FontFreeType::createFontObject(const std::string &fontName, int fontSize) return false; } - if (FT_Select_Charmap(face, face->charmaps[foundIndex]->encoding)) + _encoding = face->charmaps[foundIndex]->encoding; + if (FT_Select_Charmap(face, _encoding)) { return false; } diff --git a/cocos/2d/CCFontFreeType.h b/cocos/2d/CCFontFreeType.h index cee7a06a5f..19ccf7de54 100644 --- a/cocos/2d/CCFontFreeType.h +++ b/cocos/2d/CCFontFreeType.h @@ -53,35 +53,38 @@ class CC_DLL FontFreeType : public Font public: static const int DistanceMapSpread; - static FontFreeType * create(const std::string &fontName, int fontSize, GlyphCollection glyphs, const char *customGlyphs,bool distanceFieldEnabled = false,int outline = 0); + static FontFreeType* create(const std::string &fontName, int fontSize, GlyphCollection glyphs, + const char *customGlyphs,bool distanceFieldEnabled = false,int outline = 0); static void shutdownFreeType(); - bool isDistanceFieldEnabled() const { return _distanceFieldEnabled;} - float getOutlineSize() const { return _outlineSize; } - void renderCharAt(unsigned char *dest,int posX, int posY, unsigned char* bitmap,long bitmapWidth,long bitmapHeight); + bool isDistanceFieldEnabled() const { return _distanceFieldEnabled;} - virtual FontAtlas * createFontAtlas() override; - virtual int * getHorizontalKerningForTextUTF16(const std::u16string& text, int &outNumLetters) const override; - - unsigned char * getGlyphBitmap(unsigned short theChar, long &outWidth, long &outHeight, Rect &outRect,int &xAdvance); - - virtual int getFontMaxHeight() const override { return _lineHeight; } - virtual int getFontAscender() const; + float getOutlineSize() const { return _outlineSize; } -protected: + void renderCharAt(unsigned char *dest,int posX, int posY, unsigned char* bitmap,long bitmapWidth,long bitmapHeight); + + FT_Encoding getEncoding() const { return _encoding; } + + int* getHorizontalKerningForTextUTF16(const std::u16string& text, int &outNumLetters) const override; - FontFreeType(bool distanceFieldEnabled = false,int outline = 0); - virtual ~FontFreeType(); - bool createFontObject(const std::string &fontName, int fontSize); + unsigned char* getGlyphBitmap(unsigned short theChar, long &outWidth, long &outHeight, Rect &outRect,int &xAdvance); + int getFontAscender() const; + + virtual FontAtlas* createFontAtlas() override; + virtual int getFontMaxHeight() const override { return _lineHeight; } private: + FontFreeType(bool distanceFieldEnabled = false, int outline = 0); + virtual ~FontFreeType(); + + bool createFontObject(const std::string &fontName, int fontSize); bool initFreeType(); FT_Library getFTLibrary(); - int getHorizontalKerningForChars(unsigned short firstChar, unsigned short secondChar) const; - unsigned char * getGlyphBitmapWithOutline(unsigned short theChar, FT_BBox &bbox); + int getHorizontalKerningForChars(unsigned short firstChar, unsigned short secondChar) const; + unsigned char* getGlyphBitmapWithOutline(unsigned short code, FT_BBox &bbox); static FT_Library _FTlibrary; static bool _FTInitialized; @@ -92,6 +95,8 @@ private: float _outlineSize; int _lineHeight; FontAtlas* _fontAtlas; + + FT_Encoding _encoding; }; /// @endcond diff --git a/cocos/platform/android/java/src/org/cocos2dx/lib/Cocos2dxHelper.java b/cocos/platform/android/java/src/org/cocos2dx/lib/Cocos2dxHelper.java index 82fc925c39..d2b4eba51d 100644 --- a/cocos/platform/android/java/src/org/cocos2dx/lib/Cocos2dxHelper.java +++ b/cocos/platform/android/java/src/org/cocos2dx/lib/Cocos2dxHelper.java @@ -531,6 +531,18 @@ public class Cocos2dxHelper { editor.remove(key); editor.commit(); } + + public static byte[] conversionEncoding(byte[] text, String fromCharset,String newCharset) + { + try { + String str = new String(text,fromCharset); + return str.getBytes(newCharset); + } catch (UnsupportedEncodingException e) { + e.printStackTrace(); + } + + return null; + } // =========================================================== // Inner and Anonymous Classes diff --git a/cocos/platform/android/jni/Java_org_cocos2dx_lib_Cocos2dxHelper.cpp b/cocos/platform/android/jni/Java_org_cocos2dx_lib_Cocos2dxHelper.cpp index f482ae71d3..cf4264a45d 100644 --- a/cocos/platform/android/jni/Java_org_cocos2dx_lib_Cocos2dxHelper.cpp +++ b/cocos/platform/android/jni/Java_org_cocos2dx_lib_Cocos2dxHelper.cpp @@ -413,3 +413,25 @@ void deleteValueForKeyJNI(const char* key) t.env->DeleteLocalRef(stringArg1); } } + +void conversionEncodingJNI(const char* src, int byteSize, const char* fromCharset, char* dst, const char* newCharset) +{ + JniMethodInfo methodInfo; + + if (JniHelper::getStaticMethodInfo(methodInfo, CLASS_NAME, "conversionEncoding", "([BLjava/lang/String;Ljava/lang/String;)[B")) { + jbyteArray strArray = methodInfo.env->NewByteArray(byteSize); + methodInfo.env->SetByteArrayRegion(strArray, 0, byteSize, reinterpret_cast(src)); + + jstring stringArg1 = methodInfo.env->NewStringUTF(fromCharset); + jstring stringArg2 = methodInfo.env->NewStringUTF(newCharset); + + jbyteArray newArray = (jbyteArray)methodInfo.env->CallStaticObjectMethod(methodInfo.classID, methodInfo.methodID, strArray, stringArg1, stringArg2); + jsize theArrayLen = methodInfo.env->GetArrayLength(newArray); + methodInfo.env->GetByteArrayRegion(newArray, 0, theArrayLen, (jbyte*)dst); + + methodInfo.env->DeleteLocalRef(strArray); + methodInfo.env->DeleteLocalRef(stringArg1); + methodInfo.env->DeleteLocalRef(stringArg2); + methodInfo.env->DeleteLocalRef(methodInfo.classID); + } +} diff --git a/cocos/platform/android/jni/Java_org_cocos2dx_lib_Cocos2dxHelper.h b/cocos/platform/android/jni/Java_org_cocos2dx_lib_Cocos2dxHelper.h index 5770c265ff..4777a2318f 100644 --- a/cocos/platform/android/jni/Java_org_cocos2dx_lib_Cocos2dxHelper.h +++ b/cocos/platform/android/jni/Java_org_cocos2dx_lib_Cocos2dxHelper.h @@ -54,4 +54,7 @@ extern void setFloatForKeyJNI(const char* key, float value); extern void setDoubleForKeyJNI(const char* key, double value); extern void setStringForKeyJNI(const char* key, const char* value); extern void deleteValueForKeyJNI(const char* key); + +extern void conversionEncodingJNI(const char* src, int byteSize, const char* fromCharset, char* dst, const char* newCharset); + #endif /* __Java_org_cocos2dx_lib_Cocos2dxHelper_H__ */