Skip to content

Commit

Permalink
Added glyph ranges manager for ImGuiPresenter, added ability to use g…
Browse files Browse the repository at this point in the history
…lyph ranges for Inspector's font (#1936)

* Added glyph manager for ImGuiPresenter

* renamed glyph ranges constants

* for static code conduct

* for code of conduct 2

* removed some duplication and better format

* removed extra space
  • Loading branch information
IamSanjid authored May 29, 2024
1 parent ccf3189 commit eb0e6ef
Show file tree
Hide file tree
Showing 6 changed files with 254 additions and 31 deletions.
197 changes: 179 additions & 18 deletions extensions/ImGui/src/ImGui/ImGuiPresenter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

NS_AX_EXT_BEGIN


namespace
{
uint32_t fourccValue(std::string_view str)
Expand Down Expand Up @@ -367,17 +368,13 @@ void ImGuiPresenter::loadCustomFonts(void* ud)
auto contentZoomFactor = thiz->_contentZoomFactor;
for (auto& fontInfo : thiz->_fontsInfoMap)
{
const ImWchar* imChars = nullptr;
switch (fontInfo.second.glyphRange)
{
case CHS_GLYPH_RANGE::GENERAL:
imChars = imFonts->GetGlyphRangesChineseSimplifiedCommon();
break;
case CHS_GLYPH_RANGE::FULL:
imChars = imFonts->GetGlyphRangesChineseFull();
break;
default:;
}
auto& imChars = fontInfo.second.glyphRanges;
// if the user has explicitly called `removeGlyphRanges` or replaced with new ranges
if (imChars && !thiz->_usedGlyphRanges.contains((uintptr_t)imChars))
imChars = nullptr;

if (imChars == nullptr && thiz->_glyphRanges.contains(fontInfo.second.glyphRangesId))
imChars = thiz->_glyphRanges.at(fontInfo.second.glyphRangesId).data();

auto fontData = FileUtils::getInstance()->getDataFromFile(fontInfo.first);
AXASSERT(!fontData.isNull(), "Cannot load font for IMGUI");
Expand All @@ -388,6 +385,8 @@ void ImGuiPresenter::loadCustomFonts(void* ud)
imFonts->AddFontFromMemoryTTF(buffer, bufferSize, fontInfo.second.fontSize * contentZoomFactor, nullptr,
imChars);
}
// the temporary bucket gets emptied out
thiz->_eraseGlyphRanges.clear();
}

float ImGuiPresenter::enableDPIScale(float userScale)
Expand Down Expand Up @@ -424,11 +423,54 @@ void ImGuiPresenter::setViewResolution(float width, float height)
ImGui_ImplAx_SetViewResolution(width, height);
}

void ImGuiPresenter::addFont(std::string_view fontFile, float fontSize, CHS_GLYPH_RANGE glyphRange)
void ImGuiPresenter::addFont(std::string_view fontFile, float fontSize, GLYPH_RANGES glyphRange)
{
addGlyphRanges(glyphRange);
std::string_view glyphId = getGlyphRangesId(glyphRange);
addFont(fontFile, fontSize, glyphId);
}

void ImGuiPresenter::addFont(std::string_view fontFile, float fontSize, std::string_view glyphRangeId)
{
auto it = _glyphRanges.find(glyphRangeId);
if (it == _glyphRanges.end())
{
addFont(fontFile, fontSize, std::vector<ImWchar>(0));
return;
}

if (FileUtils::getInstance()->isFileExistInternal(fontFile))
{
ImWchar* imChars = it->second.data();

bool isDirty = _fontsInfoMap.emplace(fontFile, FontInfo{fontSize, imChars, std::string(glyphRangeId)}).second;
isDirty |=
_usedGlyphRanges.emplace((uintptr_t)imChars).second || _fontsInfoMap.at(fontFile).glyphRanges != imChars;
if (isDirty)
ImGui_ImplAx_SetDeviceObjectsDirty();
}
}

void ImGuiPresenter::addFont(std::string_view fontFile, float fontSize, const std::vector<ImWchar>& glyphRanges)
{
addFont(fontFile, fontSize, fontFile, glyphRanges);
}

