mirror of
https://github.com/WerWolv/ImHex.git
synced 2026-04-02 13:37:42 -05:00
253
plugins/fonts/include/font_atlas.hpp
Normal file
253
plugins/fonts/include/font_atlas.hpp
Normal file
@@ -0,0 +1,253 @@
|
||||
#pragma once
|
||||
|
||||
#include <imgui.h>
|
||||
#include <imgui_internal.h>
|
||||
#include <imgui_freetype.h>
|
||||
|
||||
#include <memory>
|
||||
#include <list>
|
||||
|
||||
#include <hex/api/event_manager.hpp>
|
||||
#include <hex/api/task_manager.hpp>
|
||||
|
||||
#include <romfs/romfs.hpp>
|
||||
|
||||
#include <wolv/io/file.hpp>
|
||||
|
||||
|
||||
namespace hex::fonts {
|
||||
|
||||
class Font {
|
||||
public:
|
||||
Font() = default;
|
||||
|
||||
float getDescent() const {
|
||||
return m_font->Descent;
|
||||
}
|
||||
|
||||
private:
|
||||
explicit Font(ImFont *font) : m_font(font) { }
|
||||
|
||||
private:
|
||||
friend class FontAtlas;
|
||||
|
||||
ImFont *m_font;
|
||||
};
|
||||
|
||||
class FontAtlas {
|
||||
public:
|
||||
FontAtlas() : m_fontAtlas(IM_NEW(ImFontAtlas)) {
|
||||
enableUnicodeCharacters(false);
|
||||
|
||||
// Set the default configuration for the font atlas
|
||||
m_config.OversampleH = m_config.OversampleV = 1;
|
||||
m_config.PixelSnapH = true;
|
||||
m_config.MergeMode = false;
|
||||
|
||||
// Make sure the font atlas doesn't get too large, otherwise weaker GPUs might reject it
|
||||
m_fontAtlas->Flags |= ImFontAtlasFlags_NoPowerOfTwoHeight;
|
||||
m_fontAtlas->TexDesiredWidth = 4096;
|
||||
}
|
||||
|
||||
FontAtlas(const FontAtlas &) = delete;
|
||||
FontAtlas &operator=(const FontAtlas &) = delete;
|
||||
|
||||
FontAtlas(FontAtlas &&other) noexcept {
|
||||
this->m_fontAtlas = other.m_fontAtlas;
|
||||
other.m_fontAtlas = nullptr;
|
||||
|
||||
this->m_fontSizes = std::move(other.m_fontSizes);
|
||||
this->m_config = other.m_config;
|
||||
this->m_glyphRange = std::move(other.m_glyphRange);
|
||||
this->m_fontData = std::move(other.m_fontData);
|
||||
}
|
||||
|
||||
FontAtlas &operator=(FontAtlas &&other) noexcept {
|
||||
this->m_fontAtlas = other.m_fontAtlas;
|
||||
other.m_fontAtlas = nullptr;
|
||||
|
||||
this->m_fontSizes = std::move(other.m_fontSizes);
|
||||
this->m_config = other.m_config;
|
||||
this->m_glyphRange = std::move(other.m_glyphRange);
|
||||
this->m_fontData = std::move(other.m_fontData);
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
~FontAtlas() {
|
||||
if (m_fontAtlas != nullptr)
|
||||
IM_DELETE(m_fontAtlas);
|
||||
}
|
||||
|
||||
Font addDefaultFont() {
|
||||
ImFontConfig config = m_config;
|
||||
config.FontBuilderFlags |= ImGuiFreeTypeBuilderFlags_Monochrome | ImGuiFreeTypeBuilderFlags_MonoHinting;
|
||||
config.SizePixels = std::floor(getAdjustedFontSize(ImHexApi::System::getGlobalScale() * 13.0F));
|
||||
|
||||
auto font = m_fontAtlas->AddFontDefault(&config);
|
||||
m_fontSizes.emplace_back(false, config.SizePixels);
|
||||
|
||||
m_config.MergeMode = true;
|
||||
|
||||
return Font(font);
|
||||
}
|
||||
|
||||
Font addFontFromMemory(const std::vector<u8> &fontData, float fontSize, bool scalable, ImVec2 offset, const ImVector<ImWchar> &glyphRange = {}) {
|
||||
auto &storedFontData = m_fontData.emplace_back(fontData);
|
||||
|
||||
ImFontConfig config = m_config;
|
||||
config.FontDataOwnedByAtlas = false;
|
||||
|
||||
config.GlyphOffset = { offset.x, offset.y };
|
||||
auto font = m_fontAtlas->AddFontFromMemoryTTF(storedFontData.data(), int(storedFontData.size()), getAdjustedFontSize(fontSize), &config, !glyphRange.empty() ? glyphRange.Data : m_glyphRange.Data);
|
||||
m_fontSizes.emplace_back(scalable, fontSize);
|
||||
|
||||
m_config.MergeMode = true;
|
||||
|
||||
return Font(font);
|
||||
}
|
||||
|
||||
Font addFontFromRomFs(const std::fs::path &path, float fontSize, bool scalable, ImVec2 offset, const ImVector<ImWchar> &glyphRange = {}) {
|
||||
auto data = romfs::get(path).span<u8>();
|
||||
return addFontFromMemory({ data.begin(), data.end() }, fontSize, scalable, offset, glyphRange);
|
||||
}
|
||||
|
||||
Font addFontFromFile(const std::fs::path &path, float fontSize, bool scalable, ImVec2 offset, const ImVector<ImWchar> &glyphRange = {}) {
|
||||
wolv::io::File file(path, wolv::io::File::Mode::Read);
|
||||
|
||||
auto data = file.readVector();
|
||||
return addFontFromMemory(data, fontSize, scalable, offset, glyphRange);
|
||||
}
|
||||
|
||||
void setBold(bool enabled) {
|
||||
if (enabled)
|
||||
m_config.FontBuilderFlags |= ImGuiFreeTypeBuilderFlags_Bold;
|
||||
else
|
||||
m_config.FontBuilderFlags &= ~ImGuiFreeTypeBuilderFlags_Bold;
|
||||
}
|
||||
|
||||
void setItalic(bool enabled) {
|
||||
if (enabled)
|
||||
m_config.FontBuilderFlags |= ImGuiFreeTypeBuilderFlags_Oblique;
|
||||
else
|
||||
m_config.FontBuilderFlags &= ~ImGuiFreeTypeBuilderFlags_Oblique;
|
||||
}
|
||||
|
||||
void setAntiAliasing(bool enabled) {
|
||||
if (enabled)
|
||||
m_config.FontBuilderFlags &= ~ImGuiFreeTypeBuilderFlags_Monochrome | ImGuiFreeTypeBuilderFlags_MonoHinting;
|
||||
else
|
||||
m_config.FontBuilderFlags |= ImGuiFreeTypeBuilderFlags_Monochrome | ImGuiFreeTypeBuilderFlags_MonoHinting;
|
||||
}
|
||||
|
||||
void enableUnicodeCharacters(bool enabled) {
|
||||
ImFontGlyphRangesBuilder glyphRangesBuilder;
|
||||
|
||||
{
|
||||
constexpr static std::array<ImWchar, 3> controlCodeRange = { 0x0001, 0x001F, 0 };
|
||||
constexpr static std::array<ImWchar, 3> extendedAsciiRange = { 0x007F, 0x00FF, 0 };
|
||||
constexpr static std::array<ImWchar, 3> latinExtendedARange = { 0x0100, 0x017F, 0 };
|
||||
|
||||
glyphRangesBuilder.AddRanges(controlCodeRange.data());
|
||||
glyphRangesBuilder.AddRanges(m_fontAtlas->GetGlyphRangesDefault());
|
||||
glyphRangesBuilder.AddRanges(extendedAsciiRange.data());
|
||||
glyphRangesBuilder.AddRanges(latinExtendedARange.data());
|
||||
}
|
||||
|
||||
if (enabled) {
|
||||
constexpr static std::array<ImWchar, 3> fullRange = { 0x0180, 0xFFEF, 0 };
|
||||
|
||||
glyphRangesBuilder.AddRanges(fullRange.data());
|
||||
} else {
|
||||
glyphRangesBuilder.AddRanges(m_fontAtlas->GetGlyphRangesJapanese());
|
||||
glyphRangesBuilder.AddRanges(m_fontAtlas->GetGlyphRangesChineseFull());
|
||||
glyphRangesBuilder.AddRanges(m_fontAtlas->GetGlyphRangesCyrillic());
|
||||
glyphRangesBuilder.AddRanges(m_fontAtlas->GetGlyphRangesKorean());
|
||||
glyphRangesBuilder.AddRanges(m_fontAtlas->GetGlyphRangesThai());
|
||||
glyphRangesBuilder.AddRanges(m_fontAtlas->GetGlyphRangesVietnamese());
|
||||
glyphRangesBuilder.AddText("⌘⌥⌃⇧⏎⇥⌫⇪");
|
||||
}
|
||||
|
||||
m_glyphRange.clear();
|
||||
glyphRangesBuilder.BuildRanges(&m_glyphRange);
|
||||
}
|
||||
|
||||
bool build() const {
|
||||
return m_fontAtlas->Build();
|
||||
}
|
||||
|
||||
[[nodiscard]] ImFontAtlas* getAtlas() {
|
||||
auto result = m_fontAtlas;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
float calculateFontDescend(const ImHexApi::Fonts::Font &font, float fontSize) const {
|
||||
auto atlas = std::make_unique<ImFontAtlas>();
|
||||
auto cfg = m_config;
|
||||
|
||||
// Calculate the expected font size
|
||||
auto size = fontSize;
|
||||
if (font.defaultSize.has_value())
|
||||
size = font.defaultSize.value() * std::max(1.0F, std::floor(ImHexApi::System::getGlobalScale()));
|
||||
else
|
||||
size = std::max(1.0F, std::floor(size / ImHexApi::Fonts::DefaultFontSize)) * ImHexApi::Fonts::DefaultFontSize;
|
||||
|
||||
cfg.MergeMode = false;
|
||||
cfg.SizePixels = size;
|
||||
cfg.FontDataOwnedByAtlas = false;
|
||||
|
||||
// Construct a range that only contains the first glyph of the font
|
||||
ImVector<ImWchar> queryRange;
|
||||
{
|
||||
auto firstGlyph = font.glyphRanges.empty() ? m_glyphRange.front() : font.glyphRanges.front().begin;
|
||||
queryRange.push_back(firstGlyph);
|
||||
queryRange.push_back(firstGlyph);
|
||||
}
|
||||
queryRange.push_back(0x00);
|
||||
|
||||
// Build the font atlas with the query range
|
||||
auto newFont = atlas->AddFontFromMemoryTTF(const_cast<u8 *>(font.fontData.data()), int(font.fontData.size()), 0, &cfg, queryRange.Data);
|
||||
atlas->Build();
|
||||
|
||||
return newFont->Descent;
|
||||
}
|
||||
|
||||
void reset() {
|
||||
/*IM_DELETE(m_fontAtlas);
|
||||
m_fontAtlas = IM_NEW(ImFontAtlas);*/
|
||||
|
||||
m_fontData.clear();
|
||||
m_config.MergeMode = false;
|
||||
}
|
||||
|
||||
void updateFontScaling(float newScaling) {
|
||||
for (int i = 0; i < m_fontAtlas->ConfigData.size(); i += 1) {
|
||||
const auto &[scalable, fontSize] = m_fontSizes[i];
|
||||
auto &configData = m_fontAtlas->ConfigData[i];
|
||||
|
||||
if (!scalable) {
|
||||
configData.SizePixels = fontSize * std::floor(newScaling);
|
||||
} else {
|
||||
configData.SizePixels = fontSize * newScaling;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
float getAdjustedFontSize(float fontSize) const {
|
||||
// Since macOS reports half the framebuffer size that's actually available,
|
||||
// we'll multiply all font sizes by that and then divide the global font scale
|
||||
// by the same amount to get super crisp font rendering.
|
||||
return fontSize * hex::ImHexApi::System::getBackingScaleFactor();
|
||||
}
|
||||
|
||||
private:
|
||||
ImFontAtlas* m_fontAtlas;
|
||||
std::vector<std::pair<bool, float>> m_fontSizes;
|
||||
ImFontConfig m_config;
|
||||
ImVector<ImWchar> m_glyphRange;
|
||||
|
||||
std::list<std::vector<u8>> m_fontData;
|
||||
};
|
||||
|
||||
}
|
||||
59
plugins/fonts/include/font_settings.hpp
Normal file
59
plugins/fonts/include/font_settings.hpp
Normal file
@@ -0,0 +1,59 @@
|
||||
#pragma once
|
||||
|
||||
#include <hex/api/content_registry.hpp>
|
||||
|
||||
namespace hex::fonts {
|
||||
|
||||
class FontFilePicker : public ContentRegistry::Settings::Widgets::FilePicker {
|
||||
public:
|
||||
bool draw(const std::string &name) override;
|
||||
|
||||
bool isPixelPerfectFontSelected() const;
|
||||
const std::string& getSelectedFontName() const;
|
||||
|
||||
void load(const nlohmann::json& data) override;
|
||||
nlohmann::json store() override;
|
||||
|
||||
private:
|
||||
bool updateSelectedFontName();
|
||||
|
||||
private:
|
||||
std::string m_selectedFontName;
|
||||
bool m_pixelPerfectFont = false;
|
||||
};
|
||||
|
||||
class SliderPoints : public ContentRegistry::Settings::Widgets::SliderFloat {
|
||||
public:
|
||||
SliderPoints(float defaultValue, float min, float max) : SliderFloat(defaultValue, min, max) { }
|
||||
bool draw(const std::string &name) override;
|
||||
};
|
||||
|
||||
|
||||
class FontSelector : public ContentRegistry::Settings::Widgets::Widget {
|
||||
public:
|
||||
FontSelector() : m_fontSize(16, 2, 100), m_bold(false), m_italic(false), m_antiAliased(true) { }
|
||||
|
||||
bool draw(const std::string &name) override;
|
||||
|
||||
nlohmann::json store() override;
|
||||
void load(const nlohmann::json& data) override;
|
||||
|
||||
[[nodiscard]] const std::fs::path& getFontPath() const;
|
||||
[[nodiscard]] bool isPixelPerfectFont() const;
|
||||
[[nodiscard]] float getFontSize() const;
|
||||
[[nodiscard]] bool isBold() const;
|
||||
[[nodiscard]] bool isItalic() const;
|
||||
[[nodiscard]] bool isAntiAliased() const;
|
||||
|
||||
private:
|
||||
bool drawPopup();
|
||||
|
||||
private:
|
||||
FontFilePicker m_fontFilePicker;
|
||||
SliderPoints m_fontSize;
|
||||
ContentRegistry::Settings::Widgets::Checkbox m_bold, m_italic, m_antiAliased;
|
||||
};
|
||||
|
||||
ContentRegistry::Settings::Widgets::Widget::Interface& addFontSettingsWidget(UnlocalizedString name);
|
||||
|
||||
}
|
||||
11
plugins/fonts/include/fonts/fonts.hpp
Normal file
11
plugins/fonts/include/fonts/fonts.hpp
Normal file
@@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
#include <hex/api/imhex_api.hpp>
|
||||
|
||||
namespace hex::fonts {
|
||||
|
||||
const static auto Default = []{ return ImHexApi::Fonts::getFont("hex.fonts.font.default"); };
|
||||
const static auto HexEditor = []{ return ImHexApi::Fonts::getFont("hex.fonts.font.hex_editor"); };
|
||||
const static auto CodeEditor = []{ return ImHexApi::Fonts::getFont("hex.fonts.font.code_editor"); };
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user