字体
Jul 27, 2021
»
Cocos
字体
#ifndef _COCOS2D_CCLabelBatch_h_
#define _COCOS2D_CCLabelBatch_h_
#include <string>
#include <vector>
#include "2d/CCLabel.h"
#include "cocos2d.h"
USING_NS_CC;
#define COLOR_BIT 0x1L
#define OPACITY_BIT 0x2L
#define POS_BIT 0x4L
#define VISIBLE_BIT 0x8L
#define Z_BIT 0x10L
#define IS_DIRTY(bit) (_contentDirty & bit)
#define MARK_DIRTY(bit) (_contentDirty |= bit)
#define CLEAR_DIRTY(bit) (_contentDirty &= ~bit)
#define ALL_DIRTY() _contentDirty = 0x1F
#define CC_CUSTOM_HUD_LABEL 1
class LabelBatchNode;
class LabelBatchString
{
friend class LabelBatchNode;
public:
LabelBatchString(TextHAlignment hAlignment, TextVAlignment vAlignment);
~LabelBatchString();
void setString(const std::string &text);
const std::string &getString() { return _utf8Text; }
void setColor(const Color3B &color);
const Color3B & getColor() { return _labelTxtColor; }
void setOpacity(GLubyte opacity);
void setPosition(float x, float y);
const Point &getPosition() { return _labelPosition; }
bool isVisible() { return _labelIsVisible; }
void setVisible(bool visible);
const Size &getContentSize() { return _labelContentSize; }
void setLocalZ(int Z);
int getLocalZ() { return _localZOrder; }
void setAlignment(TextHAlignment hAlignment, TextVAlignment vAlignment);
void cleanup();
#if CC_CUSTOM_HUD_LABEL
bool updateLetterDefinitions(const std::u32string &utf32Text);
bool updateQuads();
void onDraw(Renderer *renderer, const Mat4 &transform, uint32_t flags);
#endif
protected:
void alignText();
void calcAligmentOffset();
void updateContent();
void cleanupContent();
void cleanupGL();
void initGL();
protected:
static GLProgram *_glfragBatchLabelNormal;
static GLProgram *_glfragBatchLabelOutline;
std::u32string _utf32Text;
std::string _utf8Text;
Size _labelContentSize;
GLubyte _labelTxtOpacity;
Color3B _labelTxtColor;
Point _labelPosition;
int _localZOrder;
bool _labelIsVisible;
unsigned char _contentDirty;
TextHAlignment _hAlignment;
TextVAlignment _vAlignment;
Point _alignmentOffset;
LabelBatchNode *_parentBatchNode;
Vector<SpriteBatchNode *> _batchNodes;
Sprite *_reusedLetter;
Rect _reusedRect;
int _lengthOfString;
BlendFunc _blendFunc;
QuadCommand _quadCommand;
GLProgramState *_glProgramState;
};
//-------------------------------------------------------------------------------------------------------------------
typedef std::vector<LabelBatchString *> LabelStringVec;
class LabelBatchNode : public Node
{
public:
static LabelBatchNode *create(const std::string &fontFilePath, float fontSize, int outlineSize = -1);
LabelBatchString *createLabelString(TextHAlignment hAlignment = TextHAlignment::LEFT, TextVAlignment vAlignment = TextVAlignment::BOTTOM);
bool destroyLabelString(LabelBatchString *l);
void addLabelString(LabelBatchString *label);
bool removeLabelString(LabelBatchString *l);
FontAtlas *getFontAltas() { return _textFontAtlas; }
bool hasOutline() { return _isTextWithOutline; }
virtual void visit(Renderer *renderer, const Mat4 &parentTransform, uint32_t parentFlags) override;
virtual void draw(Renderer *renderer, const Mat4 &transform, uint32_t flags) override;
protected:
LabelBatchNode(const std::string &fontFilePath, float fontSize, int outlineSize = -1);
virtual ~LabelBatchNode();
protected:
LabelStringVec _labelStringMgr;
FontAtlas * _textFontAtlas;
bool _isTextWithOutline;
TTFConfig _ttfConfig;
EventListenerCustom *_purgeTextureListener;
EventListenerCustom *_resetTextureListener;
};
// end group
/// @}
#endif /*__COCOS2D_CCLABEL_H */
cpp
#include "CCLabelBatch.h"
#include "2d/CCFontAtlasCache.h"
GLProgram *LabelBatchString::_glfragBatchLabelNormal = nullptr;
GLProgram *LabelBatchString::_glfragBatchLabelOutline = nullptr;
//-------------------------------------------------------------------------------------------------------------------
LabelBatchString::LabelBatchString(TextHAlignment hAlignment, TextVAlignment vAlignment)
: _hAlignment(hAlignment), _vAlignment(vAlignment), _localZOrder(0), _labelIsVisible(true), _contentDirty(0)
{
initGL();
_labelTxtOpacity = 255;
_labelTxtColor = Color3B::WHITE;
_labelPosition = Point::ZERO;
_parentBatchNode = nullptr;
_reusedLetter = nullptr;
if (!_reusedLetter)
{
_reusedLetter = Sprite::create();
_reusedLetter->setOpacityModifyRGB(_labelTxtOpacity);
_reusedLetter->setAnchorPoint(Vec2::ANCHOR_TOP_LEFT);
_reusedLetter->retain();
}
}
LabelBatchString::~LabelBatchString()
{
CC_SAFE_RELEASE_NULL(_reusedLetter);
cleanupContent();
}
void LabelBatchString::cleanupGL()
{
if (_glfragBatchLabelNormal)
_glfragBatchLabelNormal = nullptr;
if (_glfragBatchLabelOutline)
_glfragBatchLabelOutline = nullptr;
}
void LabelBatchString::initGL()
{
if (nullptr == _glfragBatchLabelNormal)
{
_glfragBatchLabelNormal = new (std::nothrow) GLProgram();
_glfragBatchLabelNormal->initWithByteArrays(ccBatchLabel_vert, ccBatchLabelNormal_frag);
_glfragBatchLabelNormal->link();
_glfragBatchLabelNormal->updateUniforms();
_glfragBatchLabelOutline = new (std::nothrow) GLProgram();
_glfragBatchLabelOutline->initWithByteArrays(ccBatchLabel_vert, ccBatchLabelOutline_frag);
_glfragBatchLabelOutline->link();
_glfragBatchLabelOutline->updateUniforms();
}
}
void LabelBatchString::setString(const std::string &text)
{
if (text.compare(_utf8Text))
{
_utf8Text = text;
std::u32string utf32String;
if (StringUtils::UTF8ToUTF32(_utf8Text, utf32String))
{
_utf32Text = utf32String;
}
alignText();
}
}
void LabelBatchString::setColor(const Color3B &color)
{
ALL_DIRTY();
_labelTxtColor = color;
}
void LabelBatchString::setOpacity(GLubyte opacity)
{
ALL_DIRTY();
_labelTxtOpacity = opacity;
}
void LabelBatchString::setPosition(float x, float y)
{
ALL_DIRTY();
_labelPosition.x = x;
_labelPosition.y = y;
}
void LabelBatchString::setVisible(bool visible)
{
ALL_DIRTY();
_labelIsVisible = visible;
}
void LabelBatchString::setLocalZ(int Z)
{
ALL_DIRTY();
_localZOrder = Z;
}
void LabelBatchString::setAlignment(TextHAlignment hAlignment, TextVAlignment vAlignment)
{
ALL_DIRTY();
_hAlignment = hAlignment;
_vAlignment = vAlignment;
}
void LabelBatchString::cleanup()
{
_hAlignment = TextHAlignment::LEFT;
_vAlignment = TextVAlignment::BOTTOM;
_alignmentOffset = Point::ZERO;
_localZOrder = 0;
_labelIsVisible = true;
_contentDirty = 0;
_labelTxtOpacity = 255;
_labelTxtColor = Color3B::WHITE;
_labelPosition = Point::ZERO;
_parentBatchNode = nullptr;
_utf32Text.clear();
_utf8Text.clear();
cleanupContent();
}
bool LabelBatchString::updateLetterDefinitions(const std::u32string &utf32Text)
{
auto fontAtlas = _parentBatchNode->getFontAltas();
fontAtlas->prepareLetterDefinitions(utf32Text);
auto &textures = fontAtlas->getTextures();
int size = textures.size();
if (size > static_cast<size_t>(_batchNodes.size()))
{
for (auto index = static_cast<size_t>(_batchNodes.size()); index < size; ++index)
{
auto batchNode = SpriteBatchNode::createWithTexture(textures.at(index));
if (batchNode)
{
_blendFunc = batchNode->getBlendFunc();
batchNode->setAnchorPoint(Vec2::ANCHOR_TOP_LEFT);
batchNode->setPosition(Vec2::ZERO);
_batchNodes.pushBack(batchNode);
}
}
}
if (_batchNodes.empty())
return false;
_reusedLetter->setBatchNode(_batchNodes.at(0));
return true;
}
bool LabelBatchString::updateQuads()
{
_lengthOfString = static_cast<int>(_utf32Text.length());
for (auto &&batchNode : _batchNodes)
{
batchNode->getTextureAtlas()->removeAllQuads();
}
auto fontAtlas = _parentBatchNode->getFontAltas();
float advanceX = 0.0f;
int maxFontHeight = fontAtlas->getFont()->getFontMaxHeight();
for (int ctr = 0; ctr < _lengthOfString; ++ctr)
{
auto c = _utf32Text.at(ctr);
FontLetterDefinition letterDef;
fontAtlas->getLetterDefinitionForChar(c, letterDef);
_reusedRect.size.height = letterDef.height;
_reusedRect.size.width = letterDef.width;
_reusedRect.origin.x = letterDef.U;
_reusedRect.origin.y = letterDef.V;
_reusedLetter->setAnchorPoint(Point::ANCHOR_TOP_LEFT);
_reusedLetter->setTextureRect(_reusedRect, false, _reusedRect.size);
if (_reusedRect.size.height > 0.f && _reusedRect.size.width > 0.f)
{
_reusedLetter->setTextureRect(_reusedRect, false, _reusedRect.size);
float x = advanceX + letterDef.offsetX + _alignmentOffset.x + _labelPosition.x;
float y = maxFontHeight - letterDef.offsetY + _alignmentOffset.y + _labelPosition.y;
_reusedLetter->setPosition(x, y);
auto index = static_cast<int>(_batchNodes.at(letterDef.textureID)->getTextureAtlas()->getTotalQuads());
_reusedLetter->setScale(1);
_batchNodes.at(letterDef.textureID)->insertQuadFromSprite(_reusedLetter, index);
advanceX += letterDef.xAdvance;
}
}
_labelContentSize.width = advanceX;
_labelContentSize.height = maxFontHeight;
return true;
}
void LabelBatchString::onDraw(Renderer *renderer, const Mat4 &transform, uint32_t flags)
{
if (_batchNodes.empty() || !_labelIsVisible)
return;
auto textureAtlas = _batchNodes.at(0)->getTextureAtlas();
auto texture = textureAtlas->getTexture();
_quadCommand.init(_localZOrder, texture, _glProgramState, _blendFunc, textureAtlas->getQuads(), textureAtlas->getTotalQuads(), transform, flags);
renderer->addCommand(&_quadCommand);
}
void LabelBatchString::cleanupContent()
{
_batchNodes.clear();
_labelContentSize.width = 0.0f;
_labelContentSize.height = 0.0f;
}
void LabelBatchString::calcAligmentOffset()
{
switch (_hAlignment)
{
case cocos2d::TextHAlignment::CENTER: _alignmentOffset.x = -_labelContentSize.width / 2; break;
case cocos2d::TextHAlignment::RIGHT: _alignmentOffset.x = -_labelContentSize.width; break;
case cocos2d::TextHAlignment::LEFT:
default: _alignmentOffset.x = 0; break;
}
switch (_vAlignment)
{
case cocos2d::TextVAlignment::TOP: _alignmentOffset.y = -_labelContentSize.height; break;
case cocos2d::TextVAlignment::CENTER: _alignmentOffset.y = -_labelContentSize.height / 2; break;
case cocos2d::TextVAlignment::BOTTOM:
default: _alignmentOffset.y = 0; break;
}
}
void LabelBatchString::alignText()
{
if (!updateLetterDefinitions(_utf32Text))
return;
auto fontAtlas = _parentBatchNode->getFontAltas();
_lengthOfString = static_cast<int>(_utf32Text.length());
int advanceX = 0;
int maxFontHeight = fontAtlas->getFont()->getFontMaxHeight();
for (int ctr = 0; ctr < _lengthOfString; ++ctr)
{
auto c = _utf32Text.at(ctr);
FontLetterDefinition letterDef;
fontAtlas->getLetterDefinitionForChar(c, letterDef);
advanceX += letterDef.xAdvance;
}
_labelContentSize.width = advanceX;
_labelContentSize.height = maxFontHeight;
calcAligmentOffset();
updateQuads();
auto hasOutline = _parentBatchNode->hasOutline();
_glProgramState = (hasOutline) ? GLProgramState::getOrCreateWithGLProgram(_glfragBatchLabelOutline)
: GLProgramState::getOrCreateWithGLProgram(_glfragBatchLabelNormal);
}
void LabelBatchString::updateContent()
{
if (0 == _contentDirty)
return;
_reusedLetter->setColor(_labelTxtColor);
_reusedLetter->setOpacity(_labelTxtOpacity);
_reusedLetter->setVisible(_labelIsVisible);
updateQuads();
_contentDirty = 0;
}
//-------------------------------------------------------------------------------------------------------------------
LabelBatchNode::LabelBatchNode(const std::string &fontFilePath, float fontSize, int outlineSize) : _isTextWithOutline(false)
{
_textFontAtlas = nullptr;
if (FileUtils::getInstance()->isFileExist(fontFilePath))
{
_ttfConfig.fontFilePath = fontFilePath;
_ttfConfig.fontSize = fontSize;
_ttfConfig.glyphs = GlyphCollection::DYNAMIC;
_isTextWithOutline = (0 < outlineSize) ? true : false;
_ttfConfig.outlineSize = (_isTextWithOutline) ? outlineSize : 0;
char tmp[128];
snprintf(tmp, sizeof(tmp), "%s_%s_%2.0f", fontFilePath.c_str(), FONT_KEY, fontSize);
_textFontAtlas = FontAtlasCache::getFontAtlasTTF(&_ttfConfig, tmp, 1024, 1024);
//_textFontAtlas->setAliasTexParameters();
_purgeTextureListener = EventListenerCustom::create(FontAtlas::CMD_PURGE_FONTATLAS, [this](EventCustom *event) {
if (_textFontAtlas && event->getUserData() == _textFontAtlas)
{
for (auto &&it : _labelStringMgr)
{
it->cleanupContent();
it->cleanupGL();
}
if (_textFontAtlas)
{
FontAtlasCache::releaseFontAtlas(_textFontAtlas);
}
}
});
_eventDispatcher->addEventListenerWithFixedPriority(_purgeTextureListener, 1);
_resetTextureListener = EventListenerCustom::create(FontAtlas::CMD_RESET_FONTATLAS, [this](EventCustom *event) {
if (_textFontAtlas && event->getUserData() == _textFontAtlas)
{
char tmp[128];
snprintf(tmp, sizeof(tmp), "%s_%2.0f", FONT_KEY, _ttfConfig.fontSize);
_textFontAtlas = FontAtlasCache::getFontAtlasTTF(&_ttfConfig, tmp, 1024, 1024);
for (auto &&it : _labelStringMgr)
{
it->initGL();
it->alignText();
}
}
});
_eventDispatcher->addEventListenerWithFixedPriority(_resetTextureListener, 2);
}
else
{
// LOG( "FUCK!!! TTF file loading was failed in LabelBatchNode!" );
}
}
LabelBatchNode::~LabelBatchNode()
{
for (auto l : _labelStringMgr)
{
delete l;
}
_labelStringMgr.clear();
_eventDispatcher->removeEventListener(_purgeTextureListener);
_eventDispatcher->removeEventListener(_resetTextureListener);
}
LabelBatchNode *LabelBatchNode::create(const std::string &fontFilePath, float fontSize, int outlineSize)
{
if (FileUtils::getInstance()->isFileExist(fontFilePath))
{
auto ret = new (std::nothrow) LabelBatchNode(fontFilePath, fontSize, outlineSize);
return ret;
}
return nullptr;
}
LabelBatchString *LabelBatchNode::createLabelString(TextHAlignment hAlignment, TextVAlignment vAlignment)
{
auto ret = new (std::nothrow) LabelBatchString(hAlignment, vAlignment);
_labelStringMgr.push_back(ret);
ret->_parentBatchNode = this;
return ret;
}
void LabelBatchNode::addLabelString(LabelBatchString *label)
{
_labelStringMgr.push_back(label);
label->_parentBatchNode = this;
}
bool LabelBatchNode::destroyLabelString(LabelBatchString *l)
{
auto it = std::find(_labelStringMgr.begin(), _labelStringMgr.end(), l);
if (_labelStringMgr.end() == it)
return false;
delete l;
_labelStringMgr.erase(it);
return true;
}
bool LabelBatchNode::removeLabelString(LabelBatchString *l)
{
return destroyLabelString(l);
}
void LabelBatchNode::visit(Renderer *renderer, const Mat4 &parentTransform, uint32_t parentFlags)
{
if (!_visible)
{
return;
}
for (auto l : _labelStringMgr)
{
l->updateContent();
}
Node::visit(renderer, parentTransform, parentFlags);
}
void LabelBatchNode::draw(Renderer *renderer, const Mat4 &transform, uint32_t flags)
{
for (auto l : _labelStringMgr)
{
l->onDraw(renderer, transform, flags);
}
Node::draw(renderer, transform, flags);
}