void ImGuiPresenter::addFont(std::string_view fontFile,
float fontSize,
std::string_view glyphRangesId,
const std::vector<ImWchar>& glyphRanges)
{
if (FileUtils::getInstance()->isFileExistInternal(fontFile))
{
if (_fontsInfoMap.emplace(fontFile, FontInfo{fontSize, glyphRange}).second)
ImWchar* imChars = nullptr;
if (!glyphRanges.empty())
imChars = addGlyphRanges(glyphRangesId, glyphRanges);

bool isDirty = _fontsInfoMap.emplace(fontFile, FontInfo{fontSize, imChars, std::string(glyphRangesId)}).second;
isDirty |= imChars && (_usedGlyphRanges.emplace((uintptr_t)imChars).second ||
_fontsInfoMap.at(fontFile).glyphRanges != imChars);
if (isDirty)
ImGui_ImplAx_SetDeviceObjectsDirty();
}
}
Expand Down Expand Up @@ -853,18 +895,110 @@ void ImGuiPresenter::setLabelColor(Label* label, ImGuiCol col)
setLabelColor(label, ImGui::GetStyleColorVec4(col));
}

ImWchar* ImGuiPresenter::addGlyphRanges(GLYPH_RANGES glyphRange)
{
static std::unordered_map<GLYPH_RANGES, size_t> _glyph_ranges_size;
auto imFonts = ImGui::GetIO().Fonts;
const ImWchar* imChars;

switch (glyphRange)
{
case GLYPH_RANGES::DEFAULT:
imChars = imFonts->GetGlyphRangesDefault();
break;
case GLYPH_RANGES::GREEK:
imChars = imFonts->GetGlyphRangesGreek();
break;
case GLYPH_RANGES::KOREAN:
imChars = imFonts->GetGlyphRangesKorean();
break;
case GLYPH_RANGES::CHINESE_GENERAL:
imChars = imFonts->GetGlyphRangesChineseSimplifiedCommon();
break;
case GLYPH_RANGES::CHINESE_FULL:
imChars = imFonts->GetGlyphRangesChineseFull();
break;
case GLYPH_RANGES::JAPANESE:
imChars = imFonts->GetGlyphRangesJapanese();
break;
case GLYPH_RANGES::CYRILLIC:
imChars = imFonts->GetGlyphRangesCyrillic();
break;
case GLYPH_RANGES::THAI:
imChars = imFonts->GetGlyphRangesThai();
break;
case GLYPH_RANGES::VIETNAMESE:
imChars = imFonts->GetGlyphRangesVietnamese();
break;
default:
return nullptr;
}

size_t imCharsSize = 0;
if (_glyph_ranges_size.contains(glyphRange))
imCharsSize = _glyph_ranges_size[glyphRange];
else
{
// must always end with 0
while (imChars[imCharsSize] != 0)
imCharsSize++;
imCharsSize += 1;
_glyph_ranges_size[glyphRange] = imCharsSize;
}
auto glyphId = getGlyphRangesId(glyphRange);

return addGlyphRanges(glyphId, std::vector<ImWchar>(imChars, imChars + imCharsSize));
}

ImWchar* ImGuiPresenter::addGlyphRanges(std::string_view key, const std::vector<ImWchar>& ranges)
{
auto it = glyphRanges.find(key);
// the pointer must be persistant, do not replace
if (it != glyphRanges.end())
return it->second.data();
it = glyphRanges.emplace(key, ranges).first; // glyphRanges[key] = ranges;
auto it = _glyphRanges.find(key);
// store in our temporary bucket if already exists...
if (it != _glyphRanges.end())
{
// so that `loadCustomFonts` can look for the new "replaced" glyph ranges
_usedGlyphRanges.erase((uintptr_t)it->second.data());
// probably automatically gets *moved* but to make our intention more clear
_eraseGlyphRanges.push_back(std::move(it->second));
}
it = _glyphRanges.emplace(key, ranges).first; // _glyphRanges[key] = ranges;
if (ranges.empty())
it->second.push_back(0);
// the `addFont` will call `ImGui_ImplAx_SetDeviceObjectsDirty` if everything is okay,
// no need to call it if no font is using the glyph ranges...
return it->second.data();
}

void ImGuiPresenter::removeGlyphRanges(std::string_view key)
{
auto removeGlyphRange = _glyphRanges.find(key);
if (removeGlyphRange == _glyphRanges.end())
return;

auto usedCount = _usedGlyphRanges.size();
_usedGlyphRanges.erase((uintptr_t)removeGlyphRange->second.data());
_eraseGlyphRanges.push_back(std::move(removeGlyphRange->second));
_glyphRanges.erase(key);

// update the fonts to not use the glyph ranges since user wants to remove it for some reason..
if (_usedGlyphRanges.size() != usedCount)
ImGui_ImplAx_SetDeviceObjectsDirty();
}

void ImGuiPresenter::clearGlyphRanges()
{
auto usedCount = _usedGlyphRanges.size();
for (auto& glyphRange : _glyphRanges)
{
_usedGlyphRanges.erase((uintptr_t)glyphRange.second.data());
_eraseGlyphRanges.push_back(std::move(glyphRange.second));
}
_glyphRanges.clear();

if (_usedGlyphRanges.size() != usedCount)
ImGui_ImplAx_SetDeviceObjectsDirty();
}

