From 8a944222469ab23559bc01a563f4e9b516e4e701 Mon Sep 17 00:00:00 2001 From: ocornut Date: Thu, 11 Sep 2025 21:18:52 +0200 Subject: [PATCH] InputText: optimize inactive path by avoiding an early ImStrlen(). --- imgui_widgets.cpp | 34 +++++++++++++++++++++++++--------- 1 file changed, 25 insertions(+), 9 deletions(-) diff --git a/imgui_widgets.cpp b/imgui_widgets.cpp index b5980761a..c5359044e 100644 --- a/imgui_widgets.cpp +++ b/imgui_widgets.cpp @@ -4536,30 +4536,44 @@ static int* ImLowerBound(int* in_begin, int* in_end, int v) // FIXME-WORDWRAP: Bundle some of this into ImGuiTextIndex and/or extract as a different tool? // 'max_output_buffer_size' happens to be a meaningful optimization to avoid writing the full line_index when not necessarily needed (e.g. very large buffer, scrolled up, inactive) -static int InputTextLineIndexBuild(ImGuiInputTextFlags flags, ImGuiTextIndex* line_index, const char* buf, const char* buf_end, float wrap_width, int max_output_buffer_size) +static int InputTextLineIndexBuild(ImGuiInputTextFlags flags, ImGuiTextIndex* line_index, const char* buf, const char* buf_end, float wrap_width, int max_output_buffer_size, const char** out_buf_end) { ImGuiContext& g = *GImGui; int size = 0; + const char* s; if (flags & ImGuiInputTextFlags_WordWrap) { - for (const char* s = buf; s < buf_end; ) + for (s = buf; s < buf_end; s = (*s == '\n') ? s + 1 : s) { if (size++ <= max_output_buffer_size) line_index->Offsets.push_back((int)(s - buf)); s = ImFontCalcWordWrapPositionEx(g.Font, g.FontSize, s, buf_end, wrap_width, ImDrawTextFlags_WrapKeepBlanks); - s = (*s == '\n') ? s + 1 : s; } } - else + else if (buf_end != NULL) { - for (const char* s = buf; s < buf_end; ) + for (s = buf; s < buf_end; s = s ? s + 1 : buf_end) { if (size++ <= max_output_buffer_size) line_index->Offsets.push_back((int)(s - buf)); s = (const char*)ImMemchr(s, '\n', buf_end - s); - s = s ? s + 1 : buf_end; } } + else + { + const char* s_eol; + for (s = buf; ; s = s_eol + 1) + { + if (size++ <= max_output_buffer_size) + line_index->Offsets.push_back((int)(s - buf)); + if ((s_eol = strchr(s, '\n')) != NULL) + continue; + s += strlen(s); + break; + } + } + if (out_buf_end != NULL) + *out_buf_end = buf_end = s; if (size == 0) { line_index->Offsets.push_back(0); @@ -5367,8 +5381,10 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ { if (render_cursor || render_selection || g.ActiveId == id) buf_display_end = buf_display + state->TextLen; //-V595 + else if (is_multiline && !is_wordwrap) + buf_display_end = NULL; // Inactive multi-line: end of buffer will be output by InputTextLineIndexBuild() special strchr() path. else - buf_display_end = buf_display + ImStrlen(buf_display); // FIXME-OPT: For multi-line path this would optimally be folded into the InputTextLineIndex build below. + buf_display_end = buf_display + ImStrlen(buf_display); } // Calculate visibility @@ -5379,10 +5395,10 @@ bool ImGui::InputTextEx(const char* label, const char* hint, char* buf, int buf_ // Build line index for easy data access (makes code below simpler and faster) ImGuiTextIndex* line_index = &g.InputTextLineIndex; line_index->Offsets.resize(0); - line_index->EndOffset = (int)(buf_display_end - buf_display); int line_count = 1; if (is_multiline) - line_count = InputTextLineIndexBuild(flags, line_index, buf_display, buf_display_end, wrap_width, (render_cursor && state && state->CursorFollow) ? INT_MAX : line_visible_n1 + 1); + line_count = InputTextLineIndexBuild(flags, line_index, buf_display, buf_display_end, wrap_width, (render_cursor && state && state->CursorFollow) ? INT_MAX : line_visible_n1 + 1, buf_display_end ? NULL : &buf_display_end); + line_index->EndOffset = (int)(buf_display_end - buf_display); line_visible_n1 = ImMin(line_visible_n1, line_count); // Store text height (we don't need width)