BangJinM BangJinM 游戏开发 菜鸡游戏开发,邮箱mabangjin@outlook.com

字体

» 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);
}