void ImGuiPresenter::mergeFontGlyphs(ImFont* dst, ImFont* src, ImWchar start, ImWchar end)
{
if (!dst || !src || start > end)
Expand Down Expand Up @@ -901,4 +1035,31 @@ int ImGuiPresenter::getCCRefId(Object* p)
return (int)hash;
}

std::string_view ImGuiPresenter::getGlyphRangesId(GLYPH_RANGES glyphRanges)
{
switch (glyphRanges)
{
case GLYPH_RANGES::DEFAULT:
return GLYPH_RANGES_DEFAULT_ID;
case GLYPH_RANGES::GREEK:
return GLYPH_RANGES_GREEK_ID;
case GLYPH_RANGES::KOREAN:
return GLYPH_RANGES_KOREAN_ID;
case GLYPH_RANGES::CHINESE_GENERAL:
return GLYPH_RANGES_CHINESE_GENERAL_ID;
case GLYPH_RANGES::CHINESE_FULL:
return GLYPH_RANGES_CHINESE_FULL_ID;
case GLYPH_RANGES::JAPANESE:
return GLYPH_RANGES_JAPANESE_ID;
case GLYPH_RANGES::CYRILLIC:
return GLYPH_RANGES_CYRILLIC_ID;
case GLYPH_RANGES::THAI:
return GLYPH_RANGES_THAI_ID;
case GLYPH_RANGES::VIETNAMESE:
return GLYPH_RANGES_VIETNAMESE_ID;
default:
return "";
}
}

NS_AX_EXT_END
64 changes: 57 additions & 7 deletions extensions/ImGui/src/ImGui/ImGuiPresenter.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,33 @@ class ImGuiPresenter
void cleanup();

public:
enum class CHS_GLYPH_RANGE
inline static const std::string_view GLYPH_RANGES_DEFAULT_ID = "__DEFAULT_GLYPH__";
inline static const std::string_view GLYPH_RANGES_GREEK_ID = "__GREEK_GLYPH__";
inline static const std::string_view GLYPH_RANGES_KOREAN_ID = "__KOREAN_GLYPH__";
inline static const std::string_view GLYPH_RANGES_CHINESE_GENERAL_ID = "__CHINESE_SIMPLIFIED_COMMON_GLYPH__";
inline static const std::string_view GLYPH_RANGES_CHINESE_FULL_ID = "__CHINESE_FULL_GLYPH__";
inline static const std::string_view GLYPH_RANGES_JAPANESE_ID = "__JAPANESE_GLYPH__";
inline static const std::string_view GLYPH_RANGES_CYRILLIC_ID = "__CYRILLIC_GLYPH__";
inline static const std::string_view GLYPH_RANGES_THAI_ID = "__THAI_GLYPH__";
inline static const std::string_view GLYPH_RANGES_VIETNAMESE_ID = "__VIETNAMESE_GLYPH__";

// predefined glyph ranges by imgui
enum class GLYPH_RANGES
{
NONE,
GENERAL,
FULL
DEFAULT,
GREEK,
KOREAN,
CHINESE_GENERAL,
CHINESE_FULL,
JAPANESE,
CYRILLIC,
THAI,
VIETNAMESE
};

static std::string_view getGlyphRangesId(GLYPH_RANGES glyphRanges);

enum
{
DEFAULT_FONT_SIZE = 13 // see imgui.cpp
Expand Down Expand Up @@ -57,8 +77,30 @@ class ImGuiPresenter
/// <param name="fontFile"></param>
/// <param name="glyphRange"></param>
void addFont(std::string_view fontFile,
float fontSize = DEFAULT_FONT_SIZE,
CHS_GLYPH_RANGE glyphRange = CHS_GLYPH_RANGE::NONE);
float fontSize = DEFAULT_FONT_SIZE,
GLYPH_RANGES glyphRange = GLYPH_RANGES::NONE);
/// <summary>
/// Add ImGui font with contentZoomFactor and use pre-existing glyph range for the specified font
/// </summary>
/// <param name="fontFile"></param>
/// <param name="glyphRanges">The glyph range vector must end with 0 and it should be included in the size</param>
void addFont(std::string_view fontFile, float fontSize, std::string_view glyphRangesId);
/// <summary>
/// Add ImGui font with contentZoomFactor and use specified custom glyph range for the specified font
/// </summary>
/// <param name="fontFile"></param>
/// <param name="glyphRange">The glyph range vector must end with 0 and it should be included in the size</param>
void addFont(std::string_view fontFile, float fontSize, const std::vector<ImWchar>& glyphRanges);
/// <summary>
/// Add ImGui font with contentZoomFactor and use custom glyph range and specify a custom id
/// </summary>
/// <param name="fontFile"></param>
/// <param name="glyphRangesId">Custom Lookup Id</param>
/// <param name="glyphRanges">The glyph range vector must end with 0 and it should be included in the size</param>
void addFont(std::string_view fontFile,
float fontSize,
std::string_view glyphRangesId,
const std::vector<ImWchar>& glyphRanges);
void removeFont(std::string_view fontFile);
void clearFonts();

