aboutsummaryrefslogtreecommitdiffstats
path: root/src/imgui/imgui_draw.cpp
diff options
context:
space:
mode:
authorLionel Landwerlin <[email protected]>2019-01-19 21:37:35 +0000
committerLionel Landwerlin <[email protected]>2019-02-21 18:06:05 +0000
commit3950e7c11efc8682c0a141d17f30b97522e81af9 (patch)
tree7de7de8a69c0ad3ff7072959df86a6e0ec1272cc /src/imgui/imgui_draw.cpp
parent51047cd2e8a1e834d0345e2eadaeb0da70eb7bfc (diff)
imgui: bump copy
Updated at : commit f977871854af941289f2a9090dcc90f7aa3449a8 Author: omar <[email protected]> Date: Fri Feb 15 13:10:22 2019 +0100 ImFont: Minor adjustment to the structure. Examples: Removed unused variable. Signed-off-by: Lionel Landwerlin <[email protected]> Acked-by: Jason Ekstrand <[email protected]> +1-by: Mike Lothian <[email protected]> +1-by: Tapani Pälli <[email protected]> +1-by: Eric Engestrom <[email protected]> +1-by: Yurii Kolesnykov <[email protected]> +1-by: myfreeweb <[email protected]> +1-by: Kenneth Graunke <[email protected]>
Diffstat (limited to 'src/imgui/imgui_draw.cpp')
-rw-r--r--src/imgui/imgui_draw.cpp823
1 files changed, 518 insertions, 305 deletions
diff --git a/src/imgui/imgui_draw.cpp b/src/imgui/imgui_draw.cpp
index ad93de2f7e1..1d4e1d51692 100644
--- a/src/imgui/imgui_draw.cpp
+++ b/src/imgui/imgui_draw.cpp
@@ -1,13 +1,25 @@
-// dear imgui, v1.63 WIP
+// dear imgui, v1.68 WIP
// (drawing and font code)
-// Contains implementation for
-// - Default styles
-// - ImDrawList
-// - ImDrawData
-// - ImFontAtlas
-// - ImFont
-// - Default font data
+/*
+
+Index of this file:
+
+// [SECTION] STB libraries implementation
+// [SECTION] Style functions
+// [SECTION] ImDrawList
+// [SECTION] ImDrawData
+// [SECTION] Helpers ShadeVertsXXX functions
+// [SECTION] ImFontConfig
+// [SECTION] ImFontAtlas
+// [SECTION] ImFontAtlas glyph ranges helpers
+// [SECTION] ImFontGlyphRangesBuilder
+// [SECTION] ImFont
+// [SECTION] Internal Render Helpers
+// [SECTION] Decompression code
+// [SECTION] Default font data (ProggyClean.ttf)
+
+*/
#if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)
#define _CRT_SECURE_NO_WARNINGS
@@ -21,7 +33,7 @@
#include <stdio.h> // vsnprintf, sscanf, printf
#if !defined(alloca)
-#if defined(__GLIBC__) || defined(__sun) || defined(__CYGWIN__)
+#if defined(__GLIBC__) || defined(__sun) || defined(__CYGWIN__) || defined(__APPLE__)
#include <alloca.h> // alloca (glibc uses <alloca.h>. Note that Cygwin may have _WIN32 defined, so the order matters here)
#elif defined(_WIN32)
#include <malloc.h> // alloca
@@ -33,16 +45,21 @@
#endif
#endif
+// Visual Studio warnings
#ifdef _MSC_VER
#pragma warning (disable: 4505) // unreferenced local function has been removed (stb stuff)
#pragma warning (disable: 4996) // 'This function or variable may be unsafe': strcpy, strdup, sprintf, vsnprintf, sscanf, fopen
#endif
+// Clang/GCC warnings with -Weverything
#ifdef __clang__
#pragma clang diagnostic ignored "-Wold-style-cast" // warning : use of old-style cast // yes, they are more terse.
#pragma clang diagnostic ignored "-Wfloat-equal" // warning : comparing floating point with == or != is unsafe // storing and comparing against same constants ok.
#pragma clang diagnostic ignored "-Wglobal-constructors" // warning : declaration requires a global destructor // similar to above, not sure what the exact difference it.
#pragma clang diagnostic ignored "-Wsign-conversion" // warning : implicit conversion changes signedness //
+#if __has_warning("-Wzero-as-null-pointer-constant")
+#pragma clang diagnostic ignored "-Wzero-as-null-pointer-constant" // warning : zero as null pointer constant // some standard header variations use #define NULL 0
+#endif
#if __has_warning("-Wcomma")
#pragma clang diagnostic ignored "-Wcomma" // warning : possible misuse of comma operator here //
#endif
@@ -50,7 +67,7 @@
#pragma clang diagnostic ignored "-Wreserved-id-macro" // warning : macro name is a reserved identifier //
#endif
#if __has_warning("-Wdouble-promotion")
-#pragma clang diagnostic ignored "-Wdouble-promotion" // warning: implicit conversion from 'float' to 'double' when passing argument to function
+#pragma clang diagnostic ignored "-Wdouble-promotion" // warning: implicit conversion from 'float' to 'double' when passing argument to function // using printf() is a misery with this as C++ va_arg ellipsis changes float to double.
#endif
#elif defined(__GNUC__)
#pragma GCC diagnostic ignored "-Wunused-function" // warning: 'xxxx' defined but not used
@@ -62,7 +79,7 @@
#endif
//-------------------------------------------------------------------------
-// STB libraries implementation
+// [SECTION] STB libraries implementation
//-------------------------------------------------------------------------
// Compile time options:
@@ -106,7 +123,7 @@ namespace IMGUI_STB_NAMESPACE
#ifdef IMGUI_STB_RECT_PACK_FILENAME
#include IMGUI_STB_RECT_PACK_FILENAME
#else
-#include "stb_rect_pack.h"
+#include "imstb_rectpack.h"
#endif
#endif
@@ -129,7 +146,7 @@ namespace IMGUI_STB_NAMESPACE
#ifdef IMGUI_STB_TRUETYPE_FILENAME
#include IMGUI_STB_TRUETYPE_FILENAME
#else
-#include "stb_truetype.h"
+#include "imstb_truetype.h"
#endif
#endif
@@ -151,7 +168,7 @@ using namespace IMGUI_STB_NAMESPACE;
#endif
//-----------------------------------------------------------------------------
-// Style functions
+// [SECTION] Style functions
//-----------------------------------------------------------------------------
void ImGui::StyleColorsDark(ImGuiStyle* dst)
@@ -162,7 +179,7 @@ void ImGui::StyleColorsDark(ImGuiStyle* dst)
colors[ImGuiCol_Text] = ImVec4(1.00f, 1.00f, 1.00f, 1.00f);
colors[ImGuiCol_TextDisabled] = ImVec4(0.50f, 0.50f, 0.50f, 1.00f);
colors[ImGuiCol_WindowBg] = ImVec4(0.06f, 0.06f, 0.06f, 0.94f);
- colors[ImGuiCol_ChildBg] = ImVec4(1.00f, 1.00f, 1.00f, 0.00f);
+ colors[ImGuiCol_ChildBg] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f);
colors[ImGuiCol_PopupBg] = ImVec4(0.08f, 0.08f, 0.08f, 0.94f);
colors[ImGuiCol_Border] = ImVec4(0.43f, 0.43f, 0.50f, 0.50f);
colors[ImGuiCol_BorderShadow] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f);
@@ -192,6 +209,11 @@ void ImGui::StyleColorsDark(ImGuiStyle* dst)
colors[ImGuiCol_ResizeGrip] = ImVec4(0.26f, 0.59f, 0.98f, 0.25f);
colors[ImGuiCol_ResizeGripHovered] = ImVec4(0.26f, 0.59f, 0.98f, 0.67f);
colors[ImGuiCol_ResizeGripActive] = ImVec4(0.26f, 0.59f, 0.98f, 0.95f);
+ colors[ImGuiCol_Tab] = ImLerp(colors[ImGuiCol_Header], colors[ImGuiCol_TitleBgActive], 0.80f);
+ colors[ImGuiCol_TabHovered] = colors[ImGuiCol_HeaderHovered];
+ colors[ImGuiCol_TabActive] = ImLerp(colors[ImGuiCol_HeaderActive], colors[ImGuiCol_TitleBgActive], 0.60f);
+ colors[ImGuiCol_TabUnfocused] = ImLerp(colors[ImGuiCol_Tab], colors[ImGuiCol_TitleBg], 0.80f);
+ colors[ImGuiCol_TabUnfocusedActive] = ImLerp(colors[ImGuiCol_TabActive], colors[ImGuiCol_TitleBg], 0.40f);
colors[ImGuiCol_PlotLines] = ImVec4(0.61f, 0.61f, 0.61f, 1.00f);
colors[ImGuiCol_PlotLinesHovered] = ImVec4(1.00f, 0.43f, 0.35f, 1.00f);
colors[ImGuiCol_PlotHistogram] = ImVec4(0.90f, 0.70f, 0.00f, 1.00f);
@@ -242,6 +264,11 @@ void ImGui::StyleColorsClassic(ImGuiStyle* dst)
colors[ImGuiCol_ResizeGrip] = ImVec4(1.00f, 1.00f, 1.00f, 0.16f);
colors[ImGuiCol_ResizeGripHovered] = ImVec4(0.78f, 0.82f, 1.00f, 0.60f);
colors[ImGuiCol_ResizeGripActive] = ImVec4(0.78f, 0.82f, 1.00f, 0.90f);
+ colors[ImGuiCol_Tab] = ImLerp(colors[ImGuiCol_Header], colors[ImGuiCol_TitleBgActive], 0.80f);
+ colors[ImGuiCol_TabHovered] = colors[ImGuiCol_HeaderHovered];
+ colors[ImGuiCol_TabActive] = ImLerp(colors[ImGuiCol_HeaderActive], colors[ImGuiCol_TitleBgActive], 0.60f);
+ colors[ImGuiCol_TabUnfocused] = ImLerp(colors[ImGuiCol_Tab], colors[ImGuiCol_TitleBg], 0.80f);
+ colors[ImGuiCol_TabUnfocusedActive] = ImLerp(colors[ImGuiCol_TabActive], colors[ImGuiCol_TitleBg], 0.40f);
colors[ImGuiCol_PlotLines] = ImVec4(1.00f, 1.00f, 1.00f, 1.00f);
colors[ImGuiCol_PlotLinesHovered] = ImVec4(0.90f, 0.70f, 0.00f, 1.00f);
colors[ImGuiCol_PlotHistogram] = ImVec4(0.90f, 0.70f, 0.00f, 1.00f);
@@ -293,6 +320,11 @@ void ImGui::StyleColorsLight(ImGuiStyle* dst)
colors[ImGuiCol_ResizeGrip] = ImVec4(0.80f, 0.80f, 0.80f, 0.56f);
colors[ImGuiCol_ResizeGripHovered] = ImVec4(0.26f, 0.59f, 0.98f, 0.67f);
colors[ImGuiCol_ResizeGripActive] = ImVec4(0.26f, 0.59f, 0.98f, 0.95f);
+ colors[ImGuiCol_Tab] = ImLerp(colors[ImGuiCol_Header], colors[ImGuiCol_TitleBgActive], 0.90f);
+ colors[ImGuiCol_TabHovered] = colors[ImGuiCol_HeaderHovered];
+ colors[ImGuiCol_TabActive] = ImLerp(colors[ImGuiCol_HeaderActive], colors[ImGuiCol_TitleBgActive], 0.60f);
+ colors[ImGuiCol_TabUnfocused] = ImLerp(colors[ImGuiCol_Tab], colors[ImGuiCol_TitleBg], 0.80f);
+ colors[ImGuiCol_TabUnfocusedActive] = ImLerp(colors[ImGuiCol_TabActive], colors[ImGuiCol_TitleBg], 0.40f);
colors[ImGuiCol_PlotLines] = ImVec4(0.39f, 0.39f, 0.39f, 1.00f);
colors[ImGuiCol_PlotLinesHovered] = ImVec4(1.00f, 0.43f, 0.35f, 1.00f);
colors[ImGuiCol_PlotHistogram] = ImVec4(0.90f, 0.70f, 0.00f, 1.00f);
@@ -306,7 +338,7 @@ void ImGui::StyleColorsLight(ImGuiStyle* dst)
}
//-----------------------------------------------------------------------------
-// ImDrawListData
+// ImDrawList
//-----------------------------------------------------------------------------
ImDrawListSharedData::ImDrawListSharedData()
@@ -324,10 +356,6 @@ ImDrawListSharedData::ImDrawListSharedData()
}
}
-//-----------------------------------------------------------------------------
-// ImDrawList
-//-----------------------------------------------------------------------------
-
void ImDrawList::Clear()
{
CmdBuffer.resize(0);
@@ -631,7 +659,13 @@ void ImDrawList::PrimQuadUV(const ImVec2& a, const ImVec2& b, const ImVec2& c, c
_IdxWritePtr += 6;
}
+// On AddPolyline() and AddConvexPolyFilled() we intentionally avoid using ImVec2 and superflous function calls to optimize debug/non-inlined builds.
+// Those macros expects l-values.
+#define IM_NORMALIZE2F_OVER_ZERO(VX,VY) { float d2 = VX*VX + VY*VY; if (d2 > 0.0f) { float inv_len = 1.0f / ImSqrt(d2); VX *= inv_len; VY *= inv_len; } }
+#define IM_NORMALIZE2F_OVER_EPSILON_CLAMP(VX,VY,EPS,INVLENMAX) { float d2 = VX*VX + VY*VY; if (d2 > EPS) { float inv_len = 1.0f / ImSqrt(d2); if (inv_len > INVLENMAX) inv_len = INVLENMAX; VX *= inv_len; VY *= inv_len; } }
+
// TODO: Thickness anti-aliased lines cap are missing their AA fringe.
+// We avoid using the ImVec2 math operators here to reduce cost to a minimum for debug/non-inlined builds.
void ImDrawList::AddPolyline(const ImVec2* points, const int points_count, ImU32 col, bool closed, float thickness)
{
if (points_count < 2)
@@ -655,16 +689,17 @@ void ImDrawList::AddPolyline(const ImVec2* points, const int points_count, ImU32
PrimReserve(idx_count, vtx_count);
// Temporary buffer
- ImVec2* temp_normals = (ImVec2*)alloca(points_count * (thick_line ? 5 : 3) * sizeof(ImVec2));
+ ImVec2* temp_normals = (ImVec2*)alloca(points_count * (thick_line ? 5 : 3) * sizeof(ImVec2)); //-V630
ImVec2* temp_points = temp_normals + points_count;
for (int i1 = 0; i1 < count; i1++)
{
const int i2 = (i1+1) == points_count ? 0 : i1+1;
- ImVec2 diff = points[i2] - points[i1];
- diff *= ImInvLength(diff, 1.0f);
- temp_normals[i1].x = diff.y;
- temp_normals[i1].y = -diff.x;
+ float dx = points[i2].x - points[i1].x;
+ float dy = points[i2].y - points[i1].y;
+ IM_NORMALIZE2F_OVER_ZERO(dx, dy);
+ temp_normals[i1].x = dy;
+ temp_normals[i1].y = -dx;
}
if (!closed)
temp_normals[points_count-1] = temp_normals[points_count-2];
@@ -687,17 +722,18 @@ void ImDrawList::AddPolyline(const ImVec2* points, const int points_count, ImU32
unsigned int idx2 = (i1+1) == points_count ? _VtxCurrentIdx : idx1+3;
// Average normals
- ImVec2 dm = (temp_normals[i1] + temp_normals[i2]) * 0.5f;
- float dmr2 = dm.x*dm.x + dm.y*dm.y;
- if (dmr2 > 0.000001f)
- {
- float scale = 1.0f / dmr2;
- if (scale > 100.0f) scale = 100.0f;
- dm *= scale;
- }
- dm *= AA_SIZE;
- temp_points[i2*2+0] = points[i2] + dm;
- temp_points[i2*2+1] = points[i2] - dm;
+ float dm_x = (temp_normals[i1].x + temp_normals[i2].x) * 0.5f;
+ float dm_y = (temp_normals[i1].y + temp_normals[i2].y) * 0.5f;
+ IM_NORMALIZE2F_OVER_EPSILON_CLAMP(dm_x, dm_y, 0.000001f, 100.0f)
+ dm_x *= AA_SIZE;
+ dm_y *= AA_SIZE;
+
+ // Add temporary vertexes
+ ImVec2* out_vtx = &temp_points[i2*2];
+ out_vtx[0].x = points[i2].x + dm_x;
+ out_vtx[0].y = points[i2].y + dm_y;
+ out_vtx[1].x = points[i2].x - dm_x;
+ out_vtx[1].y = points[i2].y - dm_y;
// Add indexes
_IdxWritePtr[0] = (ImDrawIdx)(idx2+0); _IdxWritePtr[1] = (ImDrawIdx)(idx1+0); _IdxWritePtr[2] = (ImDrawIdx)(idx1+2);
@@ -741,20 +777,24 @@ void ImDrawList::AddPolyline(const ImVec2* points, const int points_count, ImU32
unsigned int idx2 = (i1+1) == points_count ? _VtxCurrentIdx : idx1+4;
// Average normals
- ImVec2 dm = (temp_normals[i1] + temp_normals[i2]) * 0.5f;
- float dmr2 = dm.x*dm.x + dm.y*dm.y;
- if (dmr2 > 0.000001f)
- {
- float scale = 1.0f / dmr2;
- if (scale > 100.0f) scale = 100.0f;
- dm *= scale;
- }
- ImVec2 dm_out = dm * (half_inner_thickness + AA_SIZE);
- ImVec2 dm_in = dm * half_inner_thickness;
- temp_points[i2*4+0] = points[i2] + dm_out;
- temp_points[i2*4+1] = points[i2] + dm_in;
- temp_points[i2*4+2] = points[i2] - dm_in;
- temp_points[i2*4+3] = points[i2] - dm_out;
+ float dm_x = (temp_normals[i1].x + temp_normals[i2].x) * 0.5f;
+ float dm_y = (temp_normals[i1].y + temp_normals[i2].y) * 0.5f;
+ IM_NORMALIZE2F_OVER_EPSILON_CLAMP(dm_x, dm_y, 0.000001f, 100.0f);
+ float dm_out_x = dm_x * (half_inner_thickness + AA_SIZE);
+ float dm_out_y = dm_y * (half_inner_thickness + AA_SIZE);
+ float dm_in_x = dm_x * half_inner_thickness;
+ float dm_in_y = dm_y * half_inner_thickness;
+
+ // Add temporary vertexes
+ ImVec2* out_vtx = &temp_points[i2*4];
+ out_vtx[0].x = points[i2].x + dm_out_x;
+ out_vtx[0].y = points[i2].y + dm_out_y;
+ out_vtx[1].x = points[i2].x + dm_in_x;
+ out_vtx[1].y = points[i2].y + dm_in_y;
+ out_vtx[2].x = points[i2].x - dm_in_x;
+ out_vtx[2].y = points[i2].y - dm_in_y;
+ out_vtx[3].x = points[i2].x - dm_out_x;
+ out_vtx[3].y = points[i2].y - dm_out_y;
// Add indexes
_IdxWritePtr[0] = (ImDrawIdx)(idx2+1); _IdxWritePtr[1] = (ImDrawIdx)(idx1+1); _IdxWritePtr[2] = (ImDrawIdx)(idx1+2);
@@ -792,11 +832,13 @@ void ImDrawList::AddPolyline(const ImVec2* points, const int points_count, ImU32
const int i2 = (i1+1) == points_count ? 0 : i1+1;
const ImVec2& p1 = points[i1];
const ImVec2& p2 = points[i2];
- ImVec2 diff = p2 - p1;
- diff *= ImInvLength(diff, 1.0f);
- const float dx = diff.x * (thickness * 0.5f);
- const float dy = diff.y * (thickness * 0.5f);
+ float dx = p2.x - p1.x;
+ float dy = p2.y - p1.y;
+ IM_NORMALIZE2F_OVER_ZERO(dx, dy);
+ dx *= (thickness * 0.5f);
+ dy *= (thickness * 0.5f);
+
_VtxWritePtr[0].pos.x = p1.x + dy; _VtxWritePtr[0].pos.y = p1.y - dx; _VtxWritePtr[0].uv = uv; _VtxWritePtr[0].col = col;
_VtxWritePtr[1].pos.x = p2.x + dy; _VtxWritePtr[1].pos.y = p2.y - dx; _VtxWritePtr[1].uv = uv; _VtxWritePtr[1].col = col;
_VtxWritePtr[2].pos.x = p2.x - dy; _VtxWritePtr[2].pos.y = p2.y + dx; _VtxWritePtr[2].uv = uv; _VtxWritePtr[2].col = col;
@@ -811,8 +853,12 @@ void ImDrawList::AddPolyline(const ImVec2* points, const int points_count, ImU32
}
}
+// We intentionally avoid using ImVec2 and its math operators here to reduce cost to a minimum for debug/non-inlined builds.
void ImDrawList::AddConvexPolyFilled(const ImVec2* points, const int points_count, ImU32 col)
{
+ if (points_count < 3)
+ return;
+
const ImVec2 uv = _Data->TexUvWhitePixel;
if (Flags & ImDrawListFlags_AntiAliasedFill)
@@ -834,15 +880,16 @@ void ImDrawList::AddConvexPolyFilled(const ImVec2* points, const int points_coun
}
// Compute normals
- ImVec2* temp_normals = (ImVec2*)alloca(points_count * sizeof(ImVec2));
+ ImVec2* temp_normals = (ImVec2*)alloca(points_count * sizeof(ImVec2)); //-V630
for (int i0 = points_count-1, i1 = 0; i1 < points_count; i0 = i1++)
{
const ImVec2& p0 = points[i0];
const ImVec2& p1 = points[i1];
- ImVec2 diff = p1 - p0;
- diff *= ImInvLength(diff, 1.0f);
- temp_normals[i0].x = diff.y;
- temp_normals[i0].y = -diff.x;
+ float dx = p1.x - p0.x;
+ float dy = p1.y - p0.y;
+ IM_NORMALIZE2F_OVER_ZERO(dx, dy);
+ temp_normals[i0].x = dy;
+ temp_normals[i0].y = -dx;
}
for (int i0 = points_count-1, i1 = 0; i1 < points_count; i0 = i1++)
@@ -850,19 +897,15 @@ void ImDrawList::AddConvexPolyFilled(const ImVec2* points, const int points_coun
// Average normals
const ImVec2& n0 = temp_normals[i0];
const ImVec2& n1 = temp_normals[i1];
- ImVec2 dm = (n0 + n1) * 0.5f;
- float dmr2 = dm.x*dm.x + dm.y*dm.y;
- if (dmr2 > 0.000001f)
- {
- float scale = 1.0f / dmr2;
- if (scale > 100.0f) scale = 100.0f;
- dm *= scale;
- }
- dm *= AA_SIZE * 0.5f;
+ float dm_x = (n0.x + n1.x) * 0.5f;
+ float dm_y = (n0.y + n1.y) * 0.5f;
+ IM_NORMALIZE2F_OVER_EPSILON_CLAMP(dm_x, dm_y, 0.000001f, 100.0f);
+ dm_x *= AA_SIZE * 0.5f;
+ dm_y *= AA_SIZE * 0.5f;
// Add vertices
- _VtxWritePtr[0].pos = (points[i1] - dm); _VtxWritePtr[0].uv = uv; _VtxWritePtr[0].col = col; // Inner
- _VtxWritePtr[1].pos = (points[i1] + dm); _VtxWritePtr[1].uv = uv; _VtxWritePtr[1].col = col_trans; // Outer
+ _VtxWritePtr[0].pos.x = (points[i1].x - dm_x); _VtxWritePtr[0].pos.y = (points[i1].y - dm_y); _VtxWritePtr[0].uv = uv; _VtxWritePtr[0].col = col; // Inner
+ _VtxWritePtr[1].pos.x = (points[i1].x + dm_x); _VtxWritePtr[1].pos.y = (points[i1].y + dm_y); _VtxWritePtr[1].uv = uv; _VtxWritePtr[1].col = col_trans; // Outer
_VtxWritePtr += 2;
// Add indexes for fringes
@@ -914,6 +957,9 @@ void ImDrawList::PathArcTo(const ImVec2& centre, float radius, float a_min, floa
_Path.push_back(centre);
return;
}
+
+ // Note that we are adding a point at both a_min and a_max.
+ // If you are trying to draw a full closed circle you don't want the overlapping points!
_Path.reserve(_Path.Size + (num_segments + 1));
for (int i = 0; i <= num_segments; i++)
{
@@ -1097,21 +1143,23 @@ void ImDrawList::AddTriangleFilled(const ImVec2& a, const ImVec2& b, const ImVec
void ImDrawList::AddCircle(const ImVec2& centre, float radius, ImU32 col, int num_segments, float thickness)
{
- if ((col & IM_COL32_A_MASK) == 0)
+ if ((col & IM_COL32_A_MASK) == 0 || num_segments <= 2)
return;
+ // Because we are filling a closed shape we remove 1 from the count of segments/points
const float a_max = IM_PI*2.0f * ((float)num_segments - 1.0f) / (float)num_segments;
- PathArcTo(centre, radius-0.5f, 0.0f, a_max, num_segments);
+ PathArcTo(centre, radius-0.5f, 0.0f, a_max, num_segments - 1);
PathStroke(col, true, thickness);
}
void ImDrawList::AddCircleFilled(const ImVec2& centre, float radius, ImU32 col, int num_segments)
{
- if ((col & IM_COL32_A_MASK) == 0)
+ if ((col & IM_COL32_A_MASK) == 0 || num_segments <= 2)
return;
+ // Because we are filling a closed shape we remove 1 from the count of segments/points
const float a_max = IM_PI*2.0f * ((float)num_segments - 1.0f) / (float)num_segments;
- PathArcTo(centre, radius, 0.0f, a_max, num_segments);
+ PathArcTo(centre, radius, 0.0f, a_max, num_segments - 1);
PathFillConvex(col);
}
@@ -1210,14 +1258,14 @@ void ImDrawList::AddImageRounded(ImTextureID user_texture_id, const ImVec2& a, c
PathRect(a, b, rounding, rounding_corners);
PathFillConvex(col);
int vert_end_idx = VtxBuffer.Size;
- ImGui::ShadeVertsLinearUV(VtxBuffer.Data + vert_start_idx, VtxBuffer.Data + vert_end_idx, a, b, uv_a, uv_b, true);
+ ImGui::ShadeVertsLinearUV(this, vert_start_idx, vert_end_idx, a, b, uv_a, uv_b, true);
if (push_texture_id)
PopTextureID();
}
//-----------------------------------------------------------------------------
-// ImDrawData
+// [SECTION] ImDrawData
//-----------------------------------------------------------------------------
// 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!
@@ -1239,8 +1287,10 @@ void ImDrawData::DeIndexAllBuffers()
}
}
-// Helper to scale the ClipRect field of each ImDrawCmd. Use if your final output buffer is at a different scale than ImGui expects, or if there is a difference between your window resolution and framebuffer resolution.
-void ImDrawData::ScaleClipRects(const ImVec2& scale)
+// Helper to scale the ClipRect field of each ImDrawCmd.
+// Use if your final output buffer is at a different scale than draw_data->DisplaySize,
+// or if there is a difference between your window resolution and framebuffer resolution.
+void ImDrawData::ScaleClipRects(const ImVec2& fb_scale)
{
for (int i = 0; i < CmdListsCount; i++)
{
@@ -1248,20 +1298,22 @@ void ImDrawData::ScaleClipRects(const ImVec2& scale)
for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++)
{
ImDrawCmd* cmd = &cmd_list->CmdBuffer[cmd_i];
- cmd->ClipRect = ImVec4(cmd->ClipRect.x * scale.x, cmd->ClipRect.y * scale.y, cmd->ClipRect.z * scale.x, cmd->ClipRect.w * scale.y);
+ cmd->ClipRect = ImVec4(cmd->ClipRect.x * fb_scale.x, cmd->ClipRect.y * fb_scale.y, cmd->ClipRect.z * fb_scale.x, cmd->ClipRect.w * fb_scale.y);
}
}
}
//-----------------------------------------------------------------------------
-// Shade functions
+// [SECTION] Helpers ShadeVertsXXX functions
//-----------------------------------------------------------------------------
// Generic linear color gradient, write to RGB fields, leave A untouched.
-void ImGui::ShadeVertsLinearColorGradientKeepAlpha(ImDrawVert* vert_start, ImDrawVert* vert_end, ImVec2 gradient_p0, ImVec2 gradient_p1, ImU32 col0, ImU32 col1)
+void ImGui::ShadeVertsLinearColorGradientKeepAlpha(ImDrawList* draw_list, int vert_start_idx, int vert_end_idx, ImVec2 gradient_p0, ImVec2 gradient_p1, ImU32 col0, ImU32 col1)
{
ImVec2 gradient_extent = gradient_p1 - gradient_p0;
float gradient_inv_length2 = 1.0f / ImLengthSqr(gradient_extent);
+ ImDrawVert* vert_start = draw_list->VtxBuffer.Data + vert_start_idx;
+ ImDrawVert* vert_end = draw_list->VtxBuffer.Data + vert_end_idx;
for (ImDrawVert* vert = vert_start; vert < vert_end; vert++)
{
float d = ImDot(vert->pos - gradient_p0, gradient_extent);
@@ -1274,7 +1326,7 @@ void ImGui::ShadeVertsLinearColorGradientKeepAlpha(ImDrawVert* vert_start, ImDra
}
// Distribute UV over (a, b) rectangle
-void ImGui::ShadeVertsLinearUV(ImDrawVert* vert_start, ImDrawVert* vert_end, const ImVec2& a, const ImVec2& b, const ImVec2& uv_a, const ImVec2& uv_b, bool clamp)
+void ImGui::ShadeVertsLinearUV(ImDrawList* draw_list, int vert_start_idx, int vert_end_idx, const ImVec2& a, const ImVec2& b, const ImVec2& uv_a, const ImVec2& uv_b, bool clamp)
{
const ImVec2 size = b - a;
const ImVec2 uv_size = uv_b - uv_a;
@@ -1282,11 +1334,12 @@ void ImGui::ShadeVertsLinearUV(ImDrawVert* vert_start, ImDrawVert* vert_end, con
size.x != 0.0f ? (uv_size.x / size.x) : 0.0f,
size.y != 0.0f ? (uv_size.y / size.y) : 0.0f);
+ ImDrawVert* vert_start = draw_list->VtxBuffer.Data + vert_start_idx;
+ ImDrawVert* vert_end = draw_list->VtxBuffer.Data + vert_end_idx;
if (clamp)
{
const ImVec2 min = ImMin(uv_a, uv_b);
const ImVec2 max = ImMax(uv_a, uv_b);
-
for (ImDrawVert* vertex = vert_start; vertex < vert_end; ++vertex)
vertex->uv = ImClamp(uv_a + ImMul(ImVec2(vertex->pos.x, vertex->pos.y) - a, scale), min, max);
}
@@ -1298,7 +1351,7 @@ void ImGui::ShadeVertsLinearUV(ImDrawVert* vert_start, ImDrawVert* vert_end, con
}
//-----------------------------------------------------------------------------
-// ImFontConfig
+// [SECTION] ImFontConfig
//-----------------------------------------------------------------------------
ImFontConfig::ImFontConfig()
@@ -1308,7 +1361,7 @@ ImFontConfig::ImFontConfig()
FontDataOwnedByAtlas = true;
FontNo = 0;
SizePixels = 0.0f;
- OversampleH = 3;
+ OversampleH = 3; // FIXME: 2 may be a better default?
OversampleV = 1;
PixelSnapH = false;
GlyphExtraSpacing = ImVec2(0.0f, 0.0f);
@@ -1324,7 +1377,7 @@ ImFontConfig::ImFontConfig()
}
//-----------------------------------------------------------------------------
-// ImFontAtlas
+// [SECTION] ImFontAtlas
//-----------------------------------------------------------------------------
// A work of art lies ahead! (. = white layer, X = black layer, others are blank)
@@ -1379,8 +1432,8 @@ static const ImVec2 FONT_ATLAS_DEFAULT_TEX_CURSOR_DATA[ImGuiMouseCursor_COUNT][3
ImFontAtlas::ImFontAtlas()
{
Locked = false;
- Flags = 0x00;
- TexID = NULL;
+ Flags = ImFontAtlasFlags_None;
+ TexID = (ImTextureID)NULL;
TexDesiredWidth = 0;
TexGlyphPadding = 1;
@@ -1474,7 +1527,7 @@ void ImFontAtlas::GetTexDataAsRGBA32(unsigned char** out_pixels, int* out_wid
GetTexDataAsAlpha8(&pixels, NULL, NULL);
if (pixels)
{
- TexPixelsRGBA32 = (unsigned int*)ImGui::MemAlloc((size_t)(TexWidth * TexHeight * 4));
+ TexPixelsRGBA32 = (unsigned int*)ImGui::MemAlloc((size_t)TexWidth * (size_t)TexHeight * 4);
const unsigned char* src = pixels;
unsigned int* dst = TexPixelsRGBA32;
for (int n = TexWidth * TexHeight; n > 0; n--)
@@ -1498,11 +1551,11 @@ ImFont* ImFontAtlas::AddFont(const ImFontConfig* font_cfg)
if (!font_cfg->MergeMode)
Fonts.push_back(IM_NEW(ImFont));
else
- IM_ASSERT(!Fonts.empty()); // When using MergeMode make sure that a font has already been added before. You can use ImGui::GetIO().Fonts->AddFontDefault() to add the default imgui font.
+ IM_ASSERT(!Fonts.empty() && "Cannot use MergeMode for the first font"); // When using MergeMode make sure that a font has already been added before. You can use ImGui::GetIO().Fonts->AddFontDefault() to add the default imgui font.
ConfigData.push_back(*font_cfg);
ImFontConfig& new_font_cfg = ConfigData.back();
- if (!new_font_cfg.DstFont)
+ if (new_font_cfg.DstFont == NULL)
new_font_cfg.DstFont = Fonts.back();
if (!new_font_cfg.FontDataOwnedByAtlas)
{
@@ -1541,11 +1594,14 @@ ImFont* ImFontAtlas::AddFontDefault(const ImFontConfig* font_cfg_template)
font_cfg.OversampleH = font_cfg.OversampleV = 1;
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;
+ if (font_cfg.SizePixels <= 0.0f)
+ font_cfg.SizePixels = 13.0f * 1.0f;
+ if (font_cfg.Name[0] == '\0')
+ ImFormatString(font_cfg.Name, IM_ARRAYSIZE(font_cfg.Name), "ProggyClean.ttf, %dpx", (int)font_cfg.SizePixels);
const char* ttf_compressed_base85 = GetDefaultCompressedFontDataTTFBase85();
- ImFont* font = AddFontFromMemoryCompressedBase85TTF(ttf_compressed_base85, font_cfg.SizePixels, &font_cfg, GetGlyphRangesDefault());
+ const ImWchar* glyph_ranges = font_cfg.GlyphRanges != NULL ? font_cfg.GlyphRanges : GetGlyphRangesDefault();
+ ImFont* font = AddFontFromMemoryCompressedBase85TTF(ttf_compressed_base85, font_cfg.SizePixels, &font_cfg, glyph_ranges);
font->DisplayOffset.y = 1.0f;
return font;
}
@@ -1689,139 +1745,220 @@ void ImFontAtlasBuildMultiplyRectAlpha8(const unsigned char table[256], unsig
data[i] = table[data[i]];
}
+// Temporary data for one source font (multiple source fonts can be merged into one destination ImFont)
+// (C++03 doesn't allow instancing ImVector<> with function-local types so we declare the type here.)
+struct ImFontBuildSrcData
+{
+ stbtt_fontinfo FontInfo;
+ stbtt_pack_range PackRange; // Hold the list of codepoints to pack (essentially points to Codepoints.Data)
+ stbrp_rect* Rects; // Rectangle to pack. We first fill in their size and the packer will give us their position.
+ stbtt_packedchar* PackedChars; // Output glyphs
+ const ImWchar* SrcRanges; // Ranges as requested by user (user is allowed to request too much, e.g. 0x0020..0xFFFF)
+ int DstIndex; // Index into atlas->Fonts[] and dst_tmp_array[]
+ int GlyphsHighest; // Highest requested codepoint
+ int GlyphsCount; // Glyph count (excluding missing glyphs and glyphs already set by an earlier source font)
+ ImBoolVector GlyphsSet; // Glyph bit map (random access, 1-bit per codepoint. This will be a maximum of 8KB)
+ ImVector<int> GlyphsList; // Glyph codepoints list (flattened version of GlyphsMap)
+};
+
+// Temporary data for one destination ImFont* (multiple source fonts can be merged into one destination ImFont)
+struct ImFontBuildDstData
+{
+ int SrcCount; // Number of source fonts targeting this destination font.
+ int GlyphsHighest;
+ int GlyphsCount;
+ ImBoolVector GlyphsSet; // This is used to resolve collision when multiple sources are merged into a same destination font.
+};
+
+static void UnpackBoolVectorToFlatIndexList(const ImBoolVector* in, ImVector<int>* out)
+{
+ IM_ASSERT(sizeof(in->Storage.Data[0]) == sizeof(int));
+ const int* it_begin = in->Storage.begin();
+ const int* it_end = in->Storage.end();
+ for (const int* it = it_begin; it < it_end; it++)
+ if (int entries_32 = *it)
+ for (int bit_n = 0; bit_n < 32; bit_n++)
+ if (entries_32 & (1 << bit_n))
+ out->push_back((int)((it - it_begin) << 5) + bit_n);
+}
+
bool ImFontAtlasBuildWithStbTruetype(ImFontAtlas* atlas)
{
IM_ASSERT(atlas->ConfigData.Size > 0);
ImFontAtlasBuildRegisterDefaultCustomRects(atlas);
- atlas->TexID = NULL;
+ // Clear atlas
+ atlas->TexID = (ImTextureID)NULL;
atlas->TexWidth = atlas->TexHeight = 0;
atlas->TexUvScale = ImVec2(0.0f, 0.0f);
atlas->TexUvWhitePixel = ImVec2(0.0f, 0.0f);
atlas->ClearTexData();
- // Count glyphs/ranges
- int total_glyphs_count = 0;
- int total_ranges_count = 0;
- for (int input_i = 0; input_i < atlas->ConfigData.Size; input_i++)
- {
- ImFontConfig& cfg = atlas->ConfigData[input_i];
- if (!cfg.GlyphRanges)
- 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;
- }
-
- // 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 = {};
- if (!stbtt_PackBegin(&spc, NULL, atlas->TexWidth, max_tex_height, 0, atlas->TexGlyphPadding, NULL))
- return false;
- stbtt_PackSetOversampling(&spc, 1, 1);
+ // Temporary storage for building
+ ImVector<ImFontBuildSrcData> src_tmp_array;
+ ImVector<ImFontBuildDstData> dst_tmp_array;
+ src_tmp_array.resize(atlas->ConfigData.Size);
+ dst_tmp_array.resize(atlas->Fonts.Size);
+ memset(src_tmp_array.Data, 0, (size_t)src_tmp_array.size_in_bytes());
+ memset(dst_tmp_array.Data, 0, (size_t)dst_tmp_array.size_in_bytes());
- // Pack our extra data rectangles first, so it will be on the upper-left corner of our texture (UV will have small values).
- 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)atlas->ConfigData.Size * sizeof(ImFontTempBuildData));
- for (int input_i = 0; input_i < atlas->ConfigData.Size; input_i++)
+ // 1. Initialize font loading structure, check font data validity
+ for (int src_i = 0; src_i < atlas->ConfigData.Size; src_i++)
{
- ImFontConfig& cfg = atlas->ConfigData[input_i];
- ImFontTempBuildData& tmp = tmp_array[input_i];
+ ImFontBuildSrcData& src_tmp = src_tmp_array[src_i];
+ ImFontConfig& cfg = atlas->ConfigData[src_i];
IM_ASSERT(cfg.DstFont && (!cfg.DstFont->IsLoaded() || cfg.DstFont->ContainerAtlas == atlas));
+ // Find index from cfg.DstFont (we allow the user to set cfg.DstFont. Also it makes casual debugging nicer than when storing indices)
+ src_tmp.DstIndex = -1;
+ for (int output_i = 0; output_i < atlas->Fonts.Size && src_tmp.DstIndex == -1; output_i++)
+ if (cfg.DstFont == atlas->Fonts[output_i])
+ src_tmp.DstIndex = output_i;
+ IM_ASSERT(src_tmp.DstIndex != -1); // cfg.DstFont not pointing within atlas->Fonts[] array?
+ if (src_tmp.DstIndex == -1)
+ return false;
+
+ // Initialize helper structure for font loading and verify that the TTF/OTF data is correct
const int font_offset = stbtt_GetFontOffsetForIndex((unsigned char*)cfg.FontData, cfg.FontNo);
IM_ASSERT(font_offset >= 0 && "FontData is incorrect, or FontNo cannot be found.");
- if (!stbtt_InitFont(&tmp.FontInfo, (unsigned char*)cfg.FontData, font_offset))
- {
- atlas->TexWidth = atlas->TexHeight = 0; // Reset output on failure
- ImGui::MemFree(tmp_array);
+ if (!stbtt_InitFont(&src_tmp.FontInfo, (unsigned char*)cfg.FontData, font_offset))
return false;
- }
+
+ // Measure highest codepoints
+ ImFontBuildDstData& dst_tmp = dst_tmp_array[src_tmp.DstIndex];
+ src_tmp.SrcRanges = cfg.GlyphRanges ? cfg.GlyphRanges : atlas->GetGlyphRangesDefault();
+ for (const ImWchar* src_range = src_tmp.SrcRanges; src_range[0] && src_range[1]; src_range += 2)
+ src_tmp.GlyphsHighest = ImMax(src_tmp.GlyphsHighest, (int)src_range[1]);
+ dst_tmp.SrcCount++;
+ dst_tmp.GlyphsHighest = ImMax(dst_tmp.GlyphsHighest, src_tmp.GlyphsHighest);
+ }
+
+ // 2. For every requested codepoint, check for their presence in the font data, and handle redundancy or overlaps between source fonts to avoid unused glyphs.
+ int total_glyphs_count = 0;
+ for (int src_i = 0; src_i < src_tmp_array.Size; src_i++)
+ {
+ ImFontBuildSrcData& src_tmp = src_tmp_array[src_i];
+ ImFontBuildDstData& dst_tmp = dst_tmp_array[src_tmp.DstIndex];
+ ImFontConfig& cfg = atlas->ConfigData[src_i];
+ src_tmp.GlyphsSet.Resize(src_tmp.GlyphsHighest + 1);
+ if (dst_tmp.SrcCount > 1 && dst_tmp.GlyphsSet.Storage.empty())
+ dst_tmp.GlyphsSet.Resize(dst_tmp.GlyphsHighest + 1);
+
+ for (const ImWchar* src_range = src_tmp.SrcRanges; src_range[0] && src_range[1]; src_range += 2)
+ for (int codepoint = src_range[0]; codepoint <= src_range[1]; codepoint++)
+ {
+ if (cfg.MergeMode && dst_tmp.GlyphsSet.GetBit(codepoint)) // Don't overwrite existing glyphs. We could make this an option (e.g. MergeOverwrite)
+ continue;
+ if (!stbtt_FindGlyphIndex(&src_tmp.FontInfo, codepoint)) // It is actually in the font?
+ continue;
+
+ // Add to avail set/counters
+ src_tmp.GlyphsCount++;
+ dst_tmp.GlyphsCount++;
+ src_tmp.GlyphsSet.SetBit(codepoint, true);
+ if (dst_tmp.SrcCount > 1)
+ dst_tmp.GlyphsSet.SetBit(codepoint, true);
+ total_glyphs_count++;
+ }
+ }
+
+ // 3. Unpack our bit map into a flat list (we now have all the Unicode points that we know are requested _and_ available _and_ not overlapping another)
+ for (int src_i = 0; src_i < src_tmp_array.Size; src_i++)
+ {
+ ImFontBuildSrcData& src_tmp = src_tmp_array[src_i];
+ src_tmp.GlyphsList.reserve(src_tmp.GlyphsCount);
+ UnpackBoolVectorToFlatIndexList(&src_tmp.GlyphsSet, &src_tmp.GlyphsList);
+ src_tmp.GlyphsSet.Clear();
+ IM_ASSERT(src_tmp.GlyphsList.Size == src_tmp.GlyphsCount);
}
+ for (int dst_i = 0; dst_i < dst_tmp_array.Size; dst_i++)
+ dst_tmp_array[dst_i].GlyphsSet.Clear();
+ dst_tmp_array.clear();
// Allocate packing character data and flag packed characters buffer as non-packed (x0=y0=x1=y1=0)
- int buf_packedchars_n = 0, buf_rects_n = 0, buf_ranges_n = 0;
- stbtt_packedchar* buf_packedchars = (stbtt_packedchar*)ImGui::MemAlloc(total_glyphs_count * sizeof(stbtt_packedchar));
- stbrp_rect* buf_rects = (stbrp_rect*)ImGui::MemAlloc(total_glyphs_count * sizeof(stbrp_rect));
- stbtt_pack_range* buf_ranges = (stbtt_pack_range*)ImGui::MemAlloc(total_ranges_count * sizeof(stbtt_pack_range));
- memset(buf_packedchars, 0, total_glyphs_count * sizeof(stbtt_packedchar));
- memset(buf_rects, 0, total_glyphs_count * sizeof(stbrp_rect)); // Unnecessary but let's clear this for the sake of sanity.
- 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 < atlas->ConfigData.Size; input_i++)
- {
- ImFontConfig& cfg = atlas->ConfigData[input_i];
- ImFontTempBuildData& tmp = tmp_array[input_i];
-
- // Setup ranges
- int font_glyphs_count = 0;
- int font_ranges_count = 0;
- for (const ImWchar* in_range = cfg.GlyphRanges; in_range[0] && in_range[1]; in_range += 2, font_ranges_count++)
- font_glyphs_count += (in_range[1] - in_range[0]) + 1;
- tmp.Ranges = buf_ranges + buf_ranges_n;
- tmp.RangesCount = font_ranges_count;
- buf_ranges_n += font_ranges_count;
- for (int i = 0; i < font_ranges_count; i++)
- {
- const ImWchar* in_range = &cfg.GlyphRanges[i * 2];
- stbtt_pack_range& range = tmp.Ranges[i];
- range.font_size = cfg.SizePixels;
- range.first_unicode_codepoint_in_range = in_range[0];
- range.num_chars = (in_range[1] - in_range[0]) + 1;
- range.chardata_for_range = buf_packedchars + buf_packedchars_n;
- buf_packedchars_n += range.num_chars;
- }
+ // (We technically don't need to zero-clear buf_rects, but let's do it for the sake of sanity)
+ ImVector<stbrp_rect> buf_rects;
+ ImVector<stbtt_packedchar> buf_packedchars;
+ buf_rects.resize(total_glyphs_count);
+ buf_packedchars.resize(total_glyphs_count);
+ memset(buf_rects.Data, 0, (size_t)buf_rects.size_in_bytes());
+ memset(buf_packedchars.Data, 0, (size_t)buf_packedchars.size_in_bytes());
+
+ // 4. Gather glyphs sizes so we can pack them in our virtual canvas.
+ int total_surface = 0;
+ int buf_rects_out_n = 0;
+ int buf_packedchars_out_n = 0;
+ for (int src_i = 0; src_i < src_tmp_array.Size; src_i++)
+ {
+ ImFontBuildSrcData& src_tmp = src_tmp_array[src_i];
+ if (src_tmp.GlyphsCount == 0)
+ continue;
- // Gather the sizes of all rectangle we need
- 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);
- IM_ASSERT(n == font_glyphs_count);
-
- // Detect missing glyphs and replace them with a zero-sized box instead of relying on the default glyphs
- // This allows us merging overlapping icon fonts more easily.
- int rect_i = 0;
- for (int range_i = 0; range_i < tmp.RangesCount; range_i++)
- for (int char_i = 0; char_i < tmp.Ranges[range_i].num_chars; char_i++, rect_i++)
- if (stbtt_FindGlyphIndex(&tmp.FontInfo, tmp.Ranges[range_i].first_unicode_codepoint_in_range + char_i) == 0)
- tmp.Rects[rect_i].w = tmp.Rects[rect_i].h = 0;
-
- // Pack
- stbrp_pack_rects((stbrp_context*)spc.pack_info, tmp.Rects, n);
-
- // Extend texture height
- // Also mark missing glyphs as non-packed so we don't attempt to render into them
- for (int i = 0; i < n; i++)
+ src_tmp.Rects = &buf_rects[buf_rects_out_n];
+ src_tmp.PackedChars = &buf_packedchars[buf_packedchars_out_n];
+ buf_rects_out_n += src_tmp.GlyphsCount;
+ buf_packedchars_out_n += src_tmp.GlyphsCount;
+
+ // Convert our ranges in the format stb_truetype wants
+ ImFontConfig& cfg = atlas->ConfigData[src_i];
+ src_tmp.PackRange.font_size = cfg.SizePixels;
+ src_tmp.PackRange.first_unicode_codepoint_in_range = 0;
+ src_tmp.PackRange.array_of_unicode_codepoints = src_tmp.GlyphsList.Data;
+ src_tmp.PackRange.num_chars = src_tmp.GlyphsList.Size;
+ src_tmp.PackRange.chardata_for_range = src_tmp.PackedChars;
+ src_tmp.PackRange.h_oversample = (unsigned char)cfg.OversampleH;
+ src_tmp.PackRange.v_oversample = (unsigned char)cfg.OversampleV;
+
+ // Gather the sizes of all rectangles we will need to pack (this loop is based on stbtt_PackFontRangesGatherRects)
+ const float scale = (cfg.SizePixels > 0) ? stbtt_ScaleForPixelHeight(&src_tmp.FontInfo, cfg.SizePixels) : stbtt_ScaleForMappingEmToPixels(&src_tmp.FontInfo, -cfg.SizePixels);
+ const int padding = atlas->TexGlyphPadding;
+ for (int glyph_i = 0; glyph_i < src_tmp.GlyphsList.Size; glyph_i++)
{
- if (tmp.Rects[i].w == 0 && tmp.Rects[i].h == 0)
- tmp.Rects[i].was_packed = 0;
- if (tmp.Rects[i].was_packed)
- atlas->TexHeight = ImMax(atlas->TexHeight, tmp.Rects[i].y + tmp.Rects[i].h);
+ int x0, y0, x1, y1;
+ const int glyph_index_in_font = stbtt_FindGlyphIndex(&src_tmp.FontInfo, src_tmp.GlyphsList[glyph_i]);
+ IM_ASSERT(glyph_index_in_font != 0);
+ stbtt_GetGlyphBitmapBoxSubpixel(&src_tmp.FontInfo, glyph_index_in_font, scale * cfg.OversampleH, scale * cfg.OversampleV, 0, 0, &x0, &y0, &x1, &y1);
+ src_tmp.Rects[glyph_i].w = (stbrp_coord)(x1 - x0 + padding + cfg.OversampleH - 1);
+ src_tmp.Rects[glyph_i].h = (stbrp_coord)(y1 - y0 + padding + cfg.OversampleV - 1);
+ total_surface += src_tmp.Rects[glyph_i].w * src_tmp.Rects[glyph_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
+ // We need a width for the skyline algorithm, any width!
+ // The exact width doesn't really matter much, but some API/GPU have texture size limitations and increasing width can decrease height.
+ // User can override TexDesiredWidth and TexGlyphPadding if they wish, otherwise we use a simple heuristic to select the width based on expected surface.
+ const int surface_sqrt = (int)ImSqrt((float)total_surface) + 1;
+ atlas->TexHeight = 0;
+ if (atlas->TexDesiredWidth > 0)
+ atlas->TexWidth = atlas->TexDesiredWidth;
+ else
+ atlas->TexWidth = (surface_sqrt >= 4096*0.7f) ? 4096 : (surface_sqrt >= 2048*0.7f) ? 2048 : (surface_sqrt >= 1024*0.7f) ? 1024 : 512;
+
+ // 5. Start packing
+ // Pack our extra data rectangles first, so it will be on the upper-left corner of our texture (UV will have small values).
+ const int TEX_HEIGHT_MAX = 1024 * 32;
+ stbtt_pack_context spc = {};
+ stbtt_PackBegin(&spc, NULL, atlas->TexWidth, TEX_HEIGHT_MAX, 0, atlas->TexGlyphPadding, NULL);
+ ImFontAtlasBuildPackCustomRects(atlas, spc.pack_info);
+
+ // 6. Pack each source font. No rendering yet, we are working with rectangles in an infinitely tall texture at this point.
+ for (int src_i = 0; src_i < src_tmp_array.Size; src_i++)
+ {
+ ImFontBuildSrcData& src_tmp = src_tmp_array[src_i];
+ if (src_tmp.GlyphsCount == 0)
+ continue;
+
+ stbrp_pack_rects((stbrp_context*)spc.pack_info, src_tmp.Rects, src_tmp.GlyphsCount);
+
+ // Extend texture height and mark missing glyphs as non-packed so we won't render them.
+ // FIXME: We are not handling packing failure here (would happen if we got off TEX_HEIGHT_MAX or if a single if larger than TexWidth?)
+ for (int glyph_i = 0; glyph_i < src_tmp.GlyphsCount; glyph_i++)
+ if (src_tmp.Rects[glyph_i].was_packed)
+ atlas->TexHeight = ImMax(atlas->TexHeight, src_tmp.Rects[glyph_i].y + src_tmp.Rects[glyph_i].h);
+ }
+
+ // 7. Allocate texture
atlas->TexHeight = (atlas->Flags & ImFontAtlasFlags_NoPowerOfTwoHeight) ? (atlas->TexHeight + 1) : ImUpperPowerOfTwo(atlas->TexHeight);
atlas->TexUvScale = ImVec2(1.0f / atlas->TexWidth, 1.0f / atlas->TexHeight);
atlas->TexPixelsAlpha8 = (unsigned char*)ImGui::MemAlloc(atlas->TexWidth * atlas->TexHeight);
@@ -1829,41 +1966,46 @@ bool ImFontAtlasBuildWithStbTruetype(ImFontAtlas* atlas)
spc.pixels = atlas->TexPixelsAlpha8;
spc.height = atlas->TexHeight;
- // Second pass: render font characters
- for (int input_i = 0; input_i < atlas->ConfigData.Size; input_i++)
+ // 8. Render/rasterize font characters into the texture
+ for (int src_i = 0; src_i < src_tmp_array.Size; src_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);
+ ImFontConfig& cfg = atlas->ConfigData[src_i];
+ ImFontBuildSrcData& src_tmp = src_tmp_array[src_i];
+ if (src_tmp.GlyphsCount == 0)
+ continue;
+
+ stbtt_PackFontRangesRenderIntoRects(&spc, &src_tmp.FontInfo, &src_tmp.PackRange, 1, src_tmp.Rects);
+
+ // Apply multiply operator
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++)
+ stbrp_rect* r = &src_tmp.Rects[0];
+ for (int glyph_i = 0; glyph_i < src_tmp.GlyphsCount; glyph_i++, r++)
if (r->was_packed)
- ImFontAtlasBuildMultiplyRectAlpha8(multiply_table, spc.pixels, r->x, r->y, r->w, r->h, spc.stride_in_bytes);
+ ImFontAtlasBuildMultiplyRectAlpha8(multiply_table, atlas->TexPixelsAlpha8, r->x, r->y, r->w, r->h, atlas->TexWidth * 1);
}
- tmp.Rects = NULL;
+ src_tmp.Rects = NULL;
}
// End packing
stbtt_PackEnd(&spc);
- ImGui::MemFree(buf_rects);
- buf_rects = NULL;
+ buf_rects.clear();
- // Third pass: setup ImFont and glyphs for runtime
- for (int input_i = 0; input_i < atlas->ConfigData.Size; input_i++)
+ // 9. Setup ImFont and glyphs for runtime
+ for (int src_i = 0; src_i < src_tmp_array.Size; src_i++)
{
- ImFontConfig& cfg = atlas->ConfigData[input_i];
- ImFontTempBuildData& tmp = tmp_array[input_i];
+ ImFontBuildSrcData& src_tmp = src_tmp_array[src_i];
+ if (src_tmp.GlyphsCount == 0)
+ continue;
+
+ ImFontConfig& cfg = atlas->ConfigData[src_i];
ImFont* dst_font = cfg.DstFont; // We can have multiple input fonts writing into a same destination font (when using MergeMode=true)
- if (cfg.MergeMode)
- dst_font->BuildLookupTable();
- const float font_scale = stbtt_ScaleForPixelHeight(&tmp.FontInfo, cfg.SizePixels);
+ const float font_scale = stbtt_ScaleForPixelHeight(&src_tmp.FontInfo, cfg.SizePixels);
int unscaled_ascent, unscaled_descent, unscaled_line_gap;
- stbtt_GetFontVMetrics(&tmp.FontInfo, &unscaled_ascent, &unscaled_descent, &unscaled_line_gap);
+ stbtt_GetFontVMetrics(&src_tmp.FontInfo, &unscaled_ascent, &unscaled_descent, &unscaled_line_gap);
const float ascent = ImFloor(unscaled_ascent * font_scale + ((unscaled_ascent > 0.0f) ? +1 : -1));
const float descent = ImFloor(unscaled_descent * font_scale + ((unscaled_descent > 0.0f) ? +1 : -1));
@@ -1871,40 +2013,30 @@ bool ImFontAtlasBuildWithStbTruetype(ImFontAtlas* atlas)
const float font_off_x = cfg.GlyphOffset.x;
const float font_off_y = cfg.GlyphOffset.y + (float)(int)(dst_font->Ascent + 0.5f);
- for (int i = 0; i < tmp.RangesCount; i++)
+ for (int glyph_i = 0; glyph_i < src_tmp.GlyphsCount; glyph_i++)
{
- stbtt_pack_range& range = tmp.Ranges[i];
- for (int char_idx = 0; char_idx < range.num_chars; 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_codepoint_in_range + char_idx;
- if (cfg.MergeMode && dst_font->FindGlyphNoFallback((unsigned short)codepoint))
- continue;
-
- float char_advance_x_org = pc.xadvance;
- float char_advance_x_mod = ImClamp(char_advance_x_org, cfg.GlyphMinAdvanceX, cfg.GlyphMaxAdvanceX);
- float char_off_x = font_off_x;
- if (char_advance_x_org != char_advance_x_mod)
- char_off_x += cfg.PixelSnapH ? (float)(int)((char_advance_x_mod - char_advance_x_org) * 0.5f) : (char_advance_x_mod - char_advance_x_org) * 0.5f;
-
- stbtt_aligned_quad q;
- float dummy_x = 0.0f, dummy_y = 0.0f;
- stbtt_GetPackedQuad(range.chardata_for_range, atlas->TexWidth, atlas->TexHeight, char_idx, &dummy_x, &dummy_y, &q, 0);
- dst_font->AddGlyph((ImWchar)codepoint, q.x0 + char_off_x, q.y0 + font_off_y, q.x1 + char_off_x, q.y1 + font_off_y, q.s0, q.t0, q.s1, q.t1, char_advance_x_mod);
- }
+ const int codepoint = src_tmp.GlyphsList[glyph_i];
+ const stbtt_packedchar& pc = src_tmp.PackedChars[glyph_i];
+
+ const float char_advance_x_org = pc.xadvance;
+ const float char_advance_x_mod = ImClamp(char_advance_x_org, cfg.GlyphMinAdvanceX, cfg.GlyphMaxAdvanceX);
+ float char_off_x = font_off_x;
+ if (char_advance_x_org != char_advance_x_mod)
+ char_off_x += cfg.PixelSnapH ? (float)(int)((char_advance_x_mod - char_advance_x_org) * 0.5f) : (char_advance_x_mod - char_advance_x_org) * 0.5f;
+
+ // Register glyph
+ stbtt_aligned_quad q;
+ float dummy_x = 0.0f, dummy_y = 0.0f;
+ stbtt_GetPackedQuad(src_tmp.PackedChars, atlas->TexWidth, atlas->TexHeight, glyph_i, &dummy_x, &dummy_y, &q, 0);
+ dst_font->AddGlyph((ImWchar)codepoint, q.x0 + char_off_x, q.y0 + font_off_y, q.x1 + char_off_x, q.y1 + font_off_y, q.s0, q.t0, q.s1, q.t1, char_advance_x_mod);
}
}
- // Cleanup temporaries
- ImGui::MemFree(buf_packedchars);
- ImGui::MemFree(buf_ranges);
- ImGui::MemFree(tmp_array);
+ // Cleanup temporary (ImVector doesn't honor destructor)
+ for (int src_i = 0; src_i < src_tmp_array.Size; src_i++)
+ src_tmp_array[src_i].~ImFontBuildSrcData();
ImFontAtlasBuildFinish(atlas);
-
return true;
}
@@ -1932,16 +2064,17 @@ void ImFontAtlasBuildSetupFont(ImFontAtlas* atlas, ImFont* font, ImFontConfig* f
font->ConfigDataCount++;
}
-void ImFontAtlasBuildPackCustomRects(ImFontAtlas* atlas, void* pack_context_opaque)
+void ImFontAtlasBuildPackCustomRects(ImFontAtlas* atlas, void* stbrp_context_opaque)
{
- stbrp_context* pack_context = (stbrp_context*)pack_context_opaque;
+ stbrp_context* pack_context = (stbrp_context*)stbrp_context_opaque;
+ IM_ASSERT(pack_context != NULL);
ImVector<ImFontAtlas::CustomRect>& user_rects = atlas->CustomRects;
IM_ASSERT(user_rects.Size >= 1); // We expect at least the default custom rects to be registered, else something went wrong.
ImVector<stbrp_rect> pack_rects;
pack_rects.resize(user_rects.Size);
- memset(pack_rects.Data, 0, sizeof(stbrp_rect) * user_rects.Size);
+ memset(pack_rects.Data, 0, (size_t)pack_rects.size_in_bytes());
for (int i = 0; i < user_rects.Size; i++)
{
pack_rects[i].w = user_rects[i].Width;
@@ -2041,7 +2174,8 @@ const ImWchar* ImFontAtlas::GetGlyphRangesChineseFull()
static const ImWchar ranges[] =
{
0x0020, 0x00FF, // Basic Latin + Latin Supplement
- 0x3000, 0x30FF, // Punctuations, Hiragana, Katakana
+ 0x2000, 0x206F, // General Punctuation
+ 0x3000, 0x30FF, // CJK Symbols and Punctuations, Hiragana, Katakana
0x31F0, 0x31FF, // Katakana Phonetic Extensions
0xFF00, 0xFFEF, // Half-width characters
0x4e00, 0x9FAF, // CJK Ideograms
@@ -2060,12 +2194,16 @@ static void UnpackAccumulativeOffsetsIntoRanges(int base_codepoint, const short*
out_ranges[0] = 0;
}
+//-------------------------------------------------------------------------
+// [SECTION] ImFontAtlas glyph ranges helpers
+//-------------------------------------------------------------------------
+
const ImWchar* ImFontAtlas::GetGlyphRangesChineseSimplifiedCommon()
{
// Store 2500 regularly used characters for Simplified Chinese.
// Sourced from https://zh.wiktionary.org/wiki/%E9%99%84%E5%BD%95:%E7%8E%B0%E4%BB%A3%E6%B1%89%E8%AF%AD%E5%B8%B8%E7%94%A8%E5%AD%97%E8%A1%A8
// This table covers 97.97% of all characters used during the month in July, 1987.
- // You can use ImFontAtlas::GlyphRangesBuilder to create your own ranges derived from this, by merging existing ranges or adding new characters.
+ // You can use ImFontGlyphRangesBuilder to create your own ranges derived from this, by merging existing ranges or adding new characters.
// (Stored as accumulative offsets from the initial unicode codepoint 0x4E00. This encoding is designed to helps us compact the source code size.)
static const short accumulative_offsets_from_0x4E00[] =
{
@@ -2113,9 +2251,10 @@ const ImWchar* ImFontAtlas::GetGlyphRangesChineseSimplifiedCommon()
static ImWchar base_ranges[] = // not zero-terminated
{
0x0020, 0x00FF, // Basic Latin + Latin Supplement
- 0x3000, 0x30FF, // Punctuations, Hiragana, Katakana
+ 0x2000, 0x206F, // General Punctuation
+ 0x3000, 0x30FF, // CJK Symbols and Punctuations, Hiragana, Katakana
0x31F0, 0x31FF, // Katakana Phonetic Extensions
- 0xFF00, 0xFFEF, // Half-width characters
+ 0xFF00, 0xFFEF // Half-width characters
};
static ImWchar full_ranges[IM_ARRAYSIZE(base_ranges) + IM_ARRAYSIZE(accumulative_offsets_from_0x4E00) * 2 + 1] = { 0 };
if (!full_ranges[0])
@@ -2131,7 +2270,7 @@ const ImWchar* ImFontAtlas::GetGlyphRangesJapanese()
// 1946 common ideograms code points for Japanese
// Sourced from http://theinstructionlimit.com/common-kanji-character-ranges-for-xna-spritefont-rendering
// FIXME: Source a list of the revised 2136 Joyo Kanji list from 2010 and rebuild this.
- // You can use ImFontAtlas::GlyphRangesBuilder to create your own ranges derived from this, by merging existing ranges or adding new characters.
+ // You can use ImFontGlyphRangesBuilder to create your own ranges derived from this, by merging existing ranges or adding new characters.
// (Stored as accumulative offsets from the initial unicode codepoint 0x4E00. This encoding is designed to helps us compact the source code size.)
static const short accumulative_offsets_from_0x4E00[] =
{
@@ -2171,9 +2310,9 @@ const ImWchar* ImFontAtlas::GetGlyphRangesJapanese()
static ImWchar base_ranges[] = // not zero-terminated
{
0x0020, 0x00FF, // Basic Latin + Latin Supplement
- 0x3000, 0x30FF, // Punctuations, Hiragana, Katakana
+ 0x3000, 0x30FF, // CJK Symbols and Punctuations, Hiragana, Katakana
0x31F0, 0x31FF, // Katakana Phonetic Extensions
- 0xFF00, 0xFFEF, // Half-width characters
+ 0xFF00, 0xFFEF // Half-width characters
};
static ImWchar full_ranges[IM_ARRAYSIZE(base_ranges) + IM_ARRAYSIZE(accumulative_offsets_from_0x4E00)*2 + 1] = { 0 };
if (!full_ranges[0])
@@ -2210,10 +2349,10 @@ const ImWchar* ImFontAtlas::GetGlyphRangesThai()
}
//-----------------------------------------------------------------------------
-// ImFontAtlas::GlyphRangesBuilder
+// [SECTION] ImFontGlyphRangesBuilder
//-----------------------------------------------------------------------------
-void ImFontAtlas::GlyphRangesBuilder::AddText(const char* text, const char* text_end)
+void ImFontGlyphRangesBuilder::AddText(const char* text, const char* text_end)
{
while (text_end ? (text < text_end) : *text)
{
@@ -2227,14 +2366,14 @@ void ImFontAtlas::GlyphRangesBuilder::AddText(const char* text, const char* text
}
}
-void ImFontAtlas::GlyphRangesBuilder::AddRanges(const ImWchar* ranges)
+void ImFontGlyphRangesBuilder::AddRanges(const ImWchar* ranges)
{
for (; ranges[0]; ranges += 2)
for (ImWchar c = ranges[0]; c <= ranges[1]; c++)
AddChar(c);
}
-void ImFontAtlas::GlyphRangesBuilder::BuildRanges(ImVector<ImWchar>* out_ranges)
+void ImFontGlyphRangesBuilder::BuildRanges(ImVector<ImWchar>* out_ranges)
{
for (int n = 0; n < 0x10000; n++)
if (GetBit(n))
@@ -2248,43 +2387,41 @@ void ImFontAtlas::GlyphRangesBuilder::BuildRanges(ImVector<ImWchar>* out_ranges)
}
//-----------------------------------------------------------------------------
-// ImFont
+// [SECTION] ImFont
//-----------------------------------------------------------------------------
ImFont::ImFont()
{
- Scale = 1.0f;
+ FontSize = 0.0f;
+ FallbackAdvanceX = 0.0f;
FallbackChar = (ImWchar)'?';
DisplayOffset = ImVec2(0.0f, 0.0f);
- ClearOutputData();
+ FallbackGlyph = NULL;
+ ContainerAtlas = NULL;
+ ConfigData = NULL;
+ ConfigDataCount = 0;
+ DirtyLookupTables = false;
+ Scale = 1.0f;
+ Ascent = Descent = 0.0f;
+ MetricsTotalSurface = 0;
}
ImFont::~ImFont()
{
- // Invalidate active font so that the user gets a clear crash instead of a dangling pointer.
- // If you want to delete fonts you need to do it between Render() and NewFrame().
- // FIXME-CLEANUP
- /*
- ImGuiContext& g = *GImGui;
- if (g.Font == this)
- g.Font = NULL;
- */
ClearOutputData();
}
void ImFont::ClearOutputData()
{
FontSize = 0.0f;
+ FallbackAdvanceX = 0.0f;
Glyphs.clear();
IndexAdvanceX.clear();
IndexLookup.clear();
FallbackGlyph = NULL;
- FallbackAdvanceX = 0.0f;
- ConfigDataCount = 0;
- ConfigData = NULL;
ContainerAtlas = NULL;
- Ascent = Descent = 0.0f;
DirtyLookupTables = true;
+ Ascent = Descent = 0.0f;
MetricsTotalSurface = 0;
}
@@ -2303,21 +2440,21 @@ void ImFont::BuildLookupTable()
{
int codepoint = (int)Glyphs[i].Codepoint;
IndexAdvanceX[codepoint] = Glyphs[i].AdvanceX;
- IndexLookup[codepoint] = (unsigned short)i;
+ IndexLookup[codepoint] = (ImWchar)i;
}
// Create a glyph to handle TAB
// FIXME: Needs proper TAB handling but it needs to be contextualized (or we could arbitrary say that each string starts at "column 0" ?)
- if (FindGlyph((unsigned short)' '))
+ if (FindGlyph((ImWchar)' '))
{
if (Glyphs.back().Codepoint != '\t') // So we can call this function multiple times
Glyphs.resize(Glyphs.Size + 1);
ImFontGlyph& tab_glyph = Glyphs.back();
- tab_glyph = *FindGlyph((unsigned short)' ');
+ tab_glyph = *FindGlyph((ImWchar)' ');
tab_glyph.Codepoint = '\t';
tab_glyph.AdvanceX *= 4;
IndexAdvanceX[(int)tab_glyph.Codepoint] = (float)tab_glyph.AdvanceX;
- IndexLookup[(int)tab_glyph.Codepoint] = (unsigned short)(Glyphs.Size-1);
+ IndexLookup[(int)tab_glyph.Codepoint] = (ImWchar)(Glyphs.Size-1);
}
FallbackGlyph = FindGlyphNoFallback(FallbackChar);
@@ -2339,7 +2476,7 @@ void ImFont::GrowIndex(int new_size)
if (new_size <= IndexLookup.Size)
return;
IndexAdvanceX.resize(new_size, -1.0f);
- IndexLookup.resize(new_size, (unsigned short)-1);
+ IndexLookup.resize(new_size, (ImWchar)-1);
}
// x0/y0/x1/y1 are offset from the character upper-left layout position, in pixels. Therefore x0/y0 are often fairly close to zero.
@@ -2372,13 +2509,13 @@ void ImFont::AddRemapChar(ImWchar dst, ImWchar src, bool overwrite_dst)
IM_ASSERT(IndexLookup.Size > 0); // Currently this can only be called AFTER the font has been built, aka after calling ImFontAtlas::GetTexDataAs*() function.
int index_size = IndexLookup.Size;
- if (dst < index_size && IndexLookup.Data[dst] == (unsigned short)-1 && !overwrite_dst) // 'dst' already exists
+ if (dst < index_size && IndexLookup.Data[dst] == (ImWchar)-1 && !overwrite_dst) // 'dst' already exists
return;
if (src >= index_size && dst >= index_size) // both 'dst' and 'src' don't exist -> no-op
return;
GrowIndex(dst + 1);
- IndexLookup[dst] = (src < index_size) ? IndexLookup.Data[src] : (unsigned short)-1;
+ IndexLookup[dst] = (src < index_size) ? IndexLookup.Data[src] : (ImWchar)-1;
IndexAdvanceX[dst] = (src < index_size) ? IndexAdvanceX.Data[src] : 1.0f;
}
@@ -2386,8 +2523,8 @@ const ImFontGlyph* ImFont::FindGlyph(ImWchar c) const
{
if (c >= IndexLookup.Size)
return FallbackGlyph;
- const unsigned short i = IndexLookup[c];
- if (i == (unsigned short)-1)
+ const ImWchar i = IndexLookup.Data[c];
+ if (i == (ImWchar)-1)
return FallbackGlyph;
return &Glyphs.Data[i];
}
@@ -2396,8 +2533,8 @@ const ImFontGlyph* ImFont::FindGlyphNoFallback(ImWchar c) const
{
if (c >= IndexLookup.Size)
return NULL;
- const unsigned short i = IndexLookup[c];
- if (i == (unsigned short)-1)
+ const ImWchar i = IndexLookup.Data[c];
+ if (i == (ImWchar)-1)
return NULL;
return &Glyphs.Data[i];
}
@@ -2456,7 +2593,7 @@ const char* ImFont::CalcWordWrapPositionA(float scale, const char* text, const c
}
}
- const float char_width = ((int)c < IndexAdvanceX.Size ? IndexAdvanceX[(int)c] : FallbackAdvanceX);
+ const float char_width = ((int)c < IndexAdvanceX.Size ? IndexAdvanceX.Data[c] : FallbackAdvanceX);
if (ImCharIsBlankW(c))
{
if (inside_word)
@@ -2573,7 +2710,7 @@ ImVec2 ImFont::CalcTextSizeA(float size, float max_width, float wrap_width, cons
continue;
}
- const float char_width = ((int)c < IndexAdvanceX.Size ? IndexAdvanceX[(int)c] : FallbackAdvanceX) * scale;
+ const float char_width = ((int)c < IndexAdvanceX.Size ? IndexAdvanceX.Data[c] : FallbackAdvanceX) * scale;
if (line_width + char_width >= max_width)
{
s = prev_s;
@@ -2595,7 +2732,7 @@ ImVec2 ImFont::CalcTextSizeA(float size, float max_width, float wrap_width, cons
return text_size;
}
-void ImFont::RenderChar(ImDrawList* draw_list, float size, ImVec2 pos, ImU32 col, unsigned short c) const
+void ImFont::RenderChar(ImDrawList* draw_list, float size, ImVec2 pos, ImU32 col, ImWchar c) const
{
if (c == ' ' || c == '\t' || c == '\n' || c == '\r') // Match behavior of RenderText(), those 4 codepoints are hard-coded.
return;
@@ -2627,11 +2764,32 @@ void ImFont::RenderText(ImDrawList* draw_list, float size, ImVec2 pos, ImU32 col
const bool word_wrap_enabled = (wrap_width > 0.0f);
const char* word_wrap_eol = NULL;
- // Skip non-visible lines
+ // Fast-forward to first visible line
const char* s = text_begin;
- if (!word_wrap_enabled && y + line_height < clip_rect.y)
- while (s < text_end && *s != '\n') // Fast-forward to next line
- s++;
+ if (y + line_height < clip_rect.y && !word_wrap_enabled)
+ while (y + line_height < clip_rect.y && s < text_end)
+ {
+ s = (const char*)memchr(s, '\n', text_end - s);
+ s = s ? s + 1 : text_end;
+ y += line_height;
+ }
+
+ // For large text, scan for the last visible line in order to avoid over-reserving in the call to PrimReserve()
+ // Note that very large horizontal line will still be affected by the issue (e.g. a one megabyte string buffer without a newline will likely crash atm)
+ if (text_end - s > 10000 && !word_wrap_enabled)
+ {
+ const char* s_end = s;
+ float y_end = y;
+ while (y_end < clip_rect.w && s_end < text_end)
+ {
+ s_end = (const char*)memchr(s_end, '\n', text_end - s_end);
+ s_end = s_end ? s_end + 1 : text_end;
+ y_end += line_height;
+ }
+ text_end = s_end;
+ }
+ if (s == text_end)
+ return;
// Reserve vertices for remaining worse case (over-reserving is useful and easily amortized)
const int vtx_count_max = (int)(text_end - s) * 4;
@@ -2690,12 +2848,8 @@ void ImFont::RenderText(ImDrawList* draw_list, float size, ImVec2 pos, ImU32 col
{
x = pos.x;
y += line_height;
-
if (y > clip_rect.w)
- break;
- if (!word_wrap_enabled && y + line_height < clip_rect.y)
- while (s < text_end && *s != '\n') // Fast-forward to next line
- s++;
+ break; // break out of main loop
continue;
}
if (c == '\r')
@@ -2703,7 +2857,7 @@ void ImFont::RenderText(ImDrawList* draw_list, float size, ImVec2 pos, ImU32 col
}
float char_width = 0.0f;
- if (const ImFontGlyph* glyph = FindGlyph((unsigned short)c))
+ if (const ImFontGlyph* glyph = FindGlyph((ImWchar)c))
{
char_width = glyph->AdvanceX * scale;
@@ -2782,9 +2936,53 @@ void ImFont::RenderText(ImDrawList* draw_list, float size, ImVec2 pos, ImU32 col
}
//-----------------------------------------------------------------------------
-// Internals Drawing Helpers
+// [SECTION] Internal Render Helpers
+// (progressively moved from imgui.cpp to here when they are redesigned to stop accessing ImGui global state)
+//-----------------------------------------------------------------------------
+// - RenderMouseCursor()
+// - RenderArrowPointingAt()
+// - RenderRectFilledRangeH()
+// - RenderPixelEllipsis()
//-----------------------------------------------------------------------------
+void ImGui::RenderMouseCursor(ImDrawList* draw_list, ImVec2 pos, float scale, ImGuiMouseCursor mouse_cursor)
+{
+ if (mouse_cursor == ImGuiMouseCursor_None)
+ return;
+ IM_ASSERT(mouse_cursor > ImGuiMouseCursor_None && mouse_cursor < ImGuiMouseCursor_COUNT);
+
+ const ImU32 col_shadow = IM_COL32(0, 0, 0, 48);
+ const ImU32 col_border = IM_COL32(0, 0, 0, 255); // Black
+ const ImU32 col_fill = IM_COL32(255, 255, 255, 255); // White
+
+ ImFontAtlas* font_atlas = draw_list->_Data->Font->ContainerAtlas;
+ ImVec2 offset, size, uv[4];
+ if (font_atlas->GetMouseCursorTexData(mouse_cursor, &offset, &size, &uv[0], &uv[2]))
+ {
+ pos -= offset;
+ const ImTextureID tex_id = font_atlas->TexID;
+ draw_list->PushTextureID(tex_id);
+ draw_list->AddImage(tex_id, pos + ImVec2(1,0)*scale, pos + ImVec2(1,0)*scale + size*scale, uv[2], uv[3], col_shadow);
+ draw_list->AddImage(tex_id, pos + ImVec2(2,0)*scale, pos + ImVec2(2,0)*scale + size*scale, uv[2], uv[3], col_shadow);
+ draw_list->AddImage(tex_id, pos, pos + size*scale, uv[2], uv[3], col_border);
+ draw_list->AddImage(tex_id, pos, pos + size*scale, uv[0], uv[1], col_fill);
+ draw_list->PopTextureID();
+ }
+}
+
+// Render an arrow. 'pos' is position of the arrow tip. half_sz.x is length from base to tip. half_sz.y is length on each side.
+void ImGui::RenderArrowPointingAt(ImDrawList* draw_list, ImVec2 pos, ImVec2 half_sz, ImGuiDir direction, ImU32 col)
+{
+ switch (direction)
+ {
+ case ImGuiDir_Left: draw_list->AddTriangleFilled(ImVec2(pos.x + half_sz.x, pos.y - half_sz.y), ImVec2(pos.x + half_sz.x, pos.y + half_sz.y), pos, col); return;
+ case ImGuiDir_Right: draw_list->AddTriangleFilled(ImVec2(pos.x - half_sz.x, pos.y + half_sz.y), ImVec2(pos.x - half_sz.x, pos.y - half_sz.y), pos, col); return;
+ case ImGuiDir_Up: draw_list->AddTriangleFilled(ImVec2(pos.x + half_sz.x, pos.y + half_sz.y), ImVec2(pos.x - half_sz.x, pos.y + half_sz.y), pos, col); return;
+ case ImGuiDir_Down: draw_list->AddTriangleFilled(ImVec2(pos.x - half_sz.x, pos.y - half_sz.y), ImVec2(pos.x + half_sz.x, pos.y - half_sz.y), pos, col); return;
+ case ImGuiDir_None: case ImGuiDir_COUNT: break; // Fix warnings
+ }
+}
+
static inline float ImAcos01(float x)
{
if (x <= 0.0f) return IM_PI * 0.5f;
@@ -2813,13 +3011,14 @@ void ImGui::RenderRectFilledRangeH(ImDrawList* draw_list, const ImRect& rect, Im
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 half_pi = IM_PI * 0.5f; // We will == compare to this because we know this is the exact value ImAcos01 can return.
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)
+ else if (arc0_b == 0.0f && arc0_e == half_pi)
{
draw_list->PathArcToFast(ImVec2(x0, p1.y - rounding), rounding, 3, 6); // BL
draw_list->PathArcToFast(ImVec2(x0, p0.y + rounding), rounding, 6, 9); // TR
@@ -2839,7 +3038,7 @@ void ImGui::RenderRectFilledRangeH(ImDrawList* draw_list, const ImRect& rect, Im
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)
+ else if (arc1_b == 0.0f && arc1_e == half_pi)
{
draw_list->PathArcToFast(ImVec2(x1, p0.y + rounding), rounding, 9, 12); // TR
draw_list->PathArcToFast(ImVec2(x1, p1.y - rounding), rounding, 0, 3); // BR
@@ -2853,8 +3052,20 @@ void ImGui::RenderRectFilledRangeH(ImDrawList* draw_list, const ImRect& rect, Im
draw_list->PathFillConvex(col);
}
+// FIXME: Rendering an ellipsis "..." is a surprisingly tricky problem for us... we cannot rely on font glyph having it,
+// and regular dot are typically too wide. If we render a dot/shape ourselves it comes with the risk that it wouldn't match
+// the boldness or positioning of what the font uses...
+void ImGui::RenderPixelEllipsis(ImDrawList* draw_list, ImVec2 pos, int count, ImU32 col)
+{
+ ImFont* font = draw_list->_Data->Font;
+ const float font_scale = draw_list->_Data->FontSize / font->FontSize;
+ pos.y += (float)(int)(font->DisplayOffset.y + font->Ascent * font_scale + 0.5f - 1.0f);
+ for (int dot_n = 0; dot_n < count; dot_n++)
+ draw_list->AddRectFilled(ImVec2(pos.x + dot_n * 2.0f, pos.y), ImVec2(pos.x + dot_n * 2.0f + 1.0f, pos.y + 1.0f), col);
+}
+
//-----------------------------------------------------------------------------
-// DEFAULT FONT DATA
+// [SECTION] Decompression code
//-----------------------------------------------------------------------------
// Compressed with stb_compress() then converted to a C array and encoded as base85.
// Use the program in misc/fonts/binary_to_compressed_c.cpp to create the array from a TTF file.
@@ -2974,6 +3185,8 @@ static unsigned int stb_decompress(unsigned char *output, const unsigned char *i
}
//-----------------------------------------------------------------------------
+// [SECTION] Default font data (ProggyClean.ttf)
+//-----------------------------------------------------------------------------
// ProggyClean.ttf
// Copyright (c) 2004, 2005 Tristan Grimmer
// MIT license (see License.txt in http://www.upperbounds.net/download/ProggyClean.ttf.zip)