ImFontAtlas: new AddFont() API, oversampling, subpositiong, merging fonts, etc. (#182, #220, #232, #242)
This commit is contained in:
parent
6ae8062ca0
commit
815168c7ef
@ -53,16 +53,41 @@
|
||||
LOADING INSTRUCTIONS
|
||||
---------------------------------
|
||||
|
||||
Load default font with:
|
||||
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
io.Fonts->AddFontDefault();
|
||||
|
||||
Load .TTF file with:
|
||||
|
||||
ImGuiIO& io = ImGui::GetIO();
|
||||
io.Fonts->AddFontFromFileTTF("myfontfile.ttf", size_pixels);
|
||||
|
||||
Add a third parameter to bake specific font ranges:
|
||||
Detailed options:
|
||||
|
||||
io.Fonts->LoadFromFileTTF("myfontfile.ttf", size_pixels, io.Fonts->GetGlyphRangesDefault()); // Basic Latin, Extended Latin
|
||||
io.Fonts->LoadFromFileTTF("myfontfile.ttf", size_pixels, io.Fonts->GetGlyphRangesJapanese()); // Default + Hiragana, Katakana, Half-Width, Selection of 1946 Ideographs
|
||||
io.Fonts->LoadFromFileTTF("myfontfile.ttf", size_pixels, io.Fonts->GetGlyphRangesChinese()); // Include full set of about 21000 CJK Unified Ideographs
|
||||
ImFontConfig config;
|
||||
config.OversampleH = 3;
|
||||
config.OversampleV = 3;
|
||||
config.GlyphExtraSpacing.x = 1.0f;
|
||||
io.Fonts->LoadFromFileTTF("myfontfile.ttf", size_pixels, &config);
|
||||
|
||||
Merge two fonts:
|
||||
|
||||
// Load main font
|
||||
io.Fonts->AddFontDefault();
|
||||
|
||||
// Add character ranges and merge into main font
|
||||
ImWchar ranges[] = { 0xf000, 0xf3ff, 0 };
|
||||
ImFontConfig config;
|
||||
config.MergeMode = true;
|
||||
io.Fonts->LoadFromFileTTF("fontawesome-webfont.ttf", 16.0f, &config, ranges);
|
||||
io.Fonts->LoadFromFileTTF("myfontfile.ttf", size_pixels, NULL, &config, io.Fonts->GetGlyphRangesJapanese());
|
||||
|
||||
Add a fourth parameter to bake specific font ranges only:
|
||||
|
||||
io.Fonts->LoadFromFileTTF("myfontfile.ttf", size_pixels, NULL, io.Fonts->GetGlyphRangesDefault()); // Basic Latin, Extended Latin
|
||||
io.Fonts->LoadFromFileTTF("myfontfile.ttf", size_pixels, NULL, io.Fonts->GetGlyphRangesJapanese()); // Default + Hiragana, Katakana, Half-Width, Selection of 1946 Ideographs
|
||||
io.Fonts->LoadFromFileTTF("myfontfile.ttf", size_pixels, NULL, io.Fonts->GetGlyphRangesChinese()); // Include full set of about 21000 CJK Unified Ideographs
|
||||
|
||||
Offset font vertically by altering the io.Font->DisplayOffset value:
|
||||
|
||||
|
286
imgui.cpp
286
imgui.cpp
@ -138,6 +138,7 @@
|
||||
Occasionally introducing changes that are breaking the API. The breakage are generally minor and easy to fix.
|
||||
Here is a change-log of API breaking changes, if you are using one of the functions listed, expect to have to fix some code.
|
||||
|
||||
- 2015/07/14 (1.43) - add new ImFontAtlas::AddFont() API. For the old AddFont***, moved the 'font_no' parameter of ImFontAtlas::AddFont** functions to the ImFontConfig structure.
|
||||
- 2015/07/08 (1.43) - switched rendering data to use indexed rendering. this is saving a fair amount of CPU/GPU and enables us to get anti-aliasing for a marginal cost.
|
||||
this necessary change will break your rendering function! the fix should be very easy. sorry for that :(
|
||||
- if you are using a vanilla copy of one of the imgui_impl_XXXX.cpp provided in the example, you just need to update your copy and you can ignore the rest.
|
||||
@ -333,12 +334,26 @@
|
||||
// the first loaded font gets used by default
|
||||
// use ImGui::PushFont()/ImGui::PopFont() to change the font at runtime
|
||||
|
||||
// Options
|
||||
ImFontConfig config;
|
||||
config.OversampleH = 3;
|
||||
config.OversampleV = 3;
|
||||
config.GlyphExtraSpacing.x = 1.0f;
|
||||
io.Fonts->LoadFromFileTTF("myfontfile.ttf", size_pixels, &config);
|
||||
|
||||
// Merging input from different fonts into one
|
||||
ImWchar ranges[] = { 0xf000, 0xf3ff, 0 };
|
||||
ImFontConfig config;
|
||||
config.MergeMode = true;
|
||||
io.Fonts->AddFontDefault();
|
||||
io.Fonts->LoadFromFileTTF("fontawesome-webfont.ttf", 16.0f, &config, ranges);
|
||||
io.Fonts->LoadFromFileTTF("myfontfile.ttf", size_pixels, NULL, &config, io.Fonts->GetGlyphRangesJapanese());
|
||||
|
||||
Q: How can I display and input non-latin characters such as Chinese, Japanese, Korean, Cyrillic?
|
||||
A: When loading a font, pass custom Unicode ranges to specify the glyphs to load. ImGui will support UTF-8 encoding across the board.
|
||||
Character input depends on you passing the right character code to io.AddInputCharacter(). The example applications do that.
|
||||
|
||||
io.Fonts->AddFontFromFileTTF("myfontfile.ttf", size_in_pixels, io.Fonts->GetGlyphRangesJapanese()); // Load Japanese characters
|
||||
io.Fonts->AddFontFromFileTTF("myfontfile.ttf", size_in_pixels, NULL, io.Fonts->GetGlyphRangesJapanese()); // Load Japanese characters
|
||||
io.Fonts->GetTexDataAsRGBA32() or GetTexDataAsAlpha8()
|
||||
io.ImeWindowHandle = MY_HWND; // To input using Microsoft IME, give ImGui the hwnd of your application
|
||||
|
||||
@ -9645,23 +9660,23 @@ void ImDrawData::DeIndexAllBuffers()
|
||||
// ImFontAtlias
|
||||
//-----------------------------------------------------------------------------
|
||||
|
||||
struct ImFontAtlas::ImFontAtlasData
|
||||
ImFontConfig::ImFontConfig()
|
||||
{
|
||||
// Input
|
||||
ImFont* OutFont; // Load into this font
|
||||
void* TTFData; // TTF data, we own the memory
|
||||
int TTFDataSize; // TTF data size, in bytes
|
||||
float SizePixels; // Desired output size, in pixels
|
||||
const ImWchar* GlyphRanges; // List of Unicode range (2 value per range, values are inclusive, zero-terminated list)
|
||||
int FontNo; // Index of font within .TTF file (0)
|
||||
|
||||
// Temporary Build Data
|
||||
stbtt_fontinfo FontInfo;
|
||||
stbrp_rect* Rects;
|
||||
stbtt_pack_range* Ranges;
|
||||
int RangesCount;
|
||||
int OversampleH, OversampleV;
|
||||
};
|
||||
FontData = NULL;
|
||||
FontDataSize = 0;
|
||||
FontDataOwnedByAtlas = true;
|
||||
FontNo = 0;
|
||||
SizePixels = 0.0f;
|
||||
OversampleH = 3;
|
||||
OversampleV = 1;
|
||||
PixelSnapH = false;
|
||||
GlyphExtraSpacing = ImVec2(0.0f, 0.0f);
|
||||
GlyphRanges = NULL;
|
||||
MergeMode = false;
|
||||
MergeGlyphCenterV = false;
|
||||
DstFont = NULL;
|
||||
memset(Name, 0, sizeof(Name));
|
||||
}
|
||||
|
||||
ImFontAtlas::ImFontAtlas()
|
||||
{
|
||||
@ -9679,13 +9694,12 @@ ImFontAtlas::~ImFontAtlas()
|
||||
|
||||
void ImFontAtlas::ClearInputData()
|
||||
{
|
||||
for (int i = 0; i < InputData.Size; i++)
|
||||
{
|
||||
if (InputData[i]->TTFData)
|
||||
ImGui::MemFree(InputData[i]->TTFData);
|
||||
ImGui::MemFree(InputData[i]);
|
||||
}
|
||||
InputData.clear();
|
||||
for (int i = 0; i < ConfigData.Size; i++)
|
||||
if (ConfigData[i].FontData && ConfigData[i].FontDataOwnedByAtlas)
|
||||
{
|
||||
ImGui::MemFree(ConfigData[i].FontData);
|
||||
ConfigData[i].FontData = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
void ImFontAtlas::ClearTexData()
|
||||
@ -9720,7 +9734,7 @@ void ImFontAtlas::GetTexDataAsAlpha8(unsigned char** out_pixels, int* out_wid
|
||||
// Lazily build
|
||||
if (TexPixelsAlpha8 == NULL)
|
||||
{
|
||||
if (InputData.empty())
|
||||
if (ConfigData.empty())
|
||||
AddFontDefault();
|
||||
Build();
|
||||
}
|
||||
@ -9752,6 +9766,27 @@ void ImFontAtlas::GetTexDataAsRGBA32(unsigned char** out_pixels, int* out_wid
|
||||
if (out_bytes_per_pixel) *out_bytes_per_pixel = 4;
|
||||
}
|
||||
|
||||
ImFont* ImFontAtlas::AddFont(const ImFontConfig* font_cfg)
|
||||
{
|
||||
IM_ASSERT(font_cfg->FontData != NULL && font_cfg->FontDataSize > 0);
|
||||
IM_ASSERT(font_cfg->SizePixels > 0.0f);
|
||||
|
||||
// Create new font
|
||||
if (!font_cfg->MergeMode)
|
||||
{
|
||||
ImFont* font = (ImFont*)ImGui::MemAlloc(sizeof(ImFont));
|
||||
new (font) ImFont();
|
||||
Fonts.push_back(font);
|
||||
}
|
||||
|
||||
ConfigData.push_back(*font_cfg);
|
||||
ConfigData.back().DstFont = Fonts.back();
|
||||
|
||||
// Invalidate texture
|
||||
ClearTexData();
|
||||
return Fonts.back();
|
||||
}
|
||||
|
||||
static void GetDefaultCompressedFontDataTTF(const void** ttf_compressed_data, unsigned int* ttf_compressed_size);
|
||||
static unsigned int stb_decompress_length(unsigned char *input);
|
||||
static unsigned int stb_decompress(unsigned char *output, unsigned char *i, unsigned int length);
|
||||
@ -9762,10 +9797,14 @@ ImFont* ImFontAtlas::AddFontDefault()
|
||||
unsigned int ttf_compressed_size;
|
||||
const void* ttf_compressed;
|
||||
GetDefaultCompressedFontDataTTF(&ttf_compressed, &ttf_compressed_size);
|
||||
return AddFontFromMemoryCompressedTTF(ttf_compressed, ttf_compressed_size, 13.0f, GetGlyphRangesDefault(), 0);
|
||||
ImFontConfig font_cfg;
|
||||
font_cfg.OversampleH = font_cfg.OversampleV = 1;
|
||||
font_cfg.PixelSnapH = true;
|
||||
strcpy(font_cfg.Name, "<default>");
|
||||
return AddFontFromMemoryCompressedTTF(ttf_compressed, ttf_compressed_size, 13.0f, &font_cfg, GetGlyphRangesDefault());
|
||||
}
|
||||
|
||||
ImFont* ImFontAtlas::AddFontFromFileTTF(const char* filename, float size_pixels, const ImWchar* glyph_ranges, int font_no)
|
||||
ImFont* ImFontAtlas::AddFontFromFileTTF(const char* filename, float size_pixels, const ImFontConfig* font_cfg_template, const ImWchar* glyph_ranges)
|
||||
{
|
||||
void* data = NULL;
|
||||
int data_size = 0;
|
||||
@ -9774,67 +9813,82 @@ ImFont* ImFontAtlas::AddFontFromFileTTF(const char* filename, float size_pixels,
|
||||
IM_ASSERT(0); // Could not load file.
|
||||
return NULL;
|
||||
}
|
||||
return AddFontFromMemoryTTF(data, data_size, size_pixels, glyph_ranges, font_no);
|
||||
ImFontConfig font_cfg = font_cfg_template ? *font_cfg_template : ImFontConfig();
|
||||
if (font_cfg.Name[0] == 0)
|
||||
{
|
||||
const char* p;
|
||||
for (p = filename + strlen(filename); p > filename && p[-1] != '/' && p[-1] != '\\'; p--) {}
|
||||
ImFormatString(font_cfg.Name, IM_ARRAYSIZE(font_cfg.Name), "%s", p);
|
||||
}
|
||||
return AddFontFromMemoryTTF(data, data_size, size_pixels, &font_cfg, glyph_ranges);
|
||||
}
|
||||
|
||||
// Transfer ownership of 'ttf_data' to ImFontAtlas, will be deleted after Build()
|
||||
ImFont* ImFontAtlas::AddFontFromMemoryTTF(void* ttf_data, int ttf_size, float size_pixels, const ImWchar* glyph_ranges, int font_no)
|
||||
ImFont* ImFontAtlas::AddFontFromMemoryTTF(void* ttf_data, int ttf_size, float size_pixels, const ImFontConfig* font_cfg_template, const ImWchar* glyph_ranges)
|
||||
{
|
||||
// Create new font
|
||||
ImFont* font = (ImFont*)ImGui::MemAlloc(sizeof(ImFont));
|
||||
new (font) ImFont();
|
||||
Fonts.push_back(font);
|
||||
|
||||
// Add to build list
|
||||
ImFontAtlasData* data = (ImFontAtlasData*)ImGui::MemAlloc(sizeof(ImFontAtlasData));
|
||||
memset(data, 0, sizeof(ImFontAtlasData));
|
||||
data->OutFont = font;
|
||||
data->TTFData = ttf_data;
|
||||
data->TTFDataSize = ttf_size;
|
||||
data->SizePixels = size_pixels;
|
||||
data->OversampleH = data->OversampleV = 1;
|
||||
data->GlyphRanges = glyph_ranges;
|
||||
data->FontNo = font_no;
|
||||
InputData.push_back(data);
|
||||
|
||||
// Invalidate texture
|
||||
ClearTexData();
|
||||
|
||||
return font;
|
||||
ImFontConfig font_cfg = font_cfg_template ? *font_cfg_template : ImFontConfig();
|
||||
IM_ASSERT(font_cfg.FontData == NULL);
|
||||
font_cfg.FontData = ttf_data;
|
||||
font_cfg.FontDataSize = ttf_size;
|
||||
font_cfg.SizePixels = size_pixels;
|
||||
if (glyph_ranges)
|
||||
font_cfg.GlyphRanges = glyph_ranges;
|
||||
if (!font_cfg.FontDataOwnedByAtlas)
|
||||
{
|
||||
font_cfg.FontData = ImGui::MemAlloc(ttf_size);
|
||||
font_cfg.FontDataOwnedByAtlas = true;
|
||||
memcpy(font_cfg.FontData, ttf_data, (size_t)ttf_size);
|
||||
}
|
||||
return AddFont(&font_cfg);
|
||||
}
|
||||
|
||||
ImFont* ImFontAtlas::AddFontFromMemoryCompressedTTF(const void* compressed_ttf_data, int compressed_ttf_size, float size_pixels, const ImWchar* glyph_ranges, int font_no)
|
||||
ImFont* ImFontAtlas::AddFontFromMemoryCompressedTTF(const void* compressed_ttf_data, int compressed_ttf_size, float size_pixels, const ImFontConfig* font_cfg_template, const ImWchar* glyph_ranges)
|
||||
{
|
||||
const unsigned int buf_decompressed_size = stb_decompress_length((unsigned char*)compressed_ttf_data);
|
||||
unsigned char* buf_decompressed_data = (unsigned char *)ImGui::MemAlloc(buf_decompressed_size);
|
||||
stb_decompress(buf_decompressed_data, (unsigned char*)compressed_ttf_data, (unsigned int)compressed_ttf_size);
|
||||
return AddFontFromMemoryTTF(buf_decompressed_data, (int)buf_decompressed_size, size_pixels, glyph_ranges, font_no);
|
||||
|
||||
ImFontConfig font_cfg = font_cfg_template ? *font_cfg_template : ImFontConfig();
|
||||
IM_ASSERT(font_cfg.FontData == NULL);
|
||||
font_cfg.FontDataOwnedByAtlas = true;
|
||||
return AddFontFromMemoryTTF(buf_decompressed_data, (int)buf_decompressed_size, size_pixels, font_cfg_template, glyph_ranges);
|
||||
}
|
||||
|
||||
bool ImFontAtlas::Build()
|
||||
{
|
||||
IM_ASSERT(InputData.Size > 0);
|
||||
IM_ASSERT(ConfigData.Size > 0);
|
||||
|
||||
TexID = NULL;
|
||||
TexWidth = TexHeight = 0;
|
||||
TexUvWhitePixel = ImVec2(0, 0);
|
||||
ClearTexData();
|
||||
|
||||
struct ImFontTempBuildData
|
||||
{
|
||||
stbtt_fontinfo FontInfo;
|
||||
stbrp_rect* Rects;
|
||||
stbtt_pack_range* Ranges;
|
||||
int RangesCount;
|
||||
};
|
||||
ImFontTempBuildData* tmp_array = (ImFontTempBuildData*)ImGui::MemAlloc((size_t)ConfigData.Size * sizeof(ImFontTempBuildData));
|
||||
|
||||
// Initialize font information early (so we can error without any cleanup) + count glyphs
|
||||
int total_glyph_count = 0;
|
||||
int total_glyph_range_count = 0;
|
||||
for (int input_i = 0; input_i < InputData.Size; input_i++)
|
||||
for (int input_i = 0; input_i < ConfigData.Size; input_i++)
|
||||
{
|
||||
ImFontAtlasData& data = *InputData[input_i];
|
||||
IM_ASSERT(data.OutFont && (!data.OutFont->IsLoaded() || data.OutFont->ContainerAtlas == this));
|
||||
const int font_offset = stbtt_GetFontOffsetForIndex((unsigned char*)data.TTFData, data.FontNo);
|
||||
ImFontConfig& cfg = ConfigData[input_i];
|
||||
ImFontTempBuildData& tmp = tmp_array[input_i];
|
||||
|
||||
IM_ASSERT(cfg.DstFont && (!cfg.DstFont->IsLoaded() || cfg.DstFont->ContainerAtlas == this));
|
||||
const int font_offset = stbtt_GetFontOffsetForIndex((unsigned char*)cfg.FontData, cfg.FontNo);
|
||||
IM_ASSERT(font_offset >= 0);
|
||||
if (!stbtt_InitFont(&data.FontInfo, (unsigned char*)data.TTFData, font_offset))
|
||||
if (!stbtt_InitFont(&tmp.FontInfo, (unsigned char*)cfg.FontData, font_offset))
|
||||
return false;
|
||||
|
||||
if (!data.GlyphRanges)
|
||||
data.GlyphRanges = GetGlyphRangesDefault();
|
||||
for (const ImWchar* in_range = data.GlyphRanges; in_range[0] && in_range[1]; in_range += 2)
|
||||
if (!cfg.GlyphRanges)
|
||||
cfg.GlyphRanges = GetGlyphRangesDefault();
|
||||
for (const ImWchar* in_range = cfg.GlyphRanges; in_range[0] && in_range[1]; in_range += 2)
|
||||
{
|
||||
total_glyph_count += (in_range[1] - in_range[0]) + 1;
|
||||
total_glyph_range_count++;
|
||||
@ -9846,8 +9900,7 @@ bool ImFontAtlas::Build()
|
||||
TexHeight = 0;
|
||||
const int max_tex_height = 1024*32;
|
||||
stbtt_pack_context spc;
|
||||
int ret = stbtt_PackBegin(&spc, NULL, TexWidth, max_tex_height, 0, 1, NULL);
|
||||
IM_ASSERT(ret);
|
||||
stbtt_PackBegin(&spc, NULL, TexWidth, max_tex_height, 0, 1, NULL);
|
||||
|
||||
// Pack our extra data rectangles first, so it will be on the upper-left corner of our texture (UV will have small values).
|
||||
ImVector<stbrp_rect> extra_rects;
|
||||
@ -9868,26 +9921,27 @@ bool ImFontAtlas::Build()
|
||||
memset(buf_ranges, 0, total_glyph_range_count * sizeof(stbtt_pack_range));
|
||||
|
||||
// First font pass: pack all glyphs (no rendering at this point, we are working with glyph sizes only)
|
||||
for (int input_i = 0; input_i < InputData.Size; input_i++)
|
||||
for (int input_i = 0; input_i < ConfigData.Size; input_i++)
|
||||
{
|
||||
ImFontAtlasData& data = *InputData[input_i];
|
||||
ImFontConfig& cfg = ConfigData[input_i];
|
||||
ImFontTempBuildData& tmp = tmp_array[input_i];
|
||||
|
||||
// Setup ranges
|
||||
int glyph_count = 0;
|
||||
int glyph_ranges_count = 0;
|
||||
for (const ImWchar* in_range = data.GlyphRanges; in_range[0] && in_range[1]; in_range += 2)
|
||||
for (const ImWchar* in_range = cfg.GlyphRanges; in_range[0] && in_range[1]; in_range += 2)
|
||||
{
|
||||
glyph_count += (in_range[1] - in_range[0]) + 1;
|
||||
glyph_ranges_count++;
|
||||
}
|
||||
data.Ranges = buf_ranges + buf_ranges_n;
|
||||
data.RangesCount = glyph_ranges_count;
|
||||
tmp.Ranges = buf_ranges + buf_ranges_n;
|
||||
tmp.RangesCount = glyph_ranges_count;
|
||||
buf_ranges_n += glyph_ranges_count;
|
||||
for (int i = 0; i < glyph_ranges_count; i++)
|
||||
{
|
||||
const ImWchar* in_range = &data.GlyphRanges[i * 2];
|
||||
stbtt_pack_range& range = data.Ranges[i];
|
||||
range.font_size = data.SizePixels;
|
||||
const ImWchar* in_range = &cfg.GlyphRanges[i * 2];
|
||||
stbtt_pack_range& range = tmp.Ranges[i];
|
||||
range.font_size = cfg.SizePixels;
|
||||
range.first_unicode_char_in_range = in_range[0];
|
||||
range.num_chars_in_range = (in_range[1] - in_range[0]) + 1;
|
||||
range.chardata_for_range = buf_packedchars + buf_packedchars_n;
|
||||
@ -9895,16 +9949,16 @@ bool ImFontAtlas::Build()
|
||||
}
|
||||
|
||||
// Pack
|
||||
data.Rects = buf_rects + buf_rects_n;
|
||||
tmp.Rects = buf_rects + buf_rects_n;
|
||||
buf_rects_n += glyph_count;
|
||||
stbtt_PackSetOversampling(&spc, data.OversampleH, data.OversampleV);
|
||||
const int n = stbtt_PackFontRangesGatherRects(&spc, &data.FontInfo, data.Ranges, data.RangesCount, data.Rects);
|
||||
stbrp_pack_rects((stbrp_context*)spc.pack_info, data.Rects, n);
|
||||
stbtt_PackSetOversampling(&spc, cfg.OversampleH, cfg.OversampleV);
|
||||
int n = stbtt_PackFontRangesGatherRects(&spc, &tmp.FontInfo, tmp.Ranges, tmp.RangesCount, tmp.Rects);
|
||||
stbrp_pack_rects((stbrp_context*)spc.pack_info, tmp.Rects, n);
|
||||
|
||||
// Extend texture height
|
||||
for (int i = 0; i < n; i++)
|
||||
if (data.Rects[i].was_packed)
|
||||
TexHeight = ImMax(TexHeight, data.Rects[i].y + data.Rects[i].h);
|
||||
if (tmp.Rects[i].was_packed)
|
||||
TexHeight = ImMax(TexHeight, tmp.Rects[i].y + tmp.Rects[i].h);
|
||||
}
|
||||
IM_ASSERT(buf_rects_n == total_glyph_count);
|
||||
IM_ASSERT(buf_packedchars_n == total_glyph_count);
|
||||
@ -9918,12 +9972,13 @@ bool ImFontAtlas::Build()
|
||||
spc.height = TexHeight;
|
||||
|
||||
// Second pass: render characters
|
||||
for (int input_i = 0; input_i < InputData.Size; input_i++)
|
||||
for (int input_i = 0; input_i < ConfigData.Size; input_i++)
|
||||
{
|
||||
ImFontAtlasData& data = *InputData[input_i];
|
||||
stbtt_PackSetOversampling(&spc, data.OversampleH, data.OversampleV);
|
||||
ret = stbtt_PackFontRangesRenderIntoRects(&spc, &data.FontInfo, data.Ranges, data.RangesCount, data.Rects);
|
||||
data.Rects = NULL;
|
||||
ImFontConfig& cfg = ConfigData[input_i];
|
||||
ImFontTempBuildData& tmp = tmp_array[input_i];
|
||||
stbtt_PackSetOversampling(&spc, cfg.OversampleH, cfg.OversampleV);
|
||||
stbtt_PackFontRangesRenderIntoRects(&spc, &tmp.FontInfo, tmp.Ranges, tmp.RangesCount, tmp.Rects);
|
||||
tmp.Rects = NULL;
|
||||
}
|
||||
|
||||
// End packing
|
||||
@ -9932,50 +9987,68 @@ bool ImFontAtlas::Build()
|
||||
buf_rects = NULL;
|
||||
|
||||
// Third pass: setup ImFont and glyphs for runtime
|
||||
for (int input_i = 0; input_i < InputData.Size; input_i++)
|
||||
for (int input_i = 0; input_i < ConfigData.Size; input_i++)
|
||||
{
|
||||
ImFontAtlasData& data = *InputData[input_i];
|
||||
data.OutFont->ContainerAtlas = this;
|
||||
data.OutFont->FontSize = data.SizePixels;
|
||||
ImFontConfig& cfg = ConfigData[input_i];
|
||||
ImFontTempBuildData& tmp = tmp_array[input_i];
|
||||
ImFont* dst_font = cfg.DstFont;
|
||||
|
||||
const float font_scale = stbtt_ScaleForPixelHeight(&data.FontInfo, data.SizePixels);
|
||||
int font_ascent, font_descent, font_line_gap;
|
||||
stbtt_GetFontVMetrics(&data.FontInfo, &font_ascent, &font_descent, &font_line_gap);
|
||||
data.OutFont->Ascent = (font_ascent * font_scale);
|
||||
data.OutFont->Descent = (font_descent * font_scale);
|
||||
data.OutFont->Glyphs.resize(0);
|
||||
float font_scale = stbtt_ScaleForPixelHeight(&tmp.FontInfo, cfg.SizePixels);
|
||||
int unscaled_ascent, unscaled_descent, unscaled_line_gap;
|
||||
stbtt_GetFontVMetrics(&tmp.FontInfo, &unscaled_ascent, &unscaled_descent, &unscaled_line_gap);
|
||||
|
||||
const int character_spacing_x = 1;
|
||||
for (int i = 0; i < data.RangesCount; i++)
|
||||
float ascent = unscaled_ascent * font_scale;
|
||||
float descent = unscaled_descent * font_scale;
|
||||
if (!cfg.MergeMode)
|
||||
{
|
||||
stbtt_pack_range& range = data.Ranges[i];
|
||||
dst_font->ContainerAtlas = this;
|
||||
dst_font->ConfigData = &cfg;
|
||||
dst_font->ConfigDataCount = 0;
|
||||
dst_font->FontSize = cfg.SizePixels;
|
||||
dst_font->Ascent = ascent;
|
||||
dst_font->Descent = descent;
|
||||
dst_font->Glyphs.resize(0);
|
||||
}
|
||||
dst_font->ConfigDataCount++;
|
||||
float off_y = (cfg.MergeMode && cfg.MergeGlyphCenterV) ? (ascent - dst_font->Ascent) * 0.5f : 0.0f;
|
||||
|
||||
dst_font->FallbackGlyph = NULL; // Always clear fallback so FindGlyph can return NULL. It will be set again in BuildLookupTable()
|
||||
for (int i = 0; i < tmp.RangesCount; i++)
|
||||
{
|
||||
stbtt_pack_range& range = tmp.Ranges[i];
|
||||
for (int char_idx = 0; char_idx < range.num_chars_in_range; char_idx += 1)
|
||||
{
|
||||
const stbtt_packedchar& pc = range.chardata_for_range[char_idx];
|
||||
if (!pc.x0 && !pc.x1 && !pc.y0 && !pc.y1)
|
||||
continue;
|
||||
|
||||
const int codepoint = range.first_unicode_char_in_range + char_idx;
|
||||
if (cfg.MergeMode && dst_font->FindGlyph((unsigned short)codepoint))
|
||||
continue;
|
||||
|
||||
stbtt_aligned_quad q;
|
||||
float dummy_x = 0.0f, dummy_y = 0.0f;
|
||||
stbtt_GetPackedQuad(range.chardata_for_range, TexWidth, TexHeight, char_idx, &dummy_x, &dummy_y, &q, 0);
|
||||
|
||||
data.OutFont->Glyphs.resize(data.OutFont->Glyphs.Size + 1);
|
||||
ImFont::Glyph& glyph = data.OutFont->Glyphs.back();
|
||||
glyph.Codepoint = (ImWchar)(range.first_unicode_char_in_range + char_idx);
|
||||
dst_font->Glyphs.resize(dst_font->Glyphs.Size + 1);
|
||||
ImFont::Glyph& glyph = dst_font->Glyphs.back();
|
||||
glyph.Codepoint = (ImWchar)codepoint;
|
||||
glyph.X0 = q.x0; glyph.Y0 = q.y0; glyph.X1 = q.x1; glyph.Y1 = q.y1;
|
||||
glyph.U0 = q.s0; glyph.V0 = q.t0; glyph.U1 = q.s1; glyph.V1 = q.t1;
|
||||
glyph.Y0 += (float)(int)(data.OutFont->Ascent + 0.5f);
|
||||
glyph.Y1 += (float)(int)(data.OutFont->Ascent + 0.5f);
|
||||
glyph.XAdvance = (float)(int)(pc.xadvance + character_spacing_x + 0.5f); // Bake spacing into XAdvance
|
||||
glyph.Y0 += (float)(int)(dst_font->Ascent + off_y + 0.5f);
|
||||
glyph.Y1 += (float)(int)(dst_font->Ascent + off_y + 0.5f);
|
||||
glyph.XAdvance = (pc.xadvance + cfg.GlyphExtraSpacing.x); // Bake spacing into XAdvance
|
||||
if (cfg.PixelSnapH)
|
||||
glyph.XAdvance = (float)(int)(glyph.XAdvance + 0.5f);
|
||||
}
|
||||
}
|
||||
|
||||
data.OutFont->BuildLookupTable();
|
||||
cfg.DstFont->BuildLookupTable();
|
||||
}
|
||||
|
||||
// Cleanup temporaries
|
||||
ImGui::MemFree(buf_packedchars);
|
||||
ImGui::MemFree(buf_ranges);
|
||||
ImGui::MemFree(tmp_array);
|
||||
|
||||
// Render into our custom data block
|
||||
RenderCustomTexData(1, &extra_rects);
|
||||
@ -10195,6 +10268,8 @@ void ImFont::Clear()
|
||||
{
|
||||
FontSize = 0.0f;
|
||||
DisplayOffset = ImVec2(0.0f, 1.0f);
|
||||
ConfigData = NULL;
|
||||
ConfigDataCount = 0;
|
||||
Ascent = Descent = 0.0f;
|
||||
ContainerAtlas = NULL;
|
||||
Glyphs.clear();
|
||||
@ -11160,6 +11235,7 @@ void ImGui::ShowTestWindow(bool* opened)
|
||||
ImFontAtlas* atlas = ImGui::GetIO().Fonts;
|
||||
if (ImGui::TreeNode("Atlas texture"))
|
||||
{
|
||||
ImGui::Text("%dx%d pixels", atlas->TexWidth, atlas->TexHeight);
|
||||
ImGui::Image(atlas->TexID, ImVec2((float)atlas->TexWidth, (float)atlas->TexHeight), ImVec2(0,0), ImVec2(1,1), ImColor(255,255,255,255), ImColor(255,255,255,128));
|
||||
ImGui::TreePop();
|
||||
}
|
||||
@ -11167,7 +11243,7 @@ void ImGui::ShowTestWindow(bool* opened)
|
||||
for (int i = 0; i < atlas->Fonts.Size; i++)
|
||||
{
|
||||
ImFont* font = atlas->Fonts[i];
|
||||
ImGui::BulletText("Font %d: %.2f pixels, %d glyphs", i, font->FontSize, font->Glyphs.Size);
|
||||
ImGui::BulletText("Font %d: \'%s\', %.2f px, %d glyphs", i, font->ConfigData[0].Name, font->FontSize, font->Glyphs.Size);
|
||||
ImGui::TreePush((void*)i);
|
||||
if (i > 0) { ImGui::SameLine(); if (ImGui::SmallButton("Set as default")) { atlas->Fonts[i] = atlas->Fonts[0]; atlas->Fonts[0] = font; } }
|
||||
ImGui::PushFont(font);
|
||||
@ -11178,6 +11254,8 @@ void ImGui::ShowTestWindow(bool* opened)
|
||||
ImGui::DragFloat("font scale", &font->Scale, 0.005f, 0.3f, 2.0f, "%.1f"); // scale only this font
|
||||
ImGui::Text("Ascent: %f, Descent: %f, Height: %f", font->Ascent, font->Descent, font->Ascent - font->Descent);
|
||||
ImGui::Text("Fallback character: '%c' (%d)", font->FallbackChar, font->FallbackChar);
|
||||
for (int i = 0; i < font->ConfigDataCount; i++)
|
||||
ImGui::BulletText("Input %d: \'%s\'", i, font->ConfigData[i].Name);
|
||||
ImGui::TreePop();
|
||||
}
|
||||
ImGui::TreePop();
|
||||
|
39
imgui.h
39
imgui.h
@ -1115,6 +1115,27 @@ struct ImDrawData
|
||||
void DeIndexAllBuffers(); // For backward compatibility: convert all buffers from indexed to de-indexed, in case you cannot render indexed. Note: this is slow and most likely a waste of resources. Always prefer indexed rendering!
|
||||
};
|
||||
|
||||
struct ImFontConfig
|
||||
{
|
||||
void* FontData; // // TTF data
|
||||
int FontDataSize; // // TTF data size
|
||||
bool FontDataOwnedByAtlas; // true // TTF data ownership taken by the container ImFontAtlas (will delete memory itself). Set to true
|
||||
int FontNo; // 0 // Index of font within TTF file
|
||||
float SizePixels; // // Size in pixels for rasterizer
|
||||
int OversampleH, OversampleV; // 3, 1 // Rasterize at higher quality for sub-pixel positioning. We don't use sub-pixel positions on the Y axis.
|
||||
bool PixelSnapH; // false // Align every character to pixel boundary (if enabled, set OversampleH/V to 1)
|
||||
ImVec2 GlyphExtraSpacing; // 0, 0 // Extra spacing (in pixels) between glyphs
|
||||
const ImWchar* GlyphRanges; // // List of Unicode range (2 value per range, values are inclusive, zero-terminated list)
|
||||
bool MergeMode; // false // Merge into previous ImFont, so you can combine multiple inputs font into one ImFont (e.g. ASCII font + icons + Japanese glyphs).
|
||||
bool MergeGlyphCenterV; // false // When merging (multiple ImFontInput for one ImFont), vertically center new glyphs instead of aligning their baseline
|
||||
|
||||
// [Internal]
|
||||
char Name[32]; // Name (strictly for debugging)
|
||||
ImFont* DstFont;
|
||||
|
||||
IMGUI_API ImFontConfig();
|
||||
};
|
||||
|
||||
// Load and rasterize multiple TTF fonts into a same texture.
|
||||
// Sharing a texture for multiple fonts allows us to reduce the number of draw calls during rendering.
|
||||
// We also add custom graphic data into the texture that serves for ImGui.
|
||||
@ -1127,10 +1148,11 @@ struct ImFontAtlas
|
||||
{
|
||||
IMGUI_API ImFontAtlas();
|
||||
IMGUI_API ~ImFontAtlas();
|
||||
IMGUI_API ImFont* AddFont(const ImFontConfig* font_cfg);
|
||||
IMGUI_API ImFont* AddFontDefault();
|
||||
IMGUI_API ImFont* AddFontFromFileTTF(const char* filename, float size_pixels, const ImWchar* glyph_ranges = NULL, int font_no = 0);
|
||||
IMGUI_API ImFont* AddFontFromMemoryTTF(void* ttf_data, int ttf_size, float size_pixels, const ImWchar* glyph_ranges = NULL, int font_no = 0); // Transfer ownership of 'ttf_data' to ImFontAtlas, will be deleted after Build()
|
||||
IMGUI_API ImFont* AddFontFromMemoryCompressedTTF(const void* compressed_ttf_data, int compressed_ttf_size, float size_pixels, const ImWchar* glyph_ranges = NULL, int font_no = 0); // 'compressed_ttf_data' untouched and still owned by caller. Compress with binary_to_compressed_c.cpp
|
||||
IMGUI_API ImFont* AddFontFromFileTTF(const char* filename, float size_pixels, const ImFontConfig* font_cfg = NULL, const ImWchar* glyph_ranges = NULL);
|
||||
IMGUI_API ImFont* AddFontFromMemoryTTF(void* ttf_data, int ttf_size, float size_pixels, const ImFontConfig* font_cfg = NULL, const ImWchar* glyph_ranges = NULL); // Transfer ownership of 'ttf_data' to ImFontAtlas, will be deleted after Build()
|
||||
IMGUI_API ImFont* AddFontFromMemoryCompressedTTF(const void* compressed_ttf_data, int compressed_ttf_size, float size_pixels, const ImFontConfig* font_cfg = NULL, const ImWchar* glyph_ranges = NULL); // 'compressed_ttf_data' untouched and still owned by caller. Compress with binary_to_compressed_c.cpp
|
||||
IMGUI_API void ClearTexData(); // Clear the CPU-side texture data. Saves RAM once the texture has been copied to graphics memory.
|
||||
IMGUI_API void ClearInputData(); // Clear the input TTF data (inc sizes, glyph ranges)
|
||||
IMGUI_API void ClearFonts(); // Clear the ImGui-side font data (glyphs storage, UV coordinates)
|
||||
@ -1163,15 +1185,13 @@ struct ImFontAtlas
|
||||
ImVector<ImFont*> Fonts;
|
||||
|
||||
// Private
|
||||
struct ImFontAtlasData;
|
||||
ImVector<ImFontAtlasData*> InputData; // Internal data
|
||||
ImVector<ImFontConfig> ConfigData; // Internal data
|
||||
IMGUI_API bool Build(); // Build pixels data. This is automatically for you by the GetTexData*** functions.
|
||||
IMGUI_API void RenderCustomTexData(int pass, void* rects);
|
||||
};
|
||||
|
||||
// TTF font loading and rendering
|
||||
// Font runtime data and rendering
|
||||
// ImFontAtlas automatically loads a default embedded font for you when you call GetTexDataAsAlpha8() or GetTexDataAsRGBA32().
|
||||
// Kerning isn't supported. At the moment some ImGui code does per-character CalcTextSize calls, need something more state-ful.
|
||||
struct ImFont
|
||||
{
|
||||
// Members: Settings
|
||||
@ -1179,6 +1199,8 @@ struct ImFont
|
||||
float Scale; // = 1.0f // Base font scale, multiplied by the per-window font scale which you can adjust with SetFontScale()
|
||||
ImVec2 DisplayOffset; // = (0.0f,0.0f) // Offset font rendering by xx pixels
|
||||
ImWchar FallbackChar; // = '?' // Replacement glyph if one isn't found. Only set via SetFallbackChar()
|
||||
ImFontConfig* ConfigData; // // Pointer within ImFontAtlas->ConfigData
|
||||
int ConfigDataCount; //
|
||||
|
||||
// Members: Runtime data
|
||||
struct Glyph
|
||||
@ -1188,8 +1210,7 @@ struct ImFont
|
||||
float X0, Y0, X1, Y1;
|
||||
float U0, V0, U1, V1; // Texture coordinates
|
||||
};
|
||||
float Ascent; // Distance from top to bottom of e.g. 'A' [0..FontSize]
|
||||
float Descent; //
|
||||
float Ascent, Descent; // Ascent: distance from top to bottom of e.g. 'A' [0..FontSize]
|
||||
ImFontAtlas* ContainerAtlas; // What we has been loaded into
|
||||
ImVector<Glyph> Glyphs;
|
||||
const Glyph* FallbackGlyph; // == FindGlyph(FontFallbackChar)
|
||||
|
Loading…
Reference in New Issue
Block a user