diff --git a/extensions/ImGui/src/ImGui/ImGuiPresenter.cpp b/extensions/ImGui/src/ImGui/ImGuiPresenter.cpp index 6e8263691e89..c7fc5a6c5544 100644 --- a/extensions/ImGui/src/ImGui/ImGuiPresenter.cpp +++ b/extensions/ImGui/src/ImGui/ImGuiPresenter.cpp @@ -16,6 +16,7 @@ NS_AX_EXT_BEGIN + namespace { uint32_t fourccValue(std::string_view str) @@ -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"); @@ -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) @@ -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(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& glyphRanges) +{ + addFont(fontFile, fontSize, fontFile, glyphRanges); +} + +void ImGuiPresenter::addFont(std::string_view fontFile, + float fontSize, + std::string_view glyphRangesId, + const std::vector& 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(); } } @@ -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; + 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(imChars, imChars + imCharsSize)); +} + ImWchar* ImGuiPresenter::addGlyphRanges(std::string_view key, const std::vector& 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) @@ -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 diff --git a/extensions/ImGui/src/ImGui/ImGuiPresenter.h b/extensions/ImGui/src/ImGui/ImGuiPresenter.h index 1c6294c14347..1272486f928f 100644 --- a/extensions/ImGui/src/ImGui/ImGuiPresenter.h +++ b/extensions/ImGui/src/ImGui/ImGuiPresenter.h @@ -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 @@ -57,8 +77,30 @@ class ImGuiPresenter /// /// 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); + /// + /// Add ImGui font with contentZoomFactor and use pre-existing glyph range for the specified font + /// + /// + /// The glyph range vector must end with 0 and it should be included in the size + void addFont(std::string_view fontFile, float fontSize, std::string_view glyphRangesId); + /// + /// Add ImGui font with contentZoomFactor and use specified custom glyph range for the specified font + /// + /// + /// The glyph range vector must end with 0 and it should be included in the size + void addFont(std::string_view fontFile, float fontSize, const std::vector& glyphRanges); + /// + /// Add ImGui font with contentZoomFactor and use custom glyph range and specify a custom id + /// + /// + /// Custom Lookup Id + /// The glyph range vector must end with 0 and it should be included in the size + void addFont(std::string_view fontFile, + float fontSize, + std::string_view glyphRangesId, + const std::vector& glyphRanges); void removeFont(std::string_view fontFile); void clearFonts(); @@ -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& ranges); + void removeGlyphRanges(std::string_view key); + void clearGlyphRanges(); static void mergeFontGlyphs(ImFont* dst, ImFont* src, ImWchar start, ImWchar end); int getCCRefId(Object* p); @@ -151,7 +196,11 @@ class ImGuiPresenter std::unordered_map usedCCRefIdMap; // cocos objects should be retained until next frame Vector usedCCRef; - hlookup::string_map> glyphRanges; + + hlookup::string_map> _glyphRanges; + std::unordered_set _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> _eraseGlyphRanges; float _contentZoomFactor = 1.0f; @@ -162,7 +211,8 @@ class ImGuiPresenter struct FontInfo { float fontSize; - CHS_GLYPH_RANGE glyphRange; + ImWchar* glyphRanges; + std::string glyphRangesId; }; hlookup::string_map _fontsInfoMap; diff --git a/extensions/ImGui/src/ImGui/README.md b/extensions/ImGui/src/ImGui/README.md index 157b7f34c4d5..65122e845c4f 100644 --- a/extensions/ImGui/src/ImGui/README.md +++ b/extensions/ImGui/src/ImGui/README.md @@ -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 @@ -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); diff --git a/extensions/Inspector/src/Inspector/Inspector.cpp b/extensions/Inspector/src/Inspector/Inspector.cpp index de11e331ea66..d840bd395287 100644 --- a/extensions/Inspector/src/Inspector/Inspector.cpp +++ b/extensions/Inspector/src/Inspector/Inspector.cpp @@ -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()); addPropertyHandler("__SPRITE__", std::make_unique()); @@ -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); } diff --git a/extensions/Inspector/src/Inspector/Inspector.h b/extensions/Inspector/src/Inspector/Inspector.h index fc15f598ff4f..7354f30de763 100644 --- a/extensions/Inspector/src/Inspector/Inspector.h +++ b/extensions/Inspector/src/Inspector/Inspector.h @@ -65,10 +65,12 @@ class Inspector void setAutoAddToScenes(bool autoAdd); - std::string_view getFontPath() { return _fontPath; } - float getFontSize() { return _fontSize; } + std::string_view getFontPath() const { return _fontPath; } + float getFontSize() const { return _fontSize; } + std::string_view getFontGlyphId() const { return _fontGlyphId; } void setFontPath(std::string_view fontPath); void setFontSize(float fontSize); + void setFontGlyphId(std::string_view glyphId); private: void init(); @@ -86,7 +88,8 @@ class Inspector bool _autoAddToScenes = false; std::string _fontPath; - float _fontSize = 13; // ImGuiPresenter.h + float _fontSize; + std::string _fontGlyphId; }; NS_AX_EXT_END diff --git a/extensions/SDFGen/src/SDFGen/SDFGen.cpp b/extensions/SDFGen/src/SDFGen/SDFGen.cpp index 8fb1f0188102..df5550b0935a 100644 --- a/extensions/SDFGen/src/SDFGen/SDFGen.cpp +++ b/extensions/SDFGen/src/SDFGen/SDFGen.cpp @@ -221,7 +221,7 @@ void SDFGen::open(ax::Scene* scene) 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::GLYPH_RANGES::CHINESE_GENERAL); */ ImGuiPresenter::getInstance()->enableDPIScale(); // enable dpi scale for 4K display support, depends at least one // valid ttf/ttc font was added.