mirror of
https://github.com/smallmain/cocos-enhance-kit.git
synced 2025-10-09 11:05:24 +00:00
初始化
This commit is contained in:
361
cocos2d-x/cocos/2d/CCFontAtlas.cpp
Normal file
361
cocos2d-x/cocos/2d/CCFontAtlas.cpp
Normal file
@@ -0,0 +1,361 @@
|
||||
/****************************************************************************
|
||||
Copyright (c) 2020 Xiamen Yaji Software Co., Ltd.
|
||||
|
||||
http://www.cocos.com
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
****************************************************************************/
|
||||
|
||||
#include "CCFontAtlas.h"
|
||||
#include "renderer/gfx/Texture2D.h"
|
||||
#include "renderer/gfx/DeviceGraphics.h"
|
||||
#include "base/ccConfig.h"
|
||||
#include <cassert>
|
||||
|
||||
#if CC_ENABLE_TTF_LABEL_RENDERER
|
||||
|
||||
static const int PIXEL_PADDING = 2;
|
||||
|
||||
namespace cocos2d {
|
||||
|
||||
FontAtlasFrame::FontAtlasFrame()
|
||||
{
|
||||
_currentRowX = PIXEL_PADDING;
|
||||
_currentRowY = PIXEL_PADDING;
|
||||
}
|
||||
|
||||
FontAtlasFrame::FontAtlasFrame(FontAtlasFrame&& o)
|
||||
{
|
||||
#if CC_ENABLE_CACHE_TTF_FONT_TEXTURE
|
||||
// move buffer instead of copy
|
||||
std::swap(_buffer, o._buffer);
|
||||
_dirtyFlag = o._dirtyFlag;
|
||||
_dirtyRegion = o._dirtyRegion;
|
||||
#endif
|
||||
_WIDTH = o._WIDTH;
|
||||
_HEIGHT = o._HEIGHT;
|
||||
_currentRowX = o._currentRowX;
|
||||
_currentRowY = o._currentRowY;
|
||||
_currRowHeight = o._currRowHeight;
|
||||
_pixelMode = o._pixelMode;
|
||||
_texture = o._texture;
|
||||
|
||||
o._texture = nullptr;
|
||||
}
|
||||
|
||||
FontAtlasFrame::~FontAtlasFrame()
|
||||
{
|
||||
CC_SAFE_RELEASE(_texture);
|
||||
}
|
||||
|
||||
void FontAtlasFrame::reinit(PixelMode pixelMode, int width, int height)
|
||||
{
|
||||
_pixelMode = pixelMode;
|
||||
_WIDTH = width;
|
||||
_HEIGHT = height;
|
||||
_currentRowY = PIXEL_PADDING;
|
||||
_currentRowY = PIXEL_PADDING;
|
||||
_currRowHeight = 0;
|
||||
#if CC_ENABLE_CACHE_TTF_FONT_TEXTURE
|
||||
_buffer.resize(PixelModeSize(pixelMode) * width * height);
|
||||
std::fill(_buffer.begin(), _buffer.end(), 0);
|
||||
_dirtyFlag = 0;
|
||||
#endif
|
||||
|
||||
getTexture(); // init texture
|
||||
}
|
||||
|
||||
FontAtlasFrame::FrameResult FontAtlasFrame::append(int width, int height, std::vector<uint8_t> &data, Rect &out)
|
||||
{
|
||||
#if CC_ENABLE_CACHE_TTF_FONT_TEXTURE
|
||||
assert(_buffer.size() > 0);
|
||||
assert(width <= _WIDTH && height <= _HEIGHT);
|
||||
#endif
|
||||
if (!hasSpace(width, height)) {
|
||||
return FrameResult::E_FULL;
|
||||
}
|
||||
|
||||
#if CC_ENABLE_CACHE_TTF_FONT_TEXTURE
|
||||
//update texture-data in CPU memory
|
||||
const int pixelSize = PixelModeSize(_pixelMode);
|
||||
uint8_t* dst = _buffer.data();
|
||||
uint8_t* src = data.data();
|
||||
uint8_t* dstOrigin = pixelSize * (_currentRowY * _WIDTH + _currentRowX) + dst;
|
||||
const int BytesEachRow = pixelSize * width;
|
||||
for (int i = 0; i < height; i++)
|
||||
{
|
||||
memcpy(dstOrigin + i * _WIDTH * pixelSize, src + i * BytesEachRow, BytesEachRow);
|
||||
}
|
||||
|
||||
if (_dirtyFlag == 0)
|
||||
{
|
||||
_dirtyFlag |= DIRTY_RECT;
|
||||
_dirtyRegion = Rect(_currentRowX, _currentRowY, width, height);
|
||||
}
|
||||
else
|
||||
{
|
||||
_dirtyRegion.merge(Rect(_currentRowX, _currentRowY, width, height));
|
||||
}
|
||||
#else
|
||||
//update GPU texture immediately
|
||||
renderer::Texture::SubImageOption opt;
|
||||
opt.imageData = data.data();
|
||||
opt.x = _currentRowX;
|
||||
opt.y = _currentRowY;
|
||||
opt.width = width;
|
||||
opt.height = height;
|
||||
opt.imageDataLength = data.size();
|
||||
_texture->updateSubImage(opt);
|
||||
#endif
|
||||
|
||||
|
||||
out.origin.set(_currentRowX, _currentRowY);
|
||||
out.size.width = width;
|
||||
out.size.height = height;
|
||||
// move cursor
|
||||
moveToNextCursor(width, height);
|
||||
return FrameResult::SUCCESS;
|
||||
|
||||
}
|
||||
|
||||
bool FontAtlasFrame::hasSpace(int width, int height)
|
||||
{
|
||||
if (hasRowXSpace(width) && hasYSpace(height)) {
|
||||
return true;
|
||||
}
|
||||
if (hasNextRowXSpace(width) && hasNextRowYSpace(height))
|
||||
{
|
||||
moveToNextRow();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
int FontAtlasFrame::remainRowXSpace() const
|
||||
{
|
||||
return _WIDTH - _currentRowX;
|
||||
}
|
||||
|
||||
int FontAtlasFrame::remainYSpace() const
|
||||
{
|
||||
return _HEIGHT - _currentRowY;
|
||||
}
|
||||
|
||||
bool FontAtlasFrame::hasRowXSpace(int x) const
|
||||
{
|
||||
return x + PIXEL_PADDING <= remainRowXSpace();
|
||||
}
|
||||
|
||||
bool FontAtlasFrame::hasYSpace(int y) const
|
||||
{
|
||||
return y + PIXEL_PADDING <= remainYSpace();
|
||||
}
|
||||
|
||||
bool FontAtlasFrame::hasNextRowXSpace(int x) const
|
||||
{
|
||||
return x + PIXEL_PADDING <= _WIDTH;
|
||||
}
|
||||
|
||||
bool FontAtlasFrame::hasNextRowYSpace(int y) const
|
||||
{
|
||||
return y + PIXEL_PADDING <= remainYSpace() - _currRowHeight;
|
||||
}
|
||||
|
||||
|
||||
void FontAtlasFrame::moveToNextRow()
|
||||
{
|
||||
_currentRowY += _currRowHeight + PIXEL_PADDING;
|
||||
_currentRowX = PIXEL_PADDING;
|
||||
_currRowHeight = 0;
|
||||
}
|
||||
|
||||
void FontAtlasFrame::moveToNextCursor(int width, int height)
|
||||
{
|
||||
_currRowHeight = std::max(_currRowHeight, height);
|
||||
_currentRowX += width + PIXEL_PADDING;
|
||||
}
|
||||
|
||||
renderer::Texture2D * FontAtlasFrame::getTexture()
|
||||
{
|
||||
if (!_texture)
|
||||
{
|
||||
auto* device = renderer::DeviceGraphics::getInstance();
|
||||
_texture = new cocos2d::renderer::Texture2D();
|
||||
|
||||
cocos2d::renderer::Texture::Options option;
|
||||
option.width = _WIDTH;
|
||||
option.height = _HEIGHT;
|
||||
// alpha only
|
||||
option.glFormat = GL_ALPHA;
|
||||
option.glInternalFormat = GL_ALPHA;
|
||||
|
||||
option.glType = GL_UNSIGNED_BYTE;
|
||||
option.bpp = 8 * PixelModeSize(_pixelMode);
|
||||
|
||||
renderer::Texture::Image img;
|
||||
#if CC_ENABLE_CACHE_TTF_FONT_TEXTURE
|
||||
img.data = _buffer.data();
|
||||
img.length = _buffer.size();
|
||||
#else
|
||||
std::vector<uint8_t> buffer(_WIDTH * _HEIGHT * PixelModeSize(_pixelMode), 0);
|
||||
img.length = buffer.size();
|
||||
img.data = buffer.data();
|
||||
#endif
|
||||
option.images.push_back(img);
|
||||
_texture->init(device, option);
|
||||
}
|
||||
|
||||
|
||||
#if CC_ENABLE_CACHE_TTF_FONT_TEXTURE
|
||||
if (_dirtyFlag & DIRTY_ALL)
|
||||
{
|
||||
renderer::Texture::SubImageOption opt;
|
||||
opt.imageData = _buffer.data();
|
||||
opt.x = 0;
|
||||
opt.y = 0;
|
||||
opt.width = _WIDTH;
|
||||
opt.height = _HEIGHT;
|
||||
opt.imageDataLength = (uint32_t)_buffer.size();
|
||||
_texture->updateSubImage(opt);
|
||||
}
|
||||
else if (_dirtyFlag & DIRTY_RECT)
|
||||
{
|
||||
int yMin = _dirtyRegion.getMinY();
|
||||
int yHeight = _dirtyRegion.size.height;
|
||||
renderer::Texture::SubImageOption opt;
|
||||
opt.imageData = _buffer.data() + PixelModeSize(_pixelMode) * _WIDTH * yMin;
|
||||
opt.x = 0;
|
||||
opt.y = yMin;
|
||||
opt.width = _WIDTH;
|
||||
opt.height = yHeight;
|
||||
opt.imageDataLength = PixelModeSize(_pixelMode) * _WIDTH * yHeight;
|
||||
_texture->updateSubImage(opt);
|
||||
}
|
||||
_dirtyFlag = 0;
|
||||
#endif
|
||||
return _texture;
|
||||
}
|
||||
|
||||
|
||||
FontAtlas::FontAtlas(PixelMode pixelMode, int width, int height, bool hasoutline)
|
||||
:_pixelMode(pixelMode), _width(width), _height(height), _useSDF(hasoutline)
|
||||
{
|
||||
}
|
||||
|
||||
FontAtlas::~FontAtlas()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
bool FontAtlas::init()
|
||||
{
|
||||
_textureFrame.reinit(_pixelMode, _width, _height);
|
||||
_letterMap.clear();
|
||||
return true;
|
||||
}
|
||||
|
||||
bool FontAtlas::prepareLetter(unsigned long ch, std::shared_ptr<GlyphBitmap> bitmap)
|
||||
{
|
||||
|
||||
if (_letterMap.find(ch) != _letterMap.end())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
Rect rect;
|
||||
FontAtlasFrame::FrameResult ret = _textureFrame.append(bitmap->getWidth(), bitmap->getHeight(), bitmap->getData(), rect);
|
||||
|
||||
switch (ret) {
|
||||
case FontAtlasFrame::FrameResult::E_ERROR:
|
||||
//TODO: ERROR LOG
|
||||
assert(false);
|
||||
return false;
|
||||
case FontAtlasFrame::FrameResult::E_FULL:
|
||||
// Allocate a new frame & add bitmap the frame
|
||||
_buffers.push_back(std::move(_textureFrame));
|
||||
_textureBufferIndex += 1;
|
||||
_textureFrame.reinit(_pixelMode, _width, _height);
|
||||
return prepareLetter(ch, bitmap);
|
||||
case FontAtlasFrame::FrameResult::SUCCESS:
|
||||
addLetterDef(ch, bitmap, rect);
|
||||
return true;
|
||||
default:
|
||||
//TODO: LOG
|
||||
assert(false);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void FontAtlas::addLetterDef(unsigned long ch, std::shared_ptr<GlyphBitmap> bitmap, const Rect& rect)
|
||||
{
|
||||
assert(bitmap->getPixelMode() == _pixelMode);
|
||||
|
||||
auto& def = _letterMap[ch];
|
||||
def.validate = true;
|
||||
def.textureID = _textureBufferIndex;
|
||||
def.xAdvance = bitmap->getXAdvance();
|
||||
def.rect = bitmap->getRect();
|
||||
def.texX = (rect.origin.x - 0.5f) / _textureFrame.getWidth();
|
||||
def.texY = (rect.origin.y -0.5f)/ _textureFrame.getHeight();
|
||||
def.texWidth = (rect.size.width + 1.0f) / _textureFrame.getWidth();
|
||||
def.texHeight = (rect.size.height + 1.0f) / _textureFrame.getHeight();
|
||||
def.outline = bitmap->getOutline();
|
||||
}
|
||||
|
||||
bool FontAtlas::prepareLetters(const std::u32string &text, cocos2d::FontFreeType *font)
|
||||
{
|
||||
bool ok = true;
|
||||
for (int i = 0; i < text.length(); i++)
|
||||
{
|
||||
auto it = _letterMap.find(text[i]);
|
||||
if(it == _letterMap.end())
|
||||
{
|
||||
auto glyph = font->getGlyphBitmap(text[i], _useSDF);
|
||||
ok &= prepareLetter(text[i], glyph);
|
||||
}
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
FontLetterDefinition* FontAtlas::getOrLoad(unsigned long ch, cocos2d::FontFreeType* font)
|
||||
{
|
||||
auto it = _letterMap.find(ch);
|
||||
if (it != _letterMap.end()) return &it->second;
|
||||
|
||||
if (font) {
|
||||
auto bitmap = font->getGlyphBitmap(ch, _useSDF);
|
||||
if (bitmap) {
|
||||
if (prepareLetter(ch, bitmap)) {
|
||||
return getOrLoad(ch, nullptr);
|
||||
}
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
||||
FontAtlasFrame& FontAtlas::frameAt(int idx)
|
||||
{
|
||||
return idx == _textureBufferIndex ? _textureFrame : _buffers.at(idx);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
147
cocos2d-x/cocos/2d/CCFontAtlas.h
Normal file
147
cocos2d-x/cocos/2d/CCFontAtlas.h
Normal file
@@ -0,0 +1,147 @@
|
||||
/****************************************************************************
|
||||
Copyright (c) 2020 Xiamen Yaji Software Co., Ltd.
|
||||
|
||||
http://www.cocos.com
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
****************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "CCFontFreetype.h"
|
||||
|
||||
#include <unordered_map>
|
||||
#include <algorithm>
|
||||
|
||||
#include "base/ccConfig.h"
|
||||
#if CC_ENABLE_TTF_LABEL_RENDERER
|
||||
|
||||
namespace cocos2d {
|
||||
|
||||
namespace renderer {
|
||||
class Texture2D;
|
||||
}
|
||||
|
||||
class FontAtlas;
|
||||
|
||||
struct FontLetterDefinition
|
||||
{
|
||||
float texX = 0, texY = 0;
|
||||
float texWidth = 0, texHeight = 0;
|
||||
Rect rect;
|
||||
int textureID = -1;
|
||||
float xAdvance = 0;
|
||||
int outline = 0;
|
||||
bool validate = false;
|
||||
};
|
||||
|
||||
class FontAtlasFrame
|
||||
{
|
||||
public:
|
||||
|
||||
enum class FrameResult {
|
||||
SUCCESS,
|
||||
E_FULL,
|
||||
E_ERROR
|
||||
};
|
||||
|
||||
FontAtlasFrame();
|
||||
FontAtlasFrame(FontAtlasFrame&&); //move
|
||||
|
||||
virtual ~FontAtlasFrame();
|
||||
|
||||
void reinit(PixelMode mode, int width, int height);
|
||||
FrameResult append(int width, int height, std::vector<uint8_t> &, Rect &out);
|
||||
|
||||
|
||||
float getWidth() const { return _WIDTH; }
|
||||
float getHeight() const { return _HEIGHT; }
|
||||
|
||||
renderer::Texture2D* getTexture();
|
||||
|
||||
private:
|
||||
|
||||
enum DirtyType {
|
||||
DIRTY_RECT = 1,
|
||||
DIRTY_ALL= 2,
|
||||
};
|
||||
|
||||
bool hasSpace(int width, int height);
|
||||
|
||||
int remainRowXSpace() const;
|
||||
int remainYSpace() const;
|
||||
bool hasRowXSpace(int x) const;
|
||||
bool hasYSpace(int y) const;
|
||||
bool hasNextRowXSpace(int x) const;
|
||||
bool hasNextRowYSpace(int y) const;
|
||||
|
||||
void moveToNextRow();
|
||||
void moveToNextCursor(int width, int height);
|
||||
|
||||
#if CC_ENABLE_CACHE_TTF_FONT_TEXTURE
|
||||
mutable std::vector<uint8_t> _buffer;
|
||||
int _dirtyFlag = 0;
|
||||
Rect _dirtyRegion;
|
||||
#endif
|
||||
|
||||
int _WIDTH = 0;
|
||||
int _HEIGHT = 0;
|
||||
int _currentRowY = 0;
|
||||
int _currentRowX = 0;
|
||||
int _currRowHeight = 0;
|
||||
PixelMode _pixelMode = PixelMode::A8;
|
||||
renderer::Texture2D *_texture = nullptr;
|
||||
|
||||
friend class FontAtlas;
|
||||
};
|
||||
|
||||
class FontAtlas {
|
||||
|
||||
public:
|
||||
|
||||
FontAtlas(PixelMode mode, int width, int height, bool hasoutline);
|
||||
virtual ~FontAtlas();
|
||||
|
||||
bool init();
|
||||
|
||||
bool prepareLetter(unsigned long ch, std::shared_ptr<GlyphBitmap> bitmap);
|
||||
|
||||
bool prepareLetters(const std::u32string &text, cocos2d::FontFreeType *font);
|
||||
|
||||
FontLetterDefinition* getOrLoad(unsigned long ch, FontFreeType* font);
|
||||
|
||||
FontAtlasFrame& frameAt(int idx);
|
||||
private:
|
||||
|
||||
void addLetterDef(unsigned long ch, std::shared_ptr<GlyphBitmap> bitmap, const Rect& rect);
|
||||
|
||||
std::unordered_map<uint64_t, FontLetterDefinition> _letterMap;
|
||||
|
||||
FontAtlasFrame _textureFrame;
|
||||
std::vector<FontAtlasFrame> _buffers;
|
||||
int _textureBufferIndex = 0;
|
||||
int _width = 0;
|
||||
int _height = 0;
|
||||
PixelMode _pixelMode = PixelMode::A8;
|
||||
bool _useSDF = false;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
415
cocos2d-x/cocos/2d/CCFontFreetype.cpp
Normal file
415
cocos2d-x/cocos/2d/CCFontFreetype.cpp
Normal file
@@ -0,0 +1,415 @@
|
||||
/****************************************************************************
|
||||
Copyright (c) 2020 Xiamen Yaji Software Co., Ltd.
|
||||
|
||||
http://www.cocos.com
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
****************************************************************************/
|
||||
|
||||
#include "CCFontFreetype.h"
|
||||
|
||||
#include <cassert>
|
||||
#include "platform/CCFileUtils.h"
|
||||
#include "platform/CCDevice.h"
|
||||
#include "external/sources/edtaa3func/edtaa3func.h"
|
||||
#include "base/ccConfig.h"
|
||||
|
||||
#if CC_ENABLE_TTF_LABEL_RENDERER
|
||||
|
||||
/**
|
||||
* By enable FFT_USE_SCREEN_DPI, text will be much more clear in mobile devices,
|
||||
* and more memory will be consumed.
|
||||
*/
|
||||
#define FFT_USE_SCREEN_DPI 0
|
||||
|
||||
#if FFT_USE_SCREEN_DPI
|
||||
#define SCALE_BY_DPI(x) (int)((x) * 72 / _dpi)
|
||||
#else
|
||||
#define SCALE_BY_DPI(x) (int)(x)
|
||||
#endif
|
||||
|
||||
#if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS)
|
||||
// `thread_local` can not compile on iOS 9.0 below device
|
||||
# define FFT_SDF_TMP_VECTOR (__IPHONE_OS_VERSION_MIN_REQUIRED >= 90000)
|
||||
#else
|
||||
# define FFT_SDF_TMP_VECTOR 1
|
||||
#endif // CC_TARGET_PLATFORM == CC_PLATFORM_IOS
|
||||
|
||||
#if FFT_SDF_TMP_VECTOR
|
||||
namespace {
|
||||
//cache vector in thread
|
||||
thread_local std::vector<short> xdistV;
|
||||
thread_local std::vector<short> ydistV;
|
||||
thread_local std::vector<double> gxV;
|
||||
thread_local std::vector<double> gyV;
|
||||
thread_local std::vector<double> dataV;
|
||||
thread_local std::vector<double> outsideV;
|
||||
thread_local std::vector<double> insideV;
|
||||
}
|
||||
#endif
|
||||
|
||||
namespace cocos2d {
|
||||
|
||||
class FontFreeTypeLibrary {
|
||||
public:
|
||||
FontFreeTypeLibrary() {
|
||||
memset(&_library, 0, sizeof(FT_Library));
|
||||
FT_Init_FreeType(&_library);
|
||||
}
|
||||
~FontFreeTypeLibrary()
|
||||
{
|
||||
_faceCache.clear();
|
||||
FT_Done_FreeType(_library);
|
||||
}
|
||||
|
||||
FT_Library * get() { return &_library; }
|
||||
|
||||
std::unordered_map<std::string, std::shared_ptr<Data>> &getFaceCache() {
|
||||
return _faceCache;
|
||||
}
|
||||
|
||||
private:
|
||||
std::unordered_map<std::string, std::shared_ptr<Data>> _faceCache;
|
||||
FT_Library _library;
|
||||
};
|
||||
|
||||
namespace {
|
||||
std::shared_ptr<FontFreeTypeLibrary> _sFTLibrary;
|
||||
|
||||
PixelMode FTtoPixelModel(FT_Pixel_Mode mode)
|
||||
{
|
||||
switch (mode)
|
||||
{
|
||||
case FT_PIXEL_MODE_GRAY:
|
||||
return PixelMode::A8;
|
||||
case FT_PIXEL_MODE_LCD:
|
||||
return PixelMode::RGB888;
|
||||
case FT_PIXEL_MODE_BGRA:
|
||||
return PixelMode::BGRA8888;
|
||||
default:
|
||||
assert(false); //invalidate pixelmode
|
||||
return PixelMode::INVAL;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<uint8_t> makeDistanceMap(unsigned char *img, long width, long height, int distanceMapSpread)
|
||||
{
|
||||
long pixelAmount = (width + 2 * distanceMapSpread) * (height + 2 * distanceMapSpread);
|
||||
|
||||
#if FFT_SDF_TMP_VECTOR
|
||||
xdistV.resize(pixelAmount);
|
||||
ydistV.resize(pixelAmount);
|
||||
gxV.resize(pixelAmount);
|
||||
gyV.resize(pixelAmount);
|
||||
dataV.resize(pixelAmount);
|
||||
outsideV.resize(pixelAmount);
|
||||
insideV.resize(pixelAmount);
|
||||
|
||||
std::fill(gxV.begin(), gxV.end(), 0.0);
|
||||
std::fill(gyV.begin(), gyV.end(), 0.0);
|
||||
std::fill(dataV.begin(), dataV.end(), 0.0);
|
||||
std::fill(outsideV.begin(), outsideV.end(), 0.0);
|
||||
std::fill(insideV.begin(), insideV.end(), 0.0);
|
||||
|
||||
short * xdist = xdistV.data();
|
||||
short * ydist = ydistV.data();
|
||||
double * gx = gxV.data();
|
||||
double * gy = gyV.data();
|
||||
double * data = dataV.data();
|
||||
double * outside = outsideV.data();
|
||||
double * inside = insideV.data();
|
||||
#else
|
||||
short * xdist = (short *)malloc(pixelAmount * sizeof(short));
|
||||
short * ydist = (short *)malloc(pixelAmount * sizeof(short));
|
||||
double * gx = (double *)calloc(pixelAmount, sizeof(double));
|
||||
double * gy = (double *)calloc(pixelAmount, sizeof(double));
|
||||
double * data = (double *)calloc(pixelAmount, sizeof(double));
|
||||
double * outside = (double *)calloc(pixelAmount, sizeof(double));
|
||||
double * inside = (double *)calloc(pixelAmount, sizeof(double));
|
||||
#endif
|
||||
long i, j;
|
||||
|
||||
// Convert img into double (data) rescale image levels between 0 and 1
|
||||
long outWidth = width + 2 * distanceMapSpread;
|
||||
for (i = 0; i < width; ++i)
|
||||
{
|
||||
for (j = 0; j < height; ++j)
|
||||
{
|
||||
data[(j + distanceMapSpread) * outWidth + distanceMapSpread + i] = img[j * width + i] / 255.0;
|
||||
}
|
||||
}
|
||||
|
||||
width += 2 * distanceMapSpread;
|
||||
height += 2 * distanceMapSpread;
|
||||
|
||||
// Transform background (outside contour, in areas of 0's)
|
||||
computegradient(data, (int)width, (int)height, gx, gy);
|
||||
edtaa3(data, gx, gy, (int)width, (int)height, xdist, ydist, outside);
|
||||
for (i = 0; i < pixelAmount; i++)
|
||||
if (outside[i] < 0.0)
|
||||
outside[i] = 0.0;
|
||||
|
||||
// Transform foreground (inside contour, in areas of 1's)
|
||||
for (i = 0; i < pixelAmount; i++)
|
||||
data[i] = 1 - data[i];
|
||||
computegradient(data, (int)width, (int)height, gx, gy);
|
||||
edtaa3(data, gx, gy, (int)width, (int)height, xdist, ydist, inside);
|
||||
for (i = 0; i < pixelAmount; i++)
|
||||
if (inside[i] < 0.0)
|
||||
inside[i] = 0.0;
|
||||
|
||||
// The bipolar distance field is now outside-inside
|
||||
double dist;
|
||||
/* Single channel 8-bit output (bad precision and range, but simple) */
|
||||
std::vector<uint8_t> out;
|
||||
out.resize(pixelAmount);
|
||||
for (i = 0; i < pixelAmount; i++)
|
||||
{
|
||||
dist = outside[i] - inside[i];
|
||||
dist = 128.0 - dist * 16;
|
||||
if (dist < 0) dist = 0;
|
||||
if (dist > 255) dist = 255;
|
||||
out[i] = (unsigned char)dist;
|
||||
}
|
||||
/* Dual channel 16-bit output (more complicated, but good precision and range) */
|
||||
/*unsigned char *out = (unsigned char *) malloc( pixelAmount * 3 * sizeof(unsigned char) );
|
||||
for( i=0; i< pixelAmount; i++)
|
||||
{
|
||||
dist = outside[i] - inside[i];
|
||||
dist = 128.0 - dist*16;
|
||||
if( dist < 0.0 ) dist = 0.0;
|
||||
if( dist >= 256.0 ) dist = 255.999;
|
||||
// R channel is a copy of the original grayscale image
|
||||
out[3*i] = img[i];
|
||||
// G channel is fraction
|
||||
out[3*i + 1] = (unsigned char) ( 256 - (dist - floor(dist)* 256.0 ));
|
||||
// B channel is truncated integer part
|
||||
out[3*i + 2] = (unsigned char)dist;
|
||||
}*/
|
||||
#if !FFT_SDF_TMP_VECTOR
|
||||
free(xdist);
|
||||
free(ydist);
|
||||
free(gx);
|
||||
free(gy);
|
||||
free(data);
|
||||
free(outside);
|
||||
free(inside);
|
||||
#endif
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
FontFreeType::FontFreeType(const std::string& fontName, float fontSize, LabelLayoutInfo *info)
|
||||
{
|
||||
if (!_sFTLibrary)
|
||||
{
|
||||
_sFTLibrary = std::make_shared< FontFreeTypeLibrary>();
|
||||
}
|
||||
|
||||
_fontName = fontName;
|
||||
_fontSize = fontSize;
|
||||
_info = info;
|
||||
|
||||
#if FFT_USE_SCREEN_DPI
|
||||
_dpi = Device::getDPI();
|
||||
#else
|
||||
_dpi = 72;
|
||||
#endif
|
||||
}
|
||||
|
||||
FontFreeType::~FontFreeType()
|
||||
{
|
||||
//CCLOG("~FontFreeType");
|
||||
if (_stroker) FT_Stroker_Done(_stroker);
|
||||
if (_face) FT_Done_Face(_face);
|
||||
}
|
||||
|
||||
FT_Library& FontFreeType::getFTLibrary()
|
||||
{
|
||||
return *(_sFTLibrary->get());
|
||||
}
|
||||
|
||||
bool FontFreeType::loadFont()
|
||||
{
|
||||
std::shared_ptr<Data> faceData;
|
||||
auto itr = _sFTLibrary->getFaceCache().find(_fontName);
|
||||
if (itr == _sFTLibrary->getFaceCache().end()) {
|
||||
faceData = std::make_shared<Data>(FileUtils::getInstance()->getDataFromFile(_fontName));
|
||||
_sFTLibrary->getFaceCache()[_fontName] = faceData;
|
||||
} else {
|
||||
faceData = itr->second;
|
||||
}
|
||||
|
||||
if (FT_New_Memory_Face(getFTLibrary(), faceData->getBytes(), faceData->getSize(), 0, &_face))
|
||||
{
|
||||
cocos2d::log("[error] failed to parse font %s", _fontName.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
_fontFaceData = faceData;
|
||||
|
||||
|
||||
if (FT_Select_Charmap(_face, _encoding))
|
||||
{
|
||||
int foundIndex = -1;
|
||||
for (int charmapIndex = 0; charmapIndex < _face->num_charmaps; charmapIndex++)
|
||||
{
|
||||
if (_face->charmaps[charmapIndex]->encoding != FT_ENCODING_NONE)
|
||||
{
|
||||
foundIndex = charmapIndex;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (foundIndex == -1)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
_encoding = _face->charmaps[foundIndex]->encoding;
|
||||
if (FT_Select_Charmap(_face, _encoding))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
int fontSizeInPoints = (int)(64.0f * _fontSize);
|
||||
|
||||
if (FT_Set_Char_Size(_face, fontSizeInPoints, fontSizeInPoints, _dpi, _dpi))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
_lineHeight = SCALE_BY_DPI((_face->size->metrics.ascender - _face->size->metrics.descender) >> 6);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int FontFreeType::getHorizontalKerningForChars(uint64_t a, uint64_t b) const
|
||||
{
|
||||
auto idx1 = FT_Get_Char_Index(_face, static_cast<FT_ULong>(a));
|
||||
if (!idx1)
|
||||
return 0;
|
||||
auto idx2 = FT_Get_Char_Index(_face, static_cast<FT_ULong>(b));
|
||||
if (!idx2)
|
||||
return 0;
|
||||
FT_Vector kerning;
|
||||
if (FT_Get_Kerning(_face, idx1, idx2, FT_KERNING_DEFAULT, &kerning))
|
||||
return 0;
|
||||
|
||||
return SCALE_BY_DPI(kerning.x >> 6);
|
||||
}
|
||||
|
||||
std::unique_ptr<std::vector<int>> FontFreeType::getHorizontalKerningForUTF32Text(const std::u32string& text) const
|
||||
{
|
||||
if (!_face) return nullptr;
|
||||
if (FT_HAS_KERNING(_face) == 0) return nullptr;
|
||||
|
||||
const auto letterNum = text.length();
|
||||
std::vector<int>* sizes = new std::vector<int>(letterNum, 0);
|
||||
|
||||
for (int i = 1; i < letterNum; i++)
|
||||
{
|
||||
(*sizes)[i] = SCALE_BY_DPI(getHorizontalKerningForChars(text[i - 1], text[i]));
|
||||
}
|
||||
return std::unique_ptr<std::vector<int>>(sizes);
|
||||
}
|
||||
|
||||
|
||||
int FontFreeType::getFontAscender() const
|
||||
{
|
||||
return SCALE_BY_DPI(_face->size->metrics.ascender >> 6);
|
||||
}
|
||||
|
||||
const char* FontFreeType::getFontFamily() const
|
||||
{
|
||||
if (!_face) return nullptr;
|
||||
return _face->family_name;
|
||||
}
|
||||
|
||||
std::shared_ptr<GlyphBitmap> FontFreeType::getGlyphBitmap(unsigned long ch, bool hasOutline)
|
||||
{
|
||||
return hasOutline ? getSDFGlyphBitmap(ch) : getNormalGlyphBitmap(ch);
|
||||
}
|
||||
|
||||
|
||||
std::shared_ptr<GlyphBitmap> FontFreeType::getNormalGlyphBitmap(unsigned long ch)
|
||||
{
|
||||
if (!_face) return nullptr;
|
||||
const auto load_char_flag = FT_LOAD_RENDER | FT_LOAD_NO_AUTOHINT;
|
||||
if (FT_Load_Char(_face, static_cast<FT_ULong>(ch), load_char_flag))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto& metrics = _face->glyph->metrics;
|
||||
int x = SCALE_BY_DPI(metrics.horiBearingX >> 6);
|
||||
int y = SCALE_BY_DPI(-(metrics.horiBearingY >> 6));
|
||||
int w = SCALE_BY_DPI(metrics.width >> 6);
|
||||
int h = SCALE_BY_DPI(metrics.height >> 6);
|
||||
|
||||
int adv = SCALE_BY_DPI(metrics.horiAdvance >> 6);
|
||||
|
||||
|
||||
auto& bitmap = _face->glyph->bitmap;
|
||||
int bmWidth = bitmap.width;
|
||||
int bmHeight = bitmap.rows;
|
||||
PixelMode mode = FTtoPixelModel(static_cast<FT_Pixel_Mode>(bitmap.pixel_mode));
|
||||
int size = PixelModeSize(mode) * bmWidth * bmHeight;
|
||||
std::vector<uint8_t> data((uint8_t*)bitmap.buffer, (uint8_t*)bitmap.buffer + size);
|
||||
auto* ret = new GlyphBitmap(data, bmWidth, bmHeight, Rect(x, y, w, h), adv, mode, 0);
|
||||
return std::shared_ptr<GlyphBitmap>(ret);
|
||||
}
|
||||
|
||||
std::shared_ptr<GlyphBitmap> FontFreeType::getSDFGlyphBitmap(unsigned long ch)
|
||||
{
|
||||
if (!_face) return nullptr;
|
||||
const auto load_char_flag = FT_LOAD_RENDER | FT_LOAD_NO_AUTOHINT;
|
||||
if (FT_Load_Char(_face, static_cast<FT_ULong>(ch), load_char_flag))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto& metrics = _face->glyph->metrics;
|
||||
int x = SCALE_BY_DPI(metrics.horiBearingX >> 6);
|
||||
int y = SCALE_BY_DPI(-(metrics.horiBearingY >> 6));
|
||||
int w = SCALE_BY_DPI(metrics.width >> 6);
|
||||
int h = SCALE_BY_DPI(metrics.height >> 6);
|
||||
|
||||
int adv = SCALE_BY_DPI(metrics.horiAdvance >> 6);
|
||||
|
||||
auto& bitmap = _face->glyph->bitmap;
|
||||
int bmWidth = bitmap.width;
|
||||
int bmHeight = bitmap.rows;
|
||||
PixelMode mode = FTtoPixelModel(static_cast<FT_Pixel_Mode>(bitmap.pixel_mode));
|
||||
|
||||
assert(mode == PixelMode::A8);
|
||||
|
||||
int dms = std::max(3, (int)std::max(0.2 * bmWidth, 0.2 * bmHeight));
|
||||
|
||||
// int size = PixelModeSize(mode) * bmWidth * bmHeight;
|
||||
std::vector<uint8_t> data = makeDistanceMap(bitmap.buffer, bmWidth, bmHeight, dms);
|
||||
auto* ret = new GlyphBitmap(data, bmWidth + 2 * dms, bmHeight + 2 * dms, Rect(x, y, w + 2 * dms, h + 2 * dms), adv, mode, dms);
|
||||
|
||||
return std::shared_ptr<GlyphBitmap>(ret);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
93
cocos2d-x/cocos/2d/CCFontFreetype.h
Normal file
93
cocos2d-x/cocos/2d/CCFontFreetype.h
Normal file
@@ -0,0 +1,93 @@
|
||||
/****************************************************************************
|
||||
Copyright (c) 2020 Xiamen Yaji Software Co., Ltd.
|
||||
|
||||
http://www.cocos.com
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
****************************************************************************/
|
||||
#pragma once
|
||||
|
||||
//#include "freetype2/ft2build.h"
|
||||
|
||||
#include "freetype/ft2build.h"
|
||||
|
||||
#include FT_FREETYPE_H
|
||||
#include FT_STROKER_H
|
||||
|
||||
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "CCTTFTypes.h"
|
||||
#include "base/CCData.h"
|
||||
|
||||
#include "base/ccConfig.h"
|
||||
#if CC_ENABLE_TTF_LABEL_RENDERER
|
||||
namespace cocos2d {
|
||||
|
||||
class FontFreeTypeLibrary;
|
||||
|
||||
struct LabelLayoutInfo;
|
||||
|
||||
class FontFreeType
|
||||
{
|
||||
public:
|
||||
|
||||
//const static int DistanceMapSpread;
|
||||
|
||||
FontFreeType(const std::string& fontName, float fontSize, LabelLayoutInfo* info);
|
||||
virtual ~FontFreeType();
|
||||
|
||||
FT_Library& getFTLibrary();
|
||||
|
||||
bool loadFont();
|
||||
|
||||
int getHorizontalKerningForChars(uint64_t a, uint64_t b) const;
|
||||
std::unique_ptr<std::vector<int>> getHorizontalKerningForUTF32Text(const std::u32string &text) const;
|
||||
|
||||
int getFontAscender() const;
|
||||
const char* getFontFamily() const;
|
||||
|
||||
|
||||
std::shared_ptr<GlyphBitmap> getGlyphBitmap(unsigned long ch, bool hasOutline = false);
|
||||
|
||||
private:
|
||||
|
||||
std::shared_ptr<GlyphBitmap> getNormalGlyphBitmap(unsigned long ch);
|
||||
std::shared_ptr<GlyphBitmap> getSDFGlyphBitmap(unsigned long ch);
|
||||
|
||||
//weak reference
|
||||
LabelLayoutInfo *_info = nullptr;
|
||||
float _fontSize = 0.0f;
|
||||
float _lineHeight = 0.0f;
|
||||
std::string _fontName;
|
||||
|
||||
std::shared_ptr<Data> _fontFaceData;
|
||||
FT_Face _face = { 0 };
|
||||
|
||||
FT_Stroker _stroker = { 0 };
|
||||
FT_Encoding _encoding = FT_ENCODING_UNICODE;
|
||||
|
||||
int _dpi = 72;
|
||||
};
|
||||
|
||||
}
|
||||
#endif
|
920
cocos2d-x/cocos/2d/CCLabelLayout.cpp
Normal file
920
cocos2d-x/cocos/2d/CCLabelLayout.cpp
Normal file
@@ -0,0 +1,920 @@
|
||||
/****************************************************************************
|
||||
Copyright (c) 2020 Xiamen Yaji Software Co., Ltd.
|
||||
|
||||
http://www.cocos.com
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
****************************************************************************/
|
||||
#include "2d/CCLabelLayout.h"
|
||||
#include "2d/CCTTFLabelAtlasCache.h"
|
||||
#include "2d/CCTTFLabelRenderer.h"
|
||||
|
||||
#include "base/ccUTF8.h"
|
||||
#include "MiddlewareMacro.h"
|
||||
#include "renderer/renderer/Pass.h"
|
||||
#include "renderer/renderer/Effect.h"
|
||||
#include "renderer/renderer/Technique.h"
|
||||
#include "renderer/scene/assembler/CustomAssembler.hpp"
|
||||
#include "cocos/editor-support/MeshBuffer.h"
|
||||
#include "cocos/editor-support/IOBuffer.h"
|
||||
#include "cocos/editor-support/middleware-adapter.h"
|
||||
#include "cocos/editor-support/MiddlewareManager.h"
|
||||
|
||||
#include "renderer/gfx/DeviceGraphics.h"
|
||||
#include "renderer/gfx/Texture2D.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <algorithm>
|
||||
#include <fstream>
|
||||
#include <unordered_map>
|
||||
#include <chrono>
|
||||
|
||||
#include "base/ccConfig.h"
|
||||
|
||||
#if CC_ENABLE_TTF_LABEL_RENDERER
|
||||
|
||||
#define INIT_VERTEX_SIZE 32
|
||||
|
||||
namespace {
|
||||
const std::string textureKey = "texture";
|
||||
const std::string shadowColor = "shadowColor";
|
||||
const std::string outlineSizeKey = "outlineSize";
|
||||
const std::string outlineColorKey= "outlineColor";
|
||||
}
|
||||
|
||||
using namespace cocos2d::renderer;
|
||||
using namespace cocos2d::middleware;
|
||||
|
||||
namespace cocos2d {
|
||||
|
||||
namespace {
|
||||
void recalculateUV(const Rect &oldRect, const Rect &oldUV, const Rect &newRect, Rect &newUV)
|
||||
{
|
||||
auto offsetOrigin = newRect.origin - oldRect.origin;
|
||||
float uvWidth = newRect.size.width / oldRect.size.width * oldUV.size.width;
|
||||
float uvHeight = newRect.size.height / oldRect.size.height * oldUV.size.height;
|
||||
float uvOriginX = offsetOrigin.x / oldRect.size.width * oldUV.size.width + oldUV.origin.x;
|
||||
float uvOriginY = (oldUV.size.height - uvHeight - offsetOrigin.y / oldRect.size.height * oldUV.size.height) + oldUV.origin.y;
|
||||
newUV.setRect(uvOriginX, uvOriginY, uvWidth, uvHeight);
|
||||
}
|
||||
|
||||
inline void find_2nd_3rd(float min1, float max1, float min2, float max2, float &second, float &third)
|
||||
{
|
||||
assert(max1 >= min1 && max2 >= min2);
|
||||
|
||||
if (max1 < max2)
|
||||
{
|
||||
second = min1 < min2 ? min2 : min1;
|
||||
third = max1;
|
||||
}
|
||||
else
|
||||
{
|
||||
second = min2 < min1 ? min1 : min2;
|
||||
third = max2;
|
||||
}
|
||||
}
|
||||
|
||||
void rectUnion(const Rect &a, const Rect &b, Rect &out)
|
||||
{
|
||||
float xMax, yMax;
|
||||
find_2nd_3rd(a.getMinX(), a.getMaxX(), b.getMinX(), b.getMaxX(), out.origin.x, xMax);
|
||||
find_2nd_3rd(a.getMinY(), a.getMaxY(), b.getMinY(), b.getMaxY(), out.origin.y, yMax);
|
||||
out.size.setSize(xMax - out.origin.x, yMax - out.origin.y);
|
||||
}
|
||||
|
||||
void subRect(const Rect &rect, const Rect &uv, float offsetX, float width, Rect &out1, Rect &out2)
|
||||
{
|
||||
out1.origin.set(offsetX + rect.origin.x, rect.origin.y);
|
||||
out1.size.setSize(width, rect.size.height);
|
||||
out2.origin.set(uv.origin.x + uv.size.width * offsetX / rect.size.width, uv.origin.y);
|
||||
out2.size.setSize(uv.size.width * width / rect.size.width, uv.size.height);
|
||||
}
|
||||
|
||||
void splitRectIntoThreeParts(int texId, const Rect &defRect, const Rect &targetRect, const Rect &uv, cocos2d::TextRowSpace &space)
|
||||
{
|
||||
constexpr float LEFT = 0.3f;
|
||||
constexpr float MIDDLE = 0.4f;
|
||||
constexpr float RIGHT = 0.3f;
|
||||
Rect out1, out2;
|
||||
float cursorX = 0.0f;
|
||||
//part left
|
||||
subRect(targetRect, uv, 0.0f, targetRect.size.width * LEFT, out1, out2);
|
||||
cursorX = defRect.size.width *LEFT;
|
||||
out1.size.width = cursorX;
|
||||
space.fillRect(texId, out1, out2);
|
||||
//part middle
|
||||
subRect(targetRect, uv, targetRect.size.width * LEFT, targetRect.size.width * MIDDLE, out1, out2);
|
||||
out1.origin.x = targetRect.origin.x + cursorX;
|
||||
out1.size.width = targetRect.size.width - defRect.size.width * (LEFT + RIGHT);
|
||||
cursorX = targetRect.size.width - defRect.size.width * RIGHT;
|
||||
space.fillRect(texId, out1, out2);
|
||||
//part right
|
||||
subRect(targetRect, uv, targetRect.size.width * (LEFT + MIDDLE) , targetRect.size.width *RIGHT, out1, out2);
|
||||
out1.origin.x = targetRect.origin.x + cursorX;
|
||||
out1.size.width = defRect.size.width * RIGHT;
|
||||
space.fillRect(texId, out1, out2);
|
||||
}
|
||||
}
|
||||
|
||||
class TextRenderGroupItem {
|
||||
public:
|
||||
|
||||
enum DirtyFlag {
|
||||
VERTEX_DIRTY = 1,
|
||||
INDEXES_DIRTY = 2
|
||||
};
|
||||
|
||||
TextRenderGroupItem(renderer::Texture *);
|
||||
virtual ~TextRenderGroupItem();
|
||||
|
||||
void reset();
|
||||
|
||||
void addRect(const Rect &vert, const Rect &uv, const Color4B &color, bool italics);
|
||||
|
||||
void upload();
|
||||
|
||||
inline int getRectSize() const { return _rectSize; }
|
||||
|
||||
middleware::MeshBuffer* getBuffer() { return _buffer; }
|
||||
|
||||
private:
|
||||
|
||||
void addIndexes();
|
||||
|
||||
middleware::MeshBuffer * _buffer = nullptr;
|
||||
renderer::Texture *_texture = nullptr;
|
||||
int _rectSize = 0;
|
||||
int _indexSize = 0;
|
||||
|
||||
int _dirtyFlags = -1;
|
||||
};
|
||||
|
||||
|
||||
class TextRenderGroup {
|
||||
public:
|
||||
|
||||
void reset();
|
||||
void addRect(renderer::Texture*, const Rect &vert, const Rect &uv, const Color4B &color, bool italics);
|
||||
|
||||
int fill(CustomAssembler *assembler, int, LabelLayout *layout, EffectVariant *);
|
||||
|
||||
private:
|
||||
std::unordered_map<renderer::Texture*, std::shared_ptr<TextRenderGroupItem>> _buffers;
|
||||
};
|
||||
|
||||
|
||||
TextRenderGroupItem::TextRenderGroupItem(renderer::Texture* tex)
|
||||
{
|
||||
_texture = tex;
|
||||
_buffer = new middleware::MeshBuffer(VF_XYUVC, sizeof(uint16_t) * 6 * INIT_VERTEX_SIZE, INIT_VERTEX_SIZE * 4);
|
||||
}
|
||||
TextRenderGroupItem::~TextRenderGroupItem()
|
||||
{
|
||||
delete _buffer;
|
||||
}
|
||||
|
||||
void TextRenderGroupItem::reset() {
|
||||
|
||||
_buffer->reset();
|
||||
_rectSize = 0;
|
||||
_dirtyFlags = -1;
|
||||
}
|
||||
|
||||
void TextRenderGroupItem::addRect(const Rect &rect, const Rect &uv, const Color4B &color, bool italics)
|
||||
{
|
||||
middleware::IOBuffer &vertexBuffer = _buffer->getVB();
|
||||
const int minSize = sizeof(V2F_T2F_C4B) * 4;
|
||||
vertexBuffer.checkSpace(minSize << 1, true);
|
||||
|
||||
V2F_T2F_C4B *verts = ((V2F_T2F_C4B*)vertexBuffer.getBuffer()) + 4 * _rectSize;
|
||||
|
||||
float height = rect.size.height;
|
||||
float offsetX = 0.0f;
|
||||
const float factor = 0.21255f;
|
||||
if (italics) {
|
||||
offsetX = height * factor;
|
||||
}
|
||||
|
||||
verts[0].vertex = { rect.getMinX() + offsetX, rect.getMaxY() };
|
||||
verts[1].vertex = { rect.getMaxX() + offsetX, rect.getMaxY() };
|
||||
verts[2].vertex = { rect.getMinX() - offsetX, rect.getMinY() };
|
||||
verts[3].vertex = { rect.getMaxX() - offsetX, rect.getMinY() };
|
||||
|
||||
verts[0].texCoord = { uv.getMinX(), uv.getMinY() };
|
||||
verts[1].texCoord = { uv.getMaxX(), uv.getMinY() };
|
||||
verts[2].texCoord = { uv.getMinX(), uv.getMaxY() };
|
||||
verts[3].texCoord = { uv.getMaxX(), uv.getMaxY() };
|
||||
|
||||
verts[0].color = color;
|
||||
verts[1].color = color;
|
||||
verts[2].color = color;
|
||||
verts[3].color = color;
|
||||
|
||||
vertexBuffer.move(4 * sizeof(V2F_T2F_C4B));
|
||||
_rectSize += 1;
|
||||
|
||||
_dirtyFlags |= DirtyFlag::VERTEX_DIRTY;
|
||||
}
|
||||
|
||||
void TextRenderGroupItem::addIndexes()
|
||||
{
|
||||
middleware::IOBuffer &indexBuffer = _buffer->getIB();
|
||||
int indexSize = sizeof(uint16_t) * 6 * (_rectSize - _indexSize);
|
||||
indexBuffer.checkSpace(indexSize << 1, true);
|
||||
uint16_t *indices = (uint16_t*)indexBuffer.getBuffer();
|
||||
for (int i = _indexSize; i < _rectSize; i += 1)
|
||||
{
|
||||
indices[i * 6 + 0] = 0 + 4 * i;
|
||||
indices[i * 6 + 1] = 1 + 4 * i;
|
||||
indices[i * 6 + 2] = 2 + 4 * i;
|
||||
indices[i * 6 + 3] = 1 + 4 * i;
|
||||
indices[i * 6 + 4] = 3 + 4 * i;
|
||||
indices[i * 6 + 5] = 2 + 4 * i;
|
||||
}
|
||||
|
||||
indexBuffer.move(sizeof(uint16_t) * 6 * (_rectSize - _indexSize));
|
||||
if (_indexSize < _rectSize)
|
||||
{
|
||||
_indexSize = _rectSize;
|
||||
_dirtyFlags |= DirtyFlag::INDEXES_DIRTY;
|
||||
}
|
||||
}
|
||||
|
||||
void TextRenderGroupItem::upload()
|
||||
{
|
||||
addIndexes();
|
||||
|
||||
middleware::IOBuffer &vertexBuffer = _buffer->getVB();
|
||||
middleware::IOBuffer &indexBuffer = _buffer->getIB();
|
||||
|
||||
vertexBuffer.move(_rectSize * 4 * sizeof(V2F_T2F_C4B));
|
||||
indexBuffer.move(_rectSize * 6 * sizeof(uint16_t));
|
||||
|
||||
if (_dirtyFlags | DirtyFlag::INDEXES_DIRTY)
|
||||
{
|
||||
_buffer->uploadIB();
|
||||
}
|
||||
|
||||
if (_dirtyFlags | DirtyFlag::VERTEX_DIRTY)
|
||||
{
|
||||
_buffer->uploadVB();
|
||||
}
|
||||
|
||||
_dirtyFlags = 0;
|
||||
}
|
||||
|
||||
void TextRenderGroup::addRect(renderer::Texture* tex, const Rect &vert, const Rect &uv, const Color4B &color, bool italics)
|
||||
{
|
||||
auto &itemPtr = _buffers[tex];
|
||||
if (!itemPtr) {
|
||||
itemPtr = std::make_shared<TextRenderGroupItem>(tex);
|
||||
}
|
||||
itemPtr->addRect(vert, uv, color, italics);
|
||||
}
|
||||
|
||||
|
||||
void TextRenderGroup::reset()
|
||||
{
|
||||
for (auto &it : _buffers)
|
||||
{
|
||||
it.second->reset();
|
||||
}
|
||||
_buffers.clear();
|
||||
}
|
||||
|
||||
int TextRenderGroup::fill(CustomAssembler *assembler, int startIndex, LabelLayout *layout, EffectVariant *templateEffect)
|
||||
{
|
||||
int groupIndex = startIndex;
|
||||
for (auto &it : _buffers)
|
||||
{
|
||||
auto &item = it.second;
|
||||
if (item->getRectSize() > 0)
|
||||
{
|
||||
item->upload();
|
||||
|
||||
assembler->updateIABuffer(groupIndex, item->getBuffer()->getGLVB(), item->getBuffer()->getGLIB());
|
||||
|
||||
assembler->updateIARange(groupIndex, 0, item->getRectSize() * 6);
|
||||
|
||||
auto *effect = assembler->getEffect(groupIndex);
|
||||
|
||||
if (!effect && templateEffect) {
|
||||
effect = new cocos2d::renderer::EffectVariant();
|
||||
effect->autorelease();
|
||||
effect->copy(templateEffect);
|
||||
assembler->updateEffect(groupIndex, effect);
|
||||
}
|
||||
|
||||
if(effect->getPasses().at(0)->getDefinesHash() != templateEffect->getPasses().at(0)->getDefinesHash()){
|
||||
effect->copy(templateEffect);
|
||||
}
|
||||
|
||||
if (effect) {
|
||||
effect->setProperty(textureKey, it.first);
|
||||
}
|
||||
|
||||
groupIndex += 1;
|
||||
}
|
||||
}
|
||||
return groupIndex;
|
||||
}
|
||||
|
||||
|
||||
|
||||
struct TextSpaceArray {
|
||||
|
||||
void addSpace(TextRowSpace& space)
|
||||
{
|
||||
if (space.size() > 0)
|
||||
{
|
||||
_maxWidth = std::max(_maxWidth, space.getWidth());
|
||||
}
|
||||
_data.emplace_back(std::move(space));
|
||||
}
|
||||
|
||||
float _maxWidth = FLT_MIN;
|
||||
std::vector<TextRowSpace> _data;
|
||||
};
|
||||
|
||||
|
||||
TextRowSpace::GlyphBlock & TextRowSpace::operator[](size_t i)
|
||||
{
|
||||
return _data[i];
|
||||
}
|
||||
|
||||
|
||||
TextRowSpace::GlyphBlock & TextRowSpace::appendBlock()
|
||||
{
|
||||
_data.resize(_data.size() + 1);
|
||||
return _data[_data.size() - 1];
|
||||
}
|
||||
|
||||
TextRowSpace::TextRowSpace(TextRowSpace &&other)
|
||||
{
|
||||
_left = other._left;
|
||||
_bottom = other._bottom;
|
||||
_right = other._right;
|
||||
_top = other._top;
|
||||
_x = other._x;
|
||||
_y = other._y;
|
||||
_data = std::move(other._data);
|
||||
_ignored = other._ignored;
|
||||
other.reset();
|
||||
}
|
||||
|
||||
|
||||
void TextRowSpace::reset()
|
||||
{
|
||||
_left = FLT_MAX;
|
||||
_bottom = FLT_MAX;
|
||||
_right = FLT_MIN;
|
||||
_top = FLT_MIN;
|
||||
_x = 0.0f;
|
||||
_y = 0.0f;
|
||||
_data.clear();
|
||||
_ignored = false;
|
||||
}
|
||||
|
||||
void TextRowSpace::fillRect(int texId, Rect &rect, Rect &uv)
|
||||
{
|
||||
GlyphBlock &block = appendBlock();
|
||||
_left = std::min(_left, rect.getMinX());
|
||||
_right = std::max(_right, rect.getMaxX());
|
||||
_bottom = std::min(_bottom, rect.getMinY());
|
||||
_top = std::max(_top, rect.getMaxY());
|
||||
block.area = rect;
|
||||
block.uv = uv;
|
||||
block.ignored = false;
|
||||
block.texId = texId;
|
||||
}
|
||||
|
||||
void TextRowSpace::translate(float x, float y)
|
||||
{
|
||||
_x += x;
|
||||
_y += y;
|
||||
}
|
||||
|
||||
Vec2 TextRowSpace::center() const
|
||||
{
|
||||
Vec2 ret((_left + _right) / 2.0f, (_bottom + _top) / 2.0f);
|
||||
return ret;
|
||||
}
|
||||
|
||||
float TextRowSpace::getExtentedWidth(float left, float right) const
|
||||
{
|
||||
if (_data.size() == 0)
|
||||
{
|
||||
return right - left;
|
||||
}
|
||||
return std::max(_right, right) - std::min(_left, left);
|
||||
}
|
||||
|
||||
|
||||
void TextRowSpace::clip(const Rect &rect)
|
||||
{
|
||||
Rect contentRect(_x + _left, _y + _bottom, _right - _left, _top - _bottom);
|
||||
|
||||
if (!rect.intersectsRect(contentRect)) {
|
||||
_ignored = true;
|
||||
return;
|
||||
}
|
||||
|
||||
const auto size = _data.size();
|
||||
const auto offset = getOffset();
|
||||
Rect letter;
|
||||
Rect unionRect;
|
||||
for (int i = 0; i < size; i++)
|
||||
{
|
||||
letter.size = _data[i].area.size;
|
||||
letter.origin = offset + _data[i].area.origin;
|
||||
|
||||
if (!rect.intersectsRect(letter))
|
||||
{
|
||||
_data[i].ignored = true;
|
||||
}
|
||||
else if (rect.containsPoint(letter.origin) && rect.containsPoint(Vec2(letter.getMaxX(), letter.getMaxY())))
|
||||
{
|
||||
_data[i].area.origin = letter.origin;
|
||||
}
|
||||
else
|
||||
{
|
||||
//Rect unionRect = letter.unionWithRect(rect); // incorrect result
|
||||
rectUnion(letter, rect, unionRect);
|
||||
_data[i].area = unionRect;
|
||||
recalculateUV(letter, _data[i].uv, unionRect, _data[i].uv);
|
||||
}
|
||||
|
||||
}
|
||||
//ignore translate
|
||||
_x = 0.0f;
|
||||
_y = 0.0f;
|
||||
}
|
||||
|
||||
Rect TextRowSpace::asRect() const {
|
||||
return Rect(_left + _x, _bottom + _y, _right - _left, _top - _bottom);
|
||||
}
|
||||
|
||||
bool LabelLayout::init(const std::string& font, const std::string& text, float fontSize, float retinaFontSize, LabelLayoutInfo *info)
|
||||
{
|
||||
_inited = true;
|
||||
_layoutInfo = info;
|
||||
_retinaFontSize = std::max(fontSize, retinaFontSize);
|
||||
_fontAtlas = TTFLabelAtlasCache::getInstance()->load(font, _retinaFontSize, info);
|
||||
if(!_fontAtlas) {
|
||||
return false;
|
||||
}
|
||||
_fontScale = fontSize / _fontAtlas->getFontSize();
|
||||
_groups = std::make_shared<TextRenderGroup>();
|
||||
if (info->shadowBlur >= 0)
|
||||
{
|
||||
_shadowGroups = std::make_shared<TextRenderGroup>();
|
||||
}
|
||||
_string = text;
|
||||
_font = font;
|
||||
_fontSize = fontSize;
|
||||
cocos2d::StringUtils::UTF8ToUTF32(text.c_str(), _u32string);
|
||||
|
||||
_textSpace.clear();
|
||||
|
||||
updateContent();
|
||||
return true;
|
||||
}
|
||||
|
||||
LabelLayout::~LabelLayout()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void LabelLayout::setString(const std::string &txt, bool forceUpdate)
|
||||
{
|
||||
if (_string != txt) {
|
||||
_string = txt;
|
||||
cocos2d::StringUtils::UTF8ToUTF32(txt.c_str(), _u32string);
|
||||
updateContent();
|
||||
}
|
||||
else if (forceUpdate)
|
||||
{
|
||||
updateContent();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
bool LabelLayout::updateContent()
|
||||
{
|
||||
if(!_fontAtlas)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
auto *atlas = _fontAtlas->getFontAtlas();
|
||||
auto *ttf = _fontAtlas->getTTF();
|
||||
|
||||
FontLetterDefinition* letterDef = nullptr;
|
||||
Rect row;
|
||||
|
||||
|
||||
const float LineHeight = _layoutInfo->lineHeight;
|
||||
const float SpaceX = _layoutInfo->spaceX;
|
||||
const LabelOverflow OverFlow = _layoutInfo->overflow;
|
||||
const bool ClampAndWrap = _layoutInfo->wrap && OverFlow == LabelOverflow::CLAMP;
|
||||
const bool ResizeHeight = OverFlow == LabelOverflow::RESIZE_HEIGHT;
|
||||
const int ContentWidth = _layoutInfo->width;
|
||||
const int ContentHeight = _layoutInfo->height;
|
||||
const LabelAlignmentH HAlign = _layoutInfo->halign;
|
||||
const LabelAlignmentV VAlign = _layoutInfo->valign;
|
||||
const float AnchorX = _layoutInfo->anchorX;
|
||||
const float AnchorY = _layoutInfo->anchorY;
|
||||
const bool Underline = _layoutInfo->underline;
|
||||
|
||||
//LabelCursor cursor;
|
||||
float cursorX = 0;
|
||||
|
||||
TextSpaceArray textSpaces;
|
||||
TextRowSpace rowSpace;
|
||||
|
||||
Rect letterRect;
|
||||
|
||||
std::unique_ptr<std::vector<int> > kerning = nullptr;
|
||||
if (_enableKerning) {
|
||||
kerning = ttf->getHorizontalKerningForUTF32Text(_u32string);
|
||||
}
|
||||
|
||||
|
||||
atlas->prepareLetters(_u32string, ttf);
|
||||
|
||||
for (int i = 0; i < _u32string.size(); i++)
|
||||
{
|
||||
auto ch = _u32string[i];
|
||||
|
||||
if (ch == u'\r')
|
||||
{
|
||||
cursorX = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (ch == u'\n')
|
||||
{
|
||||
textSpaces.addSpace(rowSpace);
|
||||
cursorX = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
letterDef = atlas->getOrLoad(ch, ttf);
|
||||
if (!letterDef) continue;
|
||||
|
||||
letterRect = letterDef->rect;
|
||||
letterRect.origin *= _fontScale;
|
||||
letterRect.size = Size(letterRect.size.width * _fontScale, letterRect.size.height * _fontScale);
|
||||
|
||||
if (_enableKerning && kerning) {
|
||||
auto const kerningX = kerning->at(i) * _fontScale;
|
||||
cursorX += kerningX;
|
||||
}
|
||||
|
||||
float left = cursorX - letterDef->outline * _fontScale + letterRect.getMinX();
|
||||
float bottom = letterDef->outline * _fontScale - letterRect.getMaxY();
|
||||
|
||||
if ((ResizeHeight || ClampAndWrap) && rowSpace.getExtentedWidth(left, left + letterRect.size.width) > ContentWidth)
|
||||
{
|
||||
// RESIZE_HEIGHT or (CLAMP & Enable Wrap)
|
||||
textSpaces.addSpace(rowSpace);
|
||||
cursorX = 0;
|
||||
left = cursorX - letterDef->outline * _fontScale + letterRect.getMinX();
|
||||
}
|
||||
|
||||
float width = letterRect.size.width;
|
||||
float height = letterRect.size.height;
|
||||
|
||||
Rect letterRectInline(left, bottom, width, height);
|
||||
Rect letterTexture(letterDef->texX, letterDef->texY, letterDef->texWidth, letterDef->texHeight);
|
||||
|
||||
rowSpace.fillRect(letterDef->textureID, letterRectInline, letterTexture);
|
||||
|
||||
cursorX += SpaceX + letterDef->xAdvance * _fontScale;
|
||||
}
|
||||
|
||||
textSpaces.addSpace(rowSpace);
|
||||
|
||||
auto& list = textSpaces._data;
|
||||
|
||||
//add underline
|
||||
if(Underline){
|
||||
auto underline = atlas->getOrLoad('_', ttf);
|
||||
|
||||
for (auto &space : list) {
|
||||
Rect letterRect = underline->rect;
|
||||
letterRect.origin *= _fontScale;
|
||||
letterRect.size = Size(letterRect.size.width * _fontScale, letterRect.size.height * _fontScale);
|
||||
float bottom = underline->outline * _fontScale - letterRect.getMaxY();
|
||||
float left = -letterDef->outline * _fontScale + space.getLeft();
|
||||
|
||||
Rect letterRectInline(left, bottom, space.getWidth(), letterRect.size.height);
|
||||
Rect letterTexture(underline->texX, underline->texY, underline->texWidth, underline->texHeight);
|
||||
splitRectIntoThreeParts(underline->textureID, letterRect, letterRectInline, letterTexture, space);
|
||||
}
|
||||
}
|
||||
|
||||
Vec2 newCenter;
|
||||
Vec2 delta;
|
||||
// default value in LabelOverflow::None mode
|
||||
float maxLineWidth = textSpaces._maxWidth;
|
||||
const float TotalTextHeight = textSpaces._data.size() * LineHeight;
|
||||
float topMost = (TotalTextHeight - LineHeight) * (1.0f - AnchorY);
|
||||
float leftMost = - AnchorX * maxLineWidth;
|
||||
float rightMost = (1.0f - AnchorX) * maxLineWidth;
|
||||
float centerX = (0.5f - AnchorX) * ContentWidth;
|
||||
|
||||
Rect textArea(0, 0, 0, 0);
|
||||
|
||||
for (int i = 0; i < list.size(); i++)
|
||||
{
|
||||
auto& s = list[i];
|
||||
if (!s.validate())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
if (HAlign == LabelAlignmentH::CENTER)
|
||||
{
|
||||
newCenter.set(centerX, topMost - i * LineHeight);
|
||||
}
|
||||
else if (HAlign == LabelAlignmentH::LEFT)
|
||||
{
|
||||
newCenter.set(leftMost + s.getWidth() * 0.5, topMost - i * LineHeight);
|
||||
}
|
||||
else // right
|
||||
{
|
||||
newCenter.set(rightMost - s.getWidth() * 0.5f, topMost - i * LineHeight);
|
||||
}
|
||||
delta = newCenter - s.center();
|
||||
|
||||
s.translate(delta.x, delta.y);
|
||||
|
||||
textArea.merge(s.asRect());
|
||||
|
||||
}
|
||||
|
||||
//no resize
|
||||
if (OverFlow == LabelOverflow::NONE || OverFlow == LabelOverflow::CLAMP || OverFlow == LabelOverflow::RESIZE_HEIGHT)
|
||||
{
|
||||
Vec2 centerArea(textArea.origin.x + textArea.size.width / 2.0, textArea.origin.y + textArea.size.height / 2.0);
|
||||
float tPosX, tPosY;
|
||||
|
||||
float halfAreaWidth = textArea.size.width * 0.5f;
|
||||
float halfAreaHeight = textArea.size.height * 0.5f;
|
||||
float contentWidth = ContentWidth;
|
||||
float contentHeight = ContentHeight;
|
||||
if (OverFlow == LabelOverflow::NONE)
|
||||
{
|
||||
contentWidth = textArea.size.width;
|
||||
contentHeight = textArea.size.height;
|
||||
}
|
||||
|
||||
switch (HAlign)
|
||||
{
|
||||
case LabelAlignmentH::LEFT:
|
||||
tPosX = halfAreaWidth - AnchorX * contentWidth;
|
||||
break;
|
||||
case LabelAlignmentH::CENTER:
|
||||
tPosX = (0.5f - AnchorX) * contentWidth;
|
||||
break;
|
||||
case LabelAlignmentH::RIGHT:
|
||||
tPosX = (1.0f - AnchorX) * contentWidth - halfAreaWidth;
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
|
||||
switch (VAlign)
|
||||
{
|
||||
case LabelAlignmentV::TOP:
|
||||
tPosY = (1.0f - AnchorY) * contentHeight - halfAreaHeight;
|
||||
break;
|
||||
case LabelAlignmentV::CENTER:
|
||||
tPosY = (0.5f - AnchorY) * ContentHeight;
|
||||
break;
|
||||
case LabelAlignmentV::BOTTOM:
|
||||
tPosY = halfAreaHeight - AnchorY * contentHeight;
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
|
||||
delta = Vec2(tPosX, tPosY) - centerArea;
|
||||
|
||||
for (auto &textSpace : textSpaces._data) {
|
||||
textSpace.translate(delta.x, delta.y);
|
||||
}
|
||||
|
||||
if (OverFlow == LabelOverflow::CLAMP)
|
||||
{
|
||||
// clipping
|
||||
Rect displayRegion(-ContentWidth * AnchorX, -ContentHeight * AnchorY, ContentWidth, ContentHeight);
|
||||
for (auto &textSpace : textSpaces._data) {
|
||||
textSpace.clip(displayRegion);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
else if (OverFlow == LabelOverflow::SHRINK)
|
||||
{
|
||||
float scale = std::min(ContentWidth / textArea.size.width, ContentHeight / textArea.size.height);
|
||||
scale = std::min(1.0f, scale);
|
||||
_scale = scale;
|
||||
Vec2 centerArea(textArea.origin.x + textArea.size.width * 0.5f, textArea.origin.y + textArea.size.height * 0.5f);
|
||||
float tPosXScale, tPosYScale;
|
||||
|
||||
float halfTextWidth = textArea.size.width * 0.5f * _scale;
|
||||
float halfTextHeight = textArea.size.height * 0.5f * _scale;
|
||||
|
||||
switch (HAlign)
|
||||
{
|
||||
case LabelAlignmentH::LEFT:
|
||||
tPosXScale = halfTextWidth - AnchorX * ContentWidth;
|
||||
break;
|
||||
case LabelAlignmentH::CENTER:
|
||||
tPosXScale = (0.5f - AnchorX) * ContentWidth;
|
||||
break;
|
||||
case LabelAlignmentH::RIGHT:
|
||||
tPosXScale = (1.0f - AnchorX) * ContentWidth - halfTextWidth;
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
|
||||
switch (VAlign)
|
||||
{
|
||||
case LabelAlignmentV::TOP:
|
||||
tPosYScale = (1.0f - AnchorY) * ContentHeight - halfTextHeight;
|
||||
break;
|
||||
case LabelAlignmentV::CENTER:
|
||||
tPosYScale = (0.5f - AnchorY) * ContentHeight;
|
||||
break;
|
||||
case LabelAlignmentV::BOTTOM:
|
||||
tPosYScale = halfTextHeight -AnchorY * ContentHeight;
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
delta = Vec2(tPosXScale/ _scale, tPosYScale/_scale) - centerArea;
|
||||
for (auto &textSpace : textSpaces._data) {
|
||||
textSpace.translate(delta.x, delta.y);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
se::Object *comp = _renderer->getJsComponent();
|
||||
if ((OverFlow == LabelOverflow::NONE || OverFlow == LabelOverflow::RESIZE_HEIGHT) && comp) {
|
||||
se::Value funcVal;
|
||||
se::Value nodeVal;
|
||||
if(comp->getProperty("node", &nodeVal) && nodeVal.isObject() &&
|
||||
nodeVal.toObject()->getProperty("setContentSize", &funcVal)) {
|
||||
se::ValueArray args;
|
||||
args.push_back(se::Value( OverFlow == LabelOverflow::RESIZE_HEIGHT ? ContentWidth: maxLineWidth));
|
||||
args.push_back(se::Value(TotalTextHeight));
|
||||
funcVal.toObject()->call(args, nodeVal.toObject());
|
||||
}
|
||||
}
|
||||
|
||||
_textSpace = std::move(textSpaces._data);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void LabelLayout::fillAssembler(renderer::CustomAssembler *assembler, EffectVariant *templateEffect)
|
||||
{
|
||||
assembler->reset();
|
||||
if(!_groups) {
|
||||
return;
|
||||
}
|
||||
_groups->reset();
|
||||
int groupIndex = 0;
|
||||
if (_textSpace.empty())
|
||||
return;
|
||||
|
||||
if (_shadowGroups)
|
||||
{
|
||||
_shadowGroups->reset();
|
||||
}
|
||||
|
||||
assembler->setUseModel(true);
|
||||
|
||||
auto *fontAtals = _fontAtlas->getFontAtlas();
|
||||
|
||||
Color4B textColor = _layoutInfo->color;
|
||||
|
||||
// apply transform to all rectangles
|
||||
Rect rect;
|
||||
float scale = _scale;
|
||||
|
||||
const bool Italics = _layoutInfo->italic;
|
||||
|
||||
if (_shadowGroups && _layoutInfo->shadowBlur >= 0)
|
||||
{
|
||||
Vec2 offset(_layoutInfo->shadowX, _layoutInfo->shadowY);
|
||||
Color4B shadowColor = _layoutInfo->shadowColor;
|
||||
for (auto &textSpace : _textSpace) {
|
||||
|
||||
if (textSpace.isIgnored()) continue;
|
||||
|
||||
Vec2 shadowOffset = textSpace.getOffset();
|
||||
auto size = textSpace.size();
|
||||
for (int i = 0; i < size; i++)
|
||||
{
|
||||
TextRowSpace::GlyphBlock item = textSpace[i];
|
||||
if (textSpace.isIgnored(i)) continue;
|
||||
|
||||
item.area.size = item.area.size * scale;
|
||||
item.area.origin = (offset + item.area.origin + shadowOffset) * scale;
|
||||
|
||||
_shadowGroups->addRect(fontAtals->frameAt(item.texId).getTexture(), item.area, item.uv, shadowColor, Italics);
|
||||
}
|
||||
}
|
||||
|
||||
groupIndex = _shadowGroups->fill(assembler, groupIndex, this, templateEffect);
|
||||
|
||||
Technique::Parameter outlineSizeP(outlineSizeKey, Technique::Parameter::Type::FLOAT, (float*)&(_layoutInfo->outlineSize));
|
||||
if (_layoutInfo->outlineSize > 0.0f)
|
||||
{
|
||||
// use shadow color to replace outline color
|
||||
Color4F outlineColor(_layoutInfo->shadowColor);
|
||||
Technique::Parameter outlineColorP(outlineColorKey, Technique::Parameter::Type::COLOR4, (float*)&outlineColor);
|
||||
for (auto i = 0; i < groupIndex; i++)
|
||||
{
|
||||
auto *e = assembler->getEffect(i);
|
||||
e->setProperty(outlineColorKey, outlineColorP);
|
||||
e->setProperty(outlineSizeKey, outlineSizeP);
|
||||
}
|
||||
}
|
||||
else {
|
||||
for (auto i = 0; i < groupIndex; i++)
|
||||
{
|
||||
auto *e = assembler->getEffect(i);
|
||||
e->setProperty(outlineSizeKey, outlineSizeP);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
for (auto &textSpace : _textSpace) {
|
||||
|
||||
if (textSpace.isIgnored()) continue;
|
||||
|
||||
Vec2 offset = textSpace.getOffset();
|
||||
auto size = textSpace.size();
|
||||
for (int i = 0; i < size; i++)
|
||||
{
|
||||
auto &item = textSpace[i];
|
||||
if (textSpace.isIgnored(i)) continue;
|
||||
|
||||
item.area.size = item.area.size * scale;
|
||||
item.area.origin = (offset + item.area.origin) * scale;
|
||||
|
||||
_groups->addRect(fontAtals->frameAt(item.texId).getTexture(), item.area, item.uv, textColor, Italics);
|
||||
}
|
||||
}
|
||||
int textStartIndex = groupIndex;
|
||||
groupIndex = _groups->fill(assembler, groupIndex, this, templateEffect);
|
||||
|
||||
if (_layoutInfo->outlineSize > 0.0f)
|
||||
{
|
||||
Color4F outlineColor(_layoutInfo->outlineColor);
|
||||
Technique::Parameter outlineColorP(outlineColorKey, Technique::Parameter::Type::COLOR4, (float*)&outlineColor);
|
||||
Technique::Parameter outlineSizeP(outlineSizeKey, Technique::Parameter::Type::FLOAT, (float*)&(_layoutInfo->outlineSize));
|
||||
for (auto i = textStartIndex; i < groupIndex; i++)
|
||||
{
|
||||
auto *e = assembler->getEffect(i);
|
||||
e->setProperty(outlineColorKey, outlineColorP);
|
||||
e->setProperty(outlineSizeKey, outlineSizeP);
|
||||
}
|
||||
}
|
||||
else {
|
||||
Color4F outlineColor(_layoutInfo->outlineColor);
|
||||
Technique::Parameter outlineSizeP(outlineSizeKey, Technique::Parameter::Type::FLOAT, (float*)&(_layoutInfo->outlineSize));
|
||||
for (auto i = textStartIndex; i < groupIndex; i++)
|
||||
{
|
||||
auto *e = assembler->getEffect(i);
|
||||
e->setProperty(outlineSizeKey, outlineSizeP);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
168
cocos2d-x/cocos/2d/CCLabelLayout.h
Normal file
168
cocos2d-x/cocos/2d/CCLabelLayout.h
Normal file
@@ -0,0 +1,168 @@
|
||||
/****************************************************************************
|
||||
Copyright (c) 2020 Xiamen Yaji Software Co., Ltd.
|
||||
|
||||
http://www.cocos.com
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
****************************************************************************/
|
||||
#pragma once
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "2d/CCTTFLabelAtlasCache.h"
|
||||
|
||||
#include "math/Vec3.h"
|
||||
#include "math/Vec4.h"
|
||||
#include "base/ccConfig.h"
|
||||
#if CC_ENABLE_TTF_LABEL_RENDERER
|
||||
|
||||
namespace cocos2d {
|
||||
|
||||
namespace renderer {
|
||||
class CustomAssembler;
|
||||
class Texture2D;
|
||||
class MeshBuffer;
|
||||
class Effect;
|
||||
class EffectVariant;
|
||||
}
|
||||
|
||||
namespace middleware {
|
||||
class MeshBuffer;
|
||||
}
|
||||
|
||||
class TextRenderGroup;
|
||||
class LabelRenderer;
|
||||
|
||||
|
||||
class TextRowSpace {
|
||||
/**
|
||||
* Y
|
||||
* ^
|
||||
* |
|
||||
* |
|
||||
* +------> X
|
||||
*/
|
||||
public:
|
||||
|
||||
struct GlyphBlock {
|
||||
Rect area;
|
||||
Rect uv;
|
||||
int texId = 0;
|
||||
bool ignored = false;
|
||||
};
|
||||
|
||||
TextRowSpace() = default;
|
||||
TextRowSpace(TextRowSpace &&other);
|
||||
|
||||
void fillRect(int texId, Rect &rect, Rect &uv);
|
||||
void translate(float x, float y);
|
||||
Vec2 center() const;
|
||||
|
||||
float getWidth() const { return _data.size() > 0 ? _right - _left : 0; }
|
||||
float getHeight() const { return _data.size() > 0 ? _top - _bottom : 0; }
|
||||
|
||||
float getExtentedWidth(float left, float right) const;
|
||||
|
||||
Vec2 getOffset() const { return Vec2(_x, _y); }
|
||||
|
||||
GlyphBlock & operator[] (size_t i);
|
||||
|
||||
bool isIgnored() const { return _ignored; }
|
||||
|
||||
bool isIgnored(int i) const { return _data[i].ignored; }
|
||||
|
||||
void clip(const Rect &rec);
|
||||
|
||||
size_t size() const { return _data.size(); }
|
||||
|
||||
bool validate() const { return _data.size() > 0; }
|
||||
|
||||
Rect asRect() const;
|
||||
|
||||
float getLeft() const { return _left; }
|
||||
|
||||
float getRight() const { return _right; }
|
||||
|
||||
float getTop() const { return _top; }
|
||||
|
||||
float getBottom() const { return _bottom; }
|
||||
|
||||
private:
|
||||
void reset();
|
||||
|
||||
GlyphBlock & appendBlock();
|
||||
|
||||
private:
|
||||
|
||||
float _left = FLT_MAX;
|
||||
float _bottom = FLT_MAX;
|
||||
float _right = FLT_MIN;
|
||||
float _top = FLT_MIN;
|
||||
float _x = 0.0f;
|
||||
float _y = 0.0f;
|
||||
std::vector<GlyphBlock> _data;
|
||||
bool _ignored = false;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
class LabelLayout {
|
||||
public:
|
||||
LabelLayout(LabelRenderer *r): _renderer(r){}
|
||||
bool init(const std::string& font, const std::string& text, float fontSize, float retinaFontSize, LabelLayoutInfo *info);
|
||||
virtual ~LabelLayout();
|
||||
|
||||
void setString(const std::string &txt, bool forceUpdate);
|
||||
|
||||
bool isInited() const { return _inited; }
|
||||
|
||||
void fillAssembler(renderer::CustomAssembler *assembeler, renderer::EffectVariant *effect);
|
||||
|
||||
private:
|
||||
|
||||
bool updateContent();
|
||||
|
||||
private:
|
||||
std::string _string;
|
||||
std::u32string _u32string;
|
||||
std::string _font;
|
||||
float _fontSize = 0.0f;
|
||||
float _retinaFontSize = 0.0f;
|
||||
float _fontScale = 1.0f;
|
||||
float _scale = 1.0f;
|
||||
|
||||
//weak reference
|
||||
LabelLayoutInfo *_layoutInfo = nullptr;
|
||||
|
||||
std::shared_ptr<TTFLabelAtlas> _fontAtlas;
|
||||
|
||||
bool _enableKerning = true;
|
||||
bool _inited = false;
|
||||
|
||||
std::vector<TextRowSpace> _textSpace;
|
||||
|
||||
std::shared_ptr<TextRenderGroup> _groups;
|
||||
std::shared_ptr<TextRenderGroup> _shadowGroups;
|
||||
LabelRenderer *_renderer = nullptr;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
155
cocos2d-x/cocos/2d/CCTTFLabelAtlasCache.cpp
Normal file
155
cocos2d-x/cocos/2d/CCTTFLabelAtlasCache.cpp
Normal file
@@ -0,0 +1,155 @@
|
||||
/****************************************************************************
|
||||
Copyright (c) 2020 Xiamen Yaji Software Co., Ltd.
|
||||
|
||||
http://www.cocos.com
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
****************************************************************************/
|
||||
#include "CCTTFLabelAtlasCache.h"
|
||||
|
||||
#include "platform/CCFileUtils.h"
|
||||
|
||||
#include <cmath>
|
||||
#include <cassert>
|
||||
#include "base/ccConfig.h"
|
||||
#if CC_ENABLE_TTF_LABEL_RENDERER
|
||||
|
||||
#define FTT_MAP_FONT_SIZE 0
|
||||
#define FTT_USE_FONT_MAP_SIZE_POWEROFTWO 0
|
||||
|
||||
#define FTT_TEXTURE_SIZE 1024
|
||||
|
||||
namespace cocos2d {
|
||||
|
||||
namespace {
|
||||
TTFLabelAtlasCache *_instance = nullptr;
|
||||
|
||||
|
||||
/**
|
||||
* Labels of different size can share a same FontAtlas by mapping font size.
|
||||
*/
|
||||
#if FTT_MAP_FONT_SIZE
|
||||
inline int mapFontSize(float x) {
|
||||
#if FTT_USE_FONT_MAP_SIZE_POWEROFTWO
|
||||
// round to power of 2, which waste lots of memory?
|
||||
return 1 << (int)(::ceil(::log(x) / ::log(2.0)));
|
||||
#else
|
||||
if (x <= 8.0f) return 8;
|
||||
else if (x <= 16.0f) return 16;
|
||||
else if (x <= 32.0f) return 32;
|
||||
else if (x <= 48.0f) return 48;
|
||||
return (int)x;
|
||||
#endif
|
||||
}
|
||||
#else
|
||||
#define mapFontSize(f) (int)(f)
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
TTFLabelAtlas::TTFLabelAtlas(const std::string &fontPath, float fontSize, LabelLayoutInfo *info)
|
||||
:_fontName(fontPath), _fontSize(fontSize), _info(info)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
bool TTFLabelAtlas::init()
|
||||
{
|
||||
assert(FileUtils::getInstance()->isFileExist(_fontName));
|
||||
_ttfFont = std::make_shared<FontFreeType>(_fontName, _fontSize, _info);
|
||||
|
||||
if(!_ttfFont->loadFont()){
|
||||
return false;
|
||||
}
|
||||
_fontAtlas = std::make_shared<FontAtlas>(PixelMode::A8, FTT_TEXTURE_SIZE, FTT_TEXTURE_SIZE, _info->outlineSize > 0 || _info->bold);
|
||||
_fontAtlas->init();
|
||||
return true;
|
||||
}
|
||||
|
||||
TTFLabelAtlasCache * TTFLabelAtlasCache::getInstance()
|
||||
{
|
||||
if (!_instance)
|
||||
{
|
||||
_instance = new TTFLabelAtlasCache();
|
||||
}
|
||||
return _instance;
|
||||
}
|
||||
|
||||
void TTFLabelAtlasCache::destroyInstance()
|
||||
{
|
||||
delete _instance;
|
||||
_instance = nullptr;
|
||||
}
|
||||
|
||||
void TTFLabelAtlasCache::reset()
|
||||
{
|
||||
_cache.clear();
|
||||
}
|
||||
|
||||
std::shared_ptr<TTFLabelAtlas> TTFLabelAtlasCache::load(const std::string &font, float fontSizeF, LabelLayoutInfo *info)
|
||||
{
|
||||
int fontSize = mapFontSize(fontSizeF);
|
||||
std::string keybuffer = cacheKeyFor(font, fontSize, info);
|
||||
#if CC_TTF_LABELATLAS_ENABLE_GC
|
||||
std::weak_ptr<TTFLabelAtlas> &atlasWeak= _cache[keybuffer];
|
||||
std::shared_ptr<TTFLabelAtlas> atlas = atlasWeak.lock();
|
||||
if (!atlas)
|
||||
{
|
||||
atlas = std::make_shared<TTFLabelAtlas>(font, fontSize, info);
|
||||
if(!atlas->init())
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
atlasWeak = atlas;
|
||||
}
|
||||
#else
|
||||
std::shared_ptr<TTFLabelAtals> &atlas = _cache[keybuffer];
|
||||
if (!atlas)
|
||||
{
|
||||
atlas = std::make_shared<TTFLabelAtals>(font, fontSize, info);
|
||||
if(!atlas->init())
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return atlas;
|
||||
}
|
||||
|
||||
|
||||
void TTFLabelAtlasCache::unload(TTFLabelAtlas *atlas)
|
||||
{
|
||||
int fontSize = mapFontSize(atlas->_fontSize);
|
||||
std::string key = cacheKeyFor(atlas->_fontName, fontSize, atlas->_info);
|
||||
_cache.erase(key);
|
||||
}
|
||||
|
||||
|
||||
std::string TTFLabelAtlasCache::cacheKeyFor(const std::string &font, int fontSize, LabelLayoutInfo * info)
|
||||
{
|
||||
char keybuffer[512] = { 0 };
|
||||
std::string fullPath = FileUtils::getInstance()->fullPathForFilename(font);
|
||||
|
||||
//NOTICE: fontpath may be too long
|
||||
snprintf(keybuffer, 511, "s:%d/sdf:%d/p:%s", fontSize, info->outlineSize > 0 || info->bold, fullPath.c_str());
|
||||
return keybuffer;
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
96
cocos2d-x/cocos/2d/CCTTFLabelAtlasCache.h
Normal file
96
cocos2d-x/cocos/2d/CCTTFLabelAtlasCache.h
Normal file
@@ -0,0 +1,96 @@
|
||||
/****************************************************************************
|
||||
Copyright (c) 2020 Xiamen Yaji Software Co., Ltd.
|
||||
|
||||
http://www.cocos.com
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
****************************************************************************/
|
||||
#pragma once
|
||||
|
||||
|
||||
#include "2d/CCFontFreetype.h"
|
||||
#include "2d/CCFontAtlas.h"
|
||||
|
||||
#include <unordered_map>
|
||||
#include <memory>
|
||||
|
||||
#include "base/ccConfig.h"
|
||||
#if CC_ENABLE_TTF_LABEL_RENDERER
|
||||
|
||||
#define CC_TTF_LABELATLAS_ENABLE_GC 1
|
||||
|
||||
namespace cocos2d {
|
||||
|
||||
class TTFLabelAtlasCache;
|
||||
struct LabelLayoutInfo;
|
||||
|
||||
// font atlas of specific size font
|
||||
class TTFLabelAtlas {
|
||||
public:
|
||||
|
||||
TTFLabelAtlas(const std::string &, float, LabelLayoutInfo *info);
|
||||
|
||||
|
||||
inline FontAtlas * getFontAtlas() const { return _fontAtlas.get(); }
|
||||
inline FontFreeType * getTTF() const { return _ttfFont.get(); }
|
||||
|
||||
float getFontSize() const { return _fontSize; }
|
||||
|
||||
bool init();
|
||||
|
||||
private:
|
||||
std::string _fontName;
|
||||
float _fontSize = 0.f;
|
||||
//weak reference
|
||||
LabelLayoutInfo *_info = nullptr;
|
||||
std::shared_ptr<FontAtlas> _fontAtlas;
|
||||
std::shared_ptr<FontFreeType> _ttfFont;
|
||||
|
||||
friend class TTFLabelAtlasCache;
|
||||
};
|
||||
|
||||
class TTFLabelAtlasCache {
|
||||
public:
|
||||
|
||||
static TTFLabelAtlasCache* getInstance();
|
||||
static void destroyInstance();
|
||||
|
||||
void reset();
|
||||
|
||||
std::shared_ptr<TTFLabelAtlas> load(const std::string &font, float fontSize, LabelLayoutInfo* info);
|
||||
|
||||
void unload(TTFLabelAtlas *);
|
||||
|
||||
protected:
|
||||
|
||||
std::string cacheKeyFor(const std::string &font, int fontSize, LabelLayoutInfo *info);
|
||||
|
||||
TTFLabelAtlasCache() {}
|
||||
private:
|
||||
#if CC_TTF_LABELATLAS_ENABLE_GC
|
||||
std::unordered_map< std::string, std::weak_ptr< TTFLabelAtlas>> _cache;
|
||||
#else
|
||||
std::unordered_map< std::string, std::shared_ptr< TTFLabelAtals>> _cache;
|
||||
#endif
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
167
cocos2d-x/cocos/2d/CCTTFLabelRenderer.cpp
Normal file
167
cocos2d-x/cocos/2d/CCTTFLabelRenderer.cpp
Normal file
@@ -0,0 +1,167 @@
|
||||
/****************************************************************************
|
||||
Copyright (c) 2020 Xiamen Yaji Software Co., Ltd.
|
||||
|
||||
http://www.cocos.com
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
****************************************************************************/
|
||||
#include "cocos/2d/CCTTFLabelRenderer.h"
|
||||
|
||||
#include "MiddlewareMacro.h"
|
||||
#include "renderer/renderer/Pass.h"
|
||||
#include "renderer/renderer/Technique.h"
|
||||
#include "renderer/scene/RenderFlow.hpp"
|
||||
#include "renderer/scene/assembler/CustomAssembler.hpp"
|
||||
#include "cocos/editor-support/IOBuffer.h"
|
||||
#include "cocos/editor-support/middleware-adapter.h"
|
||||
#include "cocos/editor-support/MiddlewareManager.h"
|
||||
#include "cocos/platform/CCApplication.h"
|
||||
#include "cocos/platform/CCFileUtils.h"
|
||||
#include "scripting/js-bindings/jswrapper/SeApi.h"
|
||||
|
||||
#include "CCLabelLayout.h"
|
||||
#include "base/ccConfig.h"
|
||||
#if CC_ENABLE_TTF_LABEL_RENDERER
|
||||
|
||||
USING_NS_CC;
|
||||
USING_NS_MW;
|
||||
using namespace renderer;
|
||||
|
||||
namespace cocos2d {
|
||||
|
||||
LabelRenderer::LabelRenderer()
|
||||
{
|
||||
}
|
||||
|
||||
LabelRenderer::~LabelRenderer()
|
||||
{
|
||||
CC_SAFE_RELEASE_NULL(_effect);
|
||||
CC_SAFE_RELEASE_NULL(_nodeProxy);
|
||||
if(_componentObj) _componentObj->decRef();
|
||||
}
|
||||
|
||||
|
||||
void LabelRenderer::bindNodeProxy(cocos2d::renderer::NodeProxy* node) {
|
||||
if (node == _nodeProxy) return;
|
||||
CC_SAFE_RELEASE(_nodeProxy);
|
||||
_nodeProxy = node;
|
||||
CC_SAFE_RETAIN(_nodeProxy);
|
||||
}
|
||||
|
||||
void LabelRenderer::setEffect(cocos2d::renderer::EffectVariant* arg) {
|
||||
if (_effect == arg) return;
|
||||
|
||||
CC_SAFE_RELEASE(_effect);
|
||||
_effect = arg;
|
||||
CC_SAFE_RETAIN(arg);
|
||||
_cfg->updateFlags |= UPDATE_EFFECT;
|
||||
}
|
||||
|
||||
void LabelRenderer::bindSharedBlock(se::Object * selfObj, void *cfg, void *layout)
|
||||
{
|
||||
_selfObj = selfObj;
|
||||
_cfg = static_cast<decltype(_cfg)>(cfg);
|
||||
_layoutInfo = static_cast<decltype(_layoutInfo)>(layout);
|
||||
}
|
||||
|
||||
|
||||
void LabelRenderer::genStringLayout()
|
||||
{
|
||||
std::string fontPath = getFontPath();
|
||||
std::string text = getString();
|
||||
if (!fontPath.empty() && !text.empty() && !_stringLayout)
|
||||
{
|
||||
_stringLayout.reset(new LabelLayout(this));
|
||||
_stringLayout->init(fontPath, text, _cfg->fontSize, _cfg->fontSizeRetina, _layoutInfo);
|
||||
}
|
||||
}
|
||||
|
||||
void LabelRenderer::render()
|
||||
{
|
||||
std::string text = getString();
|
||||
std::string fontPath = getFontPath();
|
||||
if (!_effect || text.empty() || fontPath.empty()) return;
|
||||
|
||||
if (!_stringLayout) {
|
||||
genStringLayout();
|
||||
_cfg->updateFlags &= ~(UPDATE_FONT | UPDATE_EFFECT);
|
||||
}
|
||||
|
||||
renderIfChange();
|
||||
|
||||
}
|
||||
|
||||
void LabelRenderer::renderIfChange()
|
||||
{
|
||||
if (!_stringLayout) return;
|
||||
|
||||
if (_cfg->updateFlags & UPDATE_FONT || _cfg->updateFlags & UPDATE_EFFECT)
|
||||
{
|
||||
// update font & string
|
||||
_stringLayout.reset();
|
||||
genStringLayout();
|
||||
|
||||
doRender();
|
||||
}
|
||||
else if (_cfg->updateFlags & UPDATE_CONTENT)
|
||||
{
|
||||
std::string text = getString(); // update content only
|
||||
if (_stringLayout->isInited())
|
||||
{
|
||||
_stringLayout->setString(text, true);
|
||||
doRender();
|
||||
}
|
||||
}
|
||||
|
||||
_cfg->updateFlags = 0;
|
||||
}
|
||||
|
||||
void LabelRenderer::doRender()
|
||||
{
|
||||
if (_stringLayout && _effect && _nodeProxy && _nodeProxy->getAssembler())
|
||||
{
|
||||
auto *assembler = (CustomAssembler*)_nodeProxy->getAssembler();
|
||||
_stringLayout->fillAssembler(assembler, _effect);
|
||||
}
|
||||
}
|
||||
|
||||
std::string LabelRenderer::getString() {
|
||||
se::Value str;
|
||||
assert(_selfObj);
|
||||
_selfObj->getProperty("string", &str);
|
||||
return str.toString();
|
||||
}
|
||||
|
||||
std::string LabelRenderer::getFontPath() {
|
||||
se::Value str;
|
||||
assert(_selfObj);
|
||||
_selfObj->getProperty("fontPath", &str);
|
||||
return str.toString();
|
||||
}
|
||||
|
||||
|
||||
void LabelRenderer::setJsComponent(se::Object *component)
|
||||
{
|
||||
if(_componentObj == component) return;
|
||||
if(_componentObj) _componentObj->decRef();
|
||||
if(component) component->incRef();
|
||||
_componentObj = component;
|
||||
}
|
||||
}
|
||||
#endif
|
106
cocos2d-x/cocos/2d/CCTTFLabelRenderer.h
Normal file
106
cocos2d-x/cocos/2d/CCTTFLabelRenderer.h
Normal file
@@ -0,0 +1,106 @@
|
||||
/****************************************************************************
|
||||
Copyright (c) 2020 Xiamen Yaji Software Co., Ltd.
|
||||
|
||||
http://www.cocos.com
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
****************************************************************************/
|
||||
#pragma once
|
||||
|
||||
|
||||
#include "base/ccMacros.h"
|
||||
#include "2d/CCTTFTypes.h"
|
||||
#include "base/ccConfig.h"
|
||||
#include <memory>
|
||||
|
||||
#if CC_ENABLE_TTF_LABEL_RENDERER
|
||||
|
||||
namespace se {
|
||||
class Object;
|
||||
}
|
||||
|
||||
namespace cocos2d {
|
||||
|
||||
namespace renderer {
|
||||
class Texture2D;
|
||||
class Effect;
|
||||
class EffectVariant;
|
||||
class NodeProxy;
|
||||
}
|
||||
class LabelLayout;
|
||||
|
||||
class LabelRenderer : public cocos2d::Ref {
|
||||
|
||||
private:
|
||||
enum UpdateFlags {
|
||||
UPDATE_CONTENT = 1 << 0,
|
||||
UPDATE_FONT = 1 << 1,
|
||||
UPDATE_EFFECT = 1 << 2
|
||||
};
|
||||
|
||||
|
||||
public:
|
||||
|
||||
struct LabelRendererConfig {
|
||||
uint32_t updateFlags = 0xFFFFFFFF;
|
||||
float fontSize = 20.0f;
|
||||
float fontSizeRetina = 0.0f;
|
||||
};
|
||||
|
||||
LabelRenderer();
|
||||
|
||||
virtual ~LabelRenderer();
|
||||
|
||||
void bindNodeProxy(cocos2d::renderer::NodeProxy* node);
|
||||
|
||||
void setEffect(cocos2d::renderer::EffectVariant* effect);
|
||||
|
||||
void render();
|
||||
|
||||
void bindSharedBlock(se::Object *selfObj, void *cfg, void *layout);
|
||||
|
||||
void setJsComponent(se::Object *component);
|
||||
|
||||
se::Object *getJsComponent() const {return _componentObj;}
|
||||
|
||||
private:
|
||||
|
||||
void genStringLayout();
|
||||
|
||||
void renderIfChange();
|
||||
void doRender();
|
||||
|
||||
std::string getString();
|
||||
std::string getFontPath();
|
||||
|
||||
std::unique_ptr<LabelLayout> _stringLayout;
|
||||
se::Object *_selfObj = nullptr;
|
||||
se::Object *_componentObj = nullptr;
|
||||
|
||||
//export arraybuffer to js
|
||||
LabelRendererConfig *_cfg = nullptr;
|
||||
LabelLayoutInfo *_layoutInfo = nullptr;
|
||||
|
||||
cocos2d::renderer::NodeProxy* _nodeProxy = nullptr;
|
||||
cocos2d::renderer::EffectVariant * _effect = nullptr;
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
69
cocos2d-x/cocos/2d/CCTTFTypes.cpp
Normal file
69
cocos2d-x/cocos/2d/CCTTFTypes.cpp
Normal file
@@ -0,0 +1,69 @@
|
||||
/****************************************************************************
|
||||
Copyright (c) 2020 Xiamen Yaji Software Co., Ltd.
|
||||
|
||||
http://www.cocos.com
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
****************************************************************************/
|
||||
#include "CCTTFTypes.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <cstdarg>
|
||||
#include <iostream>
|
||||
#include "base/ccConfig.h"
|
||||
#if CC_ENABLE_TTF_LABEL_RENDERER
|
||||
namespace cocos2d {
|
||||
|
||||
GlyphBitmap::GlyphBitmap(std::vector<uint8_t>& data, int width, int height, Rect rect, int adv, PixelMode mode, int outline)
|
||||
: _data(std::move(data)), _width(width), _height(height), _rect(rect), _xAdvance(adv), _pixelMode(mode), _outline(outline)
|
||||
{
|
||||
}
|
||||
GlyphBitmap::GlyphBitmap(GlyphBitmap&& other) noexcept
|
||||
{
|
||||
_data = std::move(other._data);
|
||||
_rect = other._rect;
|
||||
_width = other._width;
|
||||
_height = other._height;
|
||||
_xAdvance = other._xAdvance;
|
||||
_pixelMode = other._pixelMode;
|
||||
_outline = other._outline;
|
||||
}
|
||||
|
||||
|
||||
int PixelModeSize(PixelMode mode)
|
||||
{
|
||||
switch (mode)
|
||||
{
|
||||
case PixelMode::AI88:
|
||||
return 2;
|
||||
case PixelMode::A8:
|
||||
return 1;
|
||||
case PixelMode::RGB888:
|
||||
return 3;
|
||||
case PixelMode::BGRA8888:
|
||||
return 4;
|
||||
default:
|
||||
assert(false); // invalidate pixel mode
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#endif
|
114
cocos2d-x/cocos/2d/CCTTFTypes.h
Normal file
114
cocos2d-x/cocos/2d/CCTTFTypes.h
Normal file
@@ -0,0 +1,114 @@
|
||||
/****************************************************************************
|
||||
Copyright (c) 2020 Xiamen Yaji Software Co., Ltd.
|
||||
|
||||
http://www.cocos.com
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
****************************************************************************/
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include <iosfwd>
|
||||
#include <cstdint>
|
||||
|
||||
#include "math/Vec2.h"
|
||||
#include "math/CCGeometry.h"
|
||||
#include "base/ccTypes.h"
|
||||
|
||||
#include "base/ccConfig.h"
|
||||
#if CC_ENABLE_TTF_LABEL_RENDERER
|
||||
|
||||
namespace cocos2d {
|
||||
|
||||
|
||||
// ref CCMacro.js
|
||||
enum class LabelAlignmentH : uint8_t
|
||||
{
|
||||
LEFT, CENTER, RIGHT
|
||||
};
|
||||
// ref CCMacro.js
|
||||
enum class LabelAlignmentV : uint8_t {
|
||||
TOP, CENTER, BOTTOM
|
||||
};
|
||||
// ref CCLabel.js
|
||||
enum class LabelOverflow : uint8_t {
|
||||
NONE, CLAMP, SHRINK, RESIZE_HEIGHT
|
||||
};
|
||||
|
||||
struct LabelLayoutInfo {
|
||||
float lineHeight = 0.0f;
|
||||
float outlineSize = 0.0f;
|
||||
float spaceX = 0.0f;
|
||||
float width = 0.0f;
|
||||
float height = 0.0f;
|
||||
float anchorX = 0.5f;
|
||||
float anchorY = 0.5f;
|
||||
float shadowX = 0.0f;
|
||||
float shadowY = 0.0f;
|
||||
int32_t shadowBlur = -1;
|
||||
Color4B shadowColor;
|
||||
Color4B color;
|
||||
Color4B outlineColor;
|
||||
bool wrap = false;
|
||||
bool bold = false;
|
||||
bool italic = false;
|
||||
bool underline = false;
|
||||
LabelAlignmentV valign = LabelAlignmentV::CENTER;
|
||||
LabelAlignmentH halign = LabelAlignmentH::CENTER;
|
||||
LabelOverflow overflow = LabelOverflow::NONE;
|
||||
};
|
||||
|
||||
enum class PixelMode {
|
||||
AI88,
|
||||
A8,
|
||||
RGB888,
|
||||
BGRA8888,
|
||||
INVAL,
|
||||
};
|
||||
|
||||
int PixelModeSize(PixelMode mode);
|
||||
|
||||
class GlyphBitmap {
|
||||
public:
|
||||
GlyphBitmap(std::vector<uint8_t>& data, int width, int height, Rect rect, int xAdvance, PixelMode mode, int outline);
|
||||
|
||||
GlyphBitmap(GlyphBitmap&& other) noexcept;
|
||||
|
||||
int getWidth() const { return _width; }
|
||||
int getHeight() const { return _height; }
|
||||
Rect getRect() const { return _rect; }
|
||||
int getXAdvance() const { return _xAdvance; }
|
||||
int getOutline() const { return _outline; }
|
||||
PixelMode getPixelMode() const { return _pixelMode; }
|
||||
std::vector<uint8_t>& getData() { return _data; }
|
||||
|
||||
private:
|
||||
int _width = 0;
|
||||
int _height = 0;
|
||||
int _outline = 0;
|
||||
std::vector<uint8_t> _data;
|
||||
Rect _rect;
|
||||
int _xAdvance = 0;
|
||||
PixelMode _pixelMode;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
Reference in New Issue
Block a user