Fixed range-version of PushID() and GetID() not honoring the ### operator to restart from the seed value.

This commit is contained in:
omar 2019-01-21 16:05:41 +01:00
parent 28901dd104
commit f14f93ef6e
4 changed files with 52 additions and 35 deletions

View File

@ -35,6 +35,7 @@ HOW TO UPDATE?
Other Changes: Other Changes:
- Added .editorconfig file for text editors to standardize using spaces. (#2038) [@kudaba] - Added .editorconfig file for text editors to standardize using spaces. (#2038) [@kudaba]
- Fixed range-version of PushID() and GetID() not honoring the ### operator to restart from the seed value.
- ImDrawList: Fixed AddCircle(), AddCircleFilled() angle step being off, which was visible when drawing a "circle" - ImDrawList: Fixed AddCircle(), AddCircleFilled() angle step being off, which was visible when drawing a "circle"
with a small number of segments (e.g. an hexagon). (#2287) [@baktery] with a small number of segments (e.g. an hexagon). (#2287) [@baktery]
- ImGuiTextBuffer: Added append() function (unformatted). - ImGuiTextBuffer: Added append() function (unformatted).

View File

@ -702,8 +702,8 @@ CODE
you to animate labels. For example you may want to include varying information in a window title bar, you to animate labels. For example you may want to include varying information in a window title bar,
but windows are uniquely identified by their ID. Use "###" to pass a label that isn't part of ID: but windows are uniquely identified by their ID. Use "###" to pass a label that isn't part of ID:
Button("Hello###ID"); // Label = "Hello", ID = hash of (..., "ID") Button("Hello###ID"); // Label = "Hello", ID = hash of (..., "###ID")
Button("World###ID"); // Label = "World", ID = hash of (..., "ID") // Same as above, even though the label looks different Button("World###ID"); // Label = "World", ID = hash of (..., "###ID") // Same as above, even though the label looks different
sprintf(buf, "My game (%f FPS)###MyGame", fps); sprintf(buf, "My game (%f FPS)###MyGame", fps);
Begin(buf); // Variable title, ID = hash of "MyGame" Begin(buf); // Variable title, ID = hash of "MyGame"
@ -1435,7 +1435,7 @@ int ImFormatStringV(char* buf, size_t buf_size, const char* fmt, va_list args)
// CRC32 needs a 1KB lookup table (not cache friendly) // CRC32 needs a 1KB lookup table (not cache friendly)
// Although the code to generate the table is simple and shorter than the table itself, using a const table allows us to easily: // Although the code to generate the table is simple and shorter than the table itself, using a const table allows us to easily:
// - avoid an unnecessary branch/memory tap, - keep the ImHash() function usable by static constructors, - make it thread-safe. // - avoid an unnecessary branch/memory tap, - keep the ImHashXXX functions usable by static constructors, - make it thread-safe.
static const ImU32 GCrc32LookupTable[256] = static const ImU32 GCrc32LookupTable[256] =
{ {
0x00000000,0x77073096,0xEE0E612C,0x990951BA,0x076DC419,0x706AF48F,0xE963A535,0x9E6495A3,0x0EDB8832,0x79DCB8A4,0xE0D5E91E,0x97D2D988,0x09B64C2B,0x7EB17CBD,0xE7B82D07,0x90BF1D91, 0x00000000,0x77073096,0xEE0E612C,0x990951BA,0x076DC419,0x706AF48F,0xE963A535,0x9E6495A3,0x0EDB8832,0x79DCB8A4,0xE0D5E91E,0x97D2D988,0x09B64C2B,0x7EB17CBD,0xE7B82D07,0x90BF1D91,
@ -1456,33 +1456,46 @@ static const ImU32 GCrc32LookupTable[256] =
0xBDBDF21C,0xCABAC28A,0x53B39330,0x24B4A3A6,0xBAD03605,0xCDD70693,0x54DE5729,0x23D967BF,0xB3667A2E,0xC4614AB8,0x5D681B02,0x2A6F2B94,0xB40BBE37,0xC30C8EA1,0x5A05DF1B,0x2D02EF8D, 0xBDBDF21C,0xCABAC28A,0x53B39330,0x24B4A3A6,0xBAD03605,0xCDD70693,0x54DE5729,0x23D967BF,0xB3667A2E,0xC4614AB8,0x5D681B02,0x2A6F2B94,0xB40BBE37,0xC30C8EA1,0x5A05DF1B,0x2D02EF8D,
}; };
// Pass data_size == 0 for zero-terminated strings, data_size > 0 for non-string data. // Known size hash
// Pay attention that data_size==0 will yield different results than passing strlen(data) because the zero-terminated codepath handles ###. // It is ok to call ImHashData on a string with known length but the ### operator won't be supported.
// This should technically be split into two distinct functions (ImHashData/ImHashStr), perhaps once we remove the silly static variable.
// FIXME-OPT: Replace with e.g. FNV1a hash? CRC32 pretty much randomly access 1KB. Need to do proper measurements. // FIXME-OPT: Replace with e.g. FNV1a hash? CRC32 pretty much randomly access 1KB. Need to do proper measurements.
ImU32 ImHash(const void* data, int data_size, ImU32 seed) ImU32 ImHashData(const void* data_p, size_t data_size, ImU32 seed)
{
ImU32 crc = ~seed;
const unsigned char* data = (const unsigned char*)data_p;
const ImU32* crc32_lut = GCrc32LookupTable;
while (data_size-- != 0)
crc = (crc >> 8) ^ crc32_lut[(crc & 0xFF) ^ *data++];
return ~crc;
}
// Zero-terminated string hash, with support for ### to reset back to seed value
// We support a syntax of "label###id" where only "###id" is included in the hash, and only "label" gets displayed.
// Because this syntax is rarely used we are optimizing for the common case.
// - If we reach ### in the string we discard the hash so far and reset to the seed.
// - We don't do 'current += 2; continue;' after handling ### to keep the code smaller/faster (measured ~10% diff in Debug build)
// FIXME-OPT: Replace with e.g. FNV1a hash? CRC32 pretty much randomly access 1KB. Need to do proper measurements.
ImU32 ImHashStr(const char* data, size_t data_size, ImU32 seed)
{ {
seed = ~seed; seed = ~seed;
ImU32 crc = seed; ImU32 crc = seed;
const unsigned char* current = (const unsigned char*)data; const unsigned char* src = (const unsigned char*)data;
const ImU32* crc32_lut = GCrc32LookupTable; const ImU32* crc32_lut = GCrc32LookupTable;
if (data_size != 0)
if (data_size > 0)
{ {
// Known size while (data_size-- != 0)
while (data_size--) {
crc = (crc >> 8) ^ crc32_lut[(crc & 0xFF) ^ *current++]; unsigned char c = *src++;
if (c == '#' && src[0] == '#' && src[1] == '#')
crc = seed;
crc = (crc >> 8) ^ crc32_lut[(crc & 0xFF) ^ c];
}
} }
else else
{ {
// Zero-terminated string while (unsigned char c = *src++)
while (unsigned char c = *current++)
{ {
// We support a syntax of "label###id" where only "###id" is included in the hash, and only "label" gets displayed. if (c == '#' && src[0] == '#' && src[1] == '#')
// Because this syntax is rarely used we are optimizing for the common case.
// - If we reach ### in the string we discard the hash so far and reset to the seed.
// - We don't do 'current += 2; continue;' after handling ### to keep the code smaller.
if (c == '#' && current[0] == '#' && current[1] == '#')
crc = seed; crc = seed;
crc = (crc >> 8) ^ crc32_lut[(crc & 0xFF) ^ c]; crc = (crc >> 8) ^ crc32_lut[(crc & 0xFF) ^ c];
} }
@ -2479,7 +2492,7 @@ ImGuiWindow::ImGuiWindow(ImGuiContext* context, const char* name)
: DrawListInst(&context->DrawListSharedData) : DrawListInst(&context->DrawListSharedData)
{ {
Name = ImStrdup(name); Name = ImStrdup(name);
ID = ImHash(name, 0); ID = ImHashStr(name, 0);
IDStack.push_back(ID); IDStack.push_back(ID);
Flags = ImGuiWindowFlags_None; Flags = ImGuiWindowFlags_None;
Pos = ImVec2(0.0f, 0.0f); Pos = ImVec2(0.0f, 0.0f);
@ -2547,9 +2560,8 @@ ImGuiWindow::~ImGuiWindow()
ImGuiID ImGuiWindow::GetID(const char* str, const char* str_end) ImGuiID ImGuiWindow::GetID(const char* str, const char* str_end)
{ {
// FIXME: ImHash with str_end doesn't behave same as with identical zero-terminated string, because of ### handling.
ImGuiID seed = IDStack.back(); ImGuiID seed = IDStack.back();
ImGuiID id = ImHash(str, str_end ? (int)(str_end - str) : 0, seed); ImGuiID id = ImHashStr(str, str_end ? (str_end - str) : 0, seed);
ImGui::KeepAliveID(id); ImGui::KeepAliveID(id);
return id; return id;
} }
@ -2557,7 +2569,7 @@ ImGuiID ImGuiWindow::GetID(const char* str, const char* str_end)
ImGuiID ImGuiWindow::GetID(const void* ptr) ImGuiID ImGuiWindow::GetID(const void* ptr)
{ {
ImGuiID seed = IDStack.back(); ImGuiID seed = IDStack.back();
ImGuiID id = ImHash(&ptr, sizeof(void*), seed); ImGuiID id = ImHashData(&ptr, sizeof(void*), seed);
ImGui::KeepAliveID(id); ImGui::KeepAliveID(id);
return id; return id;
} }
@ -2565,13 +2577,13 @@ ImGuiID ImGuiWindow::GetID(const void* ptr)
ImGuiID ImGuiWindow::GetIDNoKeepAlive(const char* str, const char* str_end) ImGuiID ImGuiWindow::GetIDNoKeepAlive(const char* str, const char* str_end)
{ {
ImGuiID seed = IDStack.back(); ImGuiID seed = IDStack.back();
return ImHash(str, str_end ? (int)(str_end - str) : 0, seed); return ImHashStr(str, str_end ? (str_end - str) : 0, seed);
} }
ImGuiID ImGuiWindow::GetIDNoKeepAlive(const void* ptr) ImGuiID ImGuiWindow::GetIDNoKeepAlive(const void* ptr)
{ {
ImGuiID seed = IDStack.back(); ImGuiID seed = IDStack.back();
return ImHash(&ptr, sizeof(void*), seed); return ImHashData(&ptr, sizeof(void*), seed);
} }
// This is only used in rare/specific situations to manufacture an ID out of nowhere. // This is only used in rare/specific situations to manufacture an ID out of nowhere.
@ -2579,7 +2591,7 @@ ImGuiID ImGuiWindow::GetIDFromRectangle(const ImRect& r_abs)
{ {
ImGuiID seed = IDStack.back(); ImGuiID seed = IDStack.back();
const int r_rel[4] = { (int)(r_abs.Min.x - Pos.x), (int)(r_abs.Min.y - Pos.y), (int)(r_abs.Max.x - Pos.x), (int)(r_abs.Max.y - Pos.y) }; const int r_rel[4] = { (int)(r_abs.Min.x - Pos.x), (int)(r_abs.Min.y - Pos.y), (int)(r_abs.Max.x - Pos.x), (int)(r_abs.Max.y - Pos.y) };
ImGuiID id = ImHash(&r_rel, sizeof(r_rel), seed); ImGuiID id = ImHashData(&r_rel, sizeof(r_rel), seed);
ImGui::KeepAliveID(id); ImGui::KeepAliveID(id);
return id; return id;
} }
@ -3523,7 +3535,7 @@ void ImGui::Initialize(ImGuiContext* context)
// Add .ini handle for ImGuiWindow type // Add .ini handle for ImGuiWindow type
ImGuiSettingsHandler ini_handler; ImGuiSettingsHandler ini_handler;
ini_handler.TypeName = "Window"; ini_handler.TypeName = "Window";
ini_handler.TypeHash = ImHash("Window", 0, 0); ini_handler.TypeHash = ImHashStr("Window", 0);
ini_handler.ReadOpenFn = SettingsHandlerWindow_ReadOpen; ini_handler.ReadOpenFn = SettingsHandlerWindow_ReadOpen;
ini_handler.ReadLineFn = SettingsHandlerWindow_ReadLine; ini_handler.ReadLineFn = SettingsHandlerWindow_ReadLine;
ini_handler.WriteAllFn = SettingsHandlerWindow_WriteAll; ini_handler.WriteAllFn = SettingsHandlerWindow_WriteAll;
@ -4432,7 +4444,7 @@ ImGuiWindow* ImGui::FindWindowByID(ImGuiID id)
ImGuiWindow* ImGui::FindWindowByName(const char* name) ImGuiWindow* ImGui::FindWindowByName(const char* name)
{ {
ImGuiID id = ImHash(name, 0); ImGuiID id = ImHashStr(name, 0);
return FindWindowByID(id); return FindWindowByID(id);
} }
@ -8437,7 +8449,7 @@ bool ImGui::BeginDragDropSource(ImGuiDragDropFlags flags)
else else
{ {
window = NULL; window = NULL;
source_id = ImHash("#SourceExtern", 0); source_id = ImHashStr("#SourceExtern", 0);
source_drag_active = true; source_drag_active = true;
} }
@ -8852,7 +8864,7 @@ ImGuiWindowSettings* ImGui::CreateNewWindowSettings(const char* name)
g.SettingsWindows.push_back(ImGuiWindowSettings()); g.SettingsWindows.push_back(ImGuiWindowSettings());
ImGuiWindowSettings* settings = &g.SettingsWindows.back(); ImGuiWindowSettings* settings = &g.SettingsWindows.back();
settings->Name = ImStrdup(name); settings->Name = ImStrdup(name);
settings->ID = ImHash(name, 0); settings->ID = ImHashStr(name, 0);
return settings; return settings;
} }
@ -8878,7 +8890,7 @@ void ImGui::LoadIniSettingsFromDisk(const char* ini_filename)
ImGuiSettingsHandler* ImGui::FindSettingsHandler(const char* type_name) ImGuiSettingsHandler* ImGui::FindSettingsHandler(const char* type_name)
{ {
ImGuiContext& g = *GImGui; ImGuiContext& g = *GImGui;
const ImGuiID type_hash = ImHash(type_name, 0, 0); const ImGuiID type_hash = ImHashStr(type_name, 0);
for (int handler_n = 0; handler_n < g.SettingsHandlers.Size; handler_n++) for (int handler_n = 0; handler_n < g.SettingsHandlers.Size; handler_n++)
if (g.SettingsHandlers[handler_n].TypeHash == type_hash) if (g.SettingsHandlers[handler_n].TypeHash == type_hash)
return &g.SettingsHandlers[handler_n]; return &g.SettingsHandlers[handler_n];
@ -8982,7 +8994,7 @@ const char* ImGui::SaveIniSettingsToMemory(size_t* out_size)
static void* SettingsHandlerWindow_ReadOpen(ImGuiContext*, ImGuiSettingsHandler*, const char* name) static void* SettingsHandlerWindow_ReadOpen(ImGuiContext*, ImGuiSettingsHandler*, const char* name)
{ {
ImGuiWindowSettings* settings = ImGui::FindWindowSettings(ImHash(name, 0)); ImGuiWindowSettings* settings = ImGui::FindWindowSettings(ImHashStr(name, 0));
if (!settings) if (!settings)
settings = ImGui::CreateNewWindowSettings(name); settings = ImGui::CreateNewWindowSettings(name);
return (void*)settings; return (void*)settings;

View File

@ -147,7 +147,8 @@ IMGUI_API int ImTextCountUtf8BytesFromChar(const char* in_text, const
IMGUI_API int ImTextCountUtf8BytesFromStr(const ImWchar* in_text, const ImWchar* in_text_end); // return number of bytes to express string in UTF-8 IMGUI_API int ImTextCountUtf8BytesFromStr(const ImWchar* in_text, const ImWchar* in_text_end); // return number of bytes to express string in UTF-8
// Helpers: Misc // Helpers: Misc
IMGUI_API ImU32 ImHash(const void* data, int data_size, ImU32 seed = 0); // Pass data_size==0 for zero-terminated strings IMGUI_API ImU32 ImHashData(const void* data, size_t data_size, ImU32 seed = 0);
IMGUI_API ImU32 ImHashStr(const char* data, size_t data_size, ImU32 seed = 0);
IMGUI_API void* ImFileLoadToMemory(const char* filename, const char* file_open_mode, size_t* out_file_size = NULL, int padding_bytes = 0); IMGUI_API void* ImFileLoadToMemory(const char* filename, const char* file_open_mode, size_t* out_file_size = NULL, int padding_bytes = 0);
IMGUI_API FILE* ImFileOpen(const char* filename, const char* file_open_mode); IMGUI_API FILE* ImFileOpen(const char* filename, const char* file_open_mode);
static inline bool ImCharIsBlankA(char c) { return c == ' ' || c == '\t'; } static inline bool ImCharIsBlankA(char c) { return c == ' ' || c == '\t'; }
@ -155,6 +156,9 @@ static inline bool ImCharIsBlankW(unsigned int c) { return c == ' ' || c =
static inline bool ImIsPowerOfTwo(int v) { return v != 0 && (v & (v - 1)) == 0; } static inline bool ImIsPowerOfTwo(int v) { return v != 0 && (v & (v - 1)) == 0; }
static inline int ImUpperPowerOfTwo(int v) { v--; v |= v >> 1; v |= v >> 2; v |= v >> 4; v |= v >> 8; v |= v >> 16; v++; return v; } static inline int ImUpperPowerOfTwo(int v) { v--; v |= v >> 1; v |= v >> 2; v |= v >> 4; v |= v >> 8; v |= v >> 16; v++; return v; }
#define ImQsort qsort #define ImQsort qsort
#ifndef IMGUI_DISABLE_OBSOLETE_FUNCTIONS
static inline ImU32 ImHash(const void* data, int size, ImU32 seed = 0) { return size ? ImHashData(data, (size_t)size, seed) : ImHashStr((const char*)data, 0, seed); } // [moved to ImHashStr/ImHashData in 1.68]
#endif
// Helpers: Geometry // Helpers: Geometry
IMGUI_API ImVec2 ImLineClosestPoint(const ImVec2& a, const ImVec2& b, const ImVec2& p); IMGUI_API ImVec2 ImLineClosestPoint(const ImVec2& a, const ImVec2& b, const ImVec2& p);

View File

@ -6084,7 +6084,7 @@ static ImU32 ImGui::TabBarCalcTabID(ImGuiTabBar* tab_bar, const char* label)
{ {
if (tab_bar->Flags & ImGuiTabBarFlags_DockNode) if (tab_bar->Flags & ImGuiTabBarFlags_DockNode)
{ {
ImGuiID id = ImHash(label, 0); ImGuiID id = ImHashStr(label, 0);
KeepAliveID(id); KeepAliveID(id);
return id; return id;
} }