From 23501b40aa288dcc7246ac2316315da25e474cc0 Mon Sep 17 00:00:00 2001 From: Dhilan007 Date: Wed, 5 Mar 2014 15:54:40 +0800 Subject: [PATCH] issue #3629:new label support customize the effects such as Shadow[blur is unsupported], Outline --- cocos/2d/CCFontAtlas.cpp | 165 ++++++------ cocos/2d/CCFontAtlas.h | 2 + cocos/2d/CCFontAtlasCache.cpp | 27 +- cocos/2d/CCFontAtlasCache.h | 2 +- cocos/2d/CCFontFreeType.cpp | 319 ++++++++++++++++------- cocos/2d/CCFontFreeType.h | 17 +- cocos/2d/CCLabel.cpp | 297 +++++++++++++++------ cocos/2d/CCLabel.h | 93 +++++-- cocos/2d/ccShader_Label_frag.h | 2 +- cocos/2d/ccShader_Label_frag_outline.h | 28 +- tests/Classes/LabelTest/LabelTestNew.cpp | 108 +++++++- tests/Classes/LabelTest/LabelTestNew.h | 23 +- 12 files changed, 742 insertions(+), 341 deletions(-) diff --git a/cocos/2d/CCFontAtlas.cpp b/cocos/2d/CCFontAtlas.cpp index 9af53eab68..06a8df4c38 100644 --- a/cocos/2d/CCFontAtlas.cpp +++ b/cocos/2d/CCFontAtlas.cpp @@ -28,11 +28,11 @@ #include "ccUTF8.h" #include "CCDirector.h" -#define PAGE_WIDTH 1024 -#define PAGE_HEIGHT 1024 - NS_CC_BEGIN +const int FontAtlas::CacheTextureWidth = 1024; +const int FontAtlas::CacheTextureHeight = 1024; + FontAtlas::FontAtlas(Font &theFont) : _font(&theFont), _currentPageData(nullptr) @@ -44,28 +44,26 @@ _currentPageData(nullptr) { _currentPageLineHeight = _font->getFontMaxHeight(); _commonLineHeight = _currentPageLineHeight * 0.8f; - Texture2D * tex = new Texture2D; + auto texture = new Texture2D; _currentPage = 0; _currentPageOrigX = 0; _currentPageOrigY = 0; _letterPadding = 0; - _makeDistanceMap = fontTTf->isDistanceFieldEnabled(); - if(_makeDistanceMap) + if(fontTTf->isDistanceFieldEnabled()) { - _commonLineHeight += 2 * FontFreeType::DistanceMapSpread; _letterPadding += 2 * FontFreeType::DistanceMapSpread; } - _currentPageDataSize = (PAGE_WIDTH * PAGE_HEIGHT * 1); + _currentPageDataSize = CacheTextureWidth * CacheTextureHeight; + if(fontTTf->getOutlineSize() > 0) + { + _currentPageDataSize *= 2; + } _currentPageData = new unsigned char[_currentPageDataSize]; memset(_currentPageData, 0, _currentPageDataSize); - addTexture(*tex,0); - tex->release(); - } - else - { - _makeDistanceMap = false; + addTexture(*texture,0); + texture->release(); } } @@ -112,29 +110,75 @@ bool FontAtlas::prepareLetterDefinitions(unsigned short *utf16String) return false; FontFreeType* fontTTf = (FontFreeType*)_font; - - std::unordered_map fontDefs; int length = cc_wcslen(utf16String); - float offsetAdjust = _letterPadding / 2; - //find out new letter + float offsetAdjust = _letterPadding / 2; + int bitmapWidth; + int bitmapHeight; + Rect tempRect; + FontLetterDefinition tempDef; + + auto contentSize = Size(CacheTextureWidth,CacheTextureHeight); + auto scaleFactor = CC_CONTENT_SCALE_FACTOR(); + auto pixelFormat = fontTTf->getOutlineSize() > 0 ? Texture2D::PixelFormat::AI88 : Texture2D::PixelFormat::A8; + + bool existNewLetter = false; for (int i = 0; i < length; ++i) { auto outIterator = _fontLetterDefinitions.find(utf16String[i]); - + if (outIterator == _fontLetterDefinitions.end()) { - auto outIterator2 = fontDefs.find(utf16String[i]); - if(outIterator2 != fontDefs.end()) - continue; + existNewLetter = true; - Rect tempRect; - - FontLetterDefinition tempDef; - - if (!fontTTf->getBBOXFotChar(utf16String[i], tempRect,tempDef.xAdvance)) + auto bitmap = fontTTf->getGlyphBitmap(utf16String[i],bitmapWidth,bitmapHeight,tempRect,tempDef.xAdvance); + if (bitmap) { - tempDef.validDefinition = false; + tempDef.validDefinition = true; + tempDef.letteCharUTF16 = utf16String[i]; + tempDef.width = tempRect.size.width + _letterPadding; + tempDef.height = tempRect.size.height + _letterPadding; + tempDef.offsetX = tempRect.origin.x + offsetAdjust; + tempDef.offsetY = _commonLineHeight + tempRect.origin.y - offsetAdjust; + + if (_currentPageOrigX + tempDef.width > CacheTextureWidth) + { + _currentPageOrigY += _currentPageLineHeight; + _currentPageOrigX = 0; + if(_currentPageOrigY + _currentPageLineHeight >= CacheTextureHeight) + { + _atlasTextures[_currentPage]->initWithData(_currentPageData, _currentPageDataSize, pixelFormat, CacheTextureWidth, CacheTextureHeight, contentSize ); + _currentPageOrigY = 0; + + delete []_currentPageData; + _currentPageData = new unsigned char[_currentPageDataSize]; + if(_currentPageData == nullptr) + return false; + memset(_currentPageData, 0, _currentPageDataSize); + _currentPage++; + auto tex = new Texture2D; + 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; + tempDef.letteCharUTF16 = utf16String[i]; tempDef.width = 0; tempDef.height = 0; @@ -143,72 +187,17 @@ bool FontAtlas::prepareLetterDefinitions(unsigned short *utf16String) tempDef.offsetX = 0; tempDef.offsetY = 0; tempDef.textureID = 0; - tempDef.xAdvance = 0; + _currentPageOrigX += 1; } - else - { - tempDef.validDefinition = true; - tempDef.letteCharUTF16 = utf16String[i]; - tempDef.width = tempRect.size.width + _letterPadding; - tempDef.height = tempRect.size.height + _letterPadding; - tempDef.offsetX = tempRect.origin.x + offsetAdjust; - tempDef.offsetY = _commonLineHeight + tempRect.origin.y - offsetAdjust; - } - fontDefs[utf16String[i]] = tempDef; + + _fontLetterDefinitions[tempDef.letteCharUTF16] = tempDef; } } - Size _pageContentSize = Size(PAGE_WIDTH,PAGE_HEIGHT); - float scaleFactor = CC_CONTENT_SCALE_FACTOR(); - float glyphWidth; - Texture2D::PixelFormat pixelFormat = Texture2D::PixelFormat::A8; - - - for(auto it = fontDefs.begin(); it != fontDefs.end(); it++) + if(existNewLetter) { - if(it->second.validDefinition) - { - glyphWidth = it->second.width; - - if (_currentPageOrigX + glyphWidth > PAGE_WIDTH) - { - _currentPageOrigY += _currentPageLineHeight; - _currentPageOrigX = 0; - if(_currentPageOrigY + _currentPageLineHeight >= PAGE_HEIGHT) - { - _atlasTextures[_currentPage]->initWithData(_currentPageData, _currentPageDataSize, pixelFormat, PAGE_WIDTH, PAGE_HEIGHT, _pageContentSize ); - _currentPageOrigY = 0; - - delete []_currentPageData; - _currentPageData = new unsigned char[_currentPageDataSize]; - if(_currentPageData == nullptr) - return false; - memset(_currentPageData, 0, _currentPageDataSize); - _currentPage++; - Texture2D* tex = new Texture2D; - addTexture(*tex,_currentPage); - tex->release(); - } - } - fontTTf->renderCharAt(it->second.letteCharUTF16,_currentPageOrigX,_currentPageOrigY,_currentPageData,PAGE_WIDTH); - - it->second.U = _currentPageOrigX; - it->second.V = _currentPageOrigY; - it->second.textureID = _currentPage; - // take from pixels to points - it->second.width = it->second.width / scaleFactor; - it->second.height = it->second.height / scaleFactor; - it->second.U = it->second.U / scaleFactor; - it->second.V = it->second.V / scaleFactor; - } - else - glyphWidth = 0; - - _fontLetterDefinitions[it->second.letteCharUTF16] = it->second; - _currentPageOrigX += glyphWidth + 1; + _atlasTextures[_currentPage]->initWithData(_currentPageData, _currentPageDataSize, pixelFormat, CacheTextureWidth, CacheTextureHeight, contentSize ); } - if(fontDefs.size() > 0) - _atlasTextures[_currentPage]->initWithData(_currentPageData, _currentPageDataSize, pixelFormat, PAGE_WIDTH, PAGE_HEIGHT, _pageContentSize ); return true; } diff --git a/cocos/2d/CCFontAtlas.h b/cocos/2d/CCFontAtlas.h index ee154714eb..2ed6f8ac54 100644 --- a/cocos/2d/CCFontAtlas.h +++ b/cocos/2d/CCFontAtlas.h @@ -52,6 +52,8 @@ struct FontLetterDefinition class CC_DLL FontAtlas : public Ref { public: + static const int CacheTextureWidth; + static const int CacheTextureHeight; /** * @js ctor */ diff --git a/cocos/2d/CCFontAtlasCache.cpp b/cocos/2d/CCFontAtlasCache.cpp index 295c1909e1..6b7ef6a3e9 100644 --- a/cocos/2d/CCFontAtlasCache.cpp +++ b/cocos/2d/CCFontAtlasCache.cpp @@ -35,17 +35,32 @@ NS_CC_BEGIN std::unordered_map FontAtlasCache::_atlasMap; -FontAtlas * FontAtlasCache::getFontAtlasTTF(const std::string& fontFileName, int size, GlyphCollection glyphs, const char *customGlyphs, bool useDistanceField) -{ - std::string atlasName = generateFontName(fontFileName, size, glyphs, useDistanceField); +FontAtlas * FontAtlasCache::getFontAtlasTTF(const TTFConfig & config) +{ + bool useDistanceField = config.distanceFieldEnabled; + if(config.outlineSize > 0) + { + useDistanceField = false; + } + int fontSize = config.fontSize; + if (useDistanceField) + { + fontSize = Label::DefultFontSize; + } + + std::string atlasName = generateFontName(config.fontFilePath, fontSize, GlyphCollection::DYNAMIC, useDistanceField); + atlasName.append("_outline_"); + std::stringstream ss; + ss << config.outlineSize; + atlasName.append(ss.str()); + FontAtlas *tempAtlas = _atlasMap[atlasName]; - + if ( !tempAtlas ) { - FontFreeType *font = FontFreeType::create(fontFileName, size, glyphs, customGlyphs); + FontFreeType *font = FontFreeType::create(config.fontFilePath, fontSize, config.glyphs, config.customGlyphs,useDistanceField,config.outlineSize); if (font) { - font->setDistanceFieldEnabled(useDistanceField); tempAtlas = font->createFontAtlas(); if (tempAtlas) _atlasMap[atlasName] = tempAtlas; diff --git a/cocos/2d/CCFontAtlasCache.h b/cocos/2d/CCFontAtlasCache.h index 3e76726257..1806ff83f8 100644 --- a/cocos/2d/CCFontAtlasCache.h +++ b/cocos/2d/CCFontAtlasCache.h @@ -37,7 +37,7 @@ NS_CC_BEGIN class CC_DLL FontAtlasCache { public: - static FontAtlas * getFontAtlasTTF(const std::string& fontFileName, int size, GlyphCollection glyphs, const char *customGlyphs = 0, bool useDistanceField = false); + static FontAtlas * getFontAtlasTTF(const TTFConfig & config); static FontAtlas * getFontAtlasFNT(const std::string& fontFileName, const Point& imageOffset = Point::ZERO); static FontAtlas * getFontAtlasCharMap(const std::string& charMapFile, int itemWidth, int itemHeight, int startCharMap); diff --git a/cocos/2d/CCFontFreeType.cpp b/cocos/2d/CCFontFreeType.cpp index 00d6eaab90..f8160e6aae 100644 --- a/cocos/2d/CCFontFreeType.cpp +++ b/cocos/2d/CCFontFreeType.cpp @@ -30,6 +30,7 @@ THE SOFTWARE. #include "CCFontFreeType.h" #include "platform/CCFileUtils.h" #include "edtaa3func.h" +#include FT_BBOX_H NS_CC_BEGIN @@ -38,9 +39,9 @@ FT_Library FontFreeType::_FTlibrary; bool FontFreeType::_FTInitialized = false; const int FontFreeType::DistanceMapSpread = 3; -FontFreeType * FontFreeType::create(const std::string &fontName, int fontSize, GlyphCollection glyphs, const char *customGlyphs) +FontFreeType * FontFreeType::create(const std::string &fontName, int fontSize, GlyphCollection glyphs, const char *customGlyphs,bool distanceFieldEnabled /* = false */,int outline /* = 0 */) { - FontFreeType *tempFont = new FontFreeType(); + FontFreeType *tempFont = new FontFreeType(distanceFieldEnabled,outline); if (!tempFont) return nullptr; @@ -84,10 +85,21 @@ FT_Library FontFreeType::getFTLibrary() return _FTlibrary; } -FontFreeType::FontFreeType() +FontFreeType::FontFreeType(bool distanceFieldEnabled /* = false */,int outline /* = 0 */) : _fontRef(nullptr) -,_distanceFieldEnabled(false) +,_distanceFieldEnabled(distanceFieldEnabled) +,_outlineSize(outline) +,_stroker(nullptr) { + if (_outlineSize > 0) + { + FT_Stroker_New(FontFreeType::getFTLibrary(), &_stroker); + FT_Stroker_Set(_stroker, + (int)(_outlineSize * 64), + FT_STROKER_LINECAP_ROUND, + FT_STROKER_LINEJOIN_ROUND, + 0); + } } bool FontFreeType::createFontObject(const std::string &fontName, int fontSize) @@ -125,6 +137,10 @@ bool FontFreeType::createFontObject(const std::string &fontName, int fontSize) FontFreeType::~FontFreeType() { + if (_stroker) + { + FT_Stroker_Done(_stroker); + } if (_fontRef) { FT_Done_Face(_fontRef); @@ -144,41 +160,6 @@ FontAtlas * FontFreeType::createFontAtlas() return atlas; } -bool FontFreeType::getBBOXFotChar(unsigned short theChar, Rect &outRect, int &xAdvance) const -{ - if (!_fontRef) - return false; - - // get the ID to the char we need - int glyph_index = FT_Get_Char_Index(_fontRef, theChar); - - if (!glyph_index) - return false; - - // load glyph infos - if (_distanceFieldEnabled) - { - if (FT_Load_Glyph(_fontRef, glyph_index, FT_LOAD_DEFAULT | FT_LOAD_NO_HINTING | FT_LOAD_NO_AUTOHINT)) - return false; - } - else - { - if (FT_Load_Glyph(_fontRef, glyph_index, FT_LOAD_DEFAULT)) - return false; - } - - - // store result in the passed rectangle - outRect.origin.x = _fontRef->glyph->metrics.horiBearingX >> 6; - outRect.origin.y = - (_fontRef->glyph->metrics.horiBearingY >> 6); - outRect.size.width = (_fontRef->glyph->metrics.width >> 6); - outRect.size.height = (_fontRef->glyph->metrics.height >> 6); - - xAdvance = (static_cast(_fontRef->glyph->metrics.horiAdvance >> 6)); - - return true; -} - int * FontFreeType::getHorizontalKerningForTextUTF16(unsigned short *text, int &outNumLetters) const { if (!text) @@ -239,27 +220,162 @@ int FontFreeType::getFontMaxHeight() const return (static_cast(_fontRef->size->metrics.height >> 6)); } -unsigned char * FontFreeType::getGlyphBitmap(unsigned short theChar, int &outWidth, int &outHeight) const +unsigned char* FontFreeType::getGlyphBitmap(unsigned short theChar, int &outWidth, int &outHeight, Rect &outRect,int &xAdvance) { - if (!_fontRef) - return 0; - - if (_distanceFieldEnabled) + bool invalidChar = true; + unsigned char * ret = nullptr; + + do { - if (FT_Load_Char(_fontRef,theChar,FT_LOAD_RENDER | FT_LOAD_NO_HINTING | FT_LOAD_NO_AUTOHINT)) - return 0; + if (!_fontRef) + break; + + auto glyphIndex = FT_Get_Char_Index(_fontRef, theChar); + if(!glyphIndex) + break; + + if (_distanceFieldEnabled) + { + if (FT_Load_Glyph(_fontRef,glyphIndex,FT_LOAD_RENDER | FT_LOAD_NO_HINTING | FT_LOAD_NO_AUTOHINT)) + break; + } + else + { + if (FT_Load_Glyph(_fontRef,glyphIndex,FT_LOAD_RENDER)) + break; + } + + outRect.origin.x = _fontRef->glyph->metrics.horiBearingX >> 6; + outRect.origin.y = - (_fontRef->glyph->metrics.horiBearingY >> 6); + outRect.size.width = (_fontRef->glyph->metrics.width >> 6); + outRect.size.height = (_fontRef->glyph->metrics.height >> 6); + + xAdvance = (static_cast(_fontRef->glyph->metrics.horiAdvance >> 6)); + + outWidth = _fontRef->glyph->bitmap.width; + outHeight = _fontRef->glyph->bitmap.rows; + ret = _fontRef->glyph->bitmap.buffer; + + if (_outlineSize > 0) + { + auto copyBitmap = new unsigned char[outWidth * outHeight]; + memcpy(copyBitmap,ret,outWidth * outHeight * sizeof(unsigned char)); + + int bitmapWidth; + int bitmapHeight; + FT_BBox bbox; + auto outlineBitmap = getGlyphBitmapWithOutline(theChar,bbox); + if(outlineBitmap == nullptr) + { + ret = nullptr; + delete [] copyBitmap; + break; + } + + bitmapWidth = (bbox.xMax - bbox.xMin)>>6; + bitmapHeight = (bbox.yMax - bbox.yMin)>>6; + + int index; + auto blendImage = new unsigned char[bitmapWidth * bitmapHeight * 2]; + memset(blendImage, 0, bitmapWidth * bitmapHeight * 2); + for (int x = 0; x < bitmapWidth; ++x) + { + for (int y = 0; y < bitmapHeight; ++y) + { + index = x + ( y * bitmapWidth ); + blendImage[2 * index] = outlineBitmap[index]; + } + } + + int maxX = outWidth + _outlineSize; + int maxY = outHeight + _outlineSize; + for (int x = _outlineSize; x < maxX; ++x) + { + for (int y = _outlineSize; y < maxY; ++y) + { + index = x + ( y * bitmapWidth ); + + blendImage[2 * index + 1] = copyBitmap[outWidth * (y - _outlineSize) + x - _outlineSize]; + } + } + + outRect.origin.x = bbox.xMin >> 6; + outRect.origin.y = - (bbox.yMax >> 6); + + xAdvance += bitmapWidth - outRect.size.width; + + outRect.size.width = bitmapWidth; + outRect.size.height = bitmapHeight; + outWidth = bitmapWidth; + outHeight = bitmapHeight; + + delete [] outlineBitmap; + delete [] copyBitmap; + ret = blendImage; + } + + invalidChar = false; + } while (0); + + if (invalidChar) + { + outRect.size.width = 0; + outRect.size.height = 0; + xAdvance = 0; + + return nullptr; } else { - if (FT_Load_Char(_fontRef,theChar,FT_LOAD_RENDER)) - return 0; + return ret; } - - outWidth = _fontRef->glyph->bitmap.width; - outHeight = _fontRef->glyph->bitmap.rows; - - // return the pointer to the bitmap - return _fontRef->glyph->bitmap.buffer; +} + +unsigned char * FontFreeType::getGlyphBitmapWithOutline(unsigned short theChar, FT_BBox &bbox) +{ + unsigned char* ret = nullptr; + + FT_UInt gindex = FT_Get_Char_Index(_fontRef, theChar); + if (FT_Load_Glyph(_fontRef, gindex, FT_LOAD_NO_BITMAP) == 0) + { + if (_fontRef->glyph->format == FT_GLYPH_FORMAT_OUTLINE) + { + FT_Glyph glyph; + if (FT_Get_Glyph(_fontRef->glyph, &glyph) == 0) + { + FT_Glyph_StrokeBorder(&glyph, _stroker, 0, 1); + if (glyph->format == FT_GLYPH_FORMAT_OUTLINE) + { + FT_Outline *outline = &reinterpret_cast(glyph)->outline; + FT_Glyph_Get_CBox(glyph,FT_GLYPH_BBOX_GRIDFIT,&bbox); + int width = (bbox.xMax - bbox.xMin)>>6; + int rows = (bbox.yMax - bbox.yMin)>>6; + + FT_Bitmap bmp; + bmp.buffer = new unsigned char[width * rows]; + memset(bmp.buffer, 0, width * rows); + bmp.width = width; + bmp.rows = rows; + bmp.pitch = width; + bmp.pixel_mode = FT_PIXEL_MODE_GRAY; + bmp.num_grays = 256; + + FT_Raster_Params params; + memset(¶ms, 0, sizeof (params)); + params.source = outline; + params.target = &bmp; + params.flags = FT_RASTER_FLAG_AA; + FT_Outline_Translate(outline,-bbox.xMin,-bbox.yMin); + FT_Outline_Render(_FTlibrary, outline, ¶ms); + + ret = bmp.buffer; + } + FT_Done_Glyph(glyph); + } + } + } + + return ret; } unsigned char * makeDistanceMap( unsigned char *img, unsigned int width, unsigned int height) @@ -343,47 +459,33 @@ unsigned char * makeDistanceMap( unsigned char *img, unsigned int width, unsigne return out; } -void FontFreeType::setDistanceFieldEnabled(bool distanceFieldEnabled) +void FontFreeType::renderCharAt(unsigned char *dest,int posX, int posY, unsigned char* bitmap,int bitmapWidth,int bitmapHeight) { - _distanceFieldEnabled = distanceFieldEnabled; -} - -bool FontFreeType::renderCharAt(unsigned short int charToRender, int posX, int posY, unsigned char *destMemory, int destSize) -{ - unsigned char *sourceBitmap = 0; - int sourceWidth = 0; - int sourceHeight = 0; - - sourceBitmap = getGlyphBitmap(charToRender, sourceWidth, sourceHeight); - - if (!sourceBitmap) - return false; + int iX = posX; + int iY = posY; if (_distanceFieldEnabled) { - unsigned char * out = makeDistanceMap(sourceBitmap,sourceWidth,sourceHeight); + auto distanceMap = makeDistanceMap(bitmap,bitmapWidth,bitmapHeight); - int iX = posX; - int iY = posY; + bitmapWidth += 2 * DistanceMapSpread; + bitmapHeight += 2 * DistanceMapSpread; - sourceWidth += 2 * DistanceMapSpread; - sourceHeight += 2 * DistanceMapSpread; - - for (int y = 0; y < sourceHeight; ++y) + for (int y = 0; y < bitmapHeight; ++y) { - int bitmap_y = y * sourceWidth; + int bitmap_y = y * bitmapWidth; - for (int x = 0; x < sourceWidth; ++x) + for (int x = 0; x < bitmapWidth; ++x) { /* Dual channel 16-bit output (more complicated, but good precision and range) */ /*int index = (iX + ( iY * destSize )) * 3; int index2 = (bitmap_y + x)*3; - destMemory[index] = out[index2]; - destMemory[index + 1] = out[index2 + 1]; - destMemory[index + 2] = out[index2 + 2];*/ + dest[index] = out[index2]; + dest[index + 1] = out[index2 + 1]; + dest[index + 2] = out[index2 + 2];*/ //Single channel 8-bit output - destMemory[iX + ( iY * destSize )] = out[bitmap_y + x]; + dest[iX + ( iY * FontAtlas::CacheTextureWidth )] = distanceMap[bitmap_y + x]; iX += 1; } @@ -391,33 +493,50 @@ bool FontFreeType::renderCharAt(unsigned short int charToRender, int posX, int p iX = posX; iY += 1; } - free(out); - return true; + free(distanceMap); } - - int iX = posX; - int iY = posY; - - for (int y = 0; y < sourceHeight; ++y) + else if(_outlineSize > 0) { - int bitmap_y = y * sourceWidth; - - for (int x = 0; x < sourceWidth; ++x) + unsigned char tempChar; + for (int y = 0; y < bitmapHeight; ++y) { - unsigned char cTemp = sourceBitmap[bitmap_y + x]; + int bitmap_y = y * bitmapWidth; - // the final pixel - destMemory[(iX + ( iY * destSize ) )] = cTemp; + for (int x = 0; x < bitmapWidth; ++x) + { + tempChar = bitmap[(bitmap_y + x) * 2]; + dest[(iX + ( iY * FontAtlas::CacheTextureWidth ) ) * 2] = tempChar; + tempChar = bitmap[(bitmap_y + x) * 2 + 1]; + dest[(iX + ( iY * FontAtlas::CacheTextureWidth ) ) * 2 + 1] = tempChar; - iX += 1; + iX += 1; + } + + iX = posX; + iY += 1; } - - iX = posX; - iY += 1; + delete [] bitmap; } + else + { + for (int y = 0; y < bitmapHeight; ++y) + { + int bitmap_y = y * bitmapWidth; - //everything good - return true; + for (int x = 0; x < bitmapWidth; ++x) + { + unsigned char cTemp = bitmap[bitmap_y + x]; + + // the final pixel + dest[(iX + ( iY * FontAtlas::CacheTextureWidth ) )] = cTemp; + + iX += 1; + } + + iX = posX; + iY += 1; + } + } } NS_CC_END \ No newline at end of file diff --git a/cocos/2d/CCFontFreeType.h b/cocos/2d/CCFontFreeType.h index 98b182428e..49ab02d619 100644 --- a/cocos/2d/CCFontFreeType.h +++ b/cocos/2d/CCFontFreeType.h @@ -33,6 +33,7 @@ #include #include FT_FREETYPE_H +#include FT_STROKER_H NS_CC_BEGIN @@ -41,25 +42,24 @@ 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); + static FontFreeType * create(const std::string &fontName, int fontSize, GlyphCollection glyphs, const char *customGlyphs,bool distanceFieldEnabled = false,int outline = 0); static void shutdownFreeType(); - void setDistanceFieldEnabled(bool distanceFieldEnabled); bool isDistanceFieldEnabled() const { return _distanceFieldEnabled;} - bool renderCharAt(unsigned short int charToRender, int posX, int posY, unsigned char *destMemory, int destSize); + int getOutlineSize() const { return _outlineSize; } + void renderCharAt(unsigned char *dest,int posX, int posY, unsigned char* bitmap,int bitmapWidth,int bitmapHeight); virtual FontAtlas * createFontAtlas() override; virtual int * getHorizontalKerningForTextUTF16(unsigned short *text, int &outNumLetters) const override; - unsigned char * getGlyphBitmap(unsigned short theChar, int &outWidth, int &outHeight) const override; - virtual int getFontMaxHeight() const override; + unsigned char * getGlyphBitmap(unsigned short theChar, int &outWidth, int &outHeight, Rect &outRect,int &xAdvance); - bool getBBOXFotChar(unsigned short theChar, Rect &outRect,int &xAdvance) const; + virtual int getFontMaxHeight() const override; protected: - FontFreeType(); + FontFreeType(bool distanceFieldEnabled = false,int outline = 0); virtual ~FontFreeType(); bool createFontObject(const std::string &fontName, int fontSize); @@ -69,13 +69,16 @@ private: FT_Library getFTLibrary(); int getHorizontalKerningForChars(unsigned short firstChar, unsigned short secondChar) const; + unsigned char * getGlyphBitmapWithOutline(unsigned short theChar, FT_BBox &bbox); static FT_Library _FTlibrary; static bool _FTInitialized; FT_Face _fontRef; + FT_Stroker _stroker; std::string _fontName; Data _ttfData; bool _distanceFieldEnabled; + int _outlineSize; }; NS_CC_END diff --git a/cocos/2d/CCLabel.cpp b/cocos/2d/CCLabel.cpp index 7e2283ab78..456dbc09ae 100644 --- a/cocos/2d/CCLabel.cpp +++ b/cocos/2d/CCLabel.cpp @@ -34,10 +34,10 @@ #include "renderer/CCRenderer.h" #include "CCFont.h" -#define DISTANCEFIELD_ATLAS_FONTSIZE 50 - NS_CC_BEGIN +const int Label::DefultFontSize = 50; + Label* Label::create() { Label *ret = new Label(); @@ -56,11 +56,9 @@ Label* Label::createWithTTF(const TTFConfig& ttfConfig, const std::string& text, if (!ret) return nullptr; - + if (ret->setTTFConfig(ttfConfig)) { - if(ttfConfig.distanceFieldEnabled) - ret->setFontScale(1.0f * ttfConfig.fontSize / DISTANCEFIELD_ATLAS_FONTSIZE); ret->setMaxLineWidth(lineSize); ret->setString(text); ret->autorelease(); @@ -203,6 +201,7 @@ Label::Label(FontAtlas *atlas, TextHAlignment alignment, bool useDistanceField,b , _fontScale(1.0f) , _uniformEffectColor(0) ,_currNumLines(-1) +,_fontConfig(TTFConfig("")) { _cascadeColorEnabled = true; _batchNodes.push_back(this); @@ -213,7 +212,7 @@ Label::~Label() delete [] _currentUTF16String; delete [] _originalUTF16String; delete [] _horizontalKernings; - + if (_fontAtlas) FontAtlasCache::releaseFontAtlas(_fontAtlas); @@ -225,25 +224,49 @@ bool Label::init() bool ret = true; if(_fontAtlas) { + ret = SpriteBatchNode::initWithTexture(&_fontAtlas->getTexture(0), 30); if (_reusedLetter == nullptr) { _reusedLetter = Sprite::createWithTexture(&_fontAtlas->getTexture(0)); _reusedLetter->setOpacityModifyRGB(_isOpacityModifyRGB); _reusedLetter->retain(); _reusedLetter->setAnchorPoint(Point::ANCHOR_TOP_LEFT); + _reusedLetter->setBatchNode(this); } - ret = SpriteBatchNode::initWithTexture(&_fontAtlas->getTexture(0), 30); } - if (_useDistanceField) - setLabelEffect(LabelEffect::NORMAL,Color3B::BLACK); - else if(_useA8Shader) - setShaderProgram(ShaderCache::getInstance()->getProgram(GLProgram::SHADER_NAME_POSITION_TEXTURE_A8_COLOR)); - else - setShaderProgram(ShaderCache::getInstance()->getProgram(GLProgram::SHADER_NAME_POSITION_TEXTURE_COLOR)); + _currLabelEffect = LabelEffect::NORMAL; + initProgram(); return ret; } +void Label::initProgram() +{ + switch (_currLabelEffect) + { + case cocos2d::LabelEffect::NORMAL: + case cocos2d::LabelEffect::SHADOW: + if (_useDistanceField) + setShaderProgram(ShaderCache::getInstance()->getProgram(GLProgram::SHADER_NAME_LABEL_DISTANCEFIELD_NORMAL)); + else if (_useA8Shader) + setShaderProgram(ShaderCache::getInstance()->getProgram(GLProgram::SHADER_NAME_POSITION_TEXTURE_A8_COLOR)); + else + setShaderProgram(ShaderCache::getInstance()->getProgram(GLProgram::SHADER_NAME_POSITION_TEXTURE_COLOR)); + + break; + case cocos2d::LabelEffect::OUTLINE: + setShaderProgram(ShaderCache::getInstance()->getProgram(GLProgram::SHADER_NAME_LABEL_DISTANCEFIELD_OUTLINE)); + break; + case cocos2d::LabelEffect::GLOW: + if (_useDistanceField) + setShaderProgram(ShaderCache::getInstance()->getProgram(GLProgram::SHADER_NAME_LABEL_DISTANCEFIELD_GLOW)); + break; + default: + return; + } + _uniformEffectColor = glGetUniformLocation(_shaderProgram->getProgram(), "v_effectColor"); +} + bool Label::initWithFontAtlas(FontAtlas* atlas,bool distanceFieldEnabled /* = false */, bool useA8Shader /* = false */) { FontAtlas *oldAtlas = _fontAtlas; @@ -287,16 +310,32 @@ bool Label::initWithFontAtlas(FontAtlas* atlas,bool distanceFieldEnabled /* = fa bool Label::setTTFConfig(const TTFConfig& ttfConfig) { - FontAtlas *newAtlas = nullptr; - if(ttfConfig.distanceFieldEnabled) - newAtlas = FontAtlasCache::getFontAtlasTTF(ttfConfig.fontFilePath, DISTANCEFIELD_ATLAS_FONTSIZE, ttfConfig.glyphs, ttfConfig.customGlyphs,true); - else - newAtlas = FontAtlasCache::getFontAtlasTTF(ttfConfig.fontFilePath, ttfConfig.fontSize, ttfConfig.glyphs, ttfConfig.customGlyphs,false); + FontAtlas *newAtlas = FontAtlasCache::getFontAtlasTTF(ttfConfig); if (!newAtlas) return false; - return initWithFontAtlas(newAtlas,ttfConfig.distanceFieldEnabled,true); + if (initWithFontAtlas(newAtlas,ttfConfig.distanceFieldEnabled,true)) + { + _fontConfig = ttfConfig; + if (ttfConfig.outlineSize > 0) + { + _fontConfig.distanceFieldEnabled = false; + _useDistanceField = false; + _useA8Shader = false; + _currLabelEffect = LabelEffect::OUTLINE; + initProgram(); + } + else if(ttfConfig.distanceFieldEnabled) + { + this->setFontScale(1.0f * ttfConfig.fontSize / DefultFontSize); + } + return true; + } + else + { + return false; + } } bool Label::setBMFontFilePath(const std::string& bmfontFilePath, const Point& imageOffset /* = Point::ZERO */) @@ -313,14 +352,14 @@ void Label::setString(const std::string& text) { if (!_fontAtlas || _commonLineHeight <= 0) return ; - + unsigned short* utf16String = cc_utf8_to_utf16(text.c_str()); if(!utf16String) return ; _originalUTF8String = text; setCurrentString(utf16String); setOriginalString(utf16String); - + // align text alignText(); } @@ -332,7 +371,7 @@ void Label::setAlignment(TextHAlignment alignment) { // store _alignment = alignment; - + if (_currentUTF16String) { // reset the string @@ -350,7 +389,7 @@ void Label::setMaxLineWidth(float width) { // store _maxLineWidth = width; - + if (_currentUTF16String) { // reset the string @@ -368,7 +407,7 @@ void Label::setLineBreakWithoutSpace(bool breakWithoutSpace) { // store _lineBreakWithoutSpaces = breakWithoutSpace; - + // need to align text again if(_currentUTF16String) { @@ -456,10 +495,10 @@ void Label::alignText() LabelTextFormatter::createStringSprites(this); if(_maxLineWidth > 0 && _contentSize.width > _maxLineWidth && LabelTextFormatter::multilineText(this) ) LabelTextFormatter::createStringSprites(this); - + if(_currNumLines > 1 && _alignment != TextHAlignment::LEFT) LabelTextFormatter::alignText(this); - + int strLen = cc_wcslen(_currentUTF16String); Rect uvRect; Sprite* letterSprite; @@ -484,7 +523,7 @@ void Label::alignText() } } } - + int index; for (int ctr = 0; ctr < strLen; ++ctr) { @@ -507,10 +546,10 @@ bool Label::computeHorizontalKernings(unsigned short int *stringToRender) delete [] _horizontalKernings; _horizontalKernings = 0; } - + int letterCount = 0; _horizontalKernings = _fontAtlas->getFont()->getHorizontalKerningForTextUTF16(stringToRender, letterCount); - + if(!_horizontalKernings) return false; else @@ -523,13 +562,13 @@ bool Label::setOriginalString(unsigned short *stringToSet) { delete [] _originalUTF16String; } - + int newStringLenght = cc_wcslen(stringToSet); _originalUTF16String = new unsigned short int [newStringLenght + 1]; memset(_originalUTF16String, 0, (newStringLenght + 1) * 2); memcpy(_originalUTF16String, stringToSet, (newStringLenght * 2)); _originalUTF16String[newStringLenght] = 0; - + return true; } @@ -540,7 +579,7 @@ bool Label::setCurrentString(unsigned short *stringToSet) { delete [] _currentUTF16String; } - + _currentUTF16String = stringToSet; computeStringNumLines(); // compute the advances @@ -551,19 +590,19 @@ void Label::resetCurrentString() { if ((!_currentUTF16String) && (!_originalUTF16String)) return; - + // set the new string if (_currentUTF16String) { delete [] _currentUTF16String; _currentUTF16String = 0; } - + int stringLenght = cc_wcslen(_originalUTF16String); _currentUTF16String = new unsigned short int [stringLenght + 1]; memcpy(_currentUTF16String, _originalUTF16String, stringLenght * 2); _currentUTF16String[stringLenght] = 0; - + } void Label::updateSpriteWithLetterDefinition(const FontLetterDefinition &theDefinition, Texture2D *theTexture) @@ -603,7 +642,7 @@ bool Label::recordPlaceholderInfo(int spriteIndex) } _lettersInfo[spriteIndex].def.validDefinition = false; - + return false; } @@ -614,31 +653,68 @@ void Label::addChild(Node * child, int zOrder/* =0 */, int tag/* =0 */) void Label::setLabelEffect(LabelEffect effect,const Color3B& effectColor) { - if(_useDistanceField == false) - return; - - _currLabelEffect = effect; - _effectColor = effectColor; - - switch (_currLabelEffect) + switch (effect) { - case cocos2d::LabelEffect::NORMAL: - setShaderProgram(ShaderCache::getInstance()->getProgram(GLProgram::SHADER_NAME_LABEL_DISTANCEFIELD_NORMAL)); + case cocos2d::LabelEffect::NORMAL: + disableEffect(); break; case cocos2d::LabelEffect::OUTLINE: - setShaderProgram(ShaderCache::getInstance()->getProgram(GLProgram::SHADER_NAME_LABEL_DISTANCEFIELD_OUTLINE)); + enableOutline(Color4B(effectColor)); break; case cocos2d::LabelEffect::SHADOW: - setShaderProgram(ShaderCache::getInstance()->getProgram(GLProgram::SHADER_NAME_LABEL_DISTANCEFIELD_SHADOW)); + enableShadow(effectColor); break; case cocos2d::LabelEffect::GLOW: - setShaderProgram(ShaderCache::getInstance()->getProgram(GLProgram::SHADER_NAME_LABEL_DISTANCEFIELD_GLOW)); + enableGlow(effectColor); break; default: - return; + break; } +} - _uniformEffectColor = glGetUniformLocation(_shaderProgram->getProgram(), "v_effectColor"); +void Label::enableGlow(const Color3B& glowColor) +{ + if(_useDistanceField == false) + return; + _currLabelEffect = LabelEffect::GLOW; + _effectColor = glowColor; + initProgram(); +} + +void Label::enableOutline(const Color4B& outlineColor,int outlineSize /* = 1 */) +{ + _outlineColor = outlineColor; + if (outlineSize > 0) + { + _currLabelEffect = LabelEffect::OUTLINE; + if (_fontConfig.outlineSize != outlineSize) + { + _fontConfig.outlineSize = outlineSize; + setTTFConfig(_fontConfig); + } + initProgram(); + } +} + +void Label::enableShadow(const Color3B& shadowColor /* = Color3B::BLACK */,const Size &offset /* = Size(2 ,-2)*/, float opacity /* = 0.75f */, int blurRadius /* = 0 */) +{ + _shadowOpacity = opacity; + _effectColor = shadowColor; + _shadowOffset = offset; + //todo:support blur for shadow + _shadowBlurRadius = 0; + _currLabelEffect = LabelEffect::SHADOW; +} + +void Label::disableEffect() +{ + if (_currLabelEffect == LabelEffect::OUTLINE) + { + _fontConfig.outlineSize = 0; + setTTFConfig(_fontConfig); + } + _currLabelEffect = LabelEffect::NORMAL; + initProgram(); } void Label::setFontScale(float fontScale) @@ -657,28 +733,79 @@ void Label::onDraw() return; } - CC_NODE_DRAW_SETUP(); + _shaderProgram->use(); + GL::blendFunc( _blendFunc.src, _blendFunc.dst ); + bool trans = false; - if (_useDistanceField && _currLabelEffect != LabelEffect::NORMAL) + if (_currLabelEffect == LabelEffect::OUTLINE) + { + _shaderProgram->setUniformLocationWith4f(_uniformEffectColor, _outlineColor.r/255.0f,_outlineColor.g/255.0f,_outlineColor.b/255.0f,_outlineColor.a/255.0f); + } + else if (_currLabelEffect == LabelEffect::GLOW) { _shaderProgram->setUniformLocationWith3f(_uniformEffectColor, _effectColor.r/255.0f,_effectColor.g/255.0f,_effectColor.b/255.0f); } - - for(const auto &child: _children) + else if(_currLabelEffect == LabelEffect::SHADOW && _shadowBlurRadius <= 0) { - child->updateTransform(); + trans = true; + drawShadowWithoutBlur(); } - GL::blendFunc( _blendFunc.src, _blendFunc.dst ); + _shaderProgram->setUniformsForBuiltins(_modelViewTransform); + + for(const auto &child: _children) + { + if(child->getTag() >= 0) + child->updateTransform(); + } for (const auto& batchNode:_batchNodes) { batchNode->getTextureAtlas()->drawQuads(); } + if (trans) + { + kmGLPopMatrix(); + } + CC_PROFILER_STOP("Label - draw"); } +void Label::drawShadowWithoutBlur() +{ + _position.x += _shadowOffset.width; + _position.y += _shadowOffset.height; + _transformDirty = _inverseDirty = true; + Color3B oldColor = _realColor; + GLubyte oldOPacity = _displayedOpacity; + _displayedOpacity = _shadowOpacity * _displayedOpacity; + setColor(_effectColor); + + _modelViewTransform = transform(_parentTransform); + kmGLPushMatrix(); + kmGLLoadMatrix(&_modelViewTransform); + + _shaderProgram->setUniformsForBuiltins(_modelViewTransform); + for(const auto &child: _children) + { + child->updateTransform(); + } + for (const auto& batchNode:_batchNodes) + { + batchNode->getTextureAtlas()->drawQuads(); + } + + _position.x -= _shadowOffset.width; + _position.y -= _shadowOffset.height; + _transformDirty = _inverseDirty = true; + _displayedOpacity = oldOPacity; + setColor(oldColor); + _modelViewTransform = transform(_parentTransform); + kmGLLoadMatrix(&_modelViewTransform); + //kmGLPopMatrix(); +} + void Label::draw(Renderer *renderer, const kmMat4 &transform, bool transformUpdated) { _customCommand.init(_globalZOrder); @@ -692,34 +819,42 @@ void Label::visit(Renderer *renderer, const kmMat4 &parentTransform, bool parent { return; } + + if (_currLabelEffect == LabelEffect::SHADOW && _shadowBlurRadius <= 0) + { + _parentTransform = parentTransform; + draw(renderer, _modelViewTransform, true); + } + else + { + bool dirty = parentTransformUpdated || _transformUpdated; + + if(dirty) + _modelViewTransform = transform(parentTransform); + _transformUpdated = false; + + // IMPORTANT: + // To ease the migration to v3.0, we still support the kmGL stack, + // but it is deprecated and your code should not rely on it + kmGLPushMatrix(); + kmGLLoadMatrix(&_modelViewTransform); + + draw(renderer, _modelViewTransform, dirty); + + kmGLPopMatrix(); + } - bool dirty = parentTransformUpdated || _transformUpdated; - if(dirty) - _modelViewTransform = transform(parentTransform); - _transformUpdated = false; - - // IMPORTANT: - // To ease the migration to v3.0, we still support the kmGL stack, - // but it is deprecated and your code should not rely on it - kmGLPushMatrix(); - kmGLLoadMatrix(&_modelViewTransform); - - draw(renderer, _modelViewTransform, dirty); - - kmGLPopMatrix(); - setOrderOfArrival(0); } ///// PROTOCOL STUFF - Sprite * Label::getLetter(int lettetIndex) { if (lettetIndex < getStringLenght()) { if(_lettersInfo[lettetIndex].def.validDefinition == false) return nullptr; - + Sprite* sp = static_cast(this->getChildByTag(lettetIndex)); if (!sp) @@ -735,12 +870,12 @@ Sprite * Label::getLetter(int lettetIndex) sp->setAnchorPoint(Point::ANCHOR_MIDDLE); sp->setPosition(Point(_lettersInfo[lettetIndex].position.x+uvRect.size.width/2,_lettersInfo[lettetIndex].position.y-uvRect.size.height/2)); sp->setOpacity(_realOpacity); - + this->addSpriteWithoutQuad(sp, lettetIndex, lettetIndex); } return sp; } - + return nullptr; } @@ -758,14 +893,14 @@ int Label::getStringNumLines() const void Label::computeStringNumLines() { int quantityOfLines = 1; - + unsigned int stringLen = _currentUTF16String ? cc_wcslen(_currentUTF16String) : -1; if (stringLen < 1) { _currNumLines = stringLen; return; } - + // count number of lines for (unsigned int i = 0; i < stringLen - 1; ++i) { @@ -774,7 +909,7 @@ void Label::computeStringNumLines() quantityOfLines++; } } - + _currNumLines = quantityOfLines; } @@ -800,8 +935,6 @@ bool Label::breakLineWithoutSpace() const } // RGBA protocol - - bool Label::isOpacityModifyRGB() const { return _isOpacityModifyRGB; @@ -810,7 +943,7 @@ bool Label::isOpacityModifyRGB() const void Label::setOpacityModifyRGB(bool isOpacityModifyRGB) { _isOpacityModifyRGB = isOpacityModifyRGB; - + for(const auto& child: _children) { child->setOpacityModifyRGB(_isOpacityModifyRGB); } @@ -820,7 +953,7 @@ void Label::setOpacityModifyRGB(bool isOpacityModifyRGB) void Label::setColor(const Color3B& color) { - _reusedLetter->setColor(color); + _reusedLetter->setColor(color); SpriteBatchNode::setColor(color); } diff --git a/cocos/2d/CCLabel.h b/cocos/2d/CCLabel.h index 098a4694c9..a06b1a8327 100644 --- a/cocos/2d/CCLabel.h +++ b/cocos/2d/CCLabel.h @@ -57,20 +57,29 @@ typedef struct _ttfConfig GlyphCollection glyphs; const char *customGlyphs; bool distanceFieldEnabled; + int outlineSize; - _ttfConfig(const char* filePath,int size = 36, const GlyphCollection& glyphCollection = GlyphCollection::NEHE, - const char *customGlyphCollection = nullptr,bool useDistanceField = false) + _ttfConfig(const char* filePath = "",int size = 36, const GlyphCollection& glyphCollection = GlyphCollection::NEHE, + const char *customGlyphCollection = nullptr,bool useDistanceField = false,int outline = 0) :fontFilePath(filePath) ,fontSize(size) ,glyphs(glyphCollection) ,customGlyphs(customGlyphCollection) ,distanceFieldEnabled(useDistanceField) - {} + ,outlineSize(outline) + { + if(outline > 0) + { + distanceFieldEnabled = false; + } + } }TTFConfig; class CC_DLL Label : public SpriteBatchNode, public LabelProtocol { public: + static const int DefultFontSize; + static Label* create(); CC_DEPRECATED_ATTRIBUTE static Label* createWithTTF(const std::string& label, const std::string& fontFilePath, int fontSize, int lineSize = 0, TextHAlignment alignment = TextHAlignment::LEFT, GlyphCollection glyphs = GlyphCollection::NEHE, const char *customGlyphs = 0, bool useDistanceField = false); @@ -82,18 +91,33 @@ public: static Label * createWithCharMap(Texture2D* texture, int itemWidth, int itemHeight, int startCharMap); static Label * createWithCharMap(const std::string& plistFile); - bool setTTFConfig(const TTFConfig& ttfConfig); + virtual bool setTTFConfig(const TTFConfig& ttfConfig); - bool setBMFontFilePath(const std::string& bmfontFilePath, const Point& imageOffset = Point::ZERO); + virtual bool setBMFontFilePath(const std::string& bmfontFilePath, const Point& imageOffset = Point::ZERO); - bool setCharMap(const std::string& charMapFile, int itemWidth, int itemHeight, int startCharMap); - bool setCharMap(Texture2D* texture, int itemWidth, int itemHeight, int startCharMap); - bool setCharMap(const std::string& plistFile); + virtual bool setCharMap(const std::string& charMapFile, int itemWidth, int itemHeight, int startCharMap); + virtual bool setCharMap(Texture2D* texture, int itemWidth, int itemHeight, int startCharMap); + virtual bool setCharMap(const std::string& plistFile); virtual void setString(const std::string& text) override; - //only support for TTF - void setLabelEffect(LabelEffect effect,const Color3B& effectColor); + CC_DEPRECATED_ATTRIBUTE void setLabelEffect(LabelEffect effect,const Color3B& effectColor); + + /** + * Enable shadow for the label + * + * @todo support blur for shadow effect + */ + virtual void enableShadow(const Color3B& shadowColor = Color3B::BLACK,const Size &offset = Size(2,-2), float opacity = 0.75f, int blurRadius = 0); + + /** only support for TTF */ + virtual void enableOutline(const Color4B& outlineColor,int outlineSize = -1); + + /** only support for TTF */ + virtual void enableGlow(const Color3B& glowColor); + + /** disable shadow/outline/glow rendering */ + virtual void disableEffect(); virtual void setAlignment(TextHAlignment alignment); CC_DEPRECATED_ATTRIBUTE void setWidth(float width) { setMaxLineWidth(width);} @@ -112,29 +136,29 @@ public: virtual Sprite * getLetter(int lettetIndex); // font related stuff - virtual int getCommonLineHeight() const; - + int getCommonLineHeight() const; // string related stuff - virtual int getStringNumLines() const; - virtual int getStringLenght() const; - virtual TextHAlignment getTextAlignment() const; + int getStringNumLines() const; + int getStringLenght() const; + TextHAlignment getTextAlignment() const; // label related stuff - virtual float getMaxLineWidth() const; - virtual bool breakLineWithoutSpace() const; + CC_DEPRECATED_ATTRIBUTE float getWidth() const { getMaxLineWidth(); } + float getMaxLineWidth() const; + bool breakLineWithoutSpace() const; virtual const std::string& getString() const override { return _originalUTF8String; } - void addChild(Node * child, int zOrder=0, int tag=0) override; + virtual void addChild(Node * child, int zOrder=0, int tag=0) override; virtual std::string getDescription() const override; virtual void visit(Renderer *renderer, const kmMat4 &parentTransform, bool parentTransformUpdated) override; virtual void draw(Renderer *renderer, const kmMat4 &transform, bool transformUpdated) override; virtual void onDraw(); - virtual FontAtlas* getFontAtlas() const {return _fontAtlas;} + FontAtlas* getFontAtlas() const {return _fontAtlas;} -private: +protected: struct LetterInfo { FontLetterDefinition def; @@ -150,19 +174,19 @@ private: * @js NA * @lua NA */ - ~Label(); + virtual ~Label(); - bool initWithFontAtlas(FontAtlas* atlas,bool distanceFieldEnabled = false, bool useA8Shader = false); + virtual bool initWithFontAtlas(FontAtlas* atlas,bool distanceFieldEnabled = false, bool useA8Shader = false); // CCLabelTextFormat protocol implementation - virtual bool recordLetterInfo(const cocos2d::Point& point,const FontLetterDefinition& letterDef, int spriteIndex); - virtual bool recordPlaceholderInfo(int spriteIndex); + bool recordLetterInfo(const cocos2d::Point& point,const FontLetterDefinition& letterDef, int spriteIndex); + bool recordPlaceholderInfo(int spriteIndex); void setFontScale(float fontScale); - bool init(); + virtual bool init(); - void alignText(); + virtual void alignText(); bool computeHorizontalKernings(unsigned short int *stringToRender); bool setCurrentString(unsigned short *stringToSet); @@ -174,6 +198,10 @@ private: virtual void updateColor() override; + virtual void initProgram(); + + void drawShadowWithoutBlur(); + //! used for optimization Sprite *_reusedLetter; Rect _reusedRect; @@ -204,6 +232,19 @@ private: std::vector _batchNodes; + Size _shadowOffset; + float _shadowOpacity; + int _shadowBlurRadius; + + Color4B _outlineColor; + + TTFConfig _fontConfig; + + kmMat4 _parentTransform; + +private: + CC_DISALLOW_COPY_AND_ASSIGN(Label); + friend class LabelTextFormatter; }; diff --git a/cocos/2d/ccShader_Label_frag.h b/cocos/2d/ccShader_Label_frag.h index 8b843571fb..6bc6a30d99 100644 --- a/cocos/2d/ccShader_Label_frag.h +++ b/cocos/2d/ccShader_Label_frag.h @@ -18,7 +18,7 @@ void main() \n\ //float width = fwidth(dist); \n\ //assign width for constant will lead to a little bit fuzzy,it's temporary measure.\n\ float width = 0.04; \n\ - float alpha = smoothstep(0.5-width, 0.5+width, dist); \n\ + float alpha = smoothstep(0.5-width, 0.5+width, dist) * v_fragmentColor.a; \n\ gl_FragColor = vec4(v_fragmentColor.rgb,alpha); \n\ } \n\ "; diff --git a/cocos/2d/ccShader_Label_frag_outline.h b/cocos/2d/ccShader_Label_frag_outline.h index 672715fc18..ad1754ab68 100644 --- a/cocos/2d/ccShader_Label_frag_outline.h +++ b/cocos/2d/ccShader_Label_frag_outline.h @@ -6,19 +6,19 @@ precision lowp float; \n\ varying vec4 v_fragmentColor; \n\ varying vec2 v_texCoord; \n\ uniform sampler2D CC_Texture0; \n\ -uniform vec3 v_effectColor; \n\ +uniform vec4 v_effectColor; \n\ \n\ -void main() \n\ -{ \n\ - float dist = texture2D(CC_Texture0, v_texCoord).a; \n\ - //todo:Implementation 'fwidth' for glsl 1.0 \n\ - //float width = fwidth(dist); \n\ - //assign width for constant will lead to a little bit fuzzy,it's temporary measure.\n\ - float width = 0.04; \n\ - float alpha = smoothstep(0.5-width, 0.5+width, dist); \n\ - //outline \n\ - float mu = smoothstep(0.545-width, 0.545+width, dist); \n\ - vec3 rgb = v_effectColor*(1.0-mu) + v_fragmentColor.rgb*mu; \n\ - gl_FragColor = vec4(rgb, max(alpha,mu)); \n\ -} \n\ +void main() \n\ +{ \n\ + vec4 sample = texture2D(CC_Texture0, v_texCoord); \n\ + float fontAlpha = sample.a; \n\ + float outlineAlpha = sample.r; \n\ + if (outlineAlpha > 0.0){ \n\ + vec3 color = v_fragmentColor.rgb * fontAlpha + v_effectColor.rgb * (1.0 - fontAlpha);\n\ + gl_FragColor = vec4( color,max(fontAlpha,outlineAlpha)*v_fragmentColor.a); \n\ + } \n\ + else { \n\ + discard; \n\ + } \n\ +} \n\ "; diff --git a/tests/Classes/LabelTest/LabelTestNew.cpp b/tests/Classes/LabelTest/LabelTestNew.cpp index 276407e139..ac82b46d10 100644 --- a/tests/Classes/LabelTest/LabelTestNew.cpp +++ b/tests/Classes/LabelTest/LabelTestNew.cpp @@ -2,6 +2,8 @@ #include "../testResource.h" #include "renderer/CCRenderer.h" +using namespace ui; + enum { kTagTileMap = 1, kTagSpriteManager = 1, @@ -68,7 +70,8 @@ static std::function createFunctions[] = CL(LabelTTFUnicodeNew), CL(LabelBMFontTestNew), CL(LabelTTFDistanceField), - CL(LabelTTFDistanceFieldEffect), + CL(LabelOutlineAndGlowTest), + CL(LabelShadowTest), CL(LabelCharMapTest), CL(LabelCharMapColorTest), CL(LabelCrashTest), @@ -1292,7 +1295,7 @@ std::string LabelTTFDistanceField::subtitle() const return "Testing rendering base on DistanceField"; } -LabelTTFDistanceFieldEffect::LabelTTFDistanceFieldEffect() +LabelOutlineAndGlowTest::LabelOutlineAndGlowTest() { auto size = Director::getInstance()->getWinSize(); @@ -1302,36 +1305,115 @@ LabelTTFDistanceFieldEffect::LabelTTFDistanceFieldEffect() TTFConfig ttfConfig("fonts/arial.ttf", 80, GlyphCollection::DYNAMIC,nullptr,true); auto label1 = Label::createWithTTF(ttfConfig,"Glow", TextHAlignment::CENTER, size.width); - label1->setPosition( Point(size.width/2, size.height*0.65) ); + label1->setPosition( Point(size.width/2, size.height*0.7) ); label1->setColor( Color3B::GREEN ); label1->setAnchorPoint(Point::ANCHOR_MIDDLE); - label1->setLabelEffect(LabelEffect::GLOW,Color3B::YELLOW); + label1->enableGlow(Color3B::YELLOW); addChild(label1); + ttfConfig.outlineSize = 1; auto label2 = Label::createWithTTF(ttfConfig,"Outline", TextHAlignment::CENTER, size.width); - label2->setPosition( Point(size.width/2, size.height*0.5) ); + label2->setPosition( Point(size.width/2, size.height*0.6) ); label2->setColor( Color3B::RED ); label2->setAnchorPoint(Point::ANCHOR_MIDDLE); - label2->setLabelEffect(LabelEffect::OUTLINE,Color3B::BLUE); + label2->enableOutline(Color4B::BLUE); addChild(label2); - auto label3 = Label::createWithTTF(ttfConfig,"Shadow", TextHAlignment::CENTER, size.width); - label3->setPosition( Point(size.width/2, size.height*0.35f) ); + ttfConfig.outlineSize = 2; + auto label3 = Label::createWithTTF(ttfConfig,"Outline", TextHAlignment::CENTER, size.width); + label3->setPosition( Point(size.width/2, size.height*0.48) ); label3->setColor( Color3B::RED ); label3->setAnchorPoint(Point::ANCHOR_MIDDLE); - label3->setLabelEffect(LabelEffect::SHADOW,Color3B::BLACK); + label3->enableOutline(Color4B::BLUE); addChild(label3); + ttfConfig.outlineSize = 3; + auto label4 = Label::createWithTTF(ttfConfig,"Outline", TextHAlignment::CENTER, size.width); + label4->setPosition( Point(size.width/2, size.height*0.36) ); + label4->setColor( Color3B::RED ); + label4->setAnchorPoint(Point::ANCHOR_MIDDLE); + label4->enableOutline(Color4B::BLUE); + addChild(label4); } -std::string LabelTTFDistanceFieldEffect::title() const +std::string LabelOutlineAndGlowTest::title() const { - return "New Label + .TTF"; + return "New Label"; } -std::string LabelTTFDistanceFieldEffect::subtitle() const +std::string LabelOutlineAndGlowTest::subtitle() const { - return "Testing effect base on DistanceField"; + return "Testing outline and glow of label"; +} + +LabelShadowTest::LabelShadowTest() +{ + auto size = Director::getInstance()->getWinSize(); + + auto bg = LayerColor::create(Color4B(200,191,231,255)); + this->addChild(bg); + + TTFConfig ttfConfig("fonts/arial.ttf", 80, GlyphCollection::DYNAMIC,nullptr,true); + + shadowLabelTTF = Label::createWithTTF(ttfConfig,"TTF:Shadow", TextHAlignment::CENTER, size.width); + shadowLabelTTF->setPosition( Point(size.width/2, size.height*0.6f) ); + shadowLabelTTF->setColor( Color3B::RED ); + shadowLabelTTF->setAnchorPoint(Point::ANCHOR_MIDDLE); + shadowLabelTTF->enableShadow(Color3B::BLACK); + addChild(shadowLabelTTF); + + shadowLabelBMFont = Label::createWithBMFont("fonts/bitmapFontTest.fnt", "BMFont:Shadow"); + shadowLabelBMFont->setPosition( Point(size.width/2, size.height*0.4f) ); + shadowLabelBMFont->setColor( Color3B::RED ); + shadowLabelBMFont->setAnchorPoint(Point::ANCHOR_MIDDLE); + shadowLabelBMFont->enableShadow(Color3B::GREEN); + addChild(shadowLabelBMFont); + + auto slider = ui::Slider::create(); + slider->setTag(1); + slider->setTouchEnabled(true); + slider->loadBarTexture("cocosgui/sliderTrack.png"); + slider->loadSlidBallTextures("cocosgui/sliderThumb.png", "cocosgui/sliderThumb.png", ""); + slider->loadProgressBarTexture("cocosgui/sliderProgress.png"); + slider->setPosition(Point(size.width / 2.0f, size.height * 0.15f + slider->getSize().height * 2.0f)); + slider->setPercent(52); + slider->addEventListenerSlider(this, sliderpercentchangedselector(LabelShadowTest::sliderEvent)); + addChild(slider); + + auto slider2 = ui::Slider::create(); + slider2->setTag(2); + slider2->setTouchEnabled(true); + slider2->loadBarTexture("cocosgui/sliderTrack.png"); + slider2->loadSlidBallTextures("cocosgui/sliderThumb.png", "cocosgui/sliderThumb.png", ""); + slider2->loadProgressBarTexture("cocosgui/sliderProgress.png"); + slider2->setPosition(Point(size.width * 0.15f, size.height / 2.0)); + slider2->setRotation(90); + slider2->setPercent(52); + slider2->addEventListenerSlider(this, sliderpercentchangedselector(LabelShadowTest::sliderEvent)); + addChild(slider2); +} + +void LabelShadowTest::sliderEvent(Ref *pSender, ui::SliderEventType type) +{ + if (type == SLIDER_PERCENTCHANGED) + { + Slider* slider = (Slider*)this->getChildByTag(1); + Slider* slider2 = (Slider*)this->getChildByTag(2); + + auto offset = Size(slider->getPercent()-50,50 - slider2->getPercent()); + shadowLabelTTF->enableShadow(Color3B::BLACK,offset); + shadowLabelBMFont->enableShadow(Color3B::GREEN,offset); + } +} + +std::string LabelShadowTest::title() const +{ + return "New Label"; +} + +std::string LabelShadowTest::subtitle() const +{ + return "Testing shadow of label"; } LabelCharMapTest::LabelCharMapTest() diff --git a/tests/Classes/LabelTest/LabelTestNew.h b/tests/Classes/LabelTest/LabelTestNew.h index 179ea50d98..f5cde6d9f4 100644 --- a/tests/Classes/LabelTest/LabelTestNew.h +++ b/tests/Classes/LabelTest/LabelTestNew.h @@ -4,6 +4,7 @@ #include "../testBasic.h" #include "../BaseTest.h" #include "renderer/CCCustomCommand.h" +#include "gui/CocosGUI.h" class AtlasDemoNew : public BaseTest @@ -349,17 +350,33 @@ public: virtual std::string subtitle() const override; }; -class LabelTTFDistanceFieldEffect : public AtlasDemoNew +class LabelOutlineAndGlowTest : public AtlasDemoNew { public: - CREATE_FUNC(LabelTTFDistanceFieldEffect); + CREATE_FUNC(LabelOutlineAndGlowTest); - LabelTTFDistanceFieldEffect(); + LabelOutlineAndGlowTest(); virtual std::string title() const override; virtual std::string subtitle() const override; }; +class LabelShadowTest : public AtlasDemoNew +{ +public: + CREATE_FUNC(LabelShadowTest); + + LabelShadowTest(); + + virtual std::string title() const override; + virtual std::string subtitle() const override; + + void sliderEvent(Ref *pSender, ui::SliderEventType type); +private: + Label* shadowLabelTTF; + Label* shadowLabelBMFont; +}; + class LabelCharMapTest : public AtlasDemoNew { public: