INSTINCT Code Coverage Report


Directory: src/
File: internal/gui/widgets/TextAnsiColored.cpp
Date: 2025-11-25 23:34:18
Exec Total Coverage
Lines: 0 346 0.0%
Functions: 0 10 0.0%
Branches: 0 265 0.0%

Line Branch Exec Source
1 // This file is part of INSTINCT, the INS Toolkit for Integrated
2 // Navigation Concepts and Training by the Institute of Navigation of
3 // the University of Stuttgart, Germany.
4 //
5 // This Source Code Form is subject to the terms of the Mozilla Public
6 // License, v. 2.0. If a copy of the MPL was not distributed with this
7 // file, You can obtain one at https://mozilla.org/MPL/2.0/.
8
9 #include "TextAnsiColored.hpp"
10
11 #include <array>
12
13 namespace
14 {
15
16 constexpr int kMaxChar = 10000;
17 std::array<char, kMaxChar> char_buf;
18 std::array<ImU32, kMaxChar> col_buf;
19 std::array<bool, kMaxChar> char_skip;
20
21 } // namespace
22
23 namespace jet
24 {
25 namespace
26 {
27 ✗ std::vector<std::string> split(const std::string& str, const std::string& delim = " ")
28 {
29 ✗ std::vector<std::string> res;
30 ✗ std::size_t previous = 0;
31 ✗ std::size_t current = str.find(delim);
32 ✗ while (current != std::string::npos)
33 {
34 ✗ res.push_back(str.substr(previous, current - previous));
35 ✗ previous = current + delim.length();
36 ✗ current = str.find(delim, previous);
37 }
38 ✗ res.push_back(str.substr(previous, current - previous));
39 ✗ return res;
40 ✗ }
41
42 } // namespace
43 } // namespace jet
44
45 namespace ImGui
46 {
47
48 namespace
49 {
50 ✗ bool ParseColor(const char* s, ImU32* col, int* skipChars)
51 {
52 ✗ if (s[0] != '\033' || s[1] != '[')
53 {
54 ✗ return false;
55 }
56
57 ✗ if (s[2] == 'm')
58 {
59 ✗ *col = ImGui::ColorConvertFloat4ToU32(ImGui::GetStyleColorVec4(ImGuiCol_Text));
60 ✗ *skipChars = 3;
61 ✗ return true;
62 }
63
64 ✗ if (s[2] == '0' && s[3] == 'm')
65 {
66 ✗ *col = ImGui::ColorConvertFloat4ToU32(ImGui::GetStyleColorVec4(ImGuiCol_Text));
67 ✗ *skipChars = 4;
68 ✗ return true;
69 }
70
71 ✗ const char* seqEnd = &s[2];
72 ✗ while (*seqEnd != 'm')
73 {
74 ✗ seqEnd++;
75 }
76
77 ✗ std::string seq{ &s[2], seqEnd };
78 ✗ std::string colorStr;
79 ✗ for (const auto& el : jet::split(seq, ";"))
80 {
81 ✗ if (el[0] == '3' && el.size() == 2)
82 {
83 ✗ colorStr = el;
84 ✗ break;
85 }
86 ✗ }
87
88 ✗ if (!colorStr.empty())
89 {
90 ✗ switch (colorStr[1])
91 {
92 ✗ case '0':
93 ✗ *col = 0xffcccccc;
94 ✗ break;
95 ✗ case '1':
96 ✗ *col = 0xff7a77f2;
97 ✗ break;
98 ✗ case '2':
99 ✗ *col = 0xff99cc99;
100 ✗ break;
101 ✗ case '3':
102 ✗ *col = 0xff66ccff;
103 ✗ break;
104 ✗ case '4':
105 ✗ *col = 0xffcc9966;
106 ✗ break;
107 ✗ case '5':
108 ✗ *col = 0xffcc99cc;
109 ✗ break;
110 ✗ case '6':
111 ✗ *col = 0xffcccc66;
112 ✗ break;
113 ✗ case '7':
114 ✗ *col = 0xff2d2d2d;
115 ✗ break;
116 ✗ default:
117 ✗ return false;
118 }
119 }
120
121 ✗ *skipChars = static_cast<int>(seqEnd - s + 1);
122 ✗ return true;
123 ✗ }
124
125 ✗ void ImFont_RenderAnsiText(const ImFont* font,
126 ImDrawList* draw_list,
127 float size,
128 ImVec2 pos,
129 ImU32 col,
130 const ImVec4& clip_rect,
131 const char* text_begin,
132 const char* text_end,
133 float wrap_width = 0.0F,
134 bool cpu_fine_clip = false)
135 {
136 ✗ if (!text_end)
137 {
138 // ImGui functions generally already provides a valid text_end,
139 // so this is merely to handle direct calls.
140 ✗ text_end = text_begin + strlen(text_begin);
141 }
142
143 // Align to be pixel perfect
144 // Align to be pixel perfect
145 ✗ pos.x = IM_FLOOR(pos.x);
146 ✗ pos.y = IM_FLOOR(pos.y);
147 ✗ float x = pos.x;
148 ✗ float y = pos.y;
149 ✗ if (y > clip_rect.w)
150 {
151 ✗ return;
152 }
153
154 ✗ const float scale = size / font->FontSize;
155 ✗ const float line_height = font->FontSize * scale;
156 ✗ const bool word_wrap_enabled = (wrap_width > 0.0F);
157 ✗ const char* word_wrap_eol = nullptr;
158
159 // Fast-forward to first visible line
160 ✗ const char* s = text_begin;
161 ✗ if (y + line_height < clip_rect.y && !word_wrap_enabled)
162 {
163 ✗ while (y + line_height < clip_rect.y && s < text_end)
164 {
165 ✗ s = static_cast<const char*>(memchr(s, '\n', static_cast<size_t>(text_end - s)));
166 ✗ s = s ? s + 1 : text_end;
167 ✗ y += line_height;
168 }
169 }
170
171 // For large text, scan for the last visible line in order to avoid over-reserving in the call to PrimReserve()
172 // Note that very large horizontal line will still be affected by the issue (e.g. a one megabyte string buffer
173 // without a newline will likely crash atm)
174 ✗ if (text_end - s > 10000 && !word_wrap_enabled)
175 {
176 ✗ const char* s_end = s;
177 ✗ float y_end = y;
178 ✗ while (y_end < clip_rect.w && s_end < text_end)
179 {
180 ✗ s_end = static_cast<const char*>(memchr(s_end, '\n', static_cast<size_t>(text_end - s_end)));
181 ✗ s = s ? s + 1 : text_end;
182 ✗ y_end += line_height;
183 }
184 ✗ text_end = s_end;
185 }
186 ✗ if (s == text_end)
187 {
188 ✗ return;
189 }
190
191 // Reserve vertices for remaining worse case (over-reserving is useful and easily amortized)
192 ✗ const int vtx_count_max = static_cast<int>(text_end - s) * 4;
193 ✗ const int idx_count_max = static_cast<int>(text_end - s) * 6;
194 ✗ const int idx_expected_size = draw_list->IdxBuffer.Size + idx_count_max;
195 ✗ draw_list->PrimReserve(idx_count_max, vtx_count_max);
196
197 ✗ ImDrawVert* vtx_write = draw_list->_VtxWritePtr;
198 ✗ ImDrawIdx* idx_write = draw_list->_IdxWritePtr;
199 ✗ unsigned int vtx_current_idx = draw_list->_VtxCurrentIdx;
200
201 {
202 ✗ for (size_t i = 0; i < static_cast<size_t>(text_end - text_begin); i++)
203 {
204 ✗ char_skip.at(i) = false;
205 }
206 ✗ size_t index = 0;
207 ✗ int skipChars = 0;
208 ✗ const char* sLocal = s;
209 ✗ ImU32 temp_col = col;
210 ✗ while (sLocal < text_end)
211 {
212 ✗ if (sLocal < text_end - 4 && ParseColor(sLocal, &temp_col, &skipChars))
213 {
214 ✗ sLocal += skipChars;
215 ✗ for (size_t i = 0; i < static_cast<size_t>(skipChars); i++)
216 {
217 ✗ char_skip.at(index + i) = true;
218 }
219 ✗ index += static_cast<size_t>(skipChars);
220 }
221 else
222 {
223 ✗ char_buf.at(index) = *sLocal;
224 ✗ col_buf.at(index) = temp_col;
225 ✗ char_skip.at(index) = false;
226 ✗ ++index;
227 ✗ ++sLocal;
228 }
229 }
230 }
231
232 ✗ const char* s1 = s;
233 ✗ while (s < text_end)
234 {
235 ✗ if (char_skip.at(static_cast<size_t>(s - s1)))
236 {
237 ✗ s++;
238 ✗ continue;
239 }
240 ✗ if (word_wrap_enabled)
241 {
242 // Calculate how far we can render. Requires two passes on the string data but keeps the code simple and
243 // not intrusive for what's essentially an uncommon feature.
244 ✗ if (!word_wrap_eol)
245 {
246 ✗ word_wrap_eol = font->CalcWordWrapPositionA(scale, s, text_end, wrap_width - (x - pos.x));
247 ✗ if (word_wrap_eol == s) // Wrap_width is too small to fit anything. Force displaying 1 character to minimize the height discontinuity.
248 { // +1 may not be a character start point in UTF-8 but it's ok because we use s >= word_wrap_eol below
249 ✗ word_wrap_eol++;
250 }
251 }
252
253 ✗ if (s >= word_wrap_eol)
254 {
255 ✗ x = pos.x;
256 ✗ y += line_height;
257 ✗ word_wrap_eol = nullptr;
258
259 // Wrapping skips upcoming blanks
260 ✗ while (s < text_end)
261 {
262 ✗ const char c = *s;
263 ✗ if (ImCharIsBlankA(c))
264 {
265 ✗ s++;
266 }
267 ✗ else if (c == '\n')
268 {
269 ✗ s++;
270 ✗ break;
271 }
272 else
273 {
274 ✗ break;
275 }
276 }
277 ✗ continue;
278 ✗ }
279 }
280
281 // Decode and advance source
282 ✗ auto c = static_cast<unsigned int>(static_cast<unsigned char>(*s));
283 ✗ if (c < 0x80)
284 {
285 ✗ s += 1;
286 }
287 else
288 {
289 ✗ s += ImTextCharFromUtf8(&c, s, text_end);
290 ✗ if (c == 0) // Malformed UTF-8?
291 {
292 ✗ break;
293 }
294 }
295
296 ✗ if (c < 32)
297 {
298 ✗ if (c == '\n')
299 {
300 ✗ x = pos.x;
301 ✗ y += line_height;
302 ✗ if (y > clip_rect.w)
303 {
304 ✗ break; // break out of main loop
305 }
306 ✗ continue;
307 }
308 ✗ if (c == '\r')
309 {
310 ✗ continue;
311 }
312 }
313
314 ✗ float char_width = 0.0F;
315 ✗ if (const ImFontGlyph* glyph = font->FindGlyph(static_cast<ImWchar>(c)))
316 {
317 ✗ char_width = glyph->AdvanceX * scale;
318
319 // Arbitrarily assume that both space and tabs are empty glyphs as an optimization
320 ✗ if (c != ' ' && c != '\t')
321 {
322 // We don't do a second finer clipping test on the Y axis as we've already skipped anything before
323 // clip_rect.y and exit once we pass clip_rect.w
324 ✗ float x1 = x + glyph->X0 * scale;
325 ✗ float x2 = x + glyph->X1 * scale;
326 ✗ float y1 = y + glyph->Y0 * scale;
327 ✗ float y2 = y + glyph->Y1 * scale;
328 ✗ if (x1 <= clip_rect.z && x2 >= clip_rect.x)
329 {
330 // Render a character
331 ✗ float u1 = glyph->U0;
332 ✗ float v1 = glyph->V0;
333 ✗ float u2 = glyph->U1;
334 ✗ float v2 = glyph->V1;
335
336 // CPU side clipping used to fit text in their frame when the frame is too small. Only does
337 // clipping for axis aligned quads.
338 ✗ if (cpu_fine_clip)
339 {
340 ✗ if (x1 < clip_rect.x)
341 {
342 ✗ u1 = u1 + (1.0F - (x2 - clip_rect.x) / (x2 - x1)) * (u2 - u1);
343 ✗ x1 = clip_rect.x;
344 }
345 ✗ if (y1 < clip_rect.y)
346 {
347 ✗ v1 = v1 + (1.0F - (y2 - clip_rect.y) / (y2 - y1)) * (v2 - v1);
348 ✗ y1 = clip_rect.y;
349 }
350 ✗ if (x2 > clip_rect.z)
351 {
352 ✗ u2 = u1 + ((clip_rect.z - x1) / (x2 - x1)) * (u2 - u1);
353 ✗ x2 = clip_rect.z;
354 }
355 ✗ if (y2 > clip_rect.w)
356 {
357 ✗ v2 = v1 + ((clip_rect.w - y1) / (y2 - y1)) * (v2 - v1);
358 ✗ y2 = clip_rect.w;
359 }
360 ✗ if (y1 >= y2)
361 {
362 ✗ x += char_width;
363 ✗ continue;
364 }
365 }
366
367 // We are NOT calling PrimRectUV() here because non-inlined causes too much overhead in a debug
368 // builds. Inlined here:
369 ✗ ImU32 temp_col = col_buf.at(static_cast<size_t>(s - text_begin - 1));
370 {
371 ✗ idx_write[0] = vtx_current_idx;
372 ✗ idx_write[1] = vtx_current_idx + 1;
373 ✗ idx_write[2] = vtx_current_idx + 2;
374 ✗ idx_write[3] = vtx_current_idx;
375 ✗ idx_write[4] = vtx_current_idx + 2;
376 ✗ idx_write[5] = vtx_current_idx + 3;
377 ✗ vtx_write[0].pos.x = x1;
378 ✗ vtx_write[0].pos.y = y1;
379 ✗ vtx_write[0].col = temp_col;
380 ✗ vtx_write[0].uv.x = u1;
381 ✗ vtx_write[0].uv.y = v1;
382 ✗ vtx_write[1].pos.x = x2;
383 ✗ vtx_write[1].pos.y = y1;
384 ✗ vtx_write[1].col = temp_col;
385 ✗ vtx_write[1].uv.x = u2;
386 ✗ vtx_write[1].uv.y = v1;
387 ✗ vtx_write[2].pos.x = x2;
388 ✗ vtx_write[2].pos.y = y2;
389 ✗ vtx_write[2].col = temp_col;
390 ✗ vtx_write[2].uv.x = u2;
391 ✗ vtx_write[2].uv.y = v2;
392 ✗ vtx_write[3].pos.x = x1;
393 ✗ vtx_write[3].pos.y = y2;
394 ✗ vtx_write[3].col = temp_col;
395 ✗ vtx_write[3].uv.x = u1;
396 ✗ vtx_write[3].uv.y = v2;
397 ✗ vtx_write += 4;
398 ✗ vtx_current_idx += 4;
399 ✗ idx_write += 6;
400 }
401 }
402 }
403 }
404
405 ✗ x += char_width;
406 }
407
408 // Give back unused vertices
409 ✗ draw_list->VtxBuffer.resize(static_cast<int>(vtx_write - draw_list->VtxBuffer.Data));
410 ✗ draw_list->IdxBuffer.resize(static_cast<int>(idx_write - draw_list->IdxBuffer.Data));
411 ✗ draw_list->CmdBuffer[draw_list->CmdBuffer.Size - 1].ElemCount -= static_cast<unsigned int>(idx_expected_size - draw_list->IdxBuffer.Size);
412 ✗ draw_list->_VtxWritePtr = vtx_write;
413 ✗ draw_list->_IdxWritePtr = idx_write;
414 ✗ draw_list->_VtxCurrentIdx = static_cast<unsigned int>(draw_list->VtxBuffer.Size);
415 }
416
417 ✗ void ImDrawList_AddAnsiText(ImDrawList* drawList,
418 const ImFont* font,
419 float font_size,
420 const ImVec2& pos,
421 ImU32 col,
422 const char* text_begin,
423 const char* text_end = nullptr,
424 float wrap_width = 0.0F,
425 const ImVec4* cpu_fine_clip_rect = nullptr)
426 {
427 ✗ if ((col & IM_COL32_A_MASK) == 0)
428 {
429 ✗ return;
430 }
431
432 ✗ if (text_end == nullptr)
433 {
434 ✗ text_end = text_begin + strlen(text_begin);
435 }
436 ✗ if (text_begin == text_end)
437 {
438 ✗ return;
439 }
440
441 // Pull default font/size from the shared ImDrawListSharedData instance
442 ✗ if (font == nullptr)
443 {
444 ✗ font = drawList->_Data->Font;
445 }
446 ✗ if (font_size == 0.0F)
447 {
448 ✗ font_size = drawList->_Data->FontSize;
449 }
450
451 ✗ IM_ASSERT(font->ContainerAtlas->TexID == drawList->_TextureIdStack.back()); // Use high-level ImGui::PushFont()
452 // or low-level
453 // ImDrawList::PushTextureId() to
454 // change font.
455
456 ✗ ImVec4 clip_rect = drawList->_ClipRectStack.back();
457 ✗ if (cpu_fine_clip_rect)
458 {
459 ✗ clip_rect.x = ImMax(clip_rect.x, cpu_fine_clip_rect->x);
460 ✗ clip_rect.y = ImMax(clip_rect.y, cpu_fine_clip_rect->y);
461 ✗ clip_rect.z = ImMin(clip_rect.z, cpu_fine_clip_rect->z);
462 ✗ clip_rect.w = ImMin(clip_rect.w, cpu_fine_clip_rect->w);
463 }
464 ✗ ImFont_RenderAnsiText(font,
465 drawList,
466 font_size,
467 pos,
468 col,
469 clip_rect,
470 text_begin,
471 text_end,
472 wrap_width,
473 cpu_fine_clip_rect != nullptr);
474 }
475
476 ✗ void RenderAnsiText(ImVec2 pos, const char* text, const char* text_end, bool hide_text_after_hash)
477 {
478 ✗ ImGuiContext& g = *GImGui;
479 ✗ ImGuiWindow* window = g.CurrentWindow;
480
481 // Hide anything after a '##' string
482 ✗ const char* text_display_end = nullptr;
483 ✗ if (hide_text_after_hash)
484 {
485 ✗ text_display_end = FindRenderedTextEnd(text, text_end);
486 }
487 else
488 {
489 ✗ if (!text_end)
490 {
491 ✗ text_end = text + strlen(text);
492 }
493 ✗ text_display_end = text_end;
494 }
495
496 ✗ if (text != text_display_end)
497 {
498 ✗ ImDrawList_AddAnsiText(window->DrawList, g.Font, g.FontSize, pos, GetColorU32(ImGuiCol_Text), text, text_display_end);
499 ✗ if (g.LogEnabled)
500 {
501 ✗ LogRenderedText(&pos, text, text_display_end);
502 }
503 }
504 ✗ }
505
506 ✗ void RenderAnsiTextWrapped(ImVec2 pos, const char* text, const char* text_end, float wrap_width)
507 {
508 ✗ ImGuiContext& g = *GImGui;
509 ✗ ImGuiWindow* window = g.CurrentWindow;
510
511 ✗ if (!text_end)
512 {
513 ✗ text_end = text + strlen(text);
514 }
515
516 ✗ if (text != text_end)
517 {
518 ✗ ImDrawList_AddAnsiText(window->DrawList, g.Font, g.FontSize, pos, GetColorU32(ImGuiCol_Text), text, text_end, wrap_width);
519 ✗ if (g.LogEnabled)
520 {
521 ✗ LogRenderedText(&pos, text, text_end);
522 }
523 }
524 ✗ }
525
526 } // namespace
527 } // namespace ImGui
528
529 ✗ void ImGui::TextAnsiUnformatted(const char* text, const char* text_end)
530 {
531 ✗ ImGuiWindow* window = GetCurrentWindow();
532 ✗ if (window->SkipItems)
533 {
534 ✗ return;
535 }
536
537 ✗ ImGuiContext& g = *GImGui;
538 ✗ IM_ASSERT(text != nullptr);
539 ✗ const char* text_begin = text;
540 ✗ if (text_end == nullptr)
541 {
542 ✗ text_end = text + strlen(text); // NOLINT(clang-analyzer-core.NonNullParamChecker) - false positive, as checked by IM_ASSERT
543 }
544
545 ✗ const ImVec2 text_pos(window->DC.CursorPos.x, window->DC.CursorPos.y + window->DC.CurrLineTextBaseOffset);
546 ✗ const float wrap_pos_x = window->DC.TextWrapPos;
547 ✗ const bool wrap_enabled = wrap_pos_x >= 0.0F;
548 ✗ if (text_end - text > 2000 && !wrap_enabled)
549 {
550 // Long text!
551 // Perform manual coarse clipping to optimize for long multi-line text
552 // - From this point we will only compute the width of lines that are visible. Optimization only available
553 // when word-wrapping is disabled.
554 // - We also don't vertically center the text within the line full height, which is unlikely to matter
555 // because we are likely the biggest and only item on the line.
556 // - We use memchr(), pay attention that well optimized versions of those str/mem functions are much faster
557 // than a casually written loop.
558 ✗ const char* line = text;
559 ✗ const float line_height = GetTextLineHeight();
560 ✗ const ImRect clip_rect = window->ClipRect;
561 ✗ ImVec2 text_size(0, 0);
562
563 ✗ if (text_pos.y <= clip_rect.Max.y)
564 {
565 ✗ ImVec2 pos = text_pos;
566
567 // Lines to skip (can't skip when logging text)
568 ✗ if (!g.LogEnabled)
569 {
570 ✗ int lines_skippable = static_cast<int>((clip_rect.Min.y - text_pos.y) / line_height);
571 ✗ if (lines_skippable > 0)
572 {
573 ✗ int lines_skipped = 0;
574 ✗ while (line < text_end && lines_skipped < lines_skippable)
575 {
576 ✗ const char* line_end = static_cast<const char*>(memchr(line, '\n', static_cast<size_t>(text_end - line)));
577 ✗ if (!line_end)
578 {
579 ✗ line_end = text_end;
580 }
581 ✗ line = line_end + 1;
582 ✗ lines_skipped++;
583 }
584 ✗ pos.y += static_cast<float>(lines_skipped) * line_height;
585 }
586 }
587
588 // Lines to render
589 ✗ if (line < text_end)
590 {
591 ✗ ImRect line_rect(pos, pos + ImVec2(FLT_MAX, line_height));
592 ✗ while (line < text_end)
593 {
594 ✗ if (IsClippedEx(line_rect, 0))
595 {
596 ✗ break;
597 }
598
599 ✗ const char* line_end = static_cast<const char*>(memchr(line, '\n', static_cast<size_t>(text_end - line)));
600 ✗ if (!line_end)
601 {
602 ✗ line_end = text_end;
603 }
604 ✗ const ImVec2 line_size = CalcTextSize(line, line_end, false);
605 ✗ text_size.x = ImMax(text_size.x, line_size.x);
606 ✗ RenderAnsiText(pos, line, line_end, false);
607 ✗ line = line_end + 1;
608 ✗ line_rect.Min.y += line_height;
609 ✗ line_rect.Max.y += line_height;
610 ✗ pos.y += line_height;
611 }
612
613 // Count remaining lines
614 ✗ int lines_skipped = 0;
615 ✗ while (line < text_end)
616 {
617 ✗ const char* line_end = static_cast<const char*>(memchr(line, '\n', static_cast<size_t>(text_end - line)));
618 ✗ if (!line_end)
619 {
620 ✗ line_end = text_end;
621 }
622 ✗ line = line_end + 1;
623 ✗ lines_skipped++;
624 }
625 ✗ pos.y += static_cast<float>(lines_skipped) * line_height;
626 }
627
628 ✗ text_size.y += (pos - text_pos).y;
629 }
630
631 ✗ ImRect bb(text_pos, text_pos + text_size);
632 ✗ ItemSize(bb);
633 ✗ ItemAdd(bb, 0);
634 ✗ }
635 else
636 {
637 ✗ const float wrap_width = wrap_enabled ? CalcWrapWidthForPos(window->DC.CursorPos, wrap_pos_x) : 0.0F;
638 ✗ const ImVec2 text_size = CalcTextSize(text_begin, text_end, false, wrap_width);
639
640 // Account of baseline offset
641 ✗ ImRect bb(text_pos, text_pos + text_size);
642 ✗ ItemSize(text_size);
643 ✗ if (!ItemAdd(bb, 0))
644 {
645 ✗ return;
646 }
647
648 // Render (we don't hide text after ## in this end-user function)
649 ✗ RenderAnsiTextWrapped(bb.Min, text_begin, text_end, wrap_width);
650 }
651 }
652
653 ✗ void ImGui::TextAnsiV(const char* fmt, va_list args)
654 {
655 ✗ ImGuiWindow* window = GetCurrentWindow();
656 ✗ if (window->SkipItems)
657 {
658 ✗ return;
659 }
660
661 ✗ ImGuiContext& g = *GImGui;
662 #if defined(__GNUC__) || defined(__clang__)
663 #pragma GCC diagnostic push
664 #pragma GCC diagnostic ignored "-Wformat-nonliteral"
665 #endif
666 ✗ const char* text_end = g.TempBuffer.Data + ImFormatStringV(g.TempBuffer.Data, strlen(g.TempBuffer.Data), fmt, args); // NOLINT(clang-diagnostic-format-nonliteral)
667 #if defined(__GNUC__) || defined(__clang__)
668 #pragma GCC diagnostic pop
669 #endif
670 ✗ TextAnsiUnformatted(g.TempBuffer.Data, text_end);
671 }
672
673 ✗ void ImGui::TextAnsiColoredV(const ImVec4& col, const char* fmt, va_list args)
674 {
675 ✗ PushStyleColor(ImGuiCol_Text, col);
676 ✗ TextAnsiV(fmt, args);
677 ✗ PopStyleColor();
678 ✗ }
679
680 ✗ void ImGui::TextAnsiColored(const ImVec4& col, const char* fmt, ...) // NOLINT(cert-dcl50-cpp)
681 {
682 va_list args;
683 ✗ va_start(args, fmt);
684 ✗ TextAnsiColoredV(col, fmt, args);
685 ✗ va_end(args);
686 ✗ }
687