123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509 |
- /****************************************************************************
- Copyright (c) 2013 Zynga Inc.
- Copyright (c) 2013-2016 Chukong Technologies Inc.
- Copyright (c) 2017-2018 Xiamen Yaji Software Co., Ltd.
-
- 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.
- ****************************************************************************/
- #include "2d/CCFontAtlas.h"
- #if CC_TARGET_PLATFORM != CC_PLATFORM_WIN32 && CC_TARGET_PLATFORM != CC_PLATFORM_WINRT && CC_TARGET_PLATFORM != CC_PLATFORM_ANDROID
- #include <iconv.h>
- #elif CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID
- #include "platform/android/jni/Java_org_cocos2dx_lib_Cocos2dxHelper.h"
- #endif
- #include "2d/CCFontFreeType.h"
- #include "base/ccUTF8.h"
- #include "base/CCDirector.h"
- #include "base/CCEventListenerCustom.h"
- #include "base/CCEventDispatcher.h"
- #include "base/CCEventType.h"
- NS_CC_BEGIN
- const int FontAtlas::CacheTextureWidth = 512;
- const int FontAtlas::CacheTextureHeight = 512;
- const char* FontAtlas::CMD_PURGE_FONTATLAS = "__cc_PURGE_FONTATLAS";
- 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)
- , _antialiasEnabled(true)
- , _currLineHeight(0)
- {
- _font->retain();
- _fontFreeType = dynamic_cast<FontFreeType*>(_font);
- if (_fontFreeType)
- {
- _lineHeight = _font->getFontMaxHeight();
- _fontAscender = _fontFreeType->getFontAscender();
- _currentPage = 0;
- _currentPageOrigX = 0;
- _currentPageOrigY = 0;
- _letterEdgeExtend = 2;
- _letterPadding = 0;
- if (_fontFreeType->isDistanceFieldEnabled())
- {
- _letterPadding += 2 * FontFreeType::DistanceMapSpread;
- }
-
- reinit();
- #if CC_ENABLE_CACHE_TEXTURE_DATA
- auto eventDispatcher = Director::getInstance()->getEventDispatcher();
- _rendererRecreatedListener = EventListenerCustom::create(EVENT_RENDERER_RECREATED, CC_CALLBACK_1(FontAtlas::listenRendererRecreated, this));
- eventDispatcher->addEventListenerWithFixedPriority(_rendererRecreatedListener, 1);
- #endif
- }
- }
- void FontAtlas::reinit()
- {
- if (_currentPageData)
- {
- delete []_currentPageData;
- _currentPageData = nullptr;
- }
-
- auto texture = new (std::nothrow) Texture2D;
-
- _currentPageDataSize = CacheTextureWidth * CacheTextureHeight;
-
- auto outlineSize = _fontFreeType->getOutlineSize();
- if(outlineSize > 0)
- {
- _lineHeight += 2 * outlineSize;
- _currentPageDataSize *= 2;
- }
-
- _currentPageData = new (std::nothrow) unsigned char[_currentPageDataSize];
- memset(_currentPageData, 0, _currentPageDataSize);
-
- auto pixelFormat = outlineSize > 0 ? Texture2D::PixelFormat::AI88 : Texture2D::PixelFormat::A8;
- texture->initWithData(_currentPageData, _currentPageDataSize,
- pixelFormat, CacheTextureWidth, CacheTextureHeight, Size(CacheTextureWidth,CacheTextureHeight) );
-
- addTexture(texture,0);
- texture->release();
- }
- FontAtlas::~FontAtlas()
- {
- #if CC_ENABLE_CACHE_TEXTURE_DATA
- if (_fontFreeType && _rendererRecreatedListener)
- {
- auto eventDispatcher = Director::getInstance()->getEventDispatcher();
- eventDispatcher->removeEventListener(_rendererRecreatedListener);
- _rendererRecreatedListener = nullptr;
- }
- #endif
- _font->release();
- releaseTextures();
- 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::reset()
- {
- releaseTextures();
-
- _currLineHeight = 0;
- _currentPage = 0;
- _currentPageOrigX = 0;
- _currentPageOrigY = 0;
- _letterDefinitions.clear();
-
- reinit();
- }
- void FontAtlas::releaseTextures()
- {
- for( auto &item: _atlasTextures)
- {
- item.second->release();
- }
- _atlasTextures.clear();
- }
- void FontAtlas::purgeTexturesAtlas()
- {
- if (_fontFreeType)
- {
- reset();
- auto eventDispatcher = Director::getInstance()->getEventDispatcher();
- eventDispatcher->dispatchCustomEvent(CMD_PURGE_FONTATLAS,this);
- eventDispatcher->dispatchCustomEvent(CMD_RESET_FONTATLAS,this);
- }
- }
- void FontAtlas::listenRendererRecreated(EventCustom * /*event*/)
- {
- purgeTexturesAtlas();
- }
- void FontAtlas::addLetterDefinition(char32_t utf32Char, const FontLetterDefinition &letterDefinition)
- {
- _letterDefinitions[utf32Char] = letterDefinition;
- }
- void FontAtlas::scaleFontLetterDefinition(float scaleFactor)
- {
- for (auto&& fontDefinition : _letterDefinitions) {
- auto& letterDefinition = fontDefinition.second;
- letterDefinition.width *= scaleFactor;
- letterDefinition.height *= scaleFactor;
- letterDefinition.offsetX *= scaleFactor;
- letterDefinition.offsetY *= scaleFactor;
- letterDefinition.xAdvance *= scaleFactor;
- }
- }
- bool FontAtlas::getLetterDefinitionForChar(char32_t utf32Char, FontLetterDefinition &letterDefinition)
- {
- auto outIterator = _letterDefinitions.find(utf32Char);
- if (outIterator != _letterDefinitions.end())
- {
- letterDefinition = (*outIterator).second;
- return letterDefinition.validDefinition;
- }
- else
- {
- return false;
- }
- }
- void FontAtlas::conversionU32TOGB2312(const std::u32string& u32Text, std::unordered_map<unsigned int, unsigned int>& charCodeMap)
- {
- size_t strLen = u32Text.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
- std::u16string u16Text;
- cocos2d::StringUtils::UTF32ToUTF16(u32Text, u16Text);
- WideCharToMultiByte(936, NULL, (LPCWCH)u16Text.c_str(), strLen, (LPSTR)gb2312Text, gb2312StrSize, NULL, NULL);
- #elif CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID
- conversionEncodingJNI((char*)u32Text.c_str(), gb2312StrSize, "UTF-32LE", gb2312Text, "GB2312");
- #else
- if (_iconv == nullptr)
- {
- _iconv = iconv_open("GBK//TRANSLIT", "UTF-32LE");
- }
- if (_iconv == (iconv_t)-1)
- {
- CCLOG("conversion from utf32 to gb2312 not available");
- }
- else
- {
- char* pin = (char*)u32Text.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;
- char32_t u32Code;
- for (size_t index = 0, gbIndex = 0; index < strLen; ++index)
- {
- u32Code = u32Text[index];
- if (u32Code < 256)
- {
- charCodeMap[u32Code] = u32Code;
- gbIndex += 1;
- }
- else
- {
- dst[0] = gb2312Text[gbIndex + 1];
- dst[1] = gb2312Text[gbIndex];
- charCodeMap[u32Code] = gb2312Code;
- gbIndex += 2;
- }
- }
- delete[] gb2312Text;
- }
- void FontAtlas::findNewCharacters(const std::u32string& u32Text, std::unordered_map<unsigned int, unsigned int>& charCodeMap)
- {
- std::u32string newChars;
- FT_Encoding charEncoding = _fontFreeType->getEncoding();
- //find new characters
- if (_letterDefinitions.empty())
- {
- // fixed #16169: new android project crash in android 5.0.2 device (Nexus 7) when use 3.12.
- // While using clang compiler with gnustl_static on android, the copy assignment operator of `std::u32string`
- // will affect the memory validity, it means after `newChars` is destroyed, the memory of `u32Text` holds
- // will be a dead region. `u32text` represents the variable in `Label::_utf32Text`, when somewhere
- // allocates memory by `malloc, realloc, new, new[]`, the generated memory address may be the same
- // as `Label::_utf32Text` holds. If doing a `memset` or other memory operations, the orignal `Label::_utf32Text`
- // will be in an unknown state. Meanwhile, a bunch lots of logic which depends on `Label::_utf32Text`
- // will be broken.
-
- // newChars = u32Text;
-
- // Using `append` method is a workaround for this issue. So please be carefuly while using the assignment operator
- // of `std::u32string`.
- newChars.append(u32Text);
- }
- else
- {
- auto length = u32Text.length();
- newChars.reserve(length);
- for (size_t i = 0; i < length; ++i)
- {
- auto outIterator = _letterDefinitions.find(u32Text[i]);
- if (outIterator == _letterDefinitions.end())
- {
- newChars.push_back(u32Text[i]);
- }
- }
- }
- if (!newChars.empty())
- {
- switch (charEncoding)
- {
- case FT_ENCODING_UNICODE:
- {
- for (auto u32Code : newChars)
- {
- charCodeMap[u32Code] = u32Code;
- }
- break;
- }
- case FT_ENCODING_GB2312:
- {
- conversionU32TOGB2312(newChars, charCodeMap);
- break;
- }
- default:
- CCLOG("FontAtlas::findNewCharacters: Unsupported encoding:%d", charEncoding);
- break;
- }
- }
- }
- bool FontAtlas::prepareLetterDefinitions(const std::u32string& utf32Text)
- {
- if (_fontFreeType == nullptr)
- {
- return false;
- }
-
- std::unordered_map<unsigned int, unsigned int> codeMapOfNewChar;
- findNewCharacters(utf32Text, codeMapOfNewChar);
- if (codeMapOfNewChar.empty())
- {
- return false;
- }
- int adjustForDistanceMap = _letterPadding / 2;
- int adjustForExtend = _letterEdgeExtend / 2;
- long bitmapWidth;
- long bitmapHeight;
- int glyphHeight;
- Rect tempRect;
- FontLetterDefinition tempDef;
- auto scaleFactor = CC_CONTENT_SCALE_FACTOR();
- auto pixelFormat = _fontFreeType->getOutlineSize() > 0 ? Texture2D::PixelFormat::AI88 : Texture2D::PixelFormat::A8;
- float startY = _currentPageOrigY;
- for (auto&& it : codeMapOfNewChar)
- {
- 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 + _letterEdgeExtend;
- tempDef.height = tempRect.size.height + _letterPadding + _letterEdgeExtend;
- tempDef.offsetX = tempRect.origin.x - adjustForDistanceMap - adjustForExtend;
- tempDef.offsetY = _fontAscender + tempRect.origin.y - adjustForDistanceMap - adjustForExtend;
- if (_currentPageOrigX + tempDef.width > CacheTextureWidth)
- {
- _currentPageOrigY += _currLineHeight;
- _currLineHeight = 0;
- _currentPageOrigX = 0;
- if (_currentPageOrigY + _lineHeight + _letterPadding + _letterEdgeExtend >= CacheTextureHeight)
- {
- 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);
- 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();
- }
- }
- glyphHeight = static_cast<int>(bitmapHeight) + _letterPadding + _letterEdgeExtend;
- if (glyphHeight > _currLineHeight)
- {
- _currLineHeight = glyphHeight;
- }
- _fontFreeType->renderCharAt(_currentPageData, _currentPageOrigX + adjustForExtend, _currentPageOrigY + adjustForExtend, 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(bitmap)
- delete[] bitmap;
- if (tempDef.xAdvance)
- tempDef.validDefinition = true;
- else
- tempDef.validDefinition = false;
- 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;
- }
- 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 + _currLineHeight);
- return true;
- }
- void FontAtlas::addTexture(Texture2D *texture, int slot)
- {
- texture->retain();
- _atlasTextures[slot] = texture;
- }
- Texture2D* FontAtlas::getTexture(int slot)
- {
- return _atlasTextures[slot];
- }
- void FontAtlas::setLineHeight(float newHeight)
- {
- _lineHeight = newHeight;
- }
- void FontAtlas::setAliasTexParameters()
- {
- if (_antialiasEnabled)
- {
- _antialiasEnabled = false;
- for (const auto & tex : _atlasTextures)
- {
- tex.second->setAliasTexParameters();
- }
- }
- }
- void FontAtlas::setAntiAliasTexParameters()
- {
- if (! _antialiasEnabled)
- {
- _antialiasEnabled = true;
- for (const auto & tex : _atlasTextures)
- {
- tex.second->setAntiAliasTexParameters();
- }
- }
- }
- NS_CC_END
|