ImFontAtlas: new AddFont() API, oversampling, subpositiong, merging fonts, etc. (#182, #220, #232, #242)

This commit is contained in:
ocornut 2015-07-15 07:01:21 -06:00
parent 6ae8062ca0
commit 815168c7ef
3 changed files with 241 additions and 117 deletions

View File

@ -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
View File

@ -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
View File

@ -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)