Expand Down Expand Up @@ -124,7 +166,10 @@ class ImGuiPresenter
static void setLabelColor(Label* label, bool disabled = false);
static void setLabelColor(Label* label, ImGuiCol col);

ImWchar* addGlyphRanges(GLYPH_RANGES glyphRange);
ImWchar* addGlyphRanges(std::string_view key, const std::vector<ImWchar>& ranges);
void removeGlyphRanges(std::string_view key);
void clearGlyphRanges();
static void mergeFontGlyphs(ImFont* dst, ImFont* src, ImWchar start, ImWchar end);
int getCCRefId(Object* p);

Expand All @@ -151,7 +196,11 @@ class ImGuiPresenter
std::unordered_map<Object*, int> usedCCRefIdMap;
// cocos objects should be retained until next frame
Vector<Object*> usedCCRef;
hlookup::string_map<std::vector<ImWchar>> glyphRanges;

hlookup::string_map<std::vector<ImWchar>> _glyphRanges;
std::unordered_set<uintptr_t> _usedGlyphRanges; // there should be one intance of "each glyph ranges"
// temporarily stores the current erased/replaced ranges, gets cleared in the next `loadCustomFonts` interation
std::vector<std::vector<ImWchar>> _eraseGlyphRanges;

float _contentZoomFactor = 1.0f;

Expand All @@ -162,7 +211,8 @@ class ImGuiPresenter
struct FontInfo
{
float fontSize;
CHS_GLYPH_RANGE glyphRange;
ImWchar* glyphRanges;
std::string glyphRangesId;
};

hlookup::string_map<FontInfo> _fontsInfoMap;
Expand Down
5 changes: 4 additions & 1 deletion extensions/ImGui/src/ImGui/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ Sync from https://github.com/Xrysnow/cocos2d-x-imgui and do a little changes
* Use ```FOURCC``` for key of ImGui render loop
* Add dpi scale support, use ```ImGuiPresenter::getInstance()->enableDPIScale();```
* Easy font manager, stable API ```addFont,removeFont,clearFonts``` to manage ImGui fonts, with ImGui API, very hard to do correctly.
* Easy to add/use custom or imgui pre-defined glyph ranges ```ImGuiPresenter::GLYPH_RANGES,addGlyphRanges,removeGlyphRanges```
and then specify the glyph ranges id while calling `addFont`
to use those with specific font.

## How to use
```cpp
Expand All @@ -24,7 +27,7 @@ public:
ImGuiPresenter::getInstance()->addFont(R"(C:\Windows\Fonts\msyh.ttc)");
/* 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::GLYPH_RANGES::CHINESE_GENERAL);
*/
ImGuiPresenter::getInstance()->enableDPIScale(); // enable dpi scale for 4K display support, depends at least one valid ttf/ttc font was added.
ImGuiPresenter::getInstance()->addRenderLoop("#im01", AX_CALLBACK_0(GameScene::onImGuiDraw, this), this);
Expand Down
8 changes: 7 additions & 1 deletion extensions/Inspector/src/Inspector/Inspector.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -177,9 +177,15 @@ void Inspector::setFontSize(float fontSize)
_fontSize = fontSize;
}

void Inspector::setFontGlyphId(std::string_view glyphId)
{
_fontGlyphId = std::string(glyphId);
}

void Inspector::init()
{
_fontPath = "fonts/arial.ttf";
_fontSize = ImGuiPresenter::DEFAULT_FONT_SIZE;

addPropertyHandler("__NODE__", std::make_unique<InspectorNodePropertyHandler>());
addPropertyHandler("__SPRITE__", std::make_unique<InspectorSpritePropertyHandler>());
Expand Down Expand Up @@ -399,7 +405,7 @@ void Inspector::openForScene(Scene* target)
}

auto* presenter = ImGuiPresenter::getInstance();
presenter->addFont(FileUtils::getInstance()->fullPathForFilename(_fontPath), _fontSize);
presenter->addFont(FileUtils::getInstance()->fullPathForFilename(_fontPath), _fontSize, _fontGlyphId);
presenter->enableDPIScale();
presenter->addRenderLoop("#insp", AX_CALLBACK_0(Inspector::mainLoop , this), target);
}
Expand Down
Loading

0 comments on commit eb0e6ef

Please sign in to comment.