Merge pull request #12718 from WenhaiLin/v3-label-addchild

Label:Support add child nodes.
This commit is contained in:
子龙山人 2015-07-09 15:37:49 +08:00
commit 8e256dcd09
7 changed files with 448 additions and 260 deletions

View File

@ -10,7 +10,7 @@ Core Developers:
Hao Wu (Wu-Hao) Hao Wu (Wu-Hao)
Qingkui Hu (samuele3hu) Qingkui Hu (samuele3hu)
Huabing Xu (dabingnn) Huabing Xu (dabingnn)
Wenhai Lin (Dhilan007) Wenhai Lin (WenhaiLin)
Guanghui Qu (zilongshanren Guanghui Qu (zilongshanren
Wensheng Yang (yangws) Wensheng Yang (yangws)
Yulei Liao (dualface) Yulei Liao (dualface)

View File

@ -26,6 +26,7 @@
#include "2d/CCLabel.h" #include "2d/CCLabel.h"
#include "2d/CCFontAtlasCache.h" #include "2d/CCFontAtlasCache.h"
#include "2d/CCSprite.h" #include "2d/CCSprite.h"
#include "2d/CCSpriteBatchNode.h"
#include "2d/CCLabelTextFormatter.h" #include "2d/CCLabelTextFormatter.h"
#include "base/ccUTF8.h" #include "base/ccUTF8.h"
#include "platform/CCFileUtils.h" #include "platform/CCFileUtils.h"
@ -42,6 +43,99 @@ NS_CC_BEGIN
const int Label::DistanceFieldFontSize = 50; const int Label::DistanceFieldFontSize = 50;
/**
* LabelLetter used to update the quad in texture atlas without SpriteBatchNode.
*/
class LabelLetter : public Sprite
{
public:
LabelLetter()
{
_textureAtlas = nullptr;
}
static LabelLetter* createWithTexture(Texture2D *texture, const Rect& rect, bool rotated = false)
{
auto letter = new (std::nothrow) LabelLetter();
if (letter && letter->initWithTexture(texture, rect, rotated))
{
letter->setVisible(false);
letter->autorelease();
return letter;
}
CC_SAFE_DELETE(letter);
return nullptr;
}
virtual void updateTransform() override
{
if (isDirty())
{
_transformToBatch = getNodeToParentTransform();
Size &size = _rect.size;
float x1 = _offsetPosition.x;
float y1 = _offsetPosition.y;
float x2 = x1 + size.width;
float y2 = y1 + size.height;
float x = _transformToBatch.m[12];
float y = _transformToBatch.m[13];
float cr = _transformToBatch.m[0];
float sr = _transformToBatch.m[1];
float cr2 = _transformToBatch.m[5];
float sr2 = -_transformToBatch.m[4];
float ax = x1 * cr - y1 * sr2 + x;
float ay = x1 * sr + y1 * cr2 + y;
float bx = x2 * cr - y1 * sr2 + x;
float by = x2 * sr + y1 * cr2 + y;
float cx = x2 * cr - y2 * sr2 + x;
float cy = x2 * sr + y2 * cr2 + y;
float dx = x1 * cr - y2 * sr2 + x;
float dy = x1 * sr + y2 * cr2 + y;
_quad.bl.vertices.set(SPRITE_RENDER_IN_SUBPIXEL(ax), SPRITE_RENDER_IN_SUBPIXEL(ay), _positionZ);
_quad.br.vertices.set(SPRITE_RENDER_IN_SUBPIXEL(bx), SPRITE_RENDER_IN_SUBPIXEL(by), _positionZ);
_quad.tl.vertices.set(SPRITE_RENDER_IN_SUBPIXEL(dx), SPRITE_RENDER_IN_SUBPIXEL(dy), _positionZ);
_quad.tr.vertices.set(SPRITE_RENDER_IN_SUBPIXEL(cx), SPRITE_RENDER_IN_SUBPIXEL(cy), _positionZ);
if (_textureAtlas)
{
_textureAtlas->updateQuad(&_quad, _atlasIndex);
}
_recursiveDirty = false;
setDirty(false);
}
Node::updateTransform();
}
virtual void updateColor()
{
if (_textureAtlas == nullptr)
{
return;
}
Color4B color4(_displayedColor.r, _displayedColor.g, _displayedColor.b, _displayedOpacity);
// special opacity for premultiplied textures
if (_opacityModifyRGB)
{
color4.r *= _displayedOpacity / 255.0f;
color4.g *= _displayedOpacity / 255.0f;
color4.b *= _displayedOpacity / 255.0f;
}
_quad.bl.colors = color4;
_quad.br.colors = color4;
_quad.tl.colors = color4;
_quad.tr.colors = color4;
_textureAtlas->updateQuad(&_quad, _atlasIndex);
}
};
Label* Label::create() Label* Label::create()
{ {
auto ret = new (std::nothrow) Label(); auto ret = new (std::nothrow) Label();
@ -257,6 +351,7 @@ Label::Label(FontAtlas *atlas /* = nullptr */, TextHAlignment hAlignment /* = Te
, _effectColorF(Color4F::BLACK) , _effectColorF(Color4F::BLACK)
, _uniformEffectColor(0) , _uniformEffectColor(0)
, _shadowDirty(false) , _shadowDirty(false)
, _blendFunc(BlendFunc::ALPHA_PREMULTIPLIED)
, _insideBounds(true) , _insideBounds(true)
{ {
setAnchorPoint(Vec2::ANCHOR_MIDDLE); setAnchorPoint(Vec2::ANCHOR_MIDDLE);
@ -265,9 +360,11 @@ Label::Label(FontAtlas *atlas /* = nullptr */, TextHAlignment hAlignment /* = Te
_purgeTextureListener = EventListenerCustom::create(FontAtlas::CMD_PURGE_FONTATLAS, [this](EventCustom* event){ _purgeTextureListener = EventListenerCustom::create(FontAtlas::CMD_PURGE_FONTATLAS, [this](EventCustom* event){
if (_fontAtlas && _currentLabelType == LabelType::TTF && event->getUserData() == _fontAtlas) if (_fontAtlas && _currentLabelType == LabelType::TTF && event->getUserData() == _fontAtlas)
{ {
Node::removeAllChildrenWithCleanup(true); for (auto it : _letters)
{
it.second->setTexture(nullptr);
}
_batchNodes.clear(); _batchNodes.clear();
_batchNodes.push_back(this);
if (_fontAtlas) if (_fontAtlas)
{ {
@ -282,6 +379,10 @@ Label::Label(FontAtlas *atlas /* = nullptr */, TextHAlignment hAlignment /* = Te
{ {
_fontAtlas = nullptr; _fontAtlas = nullptr;
this->setTTFConfig(_fontConfig); this->setTTFConfig(_fontConfig);
for (auto it : _letters)
{
getLetter(it.first);
}
} }
}); });
_eventDispatcher->addEventListenerWithFixedPriority(_resetTextureListener, 2); _eventDispatcher->addEventListenerWithFixedPriority(_resetTextureListener, 2);
@ -299,6 +400,8 @@ Label::~Label()
_eventDispatcher->removeEventListener(_resetTextureListener); _eventDispatcher->removeEventListener(_resetTextureListener);
CC_SAFE_RELEASE_NULL(_reusedLetter); CC_SAFE_RELEASE_NULL(_reusedLetter);
CC_SAFE_RELEASE_NULL(_textSprite);
CC_SAFE_RELEASE_NULL(_shadowNode);
} }
void Label::reset() void Label::reset()
@ -311,7 +414,6 @@ void Label::reset()
_systemFontSize = 12; _systemFontSize = 12;
_batchNodes.clear(); _batchNodes.clear();
_batchNodes.push_back(this);
if (_fontAtlas) if (_fontAtlas)
{ {
@ -384,16 +486,6 @@ void Label::setFontAtlas(FontAtlas* atlas,bool distanceFieldEnabled /* = false *
} }
_fontAtlas = atlas; _fontAtlas = atlas;
if (_textureAtlas)
{
_textureAtlas->setTexture(_fontAtlas->getTexture(0));
}
else
{
SpriteBatchNode::initWithTexture(_fontAtlas->getTexture(0), 30);
}
if (_reusedLetter == nullptr) if (_reusedLetter == nullptr)
{ {
_reusedLetter = Sprite::create(); _reusedLetter = Sprite::create();
@ -401,7 +493,6 @@ void Label::setFontAtlas(FontAtlas* atlas,bool distanceFieldEnabled /* = false *
_reusedLetter->retain(); _reusedLetter->retain();
_reusedLetter->setAnchorPoint(Vec2::ANCHOR_TOP_LEFT); _reusedLetter->setAnchorPoint(Vec2::ANCHOR_TOP_LEFT);
} }
_reusedLetter->setBatchNode(this);
if (_fontAtlas) if (_fontAtlas)
{ {
@ -594,12 +685,21 @@ void Label::alignText()
for (auto index = _batchNodes.size(); index < textures.size(); ++index) for (auto index = _batchNodes.size(); index < textures.size(); ++index)
{ {
auto batchNode = SpriteBatchNode::createWithTexture(textures.at(index)); auto batchNode = SpriteBatchNode::createWithTexture(textures.at(index));
batchNode->setAnchorPoint(Vec2::ANCHOR_TOP_LEFT); if (batchNode)
batchNode->setPosition(Vec2::ZERO); {
Node::addChild(batchNode,0,Node::INVALID_TAG); _blendFunc = batchNode->getBlendFunc();
_batchNodes.push_back(batchNode); batchNode->setAnchorPoint(Vec2::ANCHOR_TOP_LEFT);
batchNode->setPosition(Vec2::ZERO);
_batchNodes.pushBack(batchNode);
}
} }
} }
if (_batchNodes.empty())
{
return;
}
_reusedLetter->setBatchNode(_batchNodes.at(0));
LabelTextFormatter::createStringSprites(this); LabelTextFormatter::createStringSprites(this);
if(_maxLineWidth > 0 && _contentSize.width > _maxLineWidth && LabelTextFormatter::multilineText(this) ) if(_maxLineWidth > 0 && _contentSize.width > _maxLineWidth && LabelTextFormatter::multilineText(this) )
LabelTextFormatter::createStringSprites(this); LabelTextFormatter::createStringSprites(this);
@ -607,39 +707,36 @@ void Label::alignText()
if(_labelWidth > 0 || (_currNumLines > 1 && _hAlignment != TextHAlignment::LEFT)) if(_labelWidth > 0 || (_currNumLines > 1 && _hAlignment != TextHAlignment::LEFT))
LabelTextFormatter::alignText(this); LabelTextFormatter::alignText(this);
if (!_children.empty()) if (!_letters.empty())
{ {
int strLen = static_cast<int>(_currentUTF16String.length());
Rect uvRect; Rect uvRect;
Sprite* letterSprite; Sprite* letterSprite;
for (auto index = 0; index < _children.size();) { int letterIndex;
auto child = _children.at(index);
int tag = child->getTag(); for (auto it = _letters.begin(); it != _letters.end();)
if (tag >= strLen) {
letterIndex = it->first;
letterSprite = it->second;
if (letterIndex >= _limitShowCount)
{ {
child->removeFromParentAndCleanup(true); Node::removeChild(letterSprite, true);
} it = _letters.erase(it);
else if (tag >= 0)
{
letterSprite = dynamic_cast<Sprite*>(child);
if (letterSprite)
{
auto& letterDef = _lettersInfo[tag].def;
uvRect.size.height = letterDef.height;
uvRect.size.width = letterDef.width;
uvRect.origin.x = letterDef.U;
uvRect.origin.y = letterDef.V;
letterSprite->setBatchNode(_batchNodes[letterDef.textureID]);
letterSprite->setTextureRect(uvRect, false, uvRect.size);
letterSprite->setPosition(_lettersInfo[tag].position.x + letterDef.width/2,
_lettersInfo[tag].position.y - letterDef.height/2);
}
++index;
} }
else else
{ {
++index; auto& letterDef = _lettersInfo[letterIndex].def;
uvRect.size.height = letterDef.height;
uvRect.size.width = letterDef.width;
uvRect.origin.x = letterDef.U;
uvRect.origin.y = letterDef.V;
letterSprite->setBatchNode(_batchNodes.at(letterDef.textureID));
letterSprite->setTextureRect(uvRect, false, uvRect.size);
letterSprite->setPosition(_lettersInfo[letterIndex].position.x + letterDef.width / 2,
_lettersInfo[letterIndex].position.y - letterDef.height / 2);
++it;
} }
} }
} }
@ -700,9 +797,9 @@ void Label::updateQuads()
_reusedLetter->setTextureRect(_reusedRect,false,_reusedRect.size); _reusedLetter->setTextureRect(_reusedRect,false,_reusedRect.size);
_reusedLetter->setPosition(_lettersInfo[ctr].position); _reusedLetter->setPosition(_lettersInfo[ctr].position);
index = static_cast<int>(_batchNodes[letterDef.textureID]->getTextureAtlas()->getTotalQuads()); index = static_cast<int>(_batchNodes.at(letterDef.textureID)->getTextureAtlas()->getTotalQuads());
_lettersInfo[ctr].atlasIndex = index; _lettersInfo[ctr].atlasIndex = index;
_batchNodes[letterDef.textureID]->insertQuadFromSprite(_reusedLetter,index); _batchNodes.at(letterDef.textureID)->insertQuadFromSprite(_reusedLetter,index);
} }
} }
} }
@ -738,16 +835,6 @@ bool Label::recordPlaceholderInfo(int spriteIndex)
return false; return false;
} }
void Label::addChild(Node * child, int zOrder/* =0 */, int tag/* =0 */)
{
CCASSERT(0, "addChild: is not supported on Label.");
}
void Label::sortAllChildren()
{
// Label ignore sort children
}
void Label::enableGlow(const Color4B& glowColor) void Label::enableGlow(const Color4B& glowColor)
{ {
if (_currentLabelType == LabelType::TTF) if (_currentLabelType == LabelType::TTF)
@ -821,7 +908,7 @@ void Label::enableShadow(const Color4B& shadowColor /* = Color4B::BLACK */,const
{ {
if (shadowColor != _shadowColor4F) if (shadowColor != _shadowColor4F)
{ {
Node::removeChild(_shadowNode, true); _shadowNode->release();
_shadowNode = nullptr; _shadowNode = nullptr;
createShadowSpriteForSystemFont(); createShadowSpriteForSystemFont();
} }
@ -872,11 +959,7 @@ void Label::disableEffect(LabelEffect effect)
if (_shadowEnabled) if (_shadowEnabled)
{ {
_shadowEnabled = false; _shadowEnabled = false;
if (_shadowNode) CC_SAFE_RELEASE_NULL(_shadowNode);
{
Node::removeChild(_shadowNode, true);
_shadowNode = nullptr;
}
} }
break; break;
case cocos2d::LabelEffect::GLOW: case cocos2d::LabelEffect::GLOW:
@ -904,121 +987,6 @@ void Label::setFontScale(float fontScale)
Node::setScale(_fontScale); Node::setScale(_fontScale);
} }
void Label::onDraw(const Mat4& transform, bool transformUpdated)
{
// Optimization: Fast Dispatch
if( _textureAtlas == NULL || (_batchNodes.size() == 1 && _textureAtlas->getTotalQuads() == 0) )
{
return;
}
auto glprogram = getGLProgram();
glprogram->use();
GL::blendFunc( _blendFunc.src, _blendFunc.dst );
if (_shadowEnabled)
{
if (_currentLabelType == LabelType::TTF)
{
glprogram->setUniformLocationWith4f(_uniformTextColor,
_shadowColor4F.r, _shadowColor4F.g, _shadowColor4F.b, _shadowColor4F.a);
if (_currLabelEffect == LabelEffect::OUTLINE || _currLabelEffect == LabelEffect::GLOW)
{
glprogram->setUniformLocationWith4f(_uniformEffectColor,
_shadowColor4F.r, _shadowColor4F.g, _shadowColor4F.b, _shadowColor4F.a);
}
getGLProgram()->setUniformsForBuiltins(_shadowTransform);
for (const auto &child : _children)
{
child->updateTransform();
}
for (const auto& batchNode : _batchNodes)
{
batchNode->getTextureAtlas()->drawQuads();
}
}
else
{
Color3B oldColor = _realColor;
GLubyte oldOPacity = _displayedOpacity;
_displayedOpacity = _shadowOpacity;
setColor(_shadowColor3B);
getGLProgram()->setUniformsForBuiltins(_shadowTransform);
for (const auto &child : _children)
{
child->updateTransform();
}
for (const auto& batchNode : _batchNodes)
{
batchNode->getTextureAtlas()->drawQuads();
}
_displayedOpacity = oldOPacity;
setColor(oldColor);
}
}
glprogram->setUniformsForBuiltins(transform);
for(const auto &child: _children)
{
child->updateTransform();
}
if (_currentLabelType == LabelType::TTF)
{
switch (_currLabelEffect) {
case LabelEffect::OUTLINE:
//draw text with outline
glprogram->setUniformLocationWith4f(_uniformTextColor,
_textColorF.r,_textColorF.g,_textColorF.b,_textColorF.a);
glprogram->setUniformLocationWith4f(_uniformEffectColor,
_effectColorF.r, _effectColorF.g, _effectColorF.b, _effectColorF.a);
for (const auto& batchNode:_batchNodes)
{
batchNode->getTextureAtlas()->drawQuads();
}
//draw text without outline
glprogram->setUniformLocationWith4f(_uniformEffectColor,
_effectColorF.r, _effectColorF.g, _effectColorF.b, 0.f);
break;
case LabelEffect::GLOW:
glprogram->setUniformLocationWith4f(_uniformEffectColor,
_effectColorF.r, _effectColorF.g, _effectColorF.b, _effectColorF.a);
case LabelEffect::NORMAL:
glprogram->setUniformLocationWith4f(_uniformTextColor,
_textColorF.r,_textColorF.g,_textColorF.b,_textColorF.a);
break;
default:
break;
}
}
for (const auto& batchNode:_batchNodes)
{
batchNode->getTextureAtlas()->drawQuads();
}
}
void Label::draw(Renderer *renderer, const Mat4 &transform, uint32_t flags)
{
// Don't do calculate the culling if the transform was not updated
bool transformUpdated = flags & FLAGS_TRANSFORM_DIRTY;
#if CC_USE_CULLING
_insideBounds = transformUpdated ? renderer->checkVisibility(transform, _contentSize) : _insideBounds;
if(_insideBounds)
#endif
{
_customCommand.init(_globalZOrder, transform, flags);
_customCommand.func = CC_CALLBACK_0(Label::onDraw, this, transform, transformUpdated);
renderer->addCommand(&_customCommand);
}
}
void Label::createSpriteForSystemFont() void Label::createSpriteForSystemFont()
{ {
_currentLabelType = LabelType::STRING_TEXTURE; _currentLabelType = LabelType::STRING_TEXTURE;
@ -1078,8 +1046,7 @@ void Label::createSpriteForSystemFont()
_textSprite->setBlendFunc(_blendFunc); _textSprite->setBlendFunc(_blendFunc);
} }
Node::addChild(_textSprite, 0, Node::INVALID_TAG); _textSprite->retain();
_textSprite->updateDisplayedColor(_displayedColor); _textSprite->updateDisplayedColor(_displayedColor);
_textSprite->updateDisplayedOpacity(_displayedOpacity); _textSprite->updateDisplayedOpacity(_displayedOpacity);
} }
@ -1117,8 +1084,8 @@ void Label::createShadowSpriteForSystemFont()
_shadowNode->setCameraMask(getCameraMask()); _shadowNode->setCameraMask(getCameraMask());
_shadowNode->setAnchorPoint(Vec2::ANCHOR_BOTTOM_LEFT); _shadowNode->setAnchorPoint(Vec2::ANCHOR_BOTTOM_LEFT);
_shadowNode->setPosition(_shadowOffset.width, _shadowOffset.height); _shadowNode->setPosition(_shadowOffset.width, _shadowOffset.height);
Node::addChild(_shadowNode, 0, Node::INVALID_TAG);
_shadowNode->retain();
_shadowNode->updateDisplayedColor(_displayedColor); _shadowNode->updateDisplayedColor(_displayedColor);
_shadowNode->updateDisplayedOpacity(_displayedOpacity); _shadowNode->updateDisplayedOpacity(_displayedOpacity);
} }
@ -1126,7 +1093,7 @@ void Label::createShadowSpriteForSystemFont()
void Label::setCameraMask(unsigned short mask, bool applyChildren) void Label::setCameraMask(unsigned short mask, bool applyChildren)
{ {
SpriteBatchNode::setCameraMask(mask, applyChildren); Node::setCameraMask(mask, applyChildren);
if (_textSprite) if (_textSprite)
{ {
@ -1163,7 +1130,6 @@ void Label::updateContent()
if (_fontAtlas) if (_fontAtlas)
{ {
_batchNodes.clear(); _batchNodes.clear();
_batchNodes.push_back(this);
FontAtlasCache::releaseFontAtlas(_fontAtlas); FontAtlasCache::releaseFontAtlas(_fontAtlas);
_fontAtlas = nullptr; _fontAtlas = nullptr;
@ -1172,16 +1138,8 @@ void Label::updateContent()
_systemFontDirty = false; _systemFontDirty = false;
} }
if (_textSprite) CC_SAFE_RELEASE_NULL(_textSprite);
{ CC_SAFE_RELEASE_NULL(_shadowNode);
Node::removeChild(_textSprite, true);
_textSprite = nullptr;
if (_shadowNode)
{
Node::removeChild(_shadowNode, true);
_shadowNode = nullptr;
}
}
if (_fontAtlas) if (_fontAtlas)
{ {
@ -1206,9 +1164,127 @@ void Label::updateContent()
_contentDirty = false; _contentDirty = false;
} }
void Label::onDrawShadow(GLProgram* glProgram)
{
if (_currentLabelType == LabelType::TTF)
{
glProgram->setUniformLocationWith4f(_uniformTextColor,
_shadowColor4F.r, _shadowColor4F.g, _shadowColor4F.b, _shadowColor4F.a);
if (_currLabelEffect == LabelEffect::OUTLINE || _currLabelEffect == LabelEffect::GLOW)
{
glProgram->setUniformLocationWith4f(_uniformEffectColor,
_shadowColor4F.r, _shadowColor4F.g, _shadowColor4F.b, _shadowColor4F.a);
}
glProgram->setUniformsForBuiltins(_shadowTransform);
for (auto it : _letters)
{
it.second->updateTransform();
}
for (const auto& batchNode : _batchNodes)
{
batchNode->getTextureAtlas()->drawQuads();
}
}
else
{
Color3B oldColor = _realColor;
GLubyte oldOPacity = _displayedOpacity;
_displayedOpacity = _shadowOpacity;
setColor(_shadowColor3B);
glProgram->setUniformsForBuiltins(_shadowTransform);
for (auto it : _letters)
{
it.second->updateTransform();
}
for (const auto& batchNode : _batchNodes)
{
batchNode->getTextureAtlas()->drawQuads();
}
_displayedOpacity = oldOPacity;
setColor(oldColor);
}
}
void Label::onDraw(const Mat4& transform, bool transformUpdated)
{
auto glprogram = getGLProgram();
glprogram->use();
GL::blendFunc(_blendFunc.src, _blendFunc.dst);
if (_shadowEnabled)
{
onDrawShadow(glprogram);
}
glprogram->setUniformsForBuiltins(transform);
for (auto it : _letters)
{
it.second->updateTransform();
}
if (_currentLabelType == LabelType::TTF)
{
switch (_currLabelEffect) {
case LabelEffect::OUTLINE:
//draw text with outline
glprogram->setUniformLocationWith4f(_uniformTextColor,
_textColorF.r, _textColorF.g, _textColorF.b, _textColorF.a);
glprogram->setUniformLocationWith4f(_uniformEffectColor,
_effectColorF.r, _effectColorF.g, _effectColorF.b, _effectColorF.a);
for (const auto& batchNode : _batchNodes)
{
batchNode->getTextureAtlas()->drawQuads();
}
//draw text without outline
glprogram->setUniformLocationWith4f(_uniformEffectColor,
_effectColorF.r, _effectColorF.g, _effectColorF.b, 0.f);
break;
case LabelEffect::GLOW:
glprogram->setUniformLocationWith4f(_uniformEffectColor,
_effectColorF.r, _effectColorF.g, _effectColorF.b, _effectColorF.a);
case LabelEffect::NORMAL:
glprogram->setUniformLocationWith4f(_uniformTextColor,
_textColorF.r, _textColorF.g, _textColorF.b, _textColorF.a);
break;
default:
break;
}
}
for (const auto& batchNode : _batchNodes)
{
batchNode->getTextureAtlas()->drawQuads();
}
}
void Label::draw(Renderer *renderer, const Mat4 &transform, uint32_t flags)
{
if (_batchNodes.empty() || _limitShowCount <= 0)
{
return;
}
// Don't do calculate the culling if the transform was not updated
bool transformUpdated = flags & FLAGS_TRANSFORM_DIRTY;
#if CC_USE_CULLING
_insideBounds = transformUpdated ? renderer->checkVisibility(transform, _contentSize) : _insideBounds;
if (_insideBounds)
#endif
{
_customCommand.init(_globalZOrder, transform, flags);
_customCommand.func = CC_CALLBACK_0(Label::onDraw, this, transform, transformUpdated);
renderer->addCommand(&_customCommand);
}
}
void Label::visit(Renderer *renderer, const Mat4 &parentTransform, uint32_t parentFlags) void Label::visit(Renderer *renderer, const Mat4 &parentTransform, uint32_t parentFlags)
{ {
if (! _visible || _originalUTF8String.empty()) if (! _visible || (_originalUTF8String.empty() && _children.empty()) )
{ {
return; return;
} }
@ -1220,7 +1296,8 @@ void Label::visit(Renderer *renderer, const Mat4 &parentTransform, uint32_t pare
uint32_t flags = processParentFlags(parentTransform, parentFlags); uint32_t flags = processParentFlags(parentTransform, parentFlags);
if (_shadowEnabled && _shadowBlurRadius <= 0 && (_shadowDirty || (flags & FLAGS_DIRTY_MASK))) if (!_originalUTF8String.empty() && _shadowEnabled && _shadowBlurRadius <= 0
&& (_shadowDirty || (flags & FLAGS_DIRTY_MASK)))
{ {
_position.x += _shadowOffset.width; _position.x += _shadowOffset.width;
_position.y += _shadowOffset.height; _position.y += _shadowOffset.height;
@ -1235,7 +1312,8 @@ void Label::visit(Renderer *renderer, const Mat4 &parentTransform, uint32_t pare
_shadowDirty = false; _shadowDirty = false;
} }
if (!_textSprite && !isVisitableByVisitingCamera()) bool visibleByCamera = isVisitableByVisitingCamera();
if (_children.empty() && !_textSprite && !visibleByCamera)
{ {
return; return;
} }
@ -1246,6 +1324,41 @@ void Label::visit(Renderer *renderer, const Mat4 &parentTransform, uint32_t pare
_director->pushMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW); _director->pushMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);
_director->loadMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW, _modelViewTransform); _director->loadMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW, _modelViewTransform);
if (!_children.empty())
{
sortAllChildren();
int i = 0;
// draw children zOrder < 0
for (; i < _children.size(); i++)
{
auto node = _children.at(i);
if (node && node->getLocalZOrder() < 0)
node->visit(renderer, _modelViewTransform, flags);
else
break;
}
// self draw
if (visibleByCamera)
this->drawSelf(renderer, flags);
for (auto it = _children.cbegin() + i; it != _children.cend(); ++it)
{
(*it)->visit(renderer, _modelViewTransform, flags);
}
}
else if (visibleByCamera)
{
this->drawSelf(renderer, flags);
}
_director->popMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);
}
void Label::drawSelf(Renderer* renderer, uint32_t flags)
{
if (_textSprite) if (_textSprite)
{ {
if (_shadowNode) if (_shadowNode)
@ -1258,12 +1371,6 @@ void Label::visit(Renderer *renderer, const Mat4 &parentTransform, uint32_t pare
{ {
draw(renderer, _modelViewTransform, flags); draw(renderer, _modelViewTransform, flags);
} }
_director->popMatrix(MATRIX_STACK_TYPE::MATRIX_STACK_MODELVIEW);
// FIX ME: Why need to set _orderOfArrival to 0??
// Please refer to https://github.com/cocos2d/cocos2d-x/pull/6920
// setOrderOfArrival(0);
} }
void Label::setSystemFontName(const std::string& systemFont) void Label::setSystemFontName(const std::string& systemFont)
@ -1287,45 +1394,63 @@ void Label::setSystemFontSize(float fontSize)
///// PROTOCOL STUFF ///// PROTOCOL STUFF
Sprite * Label::getLetter(int letterIndex) Sprite * Label::getLetter(int letterIndex)
{ {
if (_systemFontDirty || _currentLabelType == LabelType::STRING_TEXTURE) Sprite* letter = nullptr;
do
{ {
return nullptr; if (_systemFontDirty || _currentLabelType == LabelType::STRING_TEXTURE)
}
if (_contentDirty)
{
updateContent();
}
if (! _textSprite && letterIndex < _limitShowCount)
{
const auto &letter = _lettersInfo[letterIndex];
if(! letter.def.validDefinition)
return nullptr;
Sprite* sp = static_cast<Sprite*>(this->getChildByTag(letterIndex));
if (!sp)
{ {
Rect uvRect; break;
uvRect.size.height = letter.def.height; }
uvRect.size.width = letter.def.width;
uvRect.origin.x = letter.def.U;
uvRect.origin.y = letter.def.V;
sp = Sprite::createWithTexture(_fontAtlas->getTexture(letter.def.textureID),uvRect); auto contentDirty = _contentDirty;
sp->setBatchNode(_batchNodes[letter.def.textureID]); if (contentDirty)
sp->setPosition(letter.position.x + uvRect.size.width / 2, {
letter.position.y - uvRect.size.height / 2); updateContent();
sp->setOpacity(_realOpacity);
_batchNodes[letter.def.textureID]->addSpriteWithoutQuad(sp, letter.atlasIndex, letterIndex);
} }
return sp;
}
return nullptr; if (_textSprite == nullptr && letterIndex < _limitShowCount)
{
const auto &letterInfo = _lettersInfo[letterIndex];
if (!letterInfo.def.validDefinition)
{
break;
}
if (_letters.find(letterIndex) != _letters.end())
{
letter = _letters[letterIndex];
}
auto textureID = letterInfo.def.textureID;
Rect uvRect;
uvRect.size.height = letterInfo.def.height;
uvRect.size.width = letterInfo.def.width;
uvRect.origin.x = letterInfo.def.U;
uvRect.origin.y = letterInfo.def.V;
if (letter == nullptr)
{
letter = LabelLetter::createWithTexture(_fontAtlas->getTexture(textureID), uvRect);
letter->setTextureAtlas(_batchNodes.at(textureID)->getTextureAtlas());
letter->setAtlasIndex(letterInfo.atlasIndex);
letter->setPosition(letterInfo.position.x + uvRect.size.width / 2,
letterInfo.position.y - uvRect.size.height / 2);
letter->setOpacity(_realOpacity);
addChild(letter);
_letters[letterIndex] = letter;
}
else if (contentDirty)
{
letter->setTexture(_fontAtlas->getTexture(textureID));
letter->setTextureRect(uvRect, false, uvRect.size);
letter->setTextureAtlas(_batchNodes.at(textureID)->getTextureAtlas());
}
}
} while (false);
return letter;
} }
void Label::setLineHeight(float height) void Label::setLineHeight(float height)
@ -1416,10 +1541,7 @@ void Label::setOpacityModifyRGB(bool isOpacityModifyRGB)
void Label::updateDisplayedColor(const Color3B& parentColor) void Label::updateDisplayedColor(const Color3B& parentColor)
{ {
_displayedColor.r = _realColor.r * parentColor.r/255.0; Node::updateDisplayedColor(parentColor);
_displayedColor.g = _realColor.g * parentColor.g/255.0;
_displayedColor.b = _realColor.b * parentColor.b/255.0;
updateColor();
if (_textSprite) if (_textSprite)
{ {
@ -1433,8 +1555,7 @@ void Label::updateDisplayedColor(const Color3B& parentColor)
void Label::updateDisplayedOpacity(GLubyte parentOpacity) void Label::updateDisplayedOpacity(GLubyte parentOpacity)
{ {
_displayedOpacity = _realOpacity * parentOpacity/255.0; Node::updateDisplayedOpacity(parentOpacity);
updateColor();
if (_textSprite) if (_textSprite)
{ {
@ -1468,7 +1589,7 @@ void Label::setTextColor(const Color4B &color)
void Label::updateColor() void Label::updateColor()
{ {
if (nullptr == _textureAtlas) if (_batchNodes.empty())
{ {
return; return;
} }
@ -1539,4 +1660,23 @@ void Label::setBlendFunc(const BlendFunc &blendFunc)
} }
} }
void Label::removeAllChildrenWithCleanup(bool cleanup)
{
Node::removeAllChildrenWithCleanup(cleanup);
_letters.clear();
}
void Label::removeChild(Node* child, bool cleanup /* = true */)
{
Node::removeChild(child, cleanup);
for (auto it : _letters)
{
if (it.second == child)
{
_letters.erase(it.first);
break;
}
}
}
NS_CC_END NS_CC_END

View File

@ -26,7 +26,7 @@
#ifndef _COCOS2D_CCLABEL_H_ #ifndef _COCOS2D_CCLABEL_H_
#define _COCOS2D_CCLABEL_H_ #define _COCOS2D_CCLABEL_H_
#include "2d/CCSpriteBatchNode.h" #include "2d/CCNode.h"
#include "renderer/CCCustomCommand.h" #include "renderer/CCCustomCommand.h"
#include "2d/CCFontAtlas.h" #include "2d/CCFontAtlas.h"
#include "base/ccTypes.h" #include "base/ccTypes.h"
@ -83,8 +83,11 @@ typedef struct _ttfConfig
} }
}TTFConfig; }TTFConfig;
class Sprite;
class SpriteBatchNode;
/** /**
* @brief Label is a subclass of SpriteBatchNode that knows how to render text labels. * @brief Label is a subclass of Node that knows how to render text labels.
* *
* Label can be created with: * Label can be created with:
* - A true type font file. * - A true type font file.
@ -99,7 +102,7 @@ typedef struct _ttfConfig
* - http://www.angelcode.com/products/bmfont/ (Free, Windows only) * - http://www.angelcode.com/products/bmfont/ (Free, Windows only)
* @js NA * @js NA
*/ */
class CC_DLL Label : public SpriteBatchNode, public LabelProtocol class CC_DLL Label : public Node, public LabelProtocol, public BlendProtocol
{ {
public: public:
static const int DistanceFieldFontSize; static const int DistanceFieldFontSize;
@ -435,6 +438,7 @@ public:
FontAtlas* getFontAtlas() { return _fontAtlas; } FontAtlas* getFontAtlas() { return _fontAtlas; }
virtual const BlendFunc& getBlendFunc() const override { return _blendFunc; }
virtual void setBlendFunc(const BlendFunc &blendFunc) override; virtual void setBlendFunc(const BlendFunc &blendFunc) override;
virtual bool isOpacityModifyRGB() const override; virtual bool isOpacityModifyRGB() const override;
@ -448,9 +452,6 @@ public:
virtual float getScaleX() const override; virtual float getScaleX() const override;
virtual float getScaleY() const override; virtual float getScaleY() const override;
virtual void addChild(Node * child, int zOrder=0, int tag=0) override;
virtual void sortAllChildren() override;
virtual std::string getDescription() const override; virtual std::string getDescription() const override;
virtual const Size& getContentSize() const override; virtual const Size& getContentSize() const override;
@ -462,6 +463,9 @@ public:
virtual void setCameraMask(unsigned short mask, bool applyChildren = true) override; virtual void setCameraMask(unsigned short mask, bool applyChildren = true) override;
virtual void removeAllChildrenWithCleanup(bool cleanup) override;
virtual void removeChild(Node* child, bool cleanup = true) override;
CC_DEPRECATED_ATTRIBUTE static Label* create(const std::string& text, const std::string& font, float fontSize, CC_DEPRECATED_ATTRIBUTE static Label* create(const std::string& text, const std::string& font, float fontSize,
const Size& dimensions = Size::ZERO, TextHAlignment hAlignment = TextHAlignment::LEFT, const Size& dimensions = Size::ZERO, TextHAlignment hAlignment = TextHAlignment::LEFT,
TextVAlignment vAlignment = TextVAlignment::TOP); TextVAlignment vAlignment = TextVAlignment::TOP);
@ -491,6 +495,7 @@ CC_CONSTRUCTOR_ACCESS:
protected: protected:
void onDraw(const Mat4& transform, bool transformUpdated); void onDraw(const Mat4& transform, bool transformUpdated);
void onDrawShadow(GLProgram* glProgram);
struct LetterInfo struct LetterInfo
{ {
@ -533,6 +538,8 @@ protected:
void reset(); void reset();
void drawSelf(Renderer* renderer, uint32_t flags);
std::string _bmFontPath; std::string _bmFontPath;
bool _isOpacityModifyRGB; bool _isOpacityModifyRGB;
@ -543,7 +550,7 @@ protected:
float _systemFontSize; float _systemFontSize;
LabelType _currentLabelType; LabelType _currentLabelType;
std::vector<SpriteBatchNode*> _batchNodes; Vector<SpriteBatchNode*> _batchNodes;
FontAtlas * _fontAtlas; FontAtlas * _fontAtlas;
std::vector<LetterInfo> _lettersInfo; std::vector<LetterInfo> _lettersInfo;
EventListenerCustom* _purgeTextureListener; EventListenerCustom* _purgeTextureListener;
@ -607,9 +614,12 @@ protected:
bool _clipEnabled; bool _clipEnabled;
bool _blendFuncDirty; bool _blendFuncDirty;
BlendFunc _blendFunc;
/// whether or not the sprite was inside bounds the previous frame /// whether or not the sprite was inside bounds the previous frame
bool _insideBounds; bool _insideBounds;
std::unordered_map<int, Sprite*> _letters;
private: private:
CC_DISALLOW_COPY_AND_ASSIGN(Label); CC_DISALLOW_COPY_AND_ASSIGN(Label);

View File

@ -43,12 +43,6 @@ THE SOFTWARE.
NS_CC_BEGIN NS_CC_BEGIN
#if CC_SPRITEBATCHNODE_RENDER_SUBPIXEL
#define RENDER_IN_SUBPIXEL
#else
#define RENDER_IN_SUBPIXEL(__ARGS__) (ceil(__ARGS__))
#endif
// MARK: create, init, dealloc // MARK: create, init, dealloc
Sprite* Sprite::createWithTexture(Texture2D *texture) Sprite* Sprite::createWithTexture(Texture2D *texture)
{ {
@ -626,10 +620,10 @@ void Sprite::updateTransform(void)
float dx = x1 * cr - y2 * sr2 + x; float dx = x1 * cr - y2 * sr2 + x;
float dy = x1 * sr + y2 * cr2 + y; float dy = x1 * sr + y2 * cr2 + y;
_quad.bl.vertices.set(RENDER_IN_SUBPIXEL(ax), RENDER_IN_SUBPIXEL(ay), _positionZ); _quad.bl.vertices.set(SPRITE_RENDER_IN_SUBPIXEL(ax), SPRITE_RENDER_IN_SUBPIXEL(ay), _positionZ);
_quad.br.vertices.set(RENDER_IN_SUBPIXEL(bx), RENDER_IN_SUBPIXEL(by), _positionZ); _quad.br.vertices.set(SPRITE_RENDER_IN_SUBPIXEL(bx), SPRITE_RENDER_IN_SUBPIXEL(by), _positionZ);
_quad.tl.vertices.set(RENDER_IN_SUBPIXEL(dx), RENDER_IN_SUBPIXEL(dy), _positionZ); _quad.tl.vertices.set(SPRITE_RENDER_IN_SUBPIXEL(dx), SPRITE_RENDER_IN_SUBPIXEL(dy), _positionZ);
_quad.tr.vertices.set(RENDER_IN_SUBPIXEL(cx), RENDER_IN_SUBPIXEL(cy), _positionZ); _quad.tr.vertices.set(SPRITE_RENDER_IN_SUBPIXEL(cx), SPRITE_RENDER_IN_SUBPIXEL(cy), _positionZ);
} }
// MARMALADE CHANGE: ADDED CHECK FOR nullptr, TO PERMIT SPRITES WITH NO BATCH NODE / TEXTURE ATLAS // MARMALADE CHANGE: ADDED CHECK FOR nullptr, TO PERMIT SPRITES WITH NO BATCH NODE / TEXTURE ATLAS

View File

@ -47,6 +47,16 @@ class Size;
class Texture2D; class Texture2D;
struct transformValues_; struct transformValues_;
#ifdef SPRITE_RENDER_IN_SUBPIXEL
#undef SPRITE_RENDER_IN_SUBPIXEL
#endif
#if CC_SPRITEBATCHNODE_RENDER_SUBPIXEL
#define SPRITE_RENDER_IN_SUBPIXEL
#else
#define SPRITE_RENDER_IN_SUBPIXEL(__ARGS__) (ceil(__ARGS__))
#endif
/** /**
* @addtogroup _2d * @addtogroup _2d
* @{ * @{

View File

@ -80,6 +80,7 @@ NewLabelTests::NewLabelTests()
ADD_TEST_CASE(LabelIssue11699Test); ADD_TEST_CASE(LabelIssue11699Test);
ADD_TEST_CASE(LabelIssue12259Test); ADD_TEST_CASE(LabelIssue12259Test);
ADD_TEST_CASE(LabelIssue12409Test); ADD_TEST_CASE(LabelIssue12409Test);
ADD_TEST_CASE(LabelAddChildTest);
}; };
LabelTTFAlignmentNew::LabelTTFAlignmentNew() LabelTTFAlignmentNew::LabelTTFAlignmentNew()
@ -2011,3 +2012,26 @@ std::string LabelIssue12409Test::subtitle() const
{ {
return "Testing auto-wrapping without space."; return "Testing auto-wrapping without space.";
} }
LabelAddChildTest::LabelAddChildTest()
{
auto center = VisibleRect::center();
auto label = Label::createWithTTF("Label with child node:", "fonts/arial.ttf", 24);
label->setPosition(center.x, center.y);
addChild(label);
auto jump = JumpBy::create(1.0f, Vec2::ZERO, 60, 1);
auto jump_4ever = RepeatForever::create(jump);
label->runAction(jump_4ever);
auto spite = Sprite::create("Images/SpookyPeas.png");
spite->setAnchorPoint(Vec2::ANCHOR_MIDDLE_LEFT);
spite->setPosition(label->getContentSize().width, label->getContentSize().height/2);
label->addChild(spite);
}
std::string LabelAddChildTest::title() const
{
return "Label support add child nodes";
}

View File

@ -609,4 +609,14 @@ public:
virtual std::string subtitle() const override; virtual std::string subtitle() const override;
}; };
class LabelAddChildTest : public AtlasDemoNew
{
public:
CREATE_FUNC(LabelAddChildTest);
LabelAddChildTest();
virtual std::string title() const override;
};
#endif #endif