mirror of https://github.com/axmolengine/axmol.git
Add preload ttf SDF font atlas support
A extension SDFGen tool also available, see ImGuiTest
This commit is contained in:
parent
7b8d4119c7
commit
2f1efe5a51
|
@ -38,6 +38,11 @@
|
||||||
#include "base/EventDispatcher.h"
|
#include "base/EventDispatcher.h"
|
||||||
#include "base/EventType.h"
|
#include "base/EventType.h"
|
||||||
|
|
||||||
|
#include "simdjson/simdjson.h"
|
||||||
|
#include "zlib.h"
|
||||||
|
#include "fmt/format.h"
|
||||||
|
#include "base/ZipUtils.h"
|
||||||
|
|
||||||
NS_AX_BEGIN
|
NS_AX_BEGIN
|
||||||
|
|
||||||
const int FontAtlas::CacheTextureWidth = 512;
|
const int FontAtlas::CacheTextureWidth = 512;
|
||||||
|
@ -45,7 +50,101 @@ const int FontAtlas::CacheTextureHeight = 512;
|
||||||
const char* FontAtlas::CMD_PURGE_FONTATLAS = "__cc_PURGE_FONTATLAS";
|
const char* FontAtlas::CMD_PURGE_FONTATLAS = "__cc_PURGE_FONTATLAS";
|
||||||
const char* FontAtlas::CMD_RESET_FONTATLAS = "__cc_RESET_FONTATLAS";
|
const char* FontAtlas::CMD_RESET_FONTATLAS = "__cc_RESET_FONTATLAS";
|
||||||
|
|
||||||
FontAtlas::FontAtlas(Font* theFont) : _font(theFont)
|
void FontAtlas::loadFontAtlas(std::string_view fontatlasFile, hlookup::string_map<FontAtlas*>& outAtlasMap)
|
||||||
|
{
|
||||||
|
using namespace simdjson;
|
||||||
|
|
||||||
|
struct PaddingString : protected yasio::sbyte_buffer
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
using value_type = yasio::sbyte_buffer::value_type;
|
||||||
|
size_t size() const { return this->yasio::sbyte_buffer::size(); }
|
||||||
|
void resize(size_t size)
|
||||||
|
{
|
||||||
|
this->yasio::sbyte_buffer::resize_fit(size + SIMDJSON_PADDING);
|
||||||
|
this->yasio::sbyte_buffer::data()[size] = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
char* data() { return this->yasio::sbyte_buffer::data(); };
|
||||||
|
};
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
PaddingString strJson;
|
||||||
|
FileUtils::getInstance()->getContents(fontatlasFile, &strJson);
|
||||||
|
ondemand::parser parser;
|
||||||
|
padded_string_view json(strJson.data(), strJson.size());
|
||||||
|
ondemand::document settings = parser.iterate(json);
|
||||||
|
std::string_view type = settings["type"];
|
||||||
|
if (type != "fontatlas")
|
||||||
|
{
|
||||||
|
ax::print("Load fontatlas %s fail, invalid asset type: ", fontatlasFile.data(), type.data());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// std::string_view version = settings["version"];
|
||||||
|
std::string_view atlasName = settings["atlasName"];
|
||||||
|
|
||||||
|
auto it = outAtlasMap.find(atlasName);
|
||||||
|
if (it != outAtlasMap.end())
|
||||||
|
{
|
||||||
|
if (it->second->getReferenceCount() != 1)
|
||||||
|
{
|
||||||
|
ax::print("Load fontatlas %s fail, due to exist fontatlas with same key %s and in used",
|
||||||
|
fontatlasFile.data(), atlasName.data());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
it->second->release();
|
||||||
|
outAtlasMap.erase(it);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string_view sourceFont = settings["sourceFont"];
|
||||||
|
int faceSize = static_cast<int>(static_cast<int64_t>(settings["faceSize"]));
|
||||||
|
auto font = FontFreeType::create(sourceFont, faceSize, GlyphCollection::DYNAMIC, ""sv, true, 0.0f);
|
||||||
|
if (!font)
|
||||||
|
{
|
||||||
|
ax::print("Load fontatils %s fail due to create source font %s fail", fontatlasFile.data(),
|
||||||
|
sourceFont.data());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int atlasDim[2];
|
||||||
|
|
||||||
|
auto atliasDim = settings["atlasDim"].get_array();
|
||||||
|
int index = 0;
|
||||||
|
for (auto value : atliasDim)
|
||||||
|
{
|
||||||
|
atlasDim[index++] = static_cast<int>(value.get_int64());
|
||||||
|
if (index >= 2)
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto fontAtlas = new FontAtlas(font, atlasDim[0], atlasDim[1], AX_CONTENT_SCALE_FACTOR());
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
fontAtlas->initWithSettings(&settings);
|
||||||
|
outAtlasMap.emplace(atlasName, fontAtlas);
|
||||||
|
}
|
||||||
|
catch (std::exception& ex)
|
||||||
|
{
|
||||||
|
fontAtlas->release();
|
||||||
|
throw ex; // rethrow
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (std::exception& ex)
|
||||||
|
{
|
||||||
|
ax::print("Load fontatils %s fail due to exception occured: %s", fontatlasFile.data(), ex.what());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FontAtlas::FontAtlas(Font* theFont)
|
||||||
|
: FontAtlas(theFont, CacheTextureWidth, CacheTextureHeight, AX_CONTENT_SCALE_FACTOR())
|
||||||
|
{}
|
||||||
|
|
||||||
|
FontAtlas::FontAtlas(Font* theFont, int atlasWidth, int atlasHeight, float scaleFactor)
|
||||||
|
: _font(theFont), _width(atlasWidth), _height(atlasHeight), _scaleFactor(scaleFactor)
|
||||||
{
|
{
|
||||||
_font->retain();
|
_font->retain();
|
||||||
|
|
||||||
|
@ -61,7 +160,7 @@ FontAtlas::FontAtlas(Font* theFont) : _font(theFont)
|
||||||
{
|
{
|
||||||
_strideShift = 1;
|
_strideShift = 1;
|
||||||
_pixelFormat = AX_GLES_PROFILE != 200 ? backend::PixelFormat::RG8 : backend::PixelFormat::LA8;
|
_pixelFormat = AX_GLES_PROFILE != 200 ? backend::PixelFormat::RG8 : backend::PixelFormat::LA8;
|
||||||
_currentPageDataSize = CacheTextureWidth * CacheTextureHeight << _strideShift;
|
_currentPageDataSize = _width * _height << _strideShift;
|
||||||
|
|
||||||
_lineHeight += 2 * outlineSize;
|
_lineHeight += 2 * outlineSize;
|
||||||
}
|
}
|
||||||
|
@ -69,7 +168,7 @@ FontAtlas::FontAtlas(Font* theFont) : _font(theFont)
|
||||||
{
|
{
|
||||||
_strideShift = 0;
|
_strideShift = 0;
|
||||||
_pixelFormat = AX_GLES_PROFILE != 200 ? backend::PixelFormat::R8 : backend::PixelFormat::A8;
|
_pixelFormat = AX_GLES_PROFILE != 200 ? backend::PixelFormat::R8 : backend::PixelFormat::A8;
|
||||||
_currentPageDataSize = CacheTextureWidth * CacheTextureHeight;
|
_currentPageDataSize = _width * _height;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_fontFreeType->isDistanceFieldEnabled())
|
if (_fontFreeType->isDistanceFieldEnabled())
|
||||||
|
@ -113,6 +212,54 @@ FontAtlas::~FontAtlas()
|
||||||
AX_SAFE_DELETE_ARRAY(_currentPageData);
|
AX_SAFE_DELETE_ARRAY(_currentPageData);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void FontAtlas::initWithSettings(void* opaque /*simdjson::ondemand::document*/)
|
||||||
|
{
|
||||||
|
if (!_currentPageData)
|
||||||
|
_currentPageData = new uint8_t[_currentPageDataSize];
|
||||||
|
_currentPage = -1;
|
||||||
|
|
||||||
|
simdjson::ondemand::document& settings = *(simdjson::ondemand::document*)opaque;
|
||||||
|
|
||||||
|
// pages
|
||||||
|
for (auto page : settings["pages"].get_array())
|
||||||
|
{
|
||||||
|
auto comprData = utils::base64Decode(page);
|
||||||
|
auto uncomprData = ZipUtils::decompressGZ(std::span{comprData}, _currentPageDataSize);
|
||||||
|
addNewPageWithData(uncomprData.data(), uncomprData.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
_currentPageOrigX = static_cast<float>(settings["pageX"].get_double());
|
||||||
|
_currentPageOrigY = static_cast<float>(settings["pageY"].get_double());
|
||||||
|
|
||||||
|
// letters
|
||||||
|
FontLetterDefinition tempDef;
|
||||||
|
tempDef.rotated = false;
|
||||||
|
tempDef.validDefinition = true;
|
||||||
|
|
||||||
|
std::string strCharCode;
|
||||||
|
for (auto field : settings["letters"].get_object())
|
||||||
|
{
|
||||||
|
strCharCode = static_cast<std::string_view>(field.unescaped_key());
|
||||||
|
auto letterInfo = field.value();
|
||||||
|
tempDef.U = static_cast<float>(letterInfo["U"].get_double());
|
||||||
|
tempDef.V = static_cast<float>(letterInfo["V"].get_double());
|
||||||
|
tempDef.xAdvance = static_cast<float>(letterInfo["advance"].get_double());
|
||||||
|
tempDef.width = static_cast<float>(letterInfo["width"].get_double());
|
||||||
|
tempDef.height = static_cast<float>(letterInfo["height"].get_double());
|
||||||
|
tempDef.offsetX = static_cast<float>(letterInfo["offsetX"].get_double());
|
||||||
|
tempDef.offsetY = static_cast<float>(letterInfo["offsetY"].get_double());
|
||||||
|
tempDef.textureID = letterInfo["page"].get_int64();
|
||||||
|
|
||||||
|
auto charCode = atoi(strCharCode.c_str());
|
||||||
|
|
||||||
|
tempDef.U /= _scaleFactor;
|
||||||
|
tempDef.V /= _scaleFactor;
|
||||||
|
tempDef.width /= _scaleFactor;
|
||||||
|
tempDef.height /= _scaleFactor;
|
||||||
|
_letterDefinitions.emplace(charCode, tempDef);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void FontAtlas::reset()
|
void FontAtlas::reset()
|
||||||
{
|
{
|
||||||
releaseTextures();
|
releaseTextures();
|
||||||
|
@ -222,7 +369,6 @@ bool FontAtlas::prepareLetterDefinitions(const std::u32string& utf32Text)
|
||||||
Rect tempRect;
|
Rect tempRect;
|
||||||
FontLetterDefinition tempDef;
|
FontLetterDefinition tempDef;
|
||||||
|
|
||||||
auto scaleFactor = AX_CONTENT_SCALE_FACTOR();
|
|
||||||
auto pixelFormat = _pixelFormat;
|
auto pixelFormat = _pixelFormat;
|
||||||
|
|
||||||
int startY = (int)_currentPageOrigY;
|
int startY = (int)_currentPageOrigY;
|
||||||
|
@ -238,16 +384,17 @@ bool FontAtlas::prepareLetterDefinitions(const std::u32string& utf32Text)
|
||||||
tempDef.offsetX = tempRect.origin.x - adjustForDistanceMap - adjustForExtend;
|
tempDef.offsetX = tempRect.origin.x - adjustForDistanceMap - adjustForExtend;
|
||||||
tempDef.offsetY = _fontAscender + tempRect.origin.y - adjustForDistanceMap - adjustForExtend;
|
tempDef.offsetY = _fontAscender + tempRect.origin.y - adjustForDistanceMap - adjustForExtend;
|
||||||
|
|
||||||
if (_currentPageOrigX + tempDef.width > CacheTextureWidth)
|
if (_currentPageOrigX + tempDef.width > _width)
|
||||||
{
|
{
|
||||||
_currentPageOrigY += _currLineHeight;
|
_currentPageOrigY += _currLineHeight;
|
||||||
_currLineHeight = 0;
|
_currLineHeight = 0;
|
||||||
_currentPageOrigX = 0;
|
_currentPageOrigX = 0;
|
||||||
if (_currentPageOrigY + _lineHeight + _letterPadding + _letterEdgeExtend >= CacheTextureHeight)
|
if (_currentPageOrigY + _lineHeight + _letterPadding + _letterEdgeExtend >= _height)
|
||||||
{
|
{
|
||||||
updateTextureContent(pixelFormat, startY);
|
updateTextureContent(pixelFormat, startY);
|
||||||
|
|
||||||
startY = 0;
|
startY = 0;
|
||||||
|
|
||||||
addNewPage();
|
addNewPage();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -257,17 +404,18 @@ bool FontAtlas::prepareLetterDefinitions(const std::u32string& utf32Text)
|
||||||
_currLineHeight = glyphHeight;
|
_currLineHeight = glyphHeight;
|
||||||
}
|
}
|
||||||
_fontFreeType->renderCharAt(_currentPageData, (int)_currentPageOrigX + adjustForExtend,
|
_fontFreeType->renderCharAt(_currentPageData, (int)_currentPageOrigX + adjustForExtend,
|
||||||
(int)_currentPageOrigY + adjustForExtend, bitmap, bitmapWidth, bitmapHeight);
|
(int)_currentPageOrigY + adjustForExtend, bitmap, bitmapWidth, bitmapHeight,
|
||||||
|
_width, _height);
|
||||||
|
|
||||||
tempDef.U = _currentPageOrigX;
|
tempDef.U = _currentPageOrigX;
|
||||||
tempDef.V = _currentPageOrigY;
|
tempDef.V = _currentPageOrigY;
|
||||||
tempDef.textureID = _currentPage;
|
tempDef.textureID = _currentPage;
|
||||||
_currentPageOrigX += tempDef.width + 1;
|
_currentPageOrigX += tempDef.width + 1;
|
||||||
// take from pixels to points
|
// take from pixels to points
|
||||||
tempDef.width = tempDef.width / scaleFactor;
|
tempDef.width = tempDef.width / _scaleFactor;
|
||||||
tempDef.height = tempDef.height / scaleFactor;
|
tempDef.height = tempDef.height / _scaleFactor;
|
||||||
tempDef.U = tempDef.U / scaleFactor;
|
tempDef.U = tempDef.U / _scaleFactor;
|
||||||
tempDef.V = tempDef.V / scaleFactor;
|
tempDef.V = tempDef.V / _scaleFactor;
|
||||||
tempDef.rotated = false;
|
tempDef.rotated = false;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -297,18 +445,25 @@ bool FontAtlas::prepareLetterDefinitions(const std::u32string& utf32Text)
|
||||||
|
|
||||||
void FontAtlas::updateTextureContent(backend::PixelFormat format, int startY)
|
void FontAtlas::updateTextureContent(backend::PixelFormat format, int startY)
|
||||||
{
|
{
|
||||||
auto data = _currentPageData + (CacheTextureWidth * (int)startY << _strideShift);
|
auto data = _currentPageData + (_width * (int)startY << _strideShift);
|
||||||
_atlasTextures[_currentPage]->updateWithSubData(data, 0, startY, CacheTextureWidth,
|
_atlasTextures[_currentPage]->updateWithSubData(data, 0, startY, _width,
|
||||||
(int)_currentPageOrigY - startY + _currLineHeight);
|
(int)_currentPageOrigY - startY + _currLineHeight);
|
||||||
}
|
}
|
||||||
|
|
||||||
void FontAtlas::addNewPage()
|
void FontAtlas::addNewPage()
|
||||||
{
|
{
|
||||||
auto texture = new Texture2D();
|
|
||||||
|
|
||||||
memset(_currentPageData, 0, _currentPageDataSize);
|
memset(_currentPageData, 0, _currentPageDataSize);
|
||||||
|
addNewPageWithData(_currentPageData, _currentPageDataSize);
|
||||||
|
|
||||||
texture->initWithData(_currentPageData, _currentPageDataSize, _pixelFormat, CacheTextureWidth, CacheTextureHeight);
|
_currentPageOrigY = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void FontAtlas::addNewPageWithData(const uint8_t* data, size_t size)
|
||||||
|
{
|
||||||
|
assert(_currentPageDataSize == size);
|
||||||
|
|
||||||
|
auto texture = new Texture2D();
|
||||||
|
texture->initWithData(data, _currentPageDataSize, _pixelFormat, _width, _height);
|
||||||
|
|
||||||
if (_antialiasEnabled)
|
if (_antialiasEnabled)
|
||||||
texture->setAntiAliasTexParameters();
|
texture->setAntiAliasTexParameters();
|
||||||
|
@ -317,8 +472,6 @@ void FontAtlas::addNewPage()
|
||||||
|
|
||||||
setTexture(++_currentPage, texture);
|
setTexture(++_currentPage, texture);
|
||||||
texture->release();
|
texture->release();
|
||||||
|
|
||||||
_currentPageOrigY = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void FontAtlas::setTexture(unsigned int slot, Texture2D* texture)
|
void FontAtlas::setTexture(unsigned int slot, Texture2D* texture)
|
||||||
|
|
|
@ -66,10 +66,12 @@ public:
|
||||||
static const int CacheTextureHeight;
|
static const int CacheTextureHeight;
|
||||||
static const char* CMD_PURGE_FONTATLAS;
|
static const char* CMD_PURGE_FONTATLAS;
|
||||||
static const char* CMD_RESET_FONTATLAS;
|
static const char* CMD_RESET_FONTATLAS;
|
||||||
|
static void loadFontAtlas(std::string_view fontatlasFile, hlookup::string_map<FontAtlas*>& outAtlasMap);
|
||||||
/**
|
/**
|
||||||
* @js ctor
|
* @js ctor
|
||||||
*/
|
*/
|
||||||
FontAtlas(Font* theFont);
|
FontAtlas(Font* theFont);
|
||||||
|
FontAtlas(Font* theFont, int atlasWidth, int atlasHeight, float scaleFactor = 1.0f);
|
||||||
/**
|
/**
|
||||||
* @js NA
|
* @js NA
|
||||||
* @lua NA
|
* @lua NA
|
||||||
|
@ -81,9 +83,13 @@ public:
|
||||||
|
|
||||||
bool prepareLetterDefinitions(const std::u32string& utf16String);
|
bool prepareLetterDefinitions(const std::u32string& utf16String);
|
||||||
|
|
||||||
|
const auto& getLetterDefinitions() const { return _letterDefinitions; }
|
||||||
|
|
||||||
const std::unordered_map<unsigned int, Texture2D*>& getTextures() const { return _atlasTextures; }
|
const std::unordered_map<unsigned int, Texture2D*>& getTextures() const { return _atlasTextures; }
|
||||||
|
|
||||||
void addNewPage();
|
virtual void addNewPage();
|
||||||
|
|
||||||
|
void addNewPageWithData(const uint8_t* data, size_t size);
|
||||||
|
|
||||||
void setTexture(unsigned int slot, Texture2D* texture);
|
void setTexture(unsigned int slot, Texture2D* texture);
|
||||||
Texture2D* getTexture(int slot);
|
Texture2D* getTexture(int slot);
|
||||||
|
@ -118,6 +124,8 @@ public:
|
||||||
void setAliasTexParameters();
|
void setAliasTexParameters();
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
void initWithSettings(void* opaque /*simdjson::ondemand::document*/);
|
||||||
|
|
||||||
void reset();
|
void reset();
|
||||||
|
|
||||||
void reinit();
|
void reinit();
|
||||||
|
@ -137,10 +145,16 @@ protected:
|
||||||
|
|
||||||
std::unordered_map<unsigned int, Texture2D*> _atlasTextures;
|
std::unordered_map<unsigned int, Texture2D*> _atlasTextures;
|
||||||
std::unordered_map<char32_t, FontLetterDefinition> _letterDefinitions;
|
std::unordered_map<char32_t, FontLetterDefinition> _letterDefinitions;
|
||||||
float _lineHeight = 0.f;
|
|
||||||
Font* _font = nullptr;
|
Font* _font = nullptr;
|
||||||
FontFreeType* _fontFreeType = nullptr;
|
FontFreeType* _fontFreeType = nullptr;
|
||||||
|
|
||||||
|
int _width = 0; // atlas width
|
||||||
|
int _height = 0; // atlas height
|
||||||
|
float _scaleFactor = 1.0f;
|
||||||
|
|
||||||
|
float _lineHeight = 0.f;
|
||||||
|
|
||||||
// Dynamic GlyphCollection related stuff
|
// Dynamic GlyphCollection related stuff
|
||||||
int _currentPage = -1;
|
int _currentPage = -1;
|
||||||
backend::PixelFormat _pixelFormat = backend::PixelFormat::NONE;
|
backend::PixelFormat _pixelFormat = backend::PixelFormat::NONE;
|
||||||
|
|
|
@ -33,7 +33,6 @@
|
||||||
#include "2d/FontCharMap.h"
|
#include "2d/FontCharMap.h"
|
||||||
#include "2d/Label.h"
|
#include "2d/Label.h"
|
||||||
#include "platform/FileUtils.h"
|
#include "platform/FileUtils.h"
|
||||||
|
|
||||||
#include "base/format.h"
|
#include "base/format.h"
|
||||||
|
|
||||||
NS_AX_BEGIN
|
NS_AX_BEGIN
|
||||||
|
@ -53,6 +52,11 @@ void FontAtlasCache::purgeCachedData()
|
||||||
_atlasMap.clear();
|
_atlasMap.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void FontAtlasCache::preloadFontAtlas(std::string_view fontatlasFile)
|
||||||
|
{
|
||||||
|
FontAtlas::loadFontAtlas(fontatlasFile, _atlasMap);
|
||||||
|
}
|
||||||
|
|
||||||
FontAtlas* FontAtlasCache::getFontAtlasTTF(_ttfConfig* config)
|
FontAtlas* FontAtlasCache::getFontAtlasTTF(_ttfConfig* config)
|
||||||
{
|
{
|
||||||
auto& realFontFilename = config->fontFilePath;
|
auto& realFontFilename = config->fontFilePath;
|
||||||
|
@ -67,7 +71,7 @@ FontAtlas* FontAtlasCache::getFontAtlasTTF(_ttfConfig* config)
|
||||||
|
|
||||||
std::string atlasName =
|
std::string atlasName =
|
||||||
config->distanceFieldEnabled
|
config->distanceFieldEnabled
|
||||||
? fmt::format("df {} {} {}", scaledFaceSize, outlineSize, realFontFilename)
|
? fmt::format("df {} {}", scaledFaceSize, realFontFilename)
|
||||||
: fmt::format("{} {} {}", scaledFaceSize, outlineSize, realFontFilename);
|
: fmt::format("{} {} {}", scaledFaceSize, outlineSize, realFontFilename);
|
||||||
auto it = _atlasMap.find(atlasName);
|
auto it = _atlasMap.find(atlasName);
|
||||||
|
|
||||||
|
|
|
@ -41,6 +41,11 @@ struct _ttfConfig;
|
||||||
class AX_DLL FontAtlasCache
|
class AX_DLL FontAtlasCache
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
/**
|
||||||
|
* @brief preload a SDF fontatlas
|
||||||
|
* since axmol-2.1.0, must call before creating any Label
|
||||||
|
*/
|
||||||
|
static void preloadFontAtlas(std::string_view fontatlasFile);
|
||||||
static FontAtlas* getFontAtlasTTF(_ttfConfig* config);
|
static FontAtlas* getFontAtlasTTF(_ttfConfig* config);
|
||||||
|
|
||||||
static FontAtlas* getFontAtlasFNT(std::string_view fontFileName);
|
static FontAtlas* getFontAtlasFNT(std::string_view fontFileName);
|
||||||
|
|
|
@ -578,7 +578,9 @@ void FontFreeType::renderCharAt(unsigned char* dest,
|
||||||
int posY,
|
int posY,
|
||||||
unsigned char* bitmap,
|
unsigned char* bitmap,
|
||||||
int bitmapWidth,
|
int bitmapWidth,
|
||||||
int bitmapHeight)
|
int bitmapHeight,
|
||||||
|
int atlasWidth,
|
||||||
|
int atlasHeight)
|
||||||
{
|
{
|
||||||
const int iX = posX;
|
const int iX = posX;
|
||||||
int iY = posY;
|
int iY = posY;
|
||||||
|
@ -588,7 +590,7 @@ void FontFreeType::renderCharAt(unsigned char* dest,
|
||||||
for (int32_t y = 0; y < bitmapHeight; ++y)
|
for (int32_t y = 0; y < bitmapHeight; ++y)
|
||||||
{
|
{
|
||||||
int32_t bitmap_y = y * bitmapWidth;
|
int32_t bitmap_y = y * bitmapWidth;
|
||||||
memcpy(dest + (iX + (iY * FontAtlas::CacheTextureWidth)) * 2, bitmap + bitmap_y * 2, bitmapWidth * 2);
|
memcpy(dest + (iX + (iY * atlasWidth)) * 2, bitmap + bitmap_y * 2, bitmapWidth * 2);
|
||||||
++iY;
|
++iY;
|
||||||
}
|
}
|
||||||
delete[] bitmap;
|
delete[] bitmap;
|
||||||
|
@ -598,7 +600,7 @@ void FontFreeType::renderCharAt(unsigned char* dest,
|
||||||
for (int32_t y = 0; y < bitmapHeight; ++y)
|
for (int32_t y = 0; y < bitmapHeight; ++y)
|
||||||
{
|
{
|
||||||
int32_t bitmap_y = y * bitmapWidth;
|
int32_t bitmap_y = y * bitmapWidth;
|
||||||
memcpy(dest + (iX + (iY * FontAtlas::CacheTextureWidth)), bitmap + bitmap_y, bitmapWidth);
|
memcpy(dest + (iX + (iY * atlasWidth)), bitmap + bitmap_y, bitmapWidth);
|
||||||
++iY;
|
++iY;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -110,7 +110,9 @@ public:
|
||||||
int posY,
|
int posY,
|
||||||
unsigned char* bitmap,
|
unsigned char* bitmap,
|
||||||
int bitmapWidth,
|
int bitmapWidth,
|
||||||
int bitmapHeight);
|
int bitmapHeight,
|
||||||
|
int atlasWidth,
|
||||||
|
int atlasHeight);
|
||||||
|
|
||||||
int* getHorizontalKerningForTextUTF32(const std::u32string& text, int& outNumLetters) const override;
|
int* getHorizontalKerningForTextUTF32(const std::u32string& text, int& outNumLetters) const override;
|
||||||
|
|
||||||
|
@ -123,6 +125,8 @@ public:
|
||||||
virtual FontAtlas* newFontAtlas() override;
|
virtual FontAtlas* newFontAtlas() override;
|
||||||
virtual int getFontMaxHeight() const override { return _lineHeight; }
|
virtual int getFontMaxHeight() const override { return _lineHeight; }
|
||||||
|
|
||||||
|
std::string_view getGlyphCollection() const;
|
||||||
|
|
||||||
static void releaseFont(std::string_view fontName);
|
static void releaseFont(std::string_view fontName);
|
||||||
|
|
||||||
static FT_Library getFTLibrary();
|
static FT_Library getFTLibrary();
|
||||||
|
@ -146,7 +150,6 @@ private:
|
||||||
unsigned char* getGlyphBitmapWithOutline(unsigned int glyphIndex, FT_BBox& bbox);
|
unsigned char* getGlyphBitmapWithOutline(unsigned int glyphIndex, FT_BBox& bbox);
|
||||||
|
|
||||||
void setGlyphCollection(GlyphCollection glyphs, std::string_view customGlyphs);
|
void setGlyphCollection(GlyphCollection glyphs, std::string_view customGlyphs);
|
||||||
std::string_view getGlyphCollection() const;
|
|
||||||
|
|
||||||
FT_Face _fontFace;
|
FT_Face _fontFace;
|
||||||
FT_Stream _fontStream;
|
FT_Stream _fontStream;
|
||||||
|
|
|
@ -134,6 +134,7 @@ option(AX_ENABLE_EXT_EFFEKSEER "Build extension Effekseer" OFF)
|
||||||
|
|
||||||
cmake_dependent_option(AX_ENABLE_EXT_IMGUI "Build extension ImGui" ON "(WINDOWS AND NOT WINRT) OR MACOSX OR LINUX OR ANDROID OR WASM" OFF)
|
cmake_dependent_option(AX_ENABLE_EXT_IMGUI "Build extension ImGui" ON "(WINDOWS AND NOT WINRT) OR MACOSX OR LINUX OR ANDROID OR WASM" OFF)
|
||||||
cmake_dependent_option(AX_ENABLE_EXT_INSPECTOR "Build extension Inspector" ON "(WINDOWS AND NOT WINRT) OR MACOSX OR LINUX OR ANDROID OR WASM" OFF)
|
cmake_dependent_option(AX_ENABLE_EXT_INSPECTOR "Build extension Inspector" ON "(WINDOWS AND NOT WINRT) OR MACOSX OR LINUX OR ANDROID OR WASM" OFF)
|
||||||
|
cmake_dependent_option(AX_ENABLE_EXT_SDFGEN "Build extension SDFGen" ON "(WINDOWS AND NOT WINRT) OR MACOSX OR LINUX OR WASM" OFF)
|
||||||
|
|
||||||
option(AX_DISABLE_GLES2 "Whether disable GLES2 support" OFF)
|
option(AX_DISABLE_GLES2 "Whether disable GLES2 support" OFF)
|
||||||
|
|
||||||
|
|
|
@ -61,6 +61,126 @@ bool ZipUtils::s_bEncryptionKeyIsValid = false;
|
||||||
|
|
||||||
// --------------------- ZipUtils ---------------------
|
// --------------------- ZipUtils ---------------------
|
||||||
|
|
||||||
|
yasio::byte_buffer ZipUtils::compressGZ(const void* in, size_t inlen, int level)
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
Bytef buffer[512];
|
||||||
|
z_stream d_stream; /* compression stream */
|
||||||
|
|
||||||
|
d_stream.zalloc = nullptr;
|
||||||
|
d_stream.zfree = nullptr;
|
||||||
|
d_stream.opaque = (voidpf)0;
|
||||||
|
|
||||||
|
d_stream.next_in = (Bytef*)in;
|
||||||
|
d_stream.avail_in = inlen;
|
||||||
|
d_stream.next_out = buffer;
|
||||||
|
d_stream.avail_out = sizeof(buffer);
|
||||||
|
yasio::byte_buffer output;
|
||||||
|
err = deflateInit2(&d_stream, level, Z_DEFLATED, MAX_WBITS + 16 /*well: normaly, gzip is: 16*/, MAX_MEM_LEVEL - 1,
|
||||||
|
Z_DEFAULT_STRATEGY);
|
||||||
|
if (err != Z_OK) //
|
||||||
|
return output;
|
||||||
|
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
err = deflate(&d_stream, Z_FINISH);
|
||||||
|
|
||||||
|
if (err == Z_STREAM_END)
|
||||||
|
{
|
||||||
|
output.insert(output.end(), buffer, buffer + sizeof(buffer) - d_stream.avail_out);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (err)
|
||||||
|
{
|
||||||
|
case Z_NEED_DICT:
|
||||||
|
err = Z_DATA_ERROR;
|
||||||
|
case Z_DATA_ERROR:
|
||||||
|
case Z_MEM_ERROR:
|
||||||
|
goto _L_end;
|
||||||
|
}
|
||||||
|
|
||||||
|
// not enough buffer ?
|
||||||
|
if (err != Z_STREAM_END)
|
||||||
|
{
|
||||||
|
output.insert(output.end(), buffer, buffer + sizeof(buffer));
|
||||||
|
|
||||||
|
d_stream.next_out = buffer;
|
||||||
|
d_stream.avail_out = sizeof(buffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_L_end:
|
||||||
|
deflateEnd(&d_stream);
|
||||||
|
if (err != Z_STREAM_END)
|
||||||
|
{
|
||||||
|
output.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
|
yasio::byte_buffer ZipUtils::decompressGZ(const void* in, size_t inlen, int expected_size)
|
||||||
|
{ // inflate
|
||||||
|
int err;
|
||||||
|
Bytef buffer[512];
|
||||||
|
z_stream d_stream; /* decompression stream */
|
||||||
|
|
||||||
|
d_stream.zalloc = nullptr;
|
||||||
|
d_stream.zfree = nullptr;
|
||||||
|
d_stream.opaque = (voidpf)0;
|
||||||
|
|
||||||
|
d_stream.next_in = (Bytef*)in;
|
||||||
|
d_stream.avail_in = inlen;
|
||||||
|
d_stream.next_out = buffer;
|
||||||
|
d_stream.avail_out = sizeof(buffer);
|
||||||
|
yasio::byte_buffer output;
|
||||||
|
err = inflateInit2(&d_stream, MAX_WBITS + 32 /*well: normaly, gzip is: 16*/);
|
||||||
|
if (err != Z_OK) //
|
||||||
|
return output;
|
||||||
|
|
||||||
|
output.reserve(expected_size != -1 ? expected_size : (inlen << 2));
|
||||||
|
|
||||||
|
for (;;)
|
||||||
|
{
|
||||||
|
err = inflate(&d_stream, Z_NO_FLUSH);
|
||||||
|
|
||||||
|
if (err == Z_STREAM_END)
|
||||||
|
{
|
||||||
|
output.insert(output.end(), buffer, buffer + sizeof(buffer) - d_stream.avail_out);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (err)
|
||||||
|
{
|
||||||
|
case Z_NEED_DICT:
|
||||||
|
err = Z_DATA_ERROR;
|
||||||
|
case Z_DATA_ERROR:
|
||||||
|
case Z_MEM_ERROR:
|
||||||
|
goto _L_end;
|
||||||
|
}
|
||||||
|
|
||||||
|
// not enough memory ?
|
||||||
|
if (err != Z_STREAM_END)
|
||||||
|
{
|
||||||
|
// *out = (unsigned char*)realloc(*out, bufferSize * BUFFER_INC_FACTOR);
|
||||||
|
output.insert(output.end(), buffer, buffer + sizeof(buffer));
|
||||||
|
|
||||||
|
d_stream.next_out = buffer;
|
||||||
|
d_stream.avail_out = sizeof(buffer);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_L_end:
|
||||||
|
inflateEnd(&d_stream);
|
||||||
|
if (err != Z_STREAM_END)
|
||||||
|
{
|
||||||
|
output.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
return output;
|
||||||
|
}
|
||||||
|
|
||||||
inline void ZipUtils::decodeEncodedPvr(unsigned int* data, ssize_t len)
|
inline void ZipUtils::decodeEncodedPvr(unsigned int* data, ssize_t len)
|
||||||
{
|
{
|
||||||
const int enclen = 1024;
|
const int enclen = 1024;
|
||||||
|
|
|
@ -40,6 +40,10 @@ THE SOFTWARE.
|
||||||
# include "platform/StdC.h"
|
# include "platform/StdC.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#include "yasio/byte_buffer.hpp"
|
||||||
|
|
||||||
|
#include <span>
|
||||||
|
|
||||||
#ifndef _unz64_H
|
#ifndef _unz64_H
|
||||||
struct unz_file_info_s;
|
struct unz_file_info_s;
|
||||||
#endif
|
#endif
|
||||||
|
@ -73,6 +77,20 @@ enum
|
||||||
class AX_DLL ZipUtils
|
class AX_DLL ZipUtils
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
template<typename _Ty, size_t _Extent = std::dynamic_extent>
|
||||||
|
inline static yasio::byte_buffer compressGZ(std::span<_Ty, _Extent> in, int level = -1)
|
||||||
|
{
|
||||||
|
return compressGZ(in.data(), in.size_bytes(), level);
|
||||||
|
}
|
||||||
|
template <typename _Ty, size_t _Extent = std::dynamic_extent>
|
||||||
|
inline static yasio::byte_buffer decompressGZ(std::span<_Ty, _Extent> in, int expected_size = -1)
|
||||||
|
{
|
||||||
|
return decompressGZ(in.data(), in.size_bytes(), expected_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
static yasio::byte_buffer compressGZ(const void* in, size_t inlen, int level = -1);
|
||||||
|
static yasio::byte_buffer decompressGZ(const void* in, size_t inlen, int expected_size = -1);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Inflates either zlib or gzip deflated memory. The inflated memory is expected to be freed by the caller.
|
* Inflates either zlib or gzip deflated memory. The inflated memory is expected to be freed by the caller.
|
||||||
*
|
*
|
||||||
|
|
|
@ -387,14 +387,20 @@ void TextureCache::addImageAsyncCallBack(float /*dt*/)
|
||||||
Texture2D* TextureCache::getWhiteTexture()
|
Texture2D* TextureCache::getWhiteTexture()
|
||||||
{
|
{
|
||||||
constexpr std::string_view key = "/white-texture"sv;
|
constexpr std::string_view key = "/white-texture"sv;
|
||||||
|
return getWhiteTexture(key, 0xFF);
|
||||||
|
}
|
||||||
|
|
||||||
|
Texture2D* TextureCache::getWhiteTexture(std::string_view key, uint8_t luma)
|
||||||
|
{
|
||||||
// Gets the texture by key firstly.
|
// Gets the texture by key firstly.
|
||||||
auto texture = this->getTextureForKey(key);
|
auto texture = this->getTextureForKey(key);
|
||||||
if (texture) return texture;
|
if (texture)
|
||||||
|
return texture;
|
||||||
|
|
||||||
// If texture wasn't in cache, create it from RAW data.
|
// If texture wasn't in cache, create it from RAW data.
|
||||||
unsigned char texls[] = {
|
unsigned char texls[] = {
|
||||||
// RGBA8888
|
// RGBA8888
|
||||||
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF};
|
luma, luma, luma, 0xFF, luma, luma, luma, 0xFF, luma, luma, luma, 0xFF, luma, luma, luma, 0xFF};
|
||||||
|
|
||||||
Image* image = new Image(); // Notes: andorid: VolatileTextureMgr traits image as dynmaic object
|
Image* image = new Image(); // Notes: andorid: VolatileTextureMgr traits image as dynmaic object
|
||||||
bool AX_UNUSED isOK = image->initWithRawData(texls, sizeof(texls), 2, 2, 8);
|
bool AX_UNUSED isOK = image->initWithRawData(texls, sizeof(texls), 2, 2, 8);
|
||||||
|
|
|
@ -88,6 +88,9 @@ public:
|
||||||
/** Gets a 2x2 white texture */
|
/** Gets a 2x2 white texture */
|
||||||
Texture2D* getWhiteTexture();
|
Texture2D* getWhiteTexture();
|
||||||
|
|
||||||
|
/** Gets a 2x2 texture whith specify luma and key */
|
||||||
|
Texture2D* getWhiteTexture(std::string_view key, uint8_t luma);
|
||||||
|
|
||||||
/** Gets 1x1 dummy texture with alpha=0 */
|
/** Gets 1x1 dummy texture with alpha=0 */
|
||||||
Texture2D* getDummyTexture();
|
Texture2D* getDummyTexture();
|
||||||
|
|
||||||
|
|
|
@ -78,6 +78,10 @@ if(AX_ENABLE_EXT_IMGUI)
|
||||||
add_subdirectory(Inspector)
|
add_subdirectory(Inspector)
|
||||||
target_include_directories(Inspector PUBLIC ${CMAKE_CURRENT_LIST_DIR}/ImGui)
|
target_include_directories(Inspector PUBLIC ${CMAKE_CURRENT_LIST_DIR}/ImGui)
|
||||||
endif()
|
endif()
|
||||||
|
if (AX_ENABLE_EXT_SDFGEN)
|
||||||
|
add_subdirectory(SDFGen)
|
||||||
|
target_include_directories(SDFGen PUBLIC ${CMAKE_CURRENT_LIST_DIR}/ImGui)
|
||||||
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
if(AX_ENABLE_EXT_COCOSTUDIO)
|
if(AX_ENABLE_EXT_COCOSTUDIO)
|
||||||
|
|
|
@ -0,0 +1,7 @@
|
||||||
|
set(target_name SDFGen)
|
||||||
|
|
||||||
|
FILE(GLOB SDFGEN_SOURCES *.h;*.cpp;./**/*.h;./**/*.cpp)
|
||||||
|
|
||||||
|
add_library(${target_name} ${SDFGEN_SOURCES})
|
||||||
|
|
||||||
|
setup_ax_extension_config(${target_name})
|
|
@ -0,0 +1,356 @@
|
||||||
|
#include "SDFGen.h"
|
||||||
|
|
||||||
|
#include <yasio/singleton.hpp>
|
||||||
|
#include <yasio/byte_buffer.hpp>
|
||||||
|
#include "base/format.h"
|
||||||
|
|
||||||
|
#include "base/ZipUtils.h"
|
||||||
|
|
||||||
|
#include "ImGuiPresenter.h"
|
||||||
|
#include <imgui/misc/cpp/imgui_stdlib.h>
|
||||||
|
#include <zlib.h>
|
||||||
|
#include "json/json.hpp"
|
||||||
|
|
||||||
|
#include "yasio/utils.hpp"
|
||||||
|
|
||||||
|
NS_AX_EXT_BEGIN
|
||||||
|
|
||||||
|
struct FontAtlasGenParams
|
||||||
|
{
|
||||||
|
std::string sourceFont; // font relative path? choose from developer machine?
|
||||||
|
std::string fontAsset; // fontAsset .xasset
|
||||||
|
std::string glyphs; // utf-8
|
||||||
|
int faceSize = 32;
|
||||||
|
int atlasDim[2] = {512, 512}; // w,h
|
||||||
|
bool useAscii = true;
|
||||||
|
|
||||||
|
bool saved = false;
|
||||||
|
float cost = 0.0f; // milliseconds
|
||||||
|
std::string error; // save error message if fail
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* scan fonts in `Content/fonts`
|
||||||
|
* kernings don't store
|
||||||
|
* axmol .xasset format spec, sdf only
|
||||||
|
* axmol .xasset binary format: \X\A\S
|
||||||
|
* {
|
||||||
|
* "version": "2.1.0",
|
||||||
|
* "type": "fontatlas",
|
||||||
|
* "atlasName": "xxx",
|
||||||
|
* "sourceFont: "xxx",
|
||||||
|
* "spread": 6, // reserved
|
||||||
|
* "faceSize": 32,
|
||||||
|
* "atlasSize": [512, 512],
|
||||||
|
* "letters": [
|
||||||
|
* "30": {
|
||||||
|
* "page": 0,
|
||||||
|
* "xAdvance": 33,
|
||||||
|
* "width": 54,
|
||||||
|
* "height": 33,
|
||||||
|
* "offset": 5,
|
||||||
|
* "bearingY": 3,
|
||||||
|
* },
|
||||||
|
* "20": {
|
||||||
|
* }
|
||||||
|
* ]
|
||||||
|
* "pages: [ // zip + base64
|
||||||
|
* "",
|
||||||
|
* ""
|
||||||
|
* ]
|
||||||
|
* }
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace xasset
|
||||||
|
{
|
||||||
|
class FontAtlas : public ax::FontAtlas
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
FontAtlas(Font* theFont, int atlasWidth, int atlasHeight) : ax::FontAtlas(theFont, atlasWidth, atlasHeight) {}
|
||||||
|
static FontAtlas* newFontAtlas(FontAtlasGenParams* params)
|
||||||
|
{
|
||||||
|
auto font = FontFreeType::create(params->sourceFont, params->faceSize,
|
||||||
|
!params->useAscii ? ax::GlyphCollection::CUSTOM : ax::GlyphCollection::ASCII,
|
||||||
|
params->glyphs, true);
|
||||||
|
auto fontAtlas = new xasset::FontAtlas(font, params->atlasDim[0], params->atlasDim[1]);
|
||||||
|
|
||||||
|
fontAtlas->generate(params);
|
||||||
|
|
||||||
|
return fontAtlas;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool save() { return this->saveAs(_params->fontAsset); }
|
||||||
|
|
||||||
|
bool saveAs(std::string_view path)
|
||||||
|
{
|
||||||
|
std::string storePath;
|
||||||
|
auto fu = FileUtils::getInstance();
|
||||||
|
if (fu->isAbsolutePath(path))
|
||||||
|
{
|
||||||
|
storePath = path;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
storePath = fu->getDefaultResourceRootPath();
|
||||||
|
storePath += path;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto start = yasio::highp_clock();
|
||||||
|
|
||||||
|
nlohmann::json xasset;
|
||||||
|
|
||||||
|
xasset.emplace("version", AX_VERSION_STR "-" AX_GIT_COMMIT_HASH);
|
||||||
|
xasset.emplace("type", "fontatlas");
|
||||||
|
xasset.emplace("sourceFont", _params->sourceFont);
|
||||||
|
xasset.emplace("atlasName", _atlasName);
|
||||||
|
xasset.emplace("spread", 6);
|
||||||
|
xasset.emplace("faceSize", _params->faceSize);
|
||||||
|
|
||||||
|
xasset.emplace("atlasDim", _params->atlasDim);
|
||||||
|
|
||||||
|
nlohmann::json letters;
|
||||||
|
std::string charCode;
|
||||||
|
for (auto& letterInfo : getLetterDefinitions())
|
||||||
|
{
|
||||||
|
charCode.clear();
|
||||||
|
fmt::format_to(charCode, "{}", (int32_t)letterInfo.first);
|
||||||
|
|
||||||
|
nlohmann::json info;
|
||||||
|
info.emplace("U", letterInfo.second.U);
|
||||||
|
info.emplace("V", letterInfo.second.V);
|
||||||
|
info.emplace("width", letterInfo.second.width);
|
||||||
|
info.emplace("height", letterInfo.second.height);
|
||||||
|
info.emplace("offsetX", letterInfo.second.offsetX);
|
||||||
|
info.emplace("offsetY", letterInfo.second.offsetY);
|
||||||
|
info.emplace("page", letterInfo.second.textureID);
|
||||||
|
info.emplace("advance", letterInfo.second.xAdvance);
|
||||||
|
letters.emplace(charCode, std::move(info));
|
||||||
|
}
|
||||||
|
xasset.emplace("letters", std::move(letters));
|
||||||
|
|
||||||
|
nlohmann::json pages;
|
||||||
|
|
||||||
|
for (auto& data : _pageDatas)
|
||||||
|
{
|
||||||
|
auto compData = ZipUtils::compressGZ(std::span{data});
|
||||||
|
auto pixels = utils::base64Encode(std::string_view{reinterpret_cast<const char*>(compData.data()), compData.size()});
|
||||||
|
pages.push_back(std::move(pixels));
|
||||||
|
}
|
||||||
|
|
||||||
|
xasset.emplace("pages", std::move(pages));
|
||||||
|
|
||||||
|
xasset.emplace("pageX", _currentPageOrigX);
|
||||||
|
xasset.emplace("pageY", _currentPageOrigY);
|
||||||
|
|
||||||
|
auto str = xasset.dump(2);
|
||||||
|
|
||||||
|
fu->writeStringToFile(str, storePath);
|
||||||
|
|
||||||
|
_params->cost = (yasio::highp_clock() - start) / 1000.0;
|
||||||
|
|
||||||
|
_params->error.clear();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Texture2D* testTexture()
|
||||||
|
{
|
||||||
|
static Texture2D* texture = new Texture2D();
|
||||||
|
static bool inited = false;
|
||||||
|
if (!inited && !_pageDatas.empty())
|
||||||
|
{
|
||||||
|
texture->initWithData(_pageDatas[0].data(), _pageDatas[0].size(), PixelFormat::R8, 512, 512, false);
|
||||||
|
texture->retain();
|
||||||
|
inited = true;
|
||||||
|
}
|
||||||
|
return inited ? texture : nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void generate(FontAtlasGenParams* params)
|
||||||
|
{
|
||||||
|
_params = params;
|
||||||
|
|
||||||
|
// match with runtime
|
||||||
|
_atlasName = fmt::format("df {} {}", params->faceSize, params->sourceFont);
|
||||||
|
|
||||||
|
std::u32string utf32;
|
||||||
|
if (StringUtils::UTF8ToUTF32(_fontFreeType->getGlyphCollection(), utf32))
|
||||||
|
this->prepareLetterDefinitions(utf32);
|
||||||
|
|
||||||
|
_pageDatas.emplace_back(_currentPageData, _currentPageData + _currentPageDataSize);
|
||||||
|
}
|
||||||
|
|
||||||
|
void addNewPage() override
|
||||||
|
{
|
||||||
|
if (_currentPage != -1)
|
||||||
|
_pageDatas.emplace_back(_currentPageData, _currentPageData + _currentPageDataSize);
|
||||||
|
ax::FontAtlas::addNewPage();
|
||||||
|
}
|
||||||
|
|
||||||
|
FontAtlasGenParams* _params{nullptr}; // weak ref
|
||||||
|
std::string _atlasName; // match with runtime
|
||||||
|
std::vector<yasio::byte_buffer> _pageDatas;
|
||||||
|
};
|
||||||
|
}; // namespace xasset
|
||||||
|
|
||||||
|
SDFGen* SDFGen::getInstance()
|
||||||
|
{
|
||||||
|
return yasio::singleton<SDFGen>::instance();
|
||||||
|
}
|
||||||
|
void SDFGen::destroyInstance()
|
||||||
|
{
|
||||||
|
yasio::singleton<SDFGen>::destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SDFGen::open(ax::Scene* scene)
|
||||||
|
{
|
||||||
|
refreshFontList();
|
||||||
|
|
||||||
|
_atlasViewer = Sprite::create();
|
||||||
|
_atlasViewer->setTexture(Director::getInstance()->getTextureCache()->getWhiteTexture("/black-texture", 0));
|
||||||
|
_atlasViewer->setAnchorPoint(Vec2::ANCHOR_BOTTOM_LEFT);
|
||||||
|
_atlasViewer->retain();
|
||||||
|
|
||||||
|
auto defaultFontFile = FileUtils::getInstance()->fullPathForFilename(R"(fonts/arial.ttf)");
|
||||||
|
|
||||||
|
_atlasParams = new FontAtlasGenParams();
|
||||||
|
_atlasParams->sourceFont = "fonts/arial.ttf";
|
||||||
|
_atlasParams->fontAsset = "fonts/arial-SDF.xasset";
|
||||||
|
|
||||||
|
ImGuiPresenter::getInstance()->addFont(defaultFontFile);
|
||||||
|
/* For Simplified Chinese support, please use:
|
||||||
|
ImGuiPresenter::getInstance()->addFont(R"(C:\Windows\Fonts\msyh.ttc)", ImGuiPresenter::DEFAULT_FONT_SIZE,
|
||||||
|
ImGuiPresenter::CHS_GLYPH_RANGE::GENERAL);
|
||||||
|
*/
|
||||||
|
ImGuiPresenter::getInstance()->enableDPIScale(); // enable dpi scale for 4K display support, depends at least one
|
||||||
|
// valid ttf/ttc font was added.
|
||||||
|
ImGuiPresenter::getInstance()->addRenderLoop("#sdfg", AX_CALLBACK_0(SDFGen::onImGuiDraw, this), scene);
|
||||||
|
}
|
||||||
|
|
||||||
|
void SDFGen::close()
|
||||||
|
{
|
||||||
|
ImGuiPresenter::getInstance()->removeRenderLoop("#sdfg");
|
||||||
|
|
||||||
|
delete _atlasParams;
|
||||||
|
_atlasParams = nullptr;
|
||||||
|
|
||||||
|
_atlasViewer->release();
|
||||||
|
}
|
||||||
|
|
||||||
|
void SDFGen::refreshFontList()
|
||||||
|
{
|
||||||
|
_fontList.clear();
|
||||||
|
|
||||||
|
auto fu = FileUtils::getInstance();
|
||||||
|
|
||||||
|
std::vector<std::string> fileList;
|
||||||
|
fu->listFilesRecursively("fonts", &fileList);
|
||||||
|
|
||||||
|
auto& contentPath = fu->getDefaultResourceRootPath();
|
||||||
|
for (auto& filePath : fileList)
|
||||||
|
{
|
||||||
|
if (!cxx20::ic::ends_with(filePath, ".ttf") && !cxx20::ic::ends_with(filePath, ".ttc"))
|
||||||
|
continue;
|
||||||
|
if (cxx20::starts_with(filePath, contentPath))
|
||||||
|
_fontList.emplace_back(filePath.substr(contentPath.size()));
|
||||||
|
else
|
||||||
|
_fontList.emplace_back(std::move(filePath));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void SDFGen::onImGuiDraw()
|
||||||
|
{
|
||||||
|
ImGui::StyleColorsDark();
|
||||||
|
|
||||||
|
auto& style = ImGui::GetStyle();
|
||||||
|
style.Colors[ImGuiCol_WindowBg] = ImVec4(0.2f, 0.2f, 0.2f, 0.94f);
|
||||||
|
|
||||||
|
ImGui::SetNextWindowSize(ImVec2{550, 660}, ImGuiCond_FirstUseEver);
|
||||||
|
ImGui::SetNextWindowContentSize(ImVec2{0, 0});
|
||||||
|
|
||||||
|
static std::string title = fmt::format("Axmol SDF Font Creator {}", AX_VERSION_STR "-" AX_GIT_COMMIT_HASH);
|
||||||
|
if (ImGui::Begin(title.c_str(), nullptr, ImGuiWindowFlags_HorizontalScrollbar))
|
||||||
|
{
|
||||||
|
if (ImGui::BeginCombo("Source Font File", _atlasParams->sourceFont.c_str()))
|
||||||
|
{
|
||||||
|
for (int n = 0; n < _fontList.size(); n++)
|
||||||
|
{
|
||||||
|
bool is_selected = (_atlasParams->sourceFont == _fontList[n]);
|
||||||
|
if (ImGui::Selectable(_fontList[n].c_str(), is_selected))
|
||||||
|
_atlasParams->sourceFont = _fontList[n];
|
||||||
|
if (is_selected)
|
||||||
|
ImGui::SetItemDefaultFocus();
|
||||||
|
}
|
||||||
|
ImGui::EndCombo();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (ImGui::Button("Refresh Font List"))
|
||||||
|
{
|
||||||
|
refreshFontList();
|
||||||
|
}
|
||||||
|
ImGui::DragInt("Sampling Point Size", &_atlasParams->faceSize, 1, 1, 144);
|
||||||
|
ImGui::DragInt2("Atlas Resolution", _atlasParams->atlasDim, 32, 64, 4096);
|
||||||
|
|
||||||
|
bool modified = ImGui::Checkbox("Use ASCII", &_atlasParams->useAscii);
|
||||||
|
ImGui::SameLine();
|
||||||
|
ImGui::Text("%s", "Character Set");
|
||||||
|
if (!_atlasParams->useAscii)
|
||||||
|
{
|
||||||
|
ImGui::InputTextMultiline("glyphs", &_atlasParams->glyphs);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (modified)
|
||||||
|
_atlasParams->glyphs.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ImGui::Button("Generate Font Atlas"))
|
||||||
|
{
|
||||||
|
AX_SAFE_RELEASE_NULL(_fontAtlas);
|
||||||
|
_fontAtlas = xasset::FontAtlas::newFontAtlas(_atlasParams);
|
||||||
|
|
||||||
|
// TODO: display multi-pages with listview?
|
||||||
|
auto textureAtlasPage0 = _fontAtlas->getTexture(0);
|
||||||
|
_atlasViewer->setTexture(textureAtlasPage0);
|
||||||
|
|
||||||
|
Rect rect = Rect::ZERO;
|
||||||
|
rect.size = textureAtlasPage0->getContentSize();
|
||||||
|
_atlasViewer->setTextureRect(rect);
|
||||||
|
}
|
||||||
|
ImGui::InputText("Font Asset", &_atlasParams->fontAsset);
|
||||||
|
if (ImGui::Button("Save"))
|
||||||
|
{
|
||||||
|
if (_fontAtlas)
|
||||||
|
_fontAtlas->save();
|
||||||
|
else
|
||||||
|
_atlasParams->error = "Please generate first!";
|
||||||
|
|
||||||
|
_atlasParams->saved = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_atlasParams->saved)
|
||||||
|
{
|
||||||
|
ImGui::SameLine();
|
||||||
|
if (!_atlasParams->error.empty())
|
||||||
|
ImGui::TextColored(ImVec4{1.0, 0.0, 0.0, 1.0}, "%s", _atlasParams->error.c_str());
|
||||||
|
else
|
||||||
|
ImGui::TextColored(ImVec4{0.0, 1.0, 0.0, 1.0}, "Save succeed, cost %.3f (ms)", _atlasParams->cost);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto viewerSize = _atlasViewer->getContentSize();
|
||||||
|
if (viewerSize.fuzzyEquals(Vec2::ZERO, 1e-3))
|
||||||
|
{
|
||||||
|
viewerSize.width = 512;
|
||||||
|
viewerSize.height = 512;
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::Separator();
|
||||||
|
ImGui::Text("Atals View:");
|
||||||
|
ImGuiPresenter::getInstance()->image(_atlasViewer, ImVec2(viewerSize.width, viewerSize.height));
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::End();
|
||||||
|
}
|
||||||
|
|
||||||
|
NS_AX_EXT_END
|
|
@ -0,0 +1,37 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "axmol.h"
|
||||||
|
#include "ExtensionMacros.h"
|
||||||
|
|
||||||
|
NS_AX_EXT_BEGIN
|
||||||
|
|
||||||
|
struct FontAtlasGenParams;
|
||||||
|
|
||||||
|
namespace xasset
|
||||||
|
{
|
||||||
|
class FontAtlas;
|
||||||
|
}
|
||||||
|
class SDFGen
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
static SDFGen* getInstance();
|
||||||
|
static void destroyInstance();
|
||||||
|
|
||||||
|
void open(ax::Scene* = nullptr);
|
||||||
|
void close();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
void refreshFontList();
|
||||||
|
|
||||||
|
void onImGuiDraw();
|
||||||
|
|
||||||
|
protected:
|
||||||
|
FontAtlasGenParams* _atlasParams{nullptr};
|
||||||
|
|
||||||
|
ax::Sprite* _atlasViewer{nullptr};
|
||||||
|
xasset::FontAtlas* _fontAtlas{nullptr};
|
||||||
|
|
||||||
|
std::vector<std::string> _fontList;
|
||||||
|
};
|
||||||
|
|
||||||
|
NS_AX_EXT_END
|
|
@ -2,6 +2,7 @@
|
||||||
#include "ImGuiTest.h"
|
#include "ImGuiTest.h"
|
||||||
|
|
||||||
#include "ImGui/ImGuiPresenter.h"
|
#include "ImGui/ImGuiPresenter.h"
|
||||||
|
#include "SDFGen/SDFGen.h"
|
||||||
|
|
||||||
USING_NS_AX;
|
USING_NS_AX;
|
||||||
USING_NS_AX_EXT;
|
USING_NS_AX_EXT;
|
||||||
|
@ -22,7 +23,9 @@ ImGuiTests::ImGuiTests()
|
||||||
void ImGuiTest::onEnter()
|
void ImGuiTest::onEnter()
|
||||||
{
|
{
|
||||||
TestCase::onEnter();
|
TestCase::onEnter();
|
||||||
|
# if !defined(__ANDROID__)
|
||||||
|
SDFGen::getInstance()->open();
|
||||||
|
#endif
|
||||||
ImGuiPresenter::getInstance()->addFont(FileUtils::getInstance()->fullPathForFilename("fonts/arial.ttf"));
|
ImGuiPresenter::getInstance()->addFont(FileUtils::getInstance()->fullPathForFilename("fonts/arial.ttf"));
|
||||||
ImGuiPresenter::getInstance()->enableDPIScale();
|
ImGuiPresenter::getInstance()->enableDPIScale();
|
||||||
ImGuiPresenter::getInstance()->addRenderLoop("#test", AX_CALLBACK_0(ImGuiTest::onDrawImGui, this), this);
|
ImGuiPresenter::getInstance()->addRenderLoop("#test", AX_CALLBACK_0(ImGuiTest::onDrawImGui, this), this);
|
||||||
|
@ -33,6 +36,9 @@ void ImGuiTest::onExit()
|
||||||
ImGuiPresenter::getInstance()->removeRenderLoop("#test");
|
ImGuiPresenter::getInstance()->removeRenderLoop("#test");
|
||||||
ImGuiPresenter::getInstance()->clearFonts();
|
ImGuiPresenter::getInstance()->clearFonts();
|
||||||
|
|
||||||
|
# if !defined(__ANDROID__)
|
||||||
|
SDFGen::getInstance()->close();
|
||||||
|
# endif
|
||||||
ImGuiPresenter::destroyInstance();
|
ImGuiPresenter::destroyInstance();
|
||||||
|
|
||||||
TestCase::onExit();
|
TestCase::onExit();
|
||||||
|
|
Loading…
Reference in New Issue