Label:support font that contains GB2312 char map.

This commit is contained in:
WenhaiLin 2015-07-28 16:28:32 +08:00
parent 3cc039b063
commit 9edd66d4a7
7 changed files with 299 additions and 127 deletions

View File

@ -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 <iconv.h>
#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<FontFreeType*>(_font);
if (fontTTf)
_fontFreeType = dynamic_cast<FontFreeType*>(_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<FontFreeType*>(_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<FontFreeType*>(_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<FontFreeType*>(_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<unsigned short, unsigned short>& charCodeMap)
{
FontFreeType* fontTTf = dynamic_cast<FontFreeType*>(_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<unsigned short, unsigned short>& 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<unsigned short, unsigned short> 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<int>(bitmapHeight)+1;
}
if (_currentPageOrigX + tempDef.width > CacheTextureWidth)
{
_currentPageOrigY += _currLineHeight;
_currLineHeight = 0;
_currentPageOrigX = 0;
if (_currentPageOrigY + _lineHeight >= CacheTextureHeight)
{
_currLineHeight = static_cast<int>(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;
}

View File

@ -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<unsigned short, unsigned short>& charCodeMap);
void conversionU16TOGB2312(const std::u16string& u16Text, std::unordered_map<unsigned short, unsigned short>& charCodeMap);
std::unordered_map<ssize_t, Texture2D*> _atlasTextures;
std::unordered_map<char16_t, FontLetterDefinition> _letterDefinitions;
float _lineHeight;
Font* _font;
FontFreeType* _fontFreeType;
void* _iconv;
// Dynamic GlyphCollection related stuff
int _currentPage;

View File

@ -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;
}

View File

@ -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

View File

@ -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

View File

@ -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<const jbyte*>(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);
}
}

View File

@ -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__ */