Merge branch 'master' into styles

This commit is contained in:
omar
2017-09-18 15:03:25 +02:00
29 changed files with 1492 additions and 1002 deletions

View File

@ -1,4 +1,4 @@
// dear imgui, v1.51 WIP
// dear imgui, v1.52 WIP
// (drawing and font code)
// Contains implementation for
@ -21,10 +21,10 @@
#if !defined(alloca)
#ifdef _WIN32
#include <malloc.h> // alloca
#elif (defined(__FreeBSD__) || defined(FreeBSD_kernel) || defined(__DragonFly__)) && !defined(__GLIBC__)
#include <stdlib.h> // alloca. FreeBSD uses stdlib.h unless GLIBC
#else
#elif defined(__GLIBC__) || defined(__sun)
#include <alloca.h> // alloca
#else
#include <stdlib.h> // alloca
#endif
#endif
@ -694,30 +694,30 @@ void ImDrawList::PathArcToFast(const ImVec2& centre, float radius, int a_min_of_
circle_vtx_builds = true;
}
if (a_min_of_12 > a_max_of_12) return;
if (radius == 0.0f)
if (radius == 0.0f || a_min_of_12 > a_max_of_12)
{
_Path.push_back(centre);
return;
}
else
_Path.reserve(_Path.Size + (a_max_of_12 - a_min_of_12 + 1));
for (int a = a_min_of_12; a <= a_max_of_12; a++)
{
_Path.reserve(_Path.Size + (a_max_of_12 - a_min_of_12 + 1));
for (int a = a_min_of_12; a <= a_max_of_12; a++)
{
const ImVec2& c = circle_vtx[a % circle_vtx_count];
_Path.push_back(ImVec2(centre.x + c.x * radius, centre.y + c.y * radius));
}
const ImVec2& c = circle_vtx[a % circle_vtx_count];
_Path.push_back(ImVec2(centre.x + c.x * radius, centre.y + c.y * radius));
}
}
void ImDrawList::PathArcTo(const ImVec2& centre, float radius, float amin, float amax, int num_segments)
void ImDrawList::PathArcTo(const ImVec2& centre, float radius, float a_min, float a_max, int num_segments)
{
if (radius == 0.0f)
{
_Path.push_back(centre);
return;
}
_Path.reserve(_Path.Size + (num_segments + 1));
for (int i = 0; i <= num_segments; i++)
{
const float a = amin + ((float)i / (float)num_segments) * (amax - amin);
const float a = a_min + ((float)i / (float)num_segments) * (a_max - a_min);
_Path.push_back(ImVec2(centre.x + cosf(a) * radius, centre.y + sinf(a) * radius));
}
}
@ -775,31 +775,30 @@ void ImDrawList::PathBezierCurveTo(const ImVec2& p2, const ImVec2& p3, const ImV
void ImDrawList::PathRect(const ImVec2& a, const ImVec2& b, float rounding, int rounding_corners)
{
const int corners_top = ImGuiCorner_TopLeft | ImGuiCorner_TopRight;
const int corners_bottom = ImGuiCorner_BottomLeft | ImGuiCorner_BottomRight;
const int corners_left = ImGuiCorner_TopLeft | ImGuiCorner_BottomLeft;
const int corners_right = ImGuiCorner_TopRight | ImGuiCorner_BottomRight;
const int corners_bottom = ImGuiCorner_BotLeft | ImGuiCorner_BotRight;
const int corners_left = ImGuiCorner_TopLeft | ImGuiCorner_BotLeft;
const int corners_right = ImGuiCorner_TopRight | ImGuiCorner_BotRight;
float r = rounding;
r = ImMin(r, fabsf(b.x-a.x) * ( ((rounding_corners & corners_top) == corners_top) || ((rounding_corners & corners_bottom) == corners_bottom) ? 0.5f : 1.0f ) - 1.0f);
r = ImMin(r, fabsf(b.y-a.y) * ( ((rounding_corners & corners_left) == corners_left) || ((rounding_corners & corners_right) == corners_right) ? 0.5f : 1.0f ) - 1.0f);
rounding = ImMin(rounding, fabsf(b.x - a.x) * ( ((rounding_corners & corners_top) == corners_top) || ((rounding_corners & corners_bottom) == corners_bottom) ? 0.5f : 1.0f ) - 1.0f);
rounding = ImMin(rounding, fabsf(b.y - a.y) * ( ((rounding_corners & corners_left) == corners_left) || ((rounding_corners & corners_right) == corners_right) ? 0.5f : 1.0f ) - 1.0f);
if (r <= 0.0f || rounding_corners == 0)
if (rounding <= 0.0f || rounding_corners == 0)
{
PathLineTo(a);
PathLineTo(ImVec2(b.x,a.y));
PathLineTo(ImVec2(b.x, a.y));
PathLineTo(b);
PathLineTo(ImVec2(a.x,b.y));
PathLineTo(ImVec2(a.x, b.y));
}
else
{
const float r0 = (rounding_corners & ImGuiCorner_TopLeft) ? r : 0.0f;
const float r1 = (rounding_corners & ImGuiCorner_TopRight) ? r : 0.0f;
const float r2 = (rounding_corners & ImGuiCorner_BottomRight) ? r : 0.0f;
const float r3 = (rounding_corners & ImGuiCorner_BottomLeft) ? r : 0.0f;
PathArcToFast(ImVec2(a.x+r0,a.y+r0), r0, 6, 9);
PathArcToFast(ImVec2(b.x-r1,a.y+r1), r1, 9, 12);
PathArcToFast(ImVec2(b.x-r2,b.y-r2), r2, 0, 3);
PathArcToFast(ImVec2(a.x+r3,b.y-r3), r3, 3, 6);
const float rounding_tl = (rounding_corners & ImGuiCorner_TopLeft) ? rounding : 0.0f;
const float rounding_tr = (rounding_corners & ImGuiCorner_TopRight) ? rounding : 0.0f;
const float rounding_br = (rounding_corners & ImGuiCorner_BotRight) ? rounding : 0.0f;
const float rounding_bl = (rounding_corners & ImGuiCorner_BotLeft) ? rounding : 0.0f;
PathArcToFast(ImVec2(a.x + rounding_tl, a.y + rounding_tl), rounding_tl, 6, 9);
PathArcToFast(ImVec2(b.x - rounding_tr, a.y + rounding_tr), rounding_tr, 9, 12);
PathArcToFast(ImVec2(b.x - rounding_br, b.y - rounding_br), rounding_br, 0, 3);
PathArcToFast(ImVec2(a.x + rounding_bl, b.y - rounding_bl), rounding_bl, 3, 6);
}
}
@ -1051,14 +1050,52 @@ ImFontConfig::ImFontConfig()
GlyphOffset = ImVec2(0.0f, 0.0f);
GlyphRanges = NULL;
MergeMode = false;
DstFont = NULL;
RasterizerFlags = 0x00;
RasterizerMultiply = 1.0f;
memset(Name, 0, sizeof(Name));
DstFont = NULL;
}
//-----------------------------------------------------------------------------
// ImFontAtlas
//-----------------------------------------------------------------------------
// A work of art lies ahead! (. = white layer, X = black layer, others are blank)
// The white texels on the top left are the ones we'll use everywhere in ImGui to render filled shapes.
const int FONT_ATLAS_DEFAULT_TEX_DATA_W_HALF = 90;
const int FONT_ATLAS_DEFAULT_TEX_DATA_H = 27;
const int FONT_ATLAS_DEFAULT_TEX_DATA_ID = 0xF0000;
const char FONT_ATLAS_DEFAULT_TEX_DATA_PIXELS[FONT_ATLAS_DEFAULT_TEX_DATA_W_HALF * FONT_ATLAS_DEFAULT_TEX_DATA_H + 1] =
{
"..- -XXXXXXX- X - X -XXXXXXX - XXXXXXX"
"..- -X.....X- X.X - X.X -X.....X - X.....X"
"--- -XXX.XXX- X...X - X...X -X....X - X....X"
"X - X.X - X.....X - X.....X -X...X - X...X"
"XX - X.X -X.......X- X.......X -X..X.X - X.X..X"
"X.X - X.X -XXXX.XXXX- XXXX.XXXX -X.X X.X - X.X X.X"
"X..X - X.X - X.X - X.X -XX X.X - X.X XX"
"X...X - X.X - X.X - XX X.X XX - X.X - X.X "
"X....X - X.X - X.X - X.X X.X X.X - X.X - X.X "
"X.....X - X.X - X.X - X..X X.X X..X - X.X - X.X "
"X......X - X.X - X.X - X...XXXXXX.XXXXXX...X - X.X XX-XX X.X "
"X.......X - X.X - X.X -X.....................X- X.X X.X-X.X X.X "
"X........X - X.X - X.X - X...XXXXXX.XXXXXX...X - X.X..X-X..X.X "
"X.........X -XXX.XXX- X.X - X..X X.X X..X - X...X-X...X "
"X..........X-X.....X- X.X - X.X X.X X.X - X....X-X....X "
"X......XXXXX-XXXXXXX- X.X - XX X.X XX - X.....X-X.....X "
"X...X..X --------- X.X - X.X - XXXXXXX-XXXXXXX "
"X..X X..X - -XXXX.XXXX- XXXX.XXXX ------------------------------------"
"X.X X..X - -X.......X- X.......X - XX XX - "
"XX X..X - - X.....X - X.....X - X.X X.X - "
" X..X - X...X - X...X - X..X X..X - "
" XX - X.X - X.X - X...XXXXXXXXXXXXX...X - "
"------------ - X - X -X.....................X- "
" ----------------------------------- X...XXXXXXXXXXXXX...X - "
" - X..X X..X - "
" - X.X X.X - "
" - XX XX - "
};
ImFontAtlas::ImFontAtlas()
{
TexID = NULL;
@ -1091,6 +1128,7 @@ void ImFontAtlas::ClearInputData()
Fonts[i]->ConfigDataCount = 0;
}
ConfigData.clear();
CustomRects.clear();
}
void ImFontAtlas::ClearTexData()
@ -1176,8 +1214,8 @@ ImFont* ImFontAtlas::AddFont(const ImFontConfig* font_cfg)
ConfigData.push_back(*font_cfg);
ImFontConfig& new_font_cfg = ConfigData.back();
if (!new_font_cfg.DstFont)
new_font_cfg.DstFont = Fonts.back();
if (!new_font_cfg.DstFont)
new_font_cfg.DstFont = Fonts.back();
if (!new_font_cfg.FontDataOwnedByAtlas)
{
new_font_cfg.FontData = ImGui::MemAlloc(new_font_cfg.FontDataSize);
@ -1216,9 +1254,10 @@ ImFont* ImFontAtlas::AddFontDefault(const ImFontConfig* font_cfg_template)
font_cfg.PixelSnapH = true;
}
if (font_cfg.Name[0] == '\0') strcpy(font_cfg.Name, "ProggyClean.ttf, 13px");
if (font_cfg.SizePixels <= 0.0f) font_cfg.SizePixels = 13.0f;
const char* ttf_compressed_base85 = GetDefaultCompressedFontDataTTFBase85();
ImFont* font = AddFontFromMemoryCompressedBase85TTF(ttf_compressed_base85, 13.0f, &font_cfg, GetGlyphRangesDefault());
ImFont* font = AddFontFromMemoryCompressedBase85TTF(ttf_compressed_base85, font_cfg.SizePixels, &font_cfg, GetGlyphRangesDefault());
return font;
}
@ -1277,58 +1316,101 @@ ImFont* ImFontAtlas::AddFontFromMemoryCompressedBase85TTF(const char* compressed
return font;
}
int ImFontAtlas::CustomRectRegister(unsigned int id, int width, int height)
{
IM_ASSERT(width > 0 && width <= 0xFFFF);
IM_ASSERT(height > 0 && height <= 0xFFFF);
CustomRect r;
r.ID = id;
r.Width = (unsigned short)width;
r.Height = (unsigned short)height;
CustomRects.push_back(r);
return CustomRects.Size - 1; // Return index
}
void ImFontAtlas::CustomRectCalcUV(const CustomRect* rect, ImVec2* out_uv_min, ImVec2* out_uv_max)
{
IM_ASSERT(TexWidth > 0 && TexHeight > 0); // Font atlas needs to be built before we can calculate UV coordinates
IM_ASSERT(rect->IsPacked()); // Make sure the rectangle has been packed
*out_uv_min = ImVec2((float)rect->X / TexWidth, (float)rect->Y / TexHeight);
*out_uv_max = ImVec2((float)(rect->X + rect->Width) / TexWidth, (float)(rect->Y + rect->Height) / TexHeight);
}
bool ImFontAtlas::Build()
{
IM_ASSERT(ConfigData.Size > 0);
return ImFontAtlasBuildWithStbTruetype(this);
}
TexID = NULL;
TexWidth = TexHeight = 0;
TexUvWhitePixel = ImVec2(0, 0);
ClearTexData();
void ImFontAtlasBuildMultiplyCalcLookupTable(unsigned char out_table[256], float in_brighten_factor)
{
for (unsigned int i = 0; i < 256; i++)
{
unsigned int value = (unsigned int)(i * in_brighten_factor);
out_table[i] = value > 255 ? 255 : (value & 0xFF);
}
}
void ImFontAtlasBuildMultiplyRectAlpha8(const unsigned char table[256], unsigned char* pixels, int x, int y, int w, int h, int stride)
{
unsigned char* data = pixels + x + y * stride;
for (int j = h; j > 0; j--, data += stride)
for (int i = 0; i < w; i++)
data[i] = table[data[i]];
}
bool ImFontAtlasBuildWithStbTruetype(ImFontAtlas* atlas)
{
IM_ASSERT(atlas->ConfigData.Size > 0);
ImFontAtlasBuildRegisterDefaultCustomRects(atlas);
atlas->TexID = NULL;
atlas->TexWidth = atlas->TexHeight = 0;
atlas->TexUvWhitePixel = ImVec2(0, 0);
atlas->ClearTexData();
// Count glyphs/ranges
int total_glyphs_count = 0;
int total_ranges_count = 0;
for (int input_i = 0; input_i < ConfigData.Size; input_i++)
for (int input_i = 0; input_i < atlas->ConfigData.Size; input_i++)
{
ImFontConfig& cfg = ConfigData[input_i];
ImFontConfig& cfg = atlas->ConfigData[input_i];
if (!cfg.GlyphRanges)
cfg.GlyphRanges = GetGlyphRangesDefault();
cfg.GlyphRanges = atlas->GetGlyphRangesDefault();
for (const ImWchar* in_range = cfg.GlyphRanges; in_range[0] && in_range[1]; in_range += 2, total_ranges_count++)
total_glyphs_count += (in_range[1] - in_range[0]) + 1;
}
// Start packing. We need a known width for the skyline algorithm. Using a dumb heuristic here to decide of width. User can override TexDesiredWidth and TexGlyphPadding if they wish.
// After packing is done, width shouldn't matter much, but some API/GPU have texture size limitations and increasing width can decrease height.
TexWidth = (TexDesiredWidth > 0) ? TexDesiredWidth : (total_glyphs_count > 4000) ? 4096 : (total_glyphs_count > 2000) ? 2048 : (total_glyphs_count > 1000) ? 1024 : 512;
TexHeight = 0;
// We need a width for the skyline algorithm. Using a dumb heuristic here to decide of width. User can override TexDesiredWidth and TexGlyphPadding if they wish.
// Width doesn't really matter much, but some API/GPU have texture size limitations and increasing width can decrease height.
atlas->TexWidth = (atlas->TexDesiredWidth > 0) ? atlas->TexDesiredWidth : (total_glyphs_count > 4000) ? 4096 : (total_glyphs_count > 2000) ? 2048 : (total_glyphs_count > 1000) ? 1024 : 512;
atlas->TexHeight = 0;
// Start packing
const int max_tex_height = 1024*32;
stbtt_pack_context spc;
stbtt_PackBegin(&spc, NULL, TexWidth, max_tex_height, 0, TexGlyphPadding, NULL);
stbtt_PackBegin(&spc, NULL, atlas->TexWidth, max_tex_height, 0, atlas->TexGlyphPadding, NULL);
stbtt_PackSetOversampling(&spc, 1, 1);
// 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;
RenderCustomTexData(0, &extra_rects);
stbtt_PackSetOversampling(&spc, 1, 1);
stbrp_pack_rects((stbrp_context*)spc.pack_info, &extra_rects[0], extra_rects.Size);
for (int i = 0; i < extra_rects.Size; i++)
if (extra_rects[i].was_packed)
TexHeight = ImMax(TexHeight, extra_rects[i].y + extra_rects[i].h);
ImFontAtlasBuildPackCustomRects(atlas, spc.pack_info);
// Initialize font information (so we can error without any cleanup)
struct ImFontTempBuildData
{
stbtt_fontinfo FontInfo;
stbrp_rect* Rects;
int RectsCount;
stbtt_pack_range* Ranges;
int RangesCount;
};
ImFontTempBuildData* tmp_array = (ImFontTempBuildData*)ImGui::MemAlloc((size_t)ConfigData.Size * sizeof(ImFontTempBuildData));
for (int input_i = 0; input_i < ConfigData.Size; input_i++)
ImFontTempBuildData* tmp_array = (ImFontTempBuildData*)ImGui::MemAlloc((size_t)atlas->ConfigData.Size * sizeof(ImFontTempBuildData));
for (int input_i = 0; input_i < atlas->ConfigData.Size; input_i++)
{
ImFontConfig& cfg = ConfigData[input_i];
ImFontConfig& cfg = atlas->ConfigData[input_i];
ImFontTempBuildData& tmp = tmp_array[input_i];
IM_ASSERT(cfg.DstFont && (!cfg.DstFont->IsLoaded() || cfg.DstFont->ContainerAtlas == this));
IM_ASSERT(cfg.DstFont && (!cfg.DstFont->IsLoaded() || cfg.DstFont->ContainerAtlas == atlas));
const int font_offset = stbtt_GetFontOffsetForIndex((unsigned char*)cfg.FontData, cfg.FontNo);
IM_ASSERT(font_offset >= 0);
if (!stbtt_InitFont(&tmp.FontInfo, (unsigned char*)cfg.FontData, font_offset))
@ -1345,9 +1427,9 @@ bool ImFontAtlas::Build()
memset(buf_ranges, 0, total_ranges_count * sizeof(stbtt_pack_range));
// First font pass: pack all glyphs (no rendering at this point, we are working with rectangles in an infinitely tall texture at this point)
for (int input_i = 0; input_i < ConfigData.Size; input_i++)
for (int input_i = 0; input_i < atlas->ConfigData.Size; input_i++)
{
ImFontConfig& cfg = ConfigData[input_i];
ImFontConfig& cfg = atlas->ConfigData[input_i];
ImFontTempBuildData& tmp = tmp_array[input_i];
// Setup ranges
@ -1371,6 +1453,7 @@ bool ImFontAtlas::Build()
// Pack
tmp.Rects = buf_rects + buf_rects_n;
tmp.RectsCount = font_glyphs_count;
buf_rects_n += font_glyphs_count;
stbtt_PackSetOversampling(&spc, cfg.OversampleH, cfg.OversampleV);
int n = stbtt_PackFontRangesGatherRects(&spc, &tmp.FontInfo, tmp.Ranges, tmp.RangesCount, tmp.Rects);
@ -1380,26 +1463,34 @@ bool ImFontAtlas::Build()
// Extend texture height
for (int i = 0; i < n; i++)
if (tmp.Rects[i].was_packed)
TexHeight = ImMax(TexHeight, tmp.Rects[i].y + tmp.Rects[i].h);
atlas->TexHeight = ImMax(atlas->TexHeight, tmp.Rects[i].y + tmp.Rects[i].h);
}
IM_ASSERT(buf_rects_n == total_glyphs_count);
IM_ASSERT(buf_packedchars_n == total_glyphs_count);
IM_ASSERT(buf_ranges_n == total_ranges_count);
// Create texture
TexHeight = ImUpperPowerOfTwo(TexHeight);
TexPixelsAlpha8 = (unsigned char*)ImGui::MemAlloc(TexWidth * TexHeight);
memset(TexPixelsAlpha8, 0, TexWidth * TexHeight);
spc.pixels = TexPixelsAlpha8;
spc.height = TexHeight;
atlas->TexHeight = ImUpperPowerOfTwo(atlas->TexHeight);
atlas->TexPixelsAlpha8 = (unsigned char*)ImGui::MemAlloc(atlas->TexWidth * atlas->TexHeight);
memset(atlas->TexPixelsAlpha8, 0, atlas->TexWidth * atlas->TexHeight);
spc.pixels = atlas->TexPixelsAlpha8;
spc.height = atlas->TexHeight;
// Second pass: render font characters
for (int input_i = 0; input_i < ConfigData.Size; input_i++)
for (int input_i = 0; input_i < atlas->ConfigData.Size; input_i++)
{
ImFontConfig& cfg = ConfigData[input_i];
ImFontConfig& cfg = atlas->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);
if (cfg.RasterizerMultiply != 1.0f)
{
unsigned char multiply_table[256];
ImFontAtlasBuildMultiplyCalcLookupTable(multiply_table, cfg.RasterizerMultiply);
for (const stbrp_rect* r = tmp.Rects; r != tmp.Rects + tmp.RectsCount; r++)
if (r->was_packed)
ImFontAtlasBuildMultiplyRectAlpha8(multiply_table, spc.pixels, r->x, r->y, r->w, r->h, spc.stride_in_bytes);
}
tmp.Rects = NULL;
}
@ -1409,32 +1500,21 @@ bool ImFontAtlas::Build()
buf_rects = NULL;
// Third pass: setup ImFont and glyphs for runtime
for (int input_i = 0; input_i < ConfigData.Size; input_i++)
for (int input_i = 0; input_i < atlas->ConfigData.Size; input_i++)
{
ImFontConfig& cfg = ConfigData[input_i];
ImFontConfig& cfg = atlas->ConfigData[input_i];
ImFontTempBuildData& tmp = tmp_array[input_i];
ImFont* dst_font = cfg.DstFont; // We can have multiple input fonts writing into a same destination font (when using MergeMode=true)
float font_scale = stbtt_ScaleForPixelHeight(&tmp.FontInfo, cfg.SizePixels);
const 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);
float ascent = unscaled_ascent * font_scale;
float descent = unscaled_descent * font_scale;
if (!cfg.MergeMode)
{
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->MetricsTotalSurface = 0;
}
dst_font->ConfigDataCount++;
float off_x = cfg.GlyphOffset.x;
float off_y = cfg.GlyphOffset.y;
const float ascent = unscaled_ascent * font_scale;
const float descent = unscaled_descent * font_scale;
ImFontAtlasBuildSetupFont(atlas, dst_font, &cfg, ascent, descent);
const float off_x = cfg.GlyphOffset.x;
const float off_y = cfg.GlyphOffset.y + (float)(int)(dst_font->Ascent + 0.5f);
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++)
@ -1452,19 +1532,24 @@ bool ImFontAtlas::Build()
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);
stbtt_GetPackedQuad(range.chardata_for_range, atlas->TexWidth, atlas->TexHeight, char_idx, &dummy_x, &dummy_y, &q, 0);
dst_font->Glyphs.resize(dst_font->Glyphs.Size + 1);
ImFont::Glyph& glyph = dst_font->Glyphs.back();
glyph.Codepoint = (ImWchar)codepoint;
glyph.X0 = q.x0 + off_x; glyph.Y0 = q.y0 + off_y; glyph.X1 = q.x1 + off_x; glyph.Y1 = q.y1 + off_y;
glyph.U0 = q.s0; glyph.V0 = q.t0; glyph.U1 = q.s1; glyph.V1 = q.t1;
glyph.Y0 += (float)(int)(dst_font->Ascent + 0.5f);
glyph.Y1 += (float)(int)(dst_font->Ascent + 0.5f);
glyph.X0 = q.x0 + off_x;
glyph.Y0 = q.y0 + off_y;
glyph.X1 = q.x1 + off_x;
glyph.Y1 = q.y1 + off_y;
glyph.U0 = q.s0;
glyph.V0 = q.t0;
glyph.U1 = q.s1;
glyph.V1 = q.t1;
glyph.XAdvance = (pc.xadvance + cfg.GlyphExtraSpacing.x); // Bake spacing into XAdvance
if (cfg.PixelSnapH)
glyph.XAdvance = (float)(int)(glyph.XAdvance + 0.5f);
dst_font->MetricsTotalSurface += (int)((glyph.U1 - glyph.U0) * TexWidth + 1.99f) * (int)((glyph.V1 - glyph.V0) * TexHeight + 1.99f); // +1 to account for average padding, +0.99 to round
dst_font->MetricsTotalSurface += (int)((glyph.U1 - glyph.U0) * atlas->TexWidth + 1.99f) * (int)((glyph.V1 - glyph.V0) * atlas->TexHeight + 1.99f); // +1 to account for average padding, +0.99 to round
}
}
cfg.DstFont->BuildLookupTable();
@ -1476,100 +1561,105 @@ bool ImFontAtlas::Build()
ImGui::MemFree(tmp_array);
// Render into our custom data block
RenderCustomTexData(1, &extra_rects);
ImFontAtlasBuildRenderDefaultTexData(atlas);
return true;
}
void ImFontAtlas::RenderCustomTexData(int pass, void* p_rects)
void ImFontAtlasBuildRegisterDefaultCustomRects(ImFontAtlas* atlas)
{
// A work of art lies ahead! (. = white layer, X = black layer, others are blank)
// The white texels on the top left are the ones we'll use everywhere in ImGui to render filled shapes.
const int TEX_DATA_W = 90;
const int TEX_DATA_H = 27;
const char texture_data[TEX_DATA_W*TEX_DATA_H+1] =
// FIXME-WIP: We should register in the constructor (but cannot because our static instances may not have allocator ready by the time they initialize). This needs to be fixed because we can expose CustomRects.
if (atlas->CustomRects.empty())
atlas->CustomRectRegister(FONT_ATLAS_DEFAULT_TEX_DATA_ID, FONT_ATLAS_DEFAULT_TEX_DATA_W_HALF*2+1, FONT_ATLAS_DEFAULT_TEX_DATA_H);
}
void ImFontAtlasBuildSetupFont(ImFontAtlas* atlas, ImFont* font, ImFontConfig* font_config, float ascent, float descent)
{
if (!font_config->MergeMode)
{
"..- -XXXXXXX- X - X -XXXXXXX - XXXXXXX"
"..- -X.....X- X.X - X.X -X.....X - X.....X"
"--- -XXX.XXX- X...X - X...X -X....X - X....X"
"X - X.X - X.....X - X.....X -X...X - X...X"
"XX - X.X -X.......X- X.......X -X..X.X - X.X..X"
"X.X - X.X -XXXX.XXXX- XXXX.XXXX -X.X X.X - X.X X.X"
"X..X - X.X - X.X - X.X -XX X.X - X.X XX"
"X...X - X.X - X.X - XX X.X XX - X.X - X.X "
"X....X - X.X - X.X - X.X X.X X.X - X.X - X.X "
"X.....X - X.X - X.X - X..X X.X X..X - X.X - X.X "
"X......X - X.X - X.X - X...XXXXXX.XXXXXX...X - X.X XX-XX X.X "
"X.......X - X.X - X.X -X.....................X- X.X X.X-X.X X.X "
"X........X - X.X - X.X - X...XXXXXX.XXXXXX...X - X.X..X-X..X.X "
"X.........X -XXX.XXX- X.X - X..X X.X X..X - X...X-X...X "
"X..........X-X.....X- X.X - X.X X.X X.X - X....X-X....X "
"X......XXXXX-XXXXXXX- X.X - XX X.X XX - X.....X-X.....X "
"X...X..X --------- X.X - X.X - XXXXXXX-XXXXXXX "
"X..X X..X - -XXXX.XXXX- XXXX.XXXX ------------------------------------"
"X.X X..X - -X.......X- X.......X - XX XX - "
"XX X..X - - X.....X - X.....X - X.X X.X - "
" X..X - X...X - X...X - X..X X..X - "
" XX - X.X - X.X - X...XXXXXXXXXXXXX...X - "
"------------ - X - X -X.....................X- "
" ----------------------------------- X...XXXXXXXXXXXXX...X - "
" - X..X X..X - "
" - X.X X.X - "
" - XX XX - "
font->ContainerAtlas = atlas;
font->ConfigData = font_config;
font->ConfigDataCount = 0;
font->FontSize = font_config->SizePixels;
font->Ascent = ascent;
font->Descent = descent;
font->Glyphs.resize(0);
font->MetricsTotalSurface = 0;
}
font->ConfigDataCount++;
}
void ImFontAtlasBuildPackCustomRects(ImFontAtlas* atlas, void* pack_context_opaque)
{
stbrp_context* pack_context = (stbrp_context*)pack_context_opaque;
ImVector<ImFontAtlas::CustomRect>& user_rects = atlas->CustomRects;
ImVector<stbrp_rect> pack_rects;
pack_rects.resize(user_rects.Size);
memset(pack_rects.Data, 0, sizeof(stbrp_rect) * user_rects.Size);
for (int i = 0; i < user_rects.Size; i++)
{
pack_rects[i].w = user_rects[i].Width;
pack_rects[i].h = user_rects[i].Height;
}
stbrp_pack_rects(pack_context, &pack_rects[0], pack_rects.Size);
for (int i = 0; i < pack_rects.Size; i++)
if (pack_rects[i].was_packed)
{
user_rects[i].X = pack_rects[i].x;
user_rects[i].Y = pack_rects[i].y;
IM_ASSERT(pack_rects[i].w == user_rects[i].Width && pack_rects[i].h == user_rects[i].Height);
atlas->TexHeight = ImMax(atlas->TexHeight, pack_rects[i].y + pack_rects[i].h);
}
}
void ImFontAtlasBuildRenderDefaultTexData(ImFontAtlas* atlas)
{
ImFontAtlas::CustomRect& r = atlas->CustomRects[0];
IM_ASSERT(r.ID == FONT_ATLAS_DEFAULT_TEX_DATA_ID);
IM_ASSERT(r.Width == FONT_ATLAS_DEFAULT_TEX_DATA_W_HALF*2+1);
IM_ASSERT(r.Height == FONT_ATLAS_DEFAULT_TEX_DATA_H);
IM_ASSERT(r.IsPacked());
IM_ASSERT(atlas->TexPixelsAlpha8 != NULL);
// Render/copy pixels
for (int y = 0, n = 0; y < FONT_ATLAS_DEFAULT_TEX_DATA_H; y++)
for (int x = 0; x < FONT_ATLAS_DEFAULT_TEX_DATA_W_HALF; x++, n++)
{
const int offset0 = (int)(r.X + x) + (int)(r.Y + y) * atlas->TexWidth;
const int offset1 = offset0 + FONT_ATLAS_DEFAULT_TEX_DATA_W_HALF + 1;
atlas->TexPixelsAlpha8[offset0] = FONT_ATLAS_DEFAULT_TEX_DATA_PIXELS[n] == '.' ? 0xFF : 0x00;
atlas->TexPixelsAlpha8[offset1] = FONT_ATLAS_DEFAULT_TEX_DATA_PIXELS[n] == 'X' ? 0xFF : 0x00;
}
const ImVec2 tex_uv_scale(1.0f / atlas->TexWidth, 1.0f / atlas->TexHeight);
atlas->TexUvWhitePixel = ImVec2((r.X + 0.5f) * tex_uv_scale.x, (r.Y + 0.5f) * tex_uv_scale.y);
// Setup mouse cursors
const ImVec2 cursor_datas[ImGuiMouseCursor_Count_][3] =
{
// Pos ........ Size ......... Offset ......
{ ImVec2(0,3), ImVec2(12,19), ImVec2( 0, 0) }, // ImGuiMouseCursor_Arrow
{ ImVec2(13,0), ImVec2(7,16), ImVec2( 4, 8) }, // ImGuiMouseCursor_TextInput
{ ImVec2(31,0), ImVec2(23,23), ImVec2(11,11) }, // ImGuiMouseCursor_Move
{ ImVec2(21,0), ImVec2( 9,23), ImVec2( 5,11) }, // ImGuiMouseCursor_ResizeNS
{ ImVec2(55,18),ImVec2(23, 9), ImVec2(11, 5) }, // ImGuiMouseCursor_ResizeEW
{ ImVec2(73,0), ImVec2(17,17), ImVec2( 9, 9) }, // ImGuiMouseCursor_ResizeNESW
{ ImVec2(55,0), ImVec2(17,17), ImVec2( 9, 9) }, // ImGuiMouseCursor_ResizeNWSE
};
ImVector<stbrp_rect>& rects = *(ImVector<stbrp_rect>*)p_rects;
if (pass == 0)
for (int type = 0; type < ImGuiMouseCursor_Count_; type++)
{
// Request rectangles
stbrp_rect r;
memset(&r, 0, sizeof(r));
r.w = (TEX_DATA_W*2)+1;
r.h = TEX_DATA_H+1;
rects.push_back(r);
}
else if (pass == 1)
{
// Render/copy pixels
const stbrp_rect& r = rects[0];
for (int y = 0, n = 0; y < TEX_DATA_H; y++)
for (int x = 0; x < TEX_DATA_W; x++, n++)
{
const int offset0 = (int)(r.x + x) + (int)(r.y + y) * TexWidth;
const int offset1 = offset0 + 1 + TEX_DATA_W;
TexPixelsAlpha8[offset0] = texture_data[n] == '.' ? 0xFF : 0x00;
TexPixelsAlpha8[offset1] = texture_data[n] == 'X' ? 0xFF : 0x00;
}
const ImVec2 tex_uv_scale(1.0f / TexWidth, 1.0f / TexHeight);
TexUvWhitePixel = ImVec2((r.x + 0.5f) * tex_uv_scale.x, (r.y + 0.5f) * tex_uv_scale.y);
// Setup mouse cursors
const ImVec2 cursor_datas[ImGuiMouseCursor_Count_][3] =
{
// Pos ........ Size ......... Offset ......
{ ImVec2(0,3), ImVec2(12,19), ImVec2( 0, 0) }, // ImGuiMouseCursor_Arrow
{ ImVec2(13,0), ImVec2(7,16), ImVec2( 4, 8) }, // ImGuiMouseCursor_TextInput
{ ImVec2(31,0), ImVec2(23,23), ImVec2(11,11) }, // ImGuiMouseCursor_Move
{ ImVec2(21,0), ImVec2( 9,23), ImVec2( 5,11) }, // ImGuiMouseCursor_ResizeNS
{ ImVec2(55,18),ImVec2(23, 9), ImVec2(11, 5) }, // ImGuiMouseCursor_ResizeEW
{ ImVec2(73,0), ImVec2(17,17), ImVec2( 9, 9) }, // ImGuiMouseCursor_ResizeNESW
{ ImVec2(55,0), ImVec2(17,17), ImVec2( 9, 9) }, // ImGuiMouseCursor_ResizeNWSE
};
for (int type = 0; type < ImGuiMouseCursor_Count_; type++)
{
ImGuiMouseCursorData& cursor_data = GImGui->MouseCursorData[type];
ImVec2 pos = cursor_datas[type][0] + ImVec2((float)r.x, (float)r.y);
const ImVec2 size = cursor_datas[type][1];
cursor_data.Type = type;
cursor_data.Size = size;
cursor_data.HotOffset = cursor_datas[type][2];
cursor_data.TexUvMin[0] = (pos) * tex_uv_scale;
cursor_data.TexUvMax[0] = (pos + size) * tex_uv_scale;
pos.x += TEX_DATA_W+1;
cursor_data.TexUvMin[1] = (pos) * tex_uv_scale;
cursor_data.TexUvMax[1] = (pos + size) * tex_uv_scale;
}
ImGuiMouseCursorData& cursor_data = GImGui->MouseCursorData[type];
ImVec2 pos = cursor_datas[type][0] + ImVec2((float)r.X, (float)r.Y);
const ImVec2 size = cursor_datas[type][1];
cursor_data.Type = type;
cursor_data.Size = size;
cursor_data.HotOffset = cursor_datas[type][2];
cursor_data.TexUvMin[0] = (pos) * tex_uv_scale;
cursor_data.TexUvMax[0] = (pos + size) * tex_uv_scale;
pos.x += FONT_ATLAS_DEFAULT_TEX_DATA_W_HALF + 1;
cursor_data.TexUvMin[1] = (pos) * tex_uv_scale;
cursor_data.TexUvMax[1] = (pos + size) * tex_uv_scale;
}
}
@ -2236,6 +2326,78 @@ void ImFont::RenderText(ImDrawList* draw_list, float size, ImVec2 pos, ImU32 col
draw_list->_VtxCurrentIdx = (unsigned int)draw_list->VtxBuffer.Size;
}
//-----------------------------------------------------------------------------
// Internals Drawing Helpers
//-----------------------------------------------------------------------------
static inline float ImAcos01(float x)
{
if (x <= 0.0f) return IM_PI * 0.5f;
if (x >= 1.0f) return 0.0f;
return acosf(x);
//return (-0.69813170079773212f * x * x - 0.87266462599716477f) * x + 1.5707963267948966f; // Cheap approximation, may be enough for what we do.
}
// FIXME: Cleanup and move code to ImDrawList.
void ImGui::RenderRectFilledRangeH(ImDrawList* draw_list, const ImRect& rect, ImU32 col, float x_start_norm, float x_end_norm, float rounding)
{
if (x_end_norm == x_start_norm)
return;
if (x_start_norm > x_end_norm)
ImSwap(x_start_norm, x_end_norm);
ImVec2 p0 = ImVec2(ImLerp(rect.Min.x, rect.Max.x, x_start_norm), rect.Min.y);
ImVec2 p1 = ImVec2(ImLerp(rect.Min.x, rect.Max.x, x_end_norm), rect.Max.y);
if (rounding == 0.0f)
{
draw_list->AddRectFilled(p0, p1, col, 0.0f);
return;
}
rounding = ImClamp(ImMin((rect.Max.x - rect.Min.x) * 0.5f, (rect.Max.y - rect.Min.y) * 0.5f) - 1.0f, 0.0f, rounding);
const float inv_rounding = 1.0f / rounding;
const float arc0_b = ImAcos01(1.0f - (p0.x - rect.Min.x) * inv_rounding);
const float arc0_e = ImAcos01(1.0f - (p1.x - rect.Min.x) * inv_rounding);
const float x0 = ImMax(p0.x, rect.Min.x + rounding);
if (arc0_b == arc0_e)
{
draw_list->PathLineTo(ImVec2(x0, p1.y));
draw_list->PathLineTo(ImVec2(x0, p0.y));
}
else if (arc0_b == 0.0f && arc0_e == IM_PI*0.5f)
{
draw_list->PathArcToFast(ImVec2(x0, p1.y - rounding), rounding, 3, 6); // BL
draw_list->PathArcToFast(ImVec2(x0, p0.y + rounding), rounding, 6, 9); // TR
}
else
{
draw_list->PathArcTo(ImVec2(x0, p1.y - rounding), rounding, IM_PI - arc0_e, IM_PI - arc0_b, 3); // BL
draw_list->PathArcTo(ImVec2(x0, p0.y + rounding), rounding, IM_PI + arc0_b, IM_PI + arc0_e, 3); // TR
}
if (p1.x > rect.Min.x + rounding)
{
const float arc1_b = ImAcos01(1.0f - (rect.Max.x - p1.x) * inv_rounding);
const float arc1_e = ImAcos01(1.0f - (rect.Max.x - p0.x) * inv_rounding);
const float x1 = ImMin(p1.x, rect.Max.x - rounding);
if (arc1_b == arc1_e)
{
draw_list->PathLineTo(ImVec2(x1, p0.y));
draw_list->PathLineTo(ImVec2(x1, p1.y));
}
else if (arc1_b == 0.0f && arc1_e == IM_PI*0.5f)
{
draw_list->PathArcToFast(ImVec2(x1, p0.y + rounding), rounding, 9, 12); // TR
draw_list->PathArcToFast(ImVec2(x1, p1.y - rounding), rounding, 0, 3); // BR
}
else
{
draw_list->PathArcTo(ImVec2(x1, p0.y + rounding), rounding, -arc1_e, -arc1_b, 3); // TR
draw_list->PathArcTo(ImVec2(x1, p1.y - rounding), rounding, +arc1_b, +arc1_e, 3); // BR
}
}
draw_list->PathFillConvex(col);
}
//-----------------------------------------------------------------------------
// DEFAULT FONT DATA
//-----------------------------------------------------------------------------