PageRenderTime 31ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 0ms

/Engine/imgui/imgui_draw.cpp

https://gitlab.com/Strider2342/PannonEngine
C++ | 1249 lines | 1027 code | 145 blank | 77 comment | 208 complexity | dd4b4e53bfce7a3bec20223087cf64f3 MD5 | raw file
  1. // dear imgui, v1.50 WIP
  2. // (drawing and font code)
  3. // Contains implementation for
  4. // - ImDrawList
  5. // - ImDrawData
  6. // - ImFontAtlas
  7. // - ImFont
  8. // - Default font data
  9. #if defined(_MSC_VER) && !defined(_CRT_SECURE_NO_WARNINGS)
  10. #define _CRT_SECURE_NO_WARNINGS
  11. #endif
  12. #include "imgui.h"
  13. #define IMGUI_DEFINE_MATH_OPERATORS
  14. #define IMGUI_DEFINE_PLACEMENT_NEW
  15. #include "imgui_internal.h"
  16. #include <stdio.h> // vsnprintf, sscanf, printf
  17. #if !defined(alloca)
  18. #ifdef _WIN32
  19. #include <malloc.h> // alloca
  20. #elif (defined(__FreeBSD__) || defined(FreeBSD_kernel) || defined(__DragonFly__)) && !defined(__GLIBC__)
  21. #include <stdlib.h> // alloca. FreeBSD uses stdlib.h unless GLIBC
  22. #else
  23. #include <alloca.h> // alloca
  24. #endif
  25. #endif
  26. #ifdef _MSC_VER
  27. #pragma warning (disable: 4505) // unreferenced local function has been removed (stb stuff)
  28. #pragma warning (disable: 4996) // 'This function or variable may be unsafe': strcpy, strdup, sprintf, vsnprintf, sscanf, fopen
  29. #define snprintf _snprintf
  30. #endif
  31. #ifdef __clang__
  32. #pragma clang diagnostic ignored "-Wold-style-cast" // warning : use of old-style cast // yes, they are more terse.
  33. #pragma clang diagnostic ignored "-Wfloat-equal" // warning : comparing floating point with == or != is unsafe // storing and comparing against same constants ok.
  34. #pragma clang diagnostic ignored "-Wglobal-constructors" // warning : declaration requires a global destructor // similar to above, not sure what the exact difference it.
  35. #pragma clang diagnostic ignored "-Wsign-conversion" // warning : implicit conversion changes signedness //
  36. #if __has_warning("-Wreserved-id-macro")
  37. #pragma clang diagnostic ignored "-Wreserved-id-macro" // warning : macro name is a reserved identifier //
  38. #endif
  39. #elif defined(__GNUC__)
  40. #pragma GCC diagnostic ignored "-Wunused-function" // warning: 'xxxx' defined but not used
  41. #pragma GCC diagnostic ignored "-Wdouble-promotion" // warning: implicit conversion from 'float' to 'double' when passing argument to function
  42. #pragma GCC diagnostic ignored "-Wconversion" // warning: conversion to 'xxxx' from 'xxxx' may alter its value
  43. #endif
  44. //-------------------------------------------------------------------------
  45. // STB libraries implementation
  46. //-------------------------------------------------------------------------
  47. //#define IMGUI_STB_NAMESPACE ImGuiStb
  48. //#define IMGUI_DISABLE_STB_RECT_PACK_IMPLEMENTATION
  49. //#define IMGUI_DISABLE_STB_TRUETYPE_IMPLEMENTATION
  50. #ifdef IMGUI_STB_NAMESPACE
  51. namespace IMGUI_STB_NAMESPACE
  52. {
  53. #endif
  54. #ifdef _MSC_VER
  55. #pragma warning (push)
  56. #pragma warning (disable: 4456) // declaration of 'xx' hides previous local declaration
  57. #endif
  58. #ifdef __clang__
  59. #pragma clang diagnostic push
  60. #pragma clang diagnostic ignored "-Wold-style-cast" // warning : use of old-style cast // yes, they are more terse.
  61. #pragma clang diagnostic ignored "-Wunused-function"
  62. #pragma clang diagnostic ignored "-Wmissing-prototypes"
  63. #endif
  64. #ifdef __GNUC__
  65. #pragma GCC diagnostic push
  66. #pragma GCC diagnostic ignored "-Wtype-limits" // warning: comparison is always true due to limited range of data type [-Wtype-limits]
  67. #endif
  68. #define STBRP_ASSERT(x) IM_ASSERT(x)
  69. #ifndef IMGUI_DISABLE_STB_RECT_PACK_IMPLEMENTATION
  70. #define STBRP_STATIC
  71. #define STB_RECT_PACK_IMPLEMENTATION
  72. #endif
  73. #include "stb_rect_pack.h"
  74. #define STBTT_malloc(x,u) ((void)(u), ImGui::MemAlloc(x))
  75. #define STBTT_free(x,u) ((void)(u), ImGui::MemFree(x))
  76. #define STBTT_assert(x) IM_ASSERT(x)
  77. #ifndef IMGUI_DISABLE_STB_TRUETYPE_IMPLEMENTATION
  78. #define STBTT_STATIC
  79. #define STB_TRUETYPE_IMPLEMENTATION
  80. #else
  81. #define STBTT_DEF extern
  82. #endif
  83. #include "stb_truetype.h"
  84. #ifdef __GNUC__
  85. #pragma GCC diagnostic pop
  86. #endif
  87. #ifdef __clang__
  88. #pragma clang diagnostic pop
  89. #endif
  90. #ifdef _MSC_VER
  91. #pragma warning (pop)
  92. #endif
  93. #ifdef IMGUI_STB_NAMESPACE
  94. } // namespace ImGuiStb
  95. using namespace IMGUI_STB_NAMESPACE;
  96. #endif
  97. //-----------------------------------------------------------------------------
  98. // ImDrawList
  99. //-----------------------------------------------------------------------------
  100. static const ImVec4 GNullClipRect(-8192.0f, -8192.0f, +8192.0f, +8192.0f); // Large values that are easy to encode in a few bits+shift
  101. void ImDrawList::Clear()
  102. {
  103. CmdBuffer.resize(0);
  104. IdxBuffer.resize(0);
  105. VtxBuffer.resize(0);
  106. _VtxCurrentIdx = 0;
  107. _VtxWritePtr = NULL;
  108. _IdxWritePtr = NULL;
  109. _ClipRectStack.resize(0);
  110. _TextureIdStack.resize(0);
  111. _Path.resize(0);
  112. _ChannelsCurrent = 0;
  113. _ChannelsCount = 1;
  114. // NB: Do not clear channels so our allocations are re-used after the first frame.
  115. }
  116. void ImDrawList::ClearFreeMemory()
  117. {
  118. CmdBuffer.clear();
  119. IdxBuffer.clear();
  120. VtxBuffer.clear();
  121. _VtxCurrentIdx = 0;
  122. _VtxWritePtr = NULL;
  123. _IdxWritePtr = NULL;
  124. _ClipRectStack.clear();
  125. _TextureIdStack.clear();
  126. _Path.clear();
  127. _ChannelsCurrent = 0;
  128. _ChannelsCount = 1;
  129. for (int i = 0; i < _Channels.Size; i++)
  130. {
  131. if (i == 0) memset(&_Channels[0], 0, sizeof(_Channels[0])); // channel 0 is a copy of CmdBuffer/IdxBuffer, don't destruct again
  132. _Channels[i].CmdBuffer.clear();
  133. _Channels[i].IdxBuffer.clear();
  134. }
  135. _Channels.clear();
  136. }
  137. // Use macros because C++ is a terrible language, we want guaranteed inline, no code in header, and no overhead in Debug mode
  138. #define GetCurrentClipRect() (_ClipRectStack.Size ? _ClipRectStack.Data[_ClipRectStack.Size-1] : GNullClipRect)
  139. #define GetCurrentTextureId() (_TextureIdStack.Size ? _TextureIdStack.Data[_TextureIdStack.Size-1] : NULL)
  140. void ImDrawList::AddDrawCmd()
  141. {
  142. ImDrawCmd draw_cmd;
  143. draw_cmd.ClipRect = GetCurrentClipRect();
  144. draw_cmd.TextureId = GetCurrentTextureId();
  145. IM_ASSERT(draw_cmd.ClipRect.x <= draw_cmd.ClipRect.z && draw_cmd.ClipRect.y <= draw_cmd.ClipRect.w);
  146. CmdBuffer.push_back(draw_cmd);
  147. }
  148. void ImDrawList::AddCallback(ImDrawCallback callback, void* callback_data)
  149. {
  150. ImDrawCmd* current_cmd = CmdBuffer.Size ? &CmdBuffer.back() : NULL;
  151. if (!current_cmd || current_cmd->ElemCount != 0 || current_cmd->UserCallback != NULL)
  152. {
  153. AddDrawCmd();
  154. current_cmd = &CmdBuffer.back();
  155. }
  156. current_cmd->UserCallback = callback;
  157. current_cmd->UserCallbackData = callback_data;
  158. AddDrawCmd(); // Force a new command after us (see comment below)
  159. }
  160. // Our scheme may appears a bit unusual, basically we want the most-common calls AddLine AddRect etc. to not have to perform any check so we always have a command ready in the stack.
  161. // The cost of figuring out if a new command has to be added or if we can merge is paid in those Update** functions only.
  162. void ImDrawList::UpdateClipRect()
  163. {
  164. // If current command is used with different settings we need to add a new command
  165. const ImVec4 curr_clip_rect = GetCurrentClipRect();
  166. ImDrawCmd* curr_cmd = CmdBuffer.Size > 0 ? &CmdBuffer.Data[CmdBuffer.Size-1] : NULL;
  167. if (!curr_cmd || (curr_cmd->ElemCount != 0 && memcmp(&curr_cmd->ClipRect, &curr_clip_rect, sizeof(ImVec4)) != 0) || curr_cmd->UserCallback != NULL)
  168. {
  169. AddDrawCmd();
  170. return;
  171. }
  172. // Try to merge with previous command if it matches, else use current command
  173. ImDrawCmd* prev_cmd = CmdBuffer.Size > 1 ? curr_cmd - 1 : NULL;
  174. if (curr_cmd->ElemCount == 0 && prev_cmd && memcmp(&prev_cmd->ClipRect, &curr_clip_rect, sizeof(ImVec4)) == 0 && prev_cmd->TextureId == GetCurrentTextureId() && prev_cmd->UserCallback == NULL)
  175. CmdBuffer.pop_back();
  176. else
  177. curr_cmd->ClipRect = curr_clip_rect;
  178. }
  179. void ImDrawList::UpdateTextureID()
  180. {
  181. // If current command is used with different settings we need to add a new command
  182. const ImTextureID curr_texture_id = GetCurrentTextureId();
  183. ImDrawCmd* curr_cmd = CmdBuffer.Size ? &CmdBuffer.back() : NULL;
  184. if (!curr_cmd || (curr_cmd->ElemCount != 0 && curr_cmd->TextureId != curr_texture_id) || curr_cmd->UserCallback != NULL)
  185. {
  186. AddDrawCmd();
  187. return;
  188. }
  189. // Try to merge with previous command if it matches, else use current command
  190. ImDrawCmd* prev_cmd = CmdBuffer.Size > 1 ? curr_cmd - 1 : NULL;
  191. if (prev_cmd && prev_cmd->TextureId == curr_texture_id && memcmp(&prev_cmd->ClipRect, &GetCurrentClipRect(), sizeof(ImVec4)) == 0 && prev_cmd->UserCallback == NULL)
  192. CmdBuffer.pop_back();
  193. else
  194. curr_cmd->TextureId = curr_texture_id;
  195. }
  196. #undef GetCurrentClipRect
  197. #undef GetCurrentTextureId
  198. // Render-level scissoring. This is passed down to your render function but not used for CPU-side coarse clipping. Prefer using higher-level ImGui::PushClipRect() to affect logic (hit-testing and widget culling)
  199. void ImDrawList::PushClipRect(ImVec2 cr_min, ImVec2 cr_max, bool intersect_with_current_clip_rect)
  200. {
  201. ImVec4 cr(cr_min.x, cr_min.y, cr_max.x, cr_max.y);
  202. if (intersect_with_current_clip_rect && _ClipRectStack.Size)
  203. {
  204. ImVec4 current = _ClipRectStack.Data[_ClipRectStack.Size-1];
  205. if (cr.x < current.x) cr.x = current.x;
  206. if (cr.y < current.y) cr.y = current.y;
  207. if (cr.z > current.z) cr.z = current.z;
  208. if (cr.w > current.w) cr.w = current.w;
  209. }
  210. cr.z = ImMax(cr.x, cr.z);
  211. cr.w = ImMax(cr.y, cr.w);
  212. _ClipRectStack.push_back(cr);
  213. UpdateClipRect();
  214. }
  215. void ImDrawList::PushClipRectFullScreen()
  216. {
  217. PushClipRect(ImVec2(GNullClipRect.x, GNullClipRect.y), ImVec2(GNullClipRect.z, GNullClipRect.w));
  218. //PushClipRect(GetVisibleRect()); // FIXME-OPT: This would be more correct but we're not supposed to access ImGuiContext from here?
  219. }
  220. void ImDrawList::PopClipRect()
  221. {
  222. IM_ASSERT(_ClipRectStack.Size > 0);
  223. _ClipRectStack.pop_back();
  224. UpdateClipRect();
  225. }
  226. void ImDrawList::PushTextureID(const ImTextureID& texture_id)
  227. {
  228. _TextureIdStack.push_back(texture_id);
  229. UpdateTextureID();
  230. }
  231. void ImDrawList::PopTextureID()
  232. {
  233. IM_ASSERT(_TextureIdStack.Size > 0);
  234. _TextureIdStack.pop_back();
  235. UpdateTextureID();
  236. }
  237. void ImDrawList::ChannelsSplit(int channels_count)
  238. {
  239. IM_ASSERT(_ChannelsCurrent == 0 && _ChannelsCount == 1);
  240. int old_channels_count = _Channels.Size;
  241. if (old_channels_count < channels_count)
  242. _Channels.resize(channels_count);
  243. _ChannelsCount = channels_count;
  244. // _Channels[] (24 bytes each) hold storage that we'll swap with this->_CmdBuffer/_IdxBuffer
  245. // The content of _Channels[0] at this point doesn't matter. We clear it to make state tidy in a debugger but we don't strictly need to.
  246. // When we switch to the next channel, we'll copy _CmdBuffer/_IdxBuffer into _Channels[0] and then _Channels[1] into _CmdBuffer/_IdxBuffer
  247. memset(&_Channels[0], 0, sizeof(ImDrawChannel));
  248. for (int i = 1; i < channels_count; i++)
  249. {
  250. if (i >= old_channels_count)
  251. {
  252. IM_PLACEMENT_NEW(&_Channels[i]) ImDrawChannel();
  253. }
  254. else
  255. {
  256. _Channels[i].CmdBuffer.resize(0);
  257. _Channels[i].IdxBuffer.resize(0);
  258. }
  259. if (_Channels[i].CmdBuffer.Size == 0)
  260. {
  261. ImDrawCmd draw_cmd;
  262. draw_cmd.ClipRect = _ClipRectStack.back();
  263. draw_cmd.TextureId = _TextureIdStack.back();
  264. _Channels[i].CmdBuffer.push_back(draw_cmd);
  265. }
  266. }
  267. }
  268. void ImDrawList::ChannelsMerge()
  269. {
  270. // Note that we never use or rely on channels.Size because it is merely a buffer that we never shrink back to 0 to keep all sub-buffers ready for use.
  271. if (_ChannelsCount <= 1)
  272. return;
  273. ChannelsSetCurrent(0);
  274. if (CmdBuffer.Size && CmdBuffer.back().ElemCount == 0)
  275. CmdBuffer.pop_back();
  276. int new_cmd_buffer_count = 0, new_idx_buffer_count = 0;
  277. for (int i = 1; i < _ChannelsCount; i++)
  278. {
  279. ImDrawChannel& ch = _Channels[i];
  280. if (ch.CmdBuffer.Size && ch.CmdBuffer.back().ElemCount == 0)
  281. ch.CmdBuffer.pop_back();
  282. new_cmd_buffer_count += ch.CmdBuffer.Size;
  283. new_idx_buffer_count += ch.IdxBuffer.Size;
  284. }
  285. CmdBuffer.resize(CmdBuffer.Size + new_cmd_buffer_count);
  286. IdxBuffer.resize(IdxBuffer.Size + new_idx_buffer_count);
  287. ImDrawCmd* cmd_write = CmdBuffer.Data + CmdBuffer.Size - new_cmd_buffer_count;
  288. _IdxWritePtr = IdxBuffer.Data + IdxBuffer.Size - new_idx_buffer_count;
  289. for (int i = 1; i < _ChannelsCount; i++)
  290. {
  291. ImDrawChannel& ch = _Channels[i];
  292. if (int sz = ch.CmdBuffer.Size) { memcpy(cmd_write, ch.CmdBuffer.Data, sz * sizeof(ImDrawCmd)); cmd_write += sz; }
  293. if (int sz = ch.IdxBuffer.Size) { memcpy(_IdxWritePtr, ch.IdxBuffer.Data, sz * sizeof(ImDrawIdx)); _IdxWritePtr += sz; }
  294. }
  295. AddDrawCmd();
  296. _ChannelsCount = 1;
  297. }
  298. void ImDrawList::ChannelsSetCurrent(int idx)
  299. {
  300. IM_ASSERT(idx < _ChannelsCount);
  301. if (_ChannelsCurrent == idx) return;
  302. memcpy(&_Channels.Data[_ChannelsCurrent].CmdBuffer, &CmdBuffer, sizeof(CmdBuffer)); // copy 12 bytes, four times
  303. memcpy(&_Channels.Data[_ChannelsCurrent].IdxBuffer, &IdxBuffer, sizeof(IdxBuffer));
  304. _ChannelsCurrent = idx;
  305. memcpy(&CmdBuffer, &_Channels.Data[_ChannelsCurrent].CmdBuffer, sizeof(CmdBuffer));
  306. memcpy(&IdxBuffer, &_Channels.Data[_ChannelsCurrent].IdxBuffer, sizeof(IdxBuffer));
  307. _IdxWritePtr = IdxBuffer.Data + IdxBuffer.Size;
  308. }
  309. // NB: this can be called with negative count for removing primitives (as long as the result does not underflow)
  310. void ImDrawList::PrimReserve(int idx_count, int vtx_count)
  311. {
  312. ImDrawCmd& draw_cmd = CmdBuffer.Data[CmdBuffer.Size-1];
  313. draw_cmd.ElemCount += idx_count;
  314. int vtx_buffer_size = VtxBuffer.Size;
  315. VtxBuffer.resize(vtx_buffer_size + vtx_count);
  316. _VtxWritePtr = VtxBuffer.Data + vtx_buffer_size;
  317. int idx_buffer_size = IdxBuffer.Size;
  318. IdxBuffer.resize(idx_buffer_size + idx_count);
  319. _IdxWritePtr = IdxBuffer.Data + idx_buffer_size;
  320. }
  321. // Fully unrolled with inline call to keep our debug builds decently fast.
  322. void ImDrawList::PrimRect(const ImVec2& a, const ImVec2& c, ImU32 col)
  323. {
  324. ImVec2 b(c.x, a.y), d(a.x, c.y), uv(GImGui->FontTexUvWhitePixel);
  325. ImDrawIdx idx = (ImDrawIdx)_VtxCurrentIdx;
  326. _IdxWritePtr[0] = idx; _IdxWritePtr[1] = (ImDrawIdx)(idx+1); _IdxWritePtr[2] = (ImDrawIdx)(idx+2);
  327. _IdxWritePtr[3] = idx; _IdxWritePtr[4] = (ImDrawIdx)(idx+2); _IdxWritePtr[5] = (ImDrawIdx)(idx+3);
  328. _VtxWritePtr[0].pos = a; _VtxWritePtr[0].uv = uv; _VtxWritePtr[0].col = col;
  329. _VtxWritePtr[1].pos = b; _VtxWritePtr[1].uv = uv; _VtxWritePtr[1].col = col;
  330. _VtxWritePtr[2].pos = c; _VtxWritePtr[2].uv = uv; _VtxWritePtr[2].col = col;
  331. _VtxWritePtr[3].pos = d; _VtxWritePtr[3].uv = uv; _VtxWritePtr[3].col = col;
  332. _VtxWritePtr += 4;
  333. _VtxCurrentIdx += 4;
  334. _IdxWritePtr += 6;
  335. }
  336. void ImDrawList::PrimRectUV(const ImVec2& a, const ImVec2& c, const ImVec2& uv_a, const ImVec2& uv_c, ImU32 col)
  337. {
  338. ImVec2 b(c.x, a.y), d(a.x, c.y), uv_b(uv_c.x, uv_a.y), uv_d(uv_a.x, uv_c.y);
  339. ImDrawIdx idx = (ImDrawIdx)_VtxCurrentIdx;
  340. _IdxWritePtr[0] = idx; _IdxWritePtr[1] = (ImDrawIdx)(idx+1); _IdxWritePtr[2] = (ImDrawIdx)(idx+2);
  341. _IdxWritePtr[3] = idx; _IdxWritePtr[4] = (ImDrawIdx)(idx+2); _IdxWritePtr[5] = (ImDrawIdx)(idx+3);
  342. _VtxWritePtr[0].pos = a; _VtxWritePtr[0].uv = uv_a; _VtxWritePtr[0].col = col;
  343. _VtxWritePtr[1].pos = b; _VtxWritePtr[1].uv = uv_b; _VtxWritePtr[1].col = col;
  344. _VtxWritePtr[2].pos = c; _VtxWritePtr[2].uv = uv_c; _VtxWritePtr[2].col = col;
  345. _VtxWritePtr[3].pos = d; _VtxWritePtr[3].uv = uv_d; _VtxWritePtr[3].col = col;
  346. _VtxWritePtr += 4;
  347. _VtxCurrentIdx += 4;
  348. _IdxWritePtr += 6;
  349. }
  350. void ImDrawList::PrimQuadUV(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& d, const ImVec2& uv_a, const ImVec2& uv_b, const ImVec2& uv_c, const ImVec2& uv_d, ImU32 col)
  351. {
  352. ImDrawIdx idx = (ImDrawIdx)_VtxCurrentIdx;
  353. _IdxWritePtr[0] = idx; _IdxWritePtr[1] = (ImDrawIdx)(idx+1); _IdxWritePtr[2] = (ImDrawIdx)(idx+2);
  354. _IdxWritePtr[3] = idx; _IdxWritePtr[4] = (ImDrawIdx)(idx+2); _IdxWritePtr[5] = (ImDrawIdx)(idx+3);
  355. _VtxWritePtr[0].pos = a; _VtxWritePtr[0].uv = uv_a; _VtxWritePtr[0].col = col;
  356. _VtxWritePtr[1].pos = b; _VtxWritePtr[1].uv = uv_b; _VtxWritePtr[1].col = col;
  357. _VtxWritePtr[2].pos = c; _VtxWritePtr[2].uv = uv_c; _VtxWritePtr[2].col = col;
  358. _VtxWritePtr[3].pos = d; _VtxWritePtr[3].uv = uv_d; _VtxWritePtr[3].col = col;
  359. _VtxWritePtr += 4;
  360. _VtxCurrentIdx += 4;
  361. _IdxWritePtr += 6;
  362. }
  363. // TODO: Thickness anti-aliased lines cap are missing their AA fringe.
  364. void ImDrawList::AddPolyline(const ImVec2* points, const int points_count, ImU32 col, bool closed, float thickness, bool anti_aliased)
  365. {
  366. if (points_count < 2)
  367. return;
  368. const ImVec2 uv = GImGui->FontTexUvWhitePixel;
  369. anti_aliased &= GImGui->Style.AntiAliasedLines;
  370. //if (ImGui::GetIO().KeyCtrl) anti_aliased = false; // Debug
  371. int count = points_count;
  372. if (!closed)
  373. count = points_count-1;
  374. const bool thick_line = thickness > 1.0f;
  375. if (anti_aliased)
  376. {
  377. // Anti-aliased stroke
  378. const float AA_SIZE = 1.0f;
  379. const ImU32 col_trans = col & IM_COL32(255,255,255,0);
  380. const int idx_count = thick_line ? count*18 : count*12;
  381. const int vtx_count = thick_line ? points_count*4 : points_count*3;
  382. PrimReserve(idx_count, vtx_count);
  383. // Temporary buffer
  384. ImVec2* temp_normals = (ImVec2*)alloca(points_count * (thick_line ? 5 : 3) * sizeof(ImVec2));
  385. ImVec2* temp_points = temp_normals + points_count;
  386. for (int i1 = 0; i1 < count; i1++)
  387. {
  388. const int i2 = (i1+1) == points_count ? 0 : i1+1;
  389. ImVec2 diff = points[i2] - points[i1];
  390. diff *= ImInvLength(diff, 1.0f);
  391. temp_normals[i1].x = diff.y;
  392. temp_normals[i1].y = -diff.x;
  393. }
  394. if (!closed)
  395. temp_normals[points_count-1] = temp_normals[points_count-2];
  396. if (!thick_line)
  397. {
  398. if (!closed)
  399. {
  400. temp_points[0] = points[0] + temp_normals[0] * AA_SIZE;
  401. temp_points[1] = points[0] - temp_normals[0] * AA_SIZE;
  402. temp_points[(points_count-1)*2+0] = points[points_count-1] + temp_normals[points_count-1] * AA_SIZE;
  403. temp_points[(points_count-1)*2+1] = points[points_count-1] - temp_normals[points_count-1] * AA_SIZE;
  404. }
  405. // FIXME-OPT: Merge the different loops, possibly remove the temporary buffer.
  406. unsigned int idx1 = _VtxCurrentIdx;
  407. for (int i1 = 0; i1 < count; i1++)
  408. {
  409. const int i2 = (i1+1) == points_count ? 0 : i1+1;
  410. unsigned int idx2 = (i1+1) == points_count ? _VtxCurrentIdx : idx1+3;
  411. // Average normals
  412. ImVec2 dm = (temp_normals[i1] + temp_normals[i2]) * 0.5f;
  413. float dmr2 = dm.x*dm.x + dm.y*dm.y;
  414. if (dmr2 > 0.000001f)
  415. {
  416. float scale = 1.0f / dmr2;
  417. if (scale > 100.0f) scale = 100.0f;
  418. dm *= scale;
  419. }
  420. dm *= AA_SIZE;
  421. temp_points[i2*2+0] = points[i2] + dm;
  422. temp_points[i2*2+1] = points[i2] - dm;
  423. // Add indexes
  424. _IdxWritePtr[0] = (ImDrawIdx)(idx2+0); _IdxWritePtr[1] = (ImDrawIdx)(idx1+0); _IdxWritePtr[2] = (ImDrawIdx)(idx1+2);
  425. _IdxWritePtr[3] = (ImDrawIdx)(idx1+2); _IdxWritePtr[4] = (ImDrawIdx)(idx2+2); _IdxWritePtr[5] = (ImDrawIdx)(idx2+0);
  426. _IdxWritePtr[6] = (ImDrawIdx)(idx2+1); _IdxWritePtr[7] = (ImDrawIdx)(idx1+1); _IdxWritePtr[8] = (ImDrawIdx)(idx1+0);
  427. _IdxWritePtr[9] = (ImDrawIdx)(idx1+0); _IdxWritePtr[10]= (ImDrawIdx)(idx2+0); _IdxWritePtr[11]= (ImDrawIdx)(idx2+1);
  428. _IdxWritePtr += 12;
  429. idx1 = idx2;
  430. }
  431. // Add vertexes
  432. for (int i = 0; i < points_count; i++)
  433. {
  434. _VtxWritePtr[0].pos = points[i]; _VtxWritePtr[0].uv = uv; _VtxWritePtr[0].col = col;
  435. _VtxWritePtr[1].pos = temp_points[i*2+0]; _VtxWritePtr[1].uv = uv; _VtxWritePtr[1].col = col_trans;
  436. _VtxWritePtr[2].pos = temp_points[i*2+1]; _VtxWritePtr[2].uv = uv; _VtxWritePtr[2].col = col_trans;
  437. _VtxWritePtr += 3;
  438. }
  439. }
  440. else
  441. {
  442. const float half_inner_thickness = (thickness - AA_SIZE) * 0.5f;
  443. if (!closed)
  444. {
  445. temp_points[0] = points[0] + temp_normals[0] * (half_inner_thickness + AA_SIZE);
  446. temp_points[1] = points[0] + temp_normals[0] * (half_inner_thickness);
  447. temp_points[2] = points[0] - temp_normals[0] * (half_inner_thickness);
  448. temp_points[3] = points[0] - temp_normals[0] * (half_inner_thickness + AA_SIZE);
  449. temp_points[(points_count-1)*4+0] = points[points_count-1] + temp_normals[points_count-1] * (half_inner_thickness + AA_SIZE);
  450. temp_points[(points_count-1)*4+1] = points[points_count-1] + temp_normals[points_count-1] * (half_inner_thickness);
  451. temp_points[(points_count-1)*4+2] = points[points_count-1] - temp_normals[points_count-1] * (half_inner_thickness);
  452. temp_points[(points_count-1)*4+3] = points[points_count-1] - temp_normals[points_count-1] * (half_inner_thickness + AA_SIZE);
  453. }
  454. // FIXME-OPT: Merge the different loops, possibly remove the temporary buffer.
  455. unsigned int idx1 = _VtxCurrentIdx;
  456. for (int i1 = 0; i1 < count; i1++)
  457. {
  458. const int i2 = (i1+1) == points_count ? 0 : i1+1;
  459. unsigned int idx2 = (i1+1) == points_count ? _VtxCurrentIdx : idx1+4;
  460. // Average normals
  461. ImVec2 dm = (temp_normals[i1] + temp_normals[i2]) * 0.5f;
  462. float dmr2 = dm.x*dm.x + dm.y*dm.y;
  463. if (dmr2 > 0.000001f)
  464. {
  465. float scale = 1.0f / dmr2;
  466. if (scale > 100.0f) scale = 100.0f;
  467. dm *= scale;
  468. }
  469. ImVec2 dm_out = dm * (half_inner_thickness + AA_SIZE);
  470. ImVec2 dm_in = dm * half_inner_thickness;
  471. temp_points[i2*4+0] = points[i2] + dm_out;
  472. temp_points[i2*4+1] = points[i2] + dm_in;
  473. temp_points[i2*4+2] = points[i2] - dm_in;
  474. temp_points[i2*4+3] = points[i2] - dm_out;
  475. // Add indexes
  476. _IdxWritePtr[0] = (ImDrawIdx)(idx2+1); _IdxWritePtr[1] = (ImDrawIdx)(idx1+1); _IdxWritePtr[2] = (ImDrawIdx)(idx1+2);
  477. _IdxWritePtr[3] = (ImDrawIdx)(idx1+2); _IdxWritePtr[4] = (ImDrawIdx)(idx2+2); _IdxWritePtr[5] = (ImDrawIdx)(idx2+1);
  478. _IdxWritePtr[6] = (ImDrawIdx)(idx2+1); _IdxWritePtr[7] = (ImDrawIdx)(idx1+1); _IdxWritePtr[8] = (ImDrawIdx)(idx1+0);
  479. _IdxWritePtr[9] = (ImDrawIdx)(idx1+0); _IdxWritePtr[10] = (ImDrawIdx)(idx2+0); _IdxWritePtr[11] = (ImDrawIdx)(idx2+1);
  480. _IdxWritePtr[12] = (ImDrawIdx)(idx2+2); _IdxWritePtr[13] = (ImDrawIdx)(idx1+2); _IdxWritePtr[14] = (ImDrawIdx)(idx1+3);
  481. _IdxWritePtr[15] = (ImDrawIdx)(idx1+3); _IdxWritePtr[16] = (ImDrawIdx)(idx2+3); _IdxWritePtr[17] = (ImDrawIdx)(idx2+2);
  482. _IdxWritePtr += 18;
  483. idx1 = idx2;
  484. }
  485. // Add vertexes
  486. for (int i = 0; i < points_count; i++)
  487. {
  488. _VtxWritePtr[0].pos = temp_points[i*4+0]; _VtxWritePtr[0].uv = uv; _VtxWritePtr[0].col = col_trans;
  489. _VtxWritePtr[1].pos = temp_points[i*4+1]; _VtxWritePtr[1].uv = uv; _VtxWritePtr[1].col = col;
  490. _VtxWritePtr[2].pos = temp_points[i*4+2]; _VtxWritePtr[2].uv = uv; _VtxWritePtr[2].col = col;
  491. _VtxWritePtr[3].pos = temp_points[i*4+3]; _VtxWritePtr[3].uv = uv; _VtxWritePtr[3].col = col_trans;
  492. _VtxWritePtr += 4;
  493. }
  494. }
  495. _VtxCurrentIdx += (ImDrawIdx)vtx_count;
  496. }
  497. else
  498. {
  499. // Non Anti-aliased Stroke
  500. const int idx_count = count*6;
  501. const int vtx_count = count*4; // FIXME-OPT: Not sharing edges
  502. PrimReserve(idx_count, vtx_count);
  503. for (int i1 = 0; i1 < count; i1++)
  504. {
  505. const int i2 = (i1+1) == points_count ? 0 : i1+1;
  506. const ImVec2& p1 = points[i1];
  507. const ImVec2& p2 = points[i2];
  508. ImVec2 diff = p2 - p1;
  509. diff *= ImInvLength(diff, 1.0f);
  510. const float dx = diff.x * (thickness * 0.5f);
  511. const float dy = diff.y * (thickness * 0.5f);
  512. _VtxWritePtr[0].pos.x = p1.x + dy; _VtxWritePtr[0].pos.y = p1.y - dx; _VtxWritePtr[0].uv = uv; _VtxWritePtr[0].col = col;
  513. _VtxWritePtr[1].pos.x = p2.x + dy; _VtxWritePtr[1].pos.y = p2.y - dx; _VtxWritePtr[1].uv = uv; _VtxWritePtr[1].col = col;
  514. _VtxWritePtr[2].pos.x = p2.x - dy; _VtxWritePtr[2].pos.y = p2.y + dx; _VtxWritePtr[2].uv = uv; _VtxWritePtr[2].col = col;
  515. _VtxWritePtr[3].pos.x = p1.x - dy; _VtxWritePtr[3].pos.y = p1.y + dx; _VtxWritePtr[3].uv = uv; _VtxWritePtr[3].col = col;
  516. _VtxWritePtr += 4;
  517. _IdxWritePtr[0] = (ImDrawIdx)(_VtxCurrentIdx); _IdxWritePtr[1] = (ImDrawIdx)(_VtxCurrentIdx+1); _IdxWritePtr[2] = (ImDrawIdx)(_VtxCurrentIdx+2);
  518. _IdxWritePtr[3] = (ImDrawIdx)(_VtxCurrentIdx); _IdxWritePtr[4] = (ImDrawIdx)(_VtxCurrentIdx+2); _IdxWritePtr[5] = (ImDrawIdx)(_VtxCurrentIdx+3);
  519. _IdxWritePtr += 6;
  520. _VtxCurrentIdx += 4;
  521. }
  522. }
  523. }
  524. void ImDrawList::AddConvexPolyFilled(const ImVec2* points, const int points_count, ImU32 col, bool anti_aliased)
  525. {
  526. const ImVec2 uv = GImGui->FontTexUvWhitePixel;
  527. anti_aliased &= GImGui->Style.AntiAliasedShapes;
  528. //if (ImGui::GetIO().KeyCtrl) anti_aliased = false; // Debug
  529. if (anti_aliased)
  530. {
  531. // Anti-aliased Fill
  532. const float AA_SIZE = 1.0f;
  533. const ImU32 col_trans = col & IM_COL32(255,255,255,0);
  534. const int idx_count = (points_count-2)*3 + points_count*6;
  535. const int vtx_count = (points_count*2);
  536. PrimReserve(idx_count, vtx_count);
  537. // Add indexes for fill
  538. unsigned int vtx_inner_idx = _VtxCurrentIdx;
  539. unsigned int vtx_outer_idx = _VtxCurrentIdx+1;
  540. for (int i = 2; i < points_count; i++)
  541. {
  542. _IdxWritePtr[0] = (ImDrawIdx)(vtx_inner_idx); _IdxWritePtr[1] = (ImDrawIdx)(vtx_inner_idx+((i-1)<<1)); _IdxWritePtr[2] = (ImDrawIdx)(vtx_inner_idx+(i<<1));
  543. _IdxWritePtr += 3;
  544. }
  545. // Compute normals
  546. ImVec2* temp_normals = (ImVec2*)alloca(points_count * sizeof(ImVec2));
  547. for (int i0 = points_count-1, i1 = 0; i1 < points_count; i0 = i1++)
  548. {
  549. const ImVec2& p0 = points[i0];
  550. const ImVec2& p1 = points[i1];
  551. ImVec2 diff = p1 - p0;
  552. diff *= ImInvLength(diff, 1.0f);
  553. temp_normals[i0].x = diff.y;
  554. temp_normals[i0].y = -diff.x;
  555. }
  556. for (int i0 = points_count-1, i1 = 0; i1 < points_count; i0 = i1++)
  557. {
  558. // Average normals
  559. const ImVec2& n0 = temp_normals[i0];
  560. const ImVec2& n1 = temp_normals[i1];
  561. ImVec2 dm = (n0 + n1) * 0.5f;
  562. float dmr2 = dm.x*dm.x + dm.y*dm.y;
  563. if (dmr2 > 0.000001f)
  564. {
  565. float scale = 1.0f / dmr2;
  566. if (scale > 100.0f) scale = 100.0f;
  567. dm *= scale;
  568. }
  569. dm *= AA_SIZE * 0.5f;
  570. // Add vertices
  571. _VtxWritePtr[0].pos = (points[i1] - dm); _VtxWritePtr[0].uv = uv; _VtxWritePtr[0].col = col; // Inner
  572. _VtxWritePtr[1].pos = (points[i1] + dm); _VtxWritePtr[1].uv = uv; _VtxWritePtr[1].col = col_trans; // Outer
  573. _VtxWritePtr += 2;
  574. // Add indexes for fringes
  575. _IdxWritePtr[0] = (ImDrawIdx)(vtx_inner_idx+(i1<<1)); _IdxWritePtr[1] = (ImDrawIdx)(vtx_inner_idx+(i0<<1)); _IdxWritePtr[2] = (ImDrawIdx)(vtx_outer_idx+(i0<<1));
  576. _IdxWritePtr[3] = (ImDrawIdx)(vtx_outer_idx+(i0<<1)); _IdxWritePtr[4] = (ImDrawIdx)(vtx_outer_idx+(i1<<1)); _IdxWritePtr[5] = (ImDrawIdx)(vtx_inner_idx+(i1<<1));
  577. _IdxWritePtr += 6;
  578. }
  579. _VtxCurrentIdx += (ImDrawIdx)vtx_count;
  580. }
  581. else
  582. {
  583. // Non Anti-aliased Fill
  584. const int idx_count = (points_count-2)*3;
  585. const int vtx_count = points_count;
  586. PrimReserve(idx_count, vtx_count);
  587. for (int i = 0; i < vtx_count; i++)
  588. {
  589. _VtxWritePtr[0].pos = points[i]; _VtxWritePtr[0].uv = uv; _VtxWritePtr[0].col = col;
  590. _VtxWritePtr++;
  591. }
  592. for (int i = 2; i < points_count; i++)
  593. {
  594. _IdxWritePtr[0] = (ImDrawIdx)(_VtxCurrentIdx); _IdxWritePtr[1] = (ImDrawIdx)(_VtxCurrentIdx+i-1); _IdxWritePtr[2] = (ImDrawIdx)(_VtxCurrentIdx+i);
  595. _IdxWritePtr += 3;
  596. }
  597. _VtxCurrentIdx += (ImDrawIdx)vtx_count;
  598. }
  599. }
  600. void ImDrawList::PathArcToFast(const ImVec2& centre, float radius, int amin, int amax)
  601. {
  602. static ImVec2 circle_vtx[12];
  603. static bool circle_vtx_builds = false;
  604. const int circle_vtx_count = IM_ARRAYSIZE(circle_vtx);
  605. if (!circle_vtx_builds)
  606. {
  607. for (int i = 0; i < circle_vtx_count; i++)
  608. {
  609. const float a = ((float)i / (float)circle_vtx_count) * 2*IM_PI;
  610. circle_vtx[i].x = cosf(a);
  611. circle_vtx[i].y = sinf(a);
  612. }
  613. circle_vtx_builds = true;
  614. }
  615. if (amin > amax) return;
  616. if (radius == 0.0f)
  617. {
  618. _Path.push_back(centre);
  619. }
  620. else
  621. {
  622. _Path.reserve(_Path.Size + (amax - amin + 1));
  623. for (int a = amin; a <= amax; a++)
  624. {
  625. const ImVec2& c = circle_vtx[a % circle_vtx_count];
  626. _Path.push_back(ImVec2(centre.x + c.x * radius, centre.y + c.y * radius));
  627. }
  628. }
  629. }
  630. void ImDrawList::PathArcTo(const ImVec2& centre, float radius, float amin, float amax, int num_segments)
  631. {
  632. if (radius == 0.0f)
  633. _Path.push_back(centre);
  634. _Path.reserve(_Path.Size + (num_segments + 1));
  635. for (int i = 0; i <= num_segments; i++)
  636. {
  637. const float a = amin + ((float)i / (float)num_segments) * (amax - amin);
  638. _Path.push_back(ImVec2(centre.x + cosf(a) * radius, centre.y + sinf(a) * radius));
  639. }
  640. }
  641. static void PathBezierToCasteljau(ImVector<ImVec2>* path, float x1, float y1, float x2, float y2, float x3, float y3, float x4, float y4, float tess_tol, int level)
  642. {
  643. float dx = x4 - x1;
  644. float dy = y4 - y1;
  645. float d2 = ((x2 - x4) * dy - (y2 - y4) * dx);
  646. float d3 = ((x3 - x4) * dy - (y3 - y4) * dx);
  647. d2 = (d2 >= 0) ? d2 : -d2;
  648. d3 = (d3 >= 0) ? d3 : -d3;
  649. if ((d2+d3) * (d2+d3) < tess_tol * (dx*dx + dy*dy))
  650. {
  651. path->push_back(ImVec2(x4, y4));
  652. }
  653. else if (level < 10)
  654. {
  655. float x12 = (x1+x2)*0.5f, y12 = (y1+y2)*0.5f;
  656. float x23 = (x2+x3)*0.5f, y23 = (y2+y3)*0.5f;
  657. float x34 = (x3+x4)*0.5f, y34 = (y3+y4)*0.5f;
  658. float x123 = (x12+x23)*0.5f, y123 = (y12+y23)*0.5f;
  659. float x234 = (x23+x34)*0.5f, y234 = (y23+y34)*0.5f;
  660. float x1234 = (x123+x234)*0.5f, y1234 = (y123+y234)*0.5f;
  661. PathBezierToCasteljau(path, x1,y1, x12,y12, x123,y123, x1234,y1234, tess_tol, level+1);
  662. PathBezierToCasteljau(path, x1234,y1234, x234,y234, x34,y34, x4,y4, tess_tol, level+1);
  663. }
  664. }
  665. void ImDrawList::PathBezierCurveTo(const ImVec2& p2, const ImVec2& p3, const ImVec2& p4, int num_segments)
  666. {
  667. ImVec2 p1 = _Path.back();
  668. if (num_segments == 0)
  669. {
  670. // Auto-tessellated
  671. PathBezierToCasteljau(&_Path, p1.x, p1.y, p2.x, p2.y, p3.x, p3.y, p4.x, p4.y, GImGui->Style.CurveTessellationTol, 0);
  672. }
  673. else
  674. {
  675. float t_step = 1.0f / (float)num_segments;
  676. for (int i_step = 1; i_step <= num_segments; i_step++)
  677. {
  678. float t = t_step * i_step;
  679. float u = 1.0f - t;
  680. float w1 = u*u*u;
  681. float w2 = 3*u*u*t;
  682. float w3 = 3*u*t*t;
  683. float w4 = t*t*t;
  684. _Path.push_back(ImVec2(w1*p1.x + w2*p2.x + w3*p3.x + w4*p4.x, w1*p1.y + w2*p2.y + w3*p3.y + w4*p4.y));
  685. }
  686. }
  687. }
  688. void ImDrawList::PathRect(const ImVec2& a, const ImVec2& b, float rounding, int rounding_corners)
  689. {
  690. float r = rounding;
  691. r = ImMin(r, fabsf(b.x-a.x) * ( ((rounding_corners&(1|2))==(1|2)) || ((rounding_corners&(4|8))==(4|8)) ? 0.5f : 1.0f ) - 1.0f);
  692. r = ImMin(r, fabsf(b.y-a.y) * ( ((rounding_corners&(1|8))==(1|8)) || ((rounding_corners&(2|4))==(2|4)) ? 0.5f : 1.0f ) - 1.0f);
  693. if (r <= 0.0f || rounding_corners == 0)
  694. {
  695. PathLineTo(a);
  696. PathLineTo(ImVec2(b.x,a.y));
  697. PathLineTo(b);
  698. PathLineTo(ImVec2(a.x,b.y));
  699. }
  700. else
  701. {
  702. const float r0 = (rounding_corners & 1) ? r : 0.0f;
  703. const float r1 = (rounding_corners & 2) ? r : 0.0f;
  704. const float r2 = (rounding_corners & 4) ? r : 0.0f;
  705. const float r3 = (rounding_corners & 8) ? r : 0.0f;
  706. PathArcToFast(ImVec2(a.x+r0,a.y+r0), r0, 6, 9);
  707. PathArcToFast(ImVec2(b.x-r1,a.y+r1), r1, 9, 12);
  708. PathArcToFast(ImVec2(b.x-r2,b.y-r2), r2, 0, 3);
  709. PathArcToFast(ImVec2(a.x+r3,b.y-r3), r3, 3, 6);
  710. }
  711. }
  712. void ImDrawList::AddLine(const ImVec2& a, const ImVec2& b, ImU32 col, float thickness)
  713. {
  714. if ((col & IM_COL32_A_MASK) == 0)
  715. return;
  716. PathLineTo(a + ImVec2(0.5f,0.5f));
  717. PathLineTo(b + ImVec2(0.5f,0.5f));
  718. PathStroke(col, false, thickness);
  719. }
  720. // a: upper-left, b: lower-right. we don't render 1 px sized rectangles properly.
  721. void ImDrawList::AddRect(const ImVec2& a, const ImVec2& b, ImU32 col, float rounding, int rounding_corners_flags, float thickness)
  722. {
  723. if ((col & IM_COL32_A_MASK) == 0)
  724. return;
  725. PathRect(a + ImVec2(0.5f,0.5f), b - ImVec2(0.5f,0.5f), rounding, rounding_corners_flags);
  726. PathStroke(col, true, thickness);
  727. }
  728. void ImDrawList::AddRectFilled(const ImVec2& a, const ImVec2& b, ImU32 col, float rounding, int rounding_corners_flags)
  729. {
  730. if ((col & IM_COL32_A_MASK) == 0)
  731. return;
  732. if (rounding > 0.0f)
  733. {
  734. PathRect(a, b, rounding, rounding_corners_flags);
  735. PathFill(col);
  736. }
  737. else
  738. {
  739. PrimReserve(6, 4);
  740. PrimRect(a, b, col);
  741. }
  742. }
  743. void ImDrawList::AddRectFilledMultiColor(const ImVec2& a, const ImVec2& c, ImU32 col_upr_left, ImU32 col_upr_right, ImU32 col_bot_right, ImU32 col_bot_left)
  744. {
  745. if (((col_upr_left | col_upr_right | col_bot_right | col_bot_left) & IM_COL32_A_MASK) == 0)
  746. return;
  747. const ImVec2 uv = GImGui->FontTexUvWhitePixel;
  748. PrimReserve(6, 4);
  749. PrimWriteIdx((ImDrawIdx)(_VtxCurrentIdx)); PrimWriteIdx((ImDrawIdx)(_VtxCurrentIdx+1)); PrimWriteIdx((ImDrawIdx)(_VtxCurrentIdx+2));
  750. PrimWriteIdx((ImDrawIdx)(_VtxCurrentIdx)); PrimWriteIdx((ImDrawIdx)(_VtxCurrentIdx+2)); PrimWriteIdx((ImDrawIdx)(_VtxCurrentIdx+3));
  751. PrimWriteVtx(a, uv, col_upr_left);
  752. PrimWriteVtx(ImVec2(c.x, a.y), uv, col_upr_right);
  753. PrimWriteVtx(c, uv, col_bot_right);
  754. PrimWriteVtx(ImVec2(a.x, c.y), uv, col_bot_left);
  755. }
  756. void ImDrawList::AddQuad(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& d, ImU32 col, float thickness)
  757. {
  758. if ((col & IM_COL32_A_MASK) == 0)
  759. return;
  760. PathLineTo(a);
  761. PathLineTo(b);
  762. PathLineTo(c);
  763. PathLineTo(d);
  764. PathStroke(col, true, thickness);
  765. }
  766. void ImDrawList::AddQuadFilled(const ImVec2& a, const ImVec2& b, const ImVec2& c, const ImVec2& d, ImU32 col)
  767. {
  768. if ((col & IM_COL32_A_MASK) == 0)
  769. return;
  770. PathLineTo(a);
  771. PathLineTo(b);
  772. PathLineTo(c);
  773. PathLineTo(d);
  774. PathFill(col);
  775. }
  776. void ImDrawList::AddTriangle(const ImVec2& a, const ImVec2& b, const ImVec2& c, ImU32 col, float thickness)
  777. {
  778. if ((col & IM_COL32_A_MASK) == 0)
  779. return;
  780. PathLineTo(a);
  781. PathLineTo(b);
  782. PathLineTo(c);
  783. PathStroke(col, true, thickness);
  784. }
  785. void ImDrawList::AddTriangleFilled(const ImVec2& a, const ImVec2& b, const ImVec2& c, ImU32 col)
  786. {
  787. if ((col & IM_COL32_A_MASK) == 0)
  788. return;
  789. PathLineTo(a);
  790. PathLineTo(b);
  791. PathLineTo(c);
  792. PathFill(col);
  793. }
  794. void ImDrawList::AddCircle(const ImVec2& centre, float radius, ImU32 col, int num_segments, float thickness)
  795. {
  796. if ((col & IM_COL32_A_MASK) == 0)
  797. return;
  798. const float a_max = IM_PI*2.0f * ((float)num_segments - 1.0f) / (float)num_segments;
  799. PathArcTo(centre, radius-0.5f, 0.0f, a_max, num_segments);
  800. PathStroke(col, true, thickness);
  801. }
  802. void ImDrawList::AddCircleFilled(const ImVec2& centre, float radius, ImU32 col, int num_segments)
  803. {
  804. if ((col & IM_COL32_A_MASK) == 0)
  805. return;
  806. const float a_max = IM_PI*2.0f * ((float)num_segments - 1.0f) / (float)num_segments;
  807. PathArcTo(centre, radius, 0.0f, a_max, num_segments);
  808. PathFill(col);
  809. }
  810. void ImDrawList::AddBezierCurve(const ImVec2& pos0, const ImVec2& cp0, const ImVec2& cp1, const ImVec2& pos1, ImU32 col, float thickness, int num_segments)
  811. {
  812. if ((col & IM_COL32_A_MASK) == 0)
  813. return;
  814. PathLineTo(pos0);
  815. PathBezierCurveTo(cp0, cp1, pos1, num_segments);
  816. PathStroke(col, false, thickness);
  817. }
  818. void ImDrawList::AddText(const ImFont* font, float font_size, const ImVec2& pos, ImU32 col, const char* text_begin, const char* text_end, float wrap_width, const ImVec4* cpu_fine_clip_rect)
  819. {
  820. if ((col & IM_COL32_A_MASK) == 0)
  821. return;
  822. if (text_end == NULL)
  823. text_end = text_begin + strlen(text_begin);
  824. if (text_begin == text_end)
  825. return;
  826. // Note: This is one of the few instance of breaking the encapsulation of ImDrawList, as we pull this from ImGui state, but it is just SO useful.
  827. // Might just move Font/FontSize to ImDrawList?
  828. if (font == NULL)
  829. font = GImGui->Font;
  830. if (font_size == 0.0f)
  831. font_size = GImGui->FontSize;
  832. IM_ASSERT(font->ContainerAtlas->TexID == _TextureIdStack.back()); // Use high-level ImGui::PushFont() or low-level ImDrawList::PushTextureId() to change font.
  833. ImVec4 clip_rect = _ClipRectStack.back();
  834. if (cpu_fine_clip_rect)
  835. {
  836. clip_rect.x = ImMax(clip_rect.x, cpu_fine_clip_rect->x);
  837. clip_rect.y = ImMax(clip_rect.y, cpu_fine_clip_rect->y);
  838. clip_rect.z = ImMin(clip_rect.z, cpu_fine_clip_rect->z);
  839. clip_rect.w = ImMin(clip_rect.w, cpu_fine_clip_rect->w);
  840. }
  841. font->RenderText(this, font_size, pos, col, clip_rect, text_begin, text_end, wrap_width, cpu_fine_clip_rect != NULL);
  842. }
  843. void ImDrawList::AddText(const ImVec2& pos, ImU32 col, const char* text_begin, const char* text_end)
  844. {
  845. AddText(GImGui->Font, GImGui->FontSize, pos, col, text_begin, text_end);
  846. }
  847. void ImDrawList::AddImage(ImTextureID user_texture_id, const ImVec2& a, const ImVec2& b, const ImVec2& uv0, const ImVec2& uv1, ImU32 col)
  848. {
  849. if ((col & IM_COL32_A_MASK) == 0)
  850. return;
  851. // FIXME-OPT: This is wasting draw calls.
  852. const bool push_texture_id = _TextureIdStack.empty() || user_texture_id != _TextureIdStack.back();
  853. if (push_texture_id)
  854. PushTextureID(user_texture_id);
  855. PrimReserve(6, 4);
  856. PrimRectUV(a, b, uv0, uv1, col);
  857. if (push_texture_id)
  858. PopTextureID();
  859. }
  860. //-----------------------------------------------------------------------------
  861. // ImDrawData
  862. //-----------------------------------------------------------------------------
  863. // 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!
  864. void ImDrawData::DeIndexAllBuffers()
  865. {
  866. ImVector<ImDrawVert> new_vtx_buffer;
  867. TotalVtxCount = TotalIdxCount = 0;
  868. for (int i = 0; i < CmdListsCount; i++)
  869. {
  870. ImDrawList* cmd_list = CmdLists[i];
  871. if (cmd_list->IdxBuffer.empty())
  872. continue;
  873. new_vtx_buffer.resize(cmd_list->IdxBuffer.Size);
  874. for (int j = 0; j < cmd_list->IdxBuffer.Size; j++)
  875. new_vtx_buffer[j] = cmd_list->VtxBuffer[cmd_list->IdxBuffer[j]];
  876. cmd_list->VtxBuffer.swap(new_vtx_buffer);
  877. cmd_list->IdxBuffer.resize(0);
  878. TotalVtxCount += cmd_list->VtxBuffer.Size;
  879. }
  880. }
  881. // 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.
  882. void ImDrawData::ScaleClipRects(const ImVec2& scale)
  883. {
  884. for (int i = 0; i < CmdListsCount; i++)
  885. {
  886. ImDrawList* cmd_list = CmdLists[i];
  887. for (int cmd_i = 0; cmd_i < cmd_list->CmdBuffer.Size; cmd_i++)
  888. {
  889. ImDrawCmd* cmd = &cmd_list->CmdBuffer[cmd_i];
  890. cmd->ClipRect = ImVec4(cmd->ClipRect.x * scale.x, cmd->ClipRect.y * scale.y, cmd->ClipRect.z * scale.x, cmd->ClipRect.w * scale.y);
  891. }
  892. }
  893. }
  894. //-----------------------------------------------------------------------------
  895. // ImFontAtlas
  896. //-----------------------------------------------------------------------------
  897. ImFontConfig::ImFontConfig()
  898. {
  899. FontData = NULL;
  900. FontDataSize = 0;
  901. FontDataOwnedByAtlas = true;
  902. FontNo = 0;
  903. SizePixels = 0.0f;
  904. OversampleH = 3;
  905. OversampleV = 1;
  906. PixelSnapH = false;
  907. GlyphExtraSpacing = ImVec2(0.0f, 0.0f);
  908. GlyphRanges = NULL;
  909. MergeMode = false;
  910. MergeGlyphCenterV = false;
  911. DstFont = NULL;
  912. memset(Name, 0, sizeof(Name));
  913. }
  914. ImFontAtlas::ImFontAtlas()
  915. {
  916. TexID = NULL;
  917. TexPixelsAlpha8 = NULL;
  918. TexPixelsRGBA32 = NULL;
  919. TexWidth = TexHeight = TexDesiredWidth = 0;
  920. TexUvWhitePixel = ImVec2(0, 0);
  921. }
  922. ImFontAtlas::~ImFontAtlas()
  923. {
  924. Clear();
  925. }
  926. void ImFontAtlas::ClearInputData()
  927. {
  928. for (int i = 0; i < ConfigData.Size; i++)
  929. if (ConfigData[i].FontData && ConfigData[i].FontDataOwnedByAtlas)
  930. {
  931. ImGui::MemFree(ConfigData[i].FontData);
  932. ConfigData[i].FontData = NULL;
  933. }
  934. // When clearing this we lose access to the font name and other information used to build the font.
  935. for (int i = 0; i < Fonts.Size; i++)
  936. if (Fonts[i]->ConfigData >= ConfigData.Data && Fonts[i]->ConfigData < ConfigData.Data + ConfigData.Size)
  937. {
  938. Fonts[i]->ConfigData = NULL;
  939. Fonts[i]->ConfigDataCount = 0;
  940. }
  941. ConfigData.clear();
  942. }
  943. void ImFontAtlas::ClearTexData()
  944. {
  945. if (TexPixelsAlpha8)
  946. ImGui::MemFree(TexPixelsAlpha8);
  947. if (TexPixelsRGBA32)
  948. ImGui::MemFree(TexPixelsRGBA32);
  949. TexPixelsAlpha8 = NULL;
  950. TexPixelsRGBA32 = NULL;
  951. }
  952. void ImFontAtlas::ClearFonts()
  953. {
  954. for (int i = 0; i < Fonts.Size; i++)
  955. {
  956. Fonts[i]->~ImFont();
  957. ImGui::MemFree(Fonts[i]);
  958. }
  959. Fonts.clear();
  960. }
  961. void ImFontAtlas::Clear()
  962. {
  963. ClearInputData();
  964. ClearTexData();
  965. ClearFonts();
  966. }
  967. void ImFontAtlas::GetTexDataAsAlpha8(unsigned char** out_pixels, int* out_width, int* out_height, int* out_bytes_per_pixel)
  968. {
  969. // Build atlas on demand
  970. if (TexPixelsAlpha8 == NULL)
  971. {
  972. if (ConfigData.empty())
  973. AddFontDefault();
  974. Build();
  975. }
  976. *out_pixels = TexPixelsAlpha8;
  977. if (out_width) *out_width = TexWidth;
  978. if (out_height) *out_height = TexHeight;
  979. if (out_bytes_per_pixel) *out_bytes_per_pixel = 1;
  980. }
  981. void ImFontAtlas::GetTexDataAsRGBA32(unsigned char** out_pixels, int* out_width, int* out_height, int* out_bytes_per_pixel)
  982. {
  983. // Convert to RGBA32 format on demand
  984. // Although it is likely to be the most commonly used format, our font rendering is 1 channel / 8 bpp
  985. if (!TexPixelsRGBA32)
  986. {
  987. unsigned char* pixels;
  988. GetTexDataAsAlpha8(&pixels, NULL, NULL);
  989. TexPixelsRGBA32 = (unsigned int*)ImGui::MemAlloc((size_t)(TexWidth * TexHeight * 4));
  990. const unsigned char* src = pixels;
  991. unsigned int* dst = TexPixelsRGBA32;
  992. for (int n = TexWidth * TexHeight; n > 0; n--)
  993. *dst++ = IM_COL32(255, 255, 255, (unsigned int)(*src++));
  994. }
  995. *out_pixels = (unsigned char*)TexPixelsRGBA32;
  996. if (out_width) *out_width = TexWidth;
  997. if (out_height) *out_height = TexHeight;
  998. if (out_bytes_per_pixel) *out_bytes_per_pixel = 4;
  999. }
  1000. ImFont* ImFontAtlas::AddFont(const ImFontConfig* font_cfg)
  1001. {
  1002. IM_ASSERT(font_cfg->FontData != NULL && font_cfg->FontDataSize > 0);
  1003. IM_ASSERT(font_cfg->SizePixels > 0.0f);
  1004. // Create new font
  1005. if (!font_cfg->MergeMode)
  1006. {
  1007. ImFont* font = (ImFont*)ImGui::MemAlloc(sizeof(ImFont));
  1008. IM_PLACEMENT_NEW(font) ImFont();
  1009. Fonts.push_back(font);
  1010. }
  1011. ConfigData.push_back(*font_cfg);
  1012. ImFontConfig& new_font_cfg = ConfigData.back();
  1013. if (!new_font_cfg.DstFont)
  1014. new_font_cfg.DstFont = Fonts.back();
  1015. if (!new_font_cfg.FontDataOwnedByAtlas)
  1016. {
  1017. new_font_cfg.FontData = ImGui::MemAlloc(new_font_cfg.FontDataSize);
  1018. new_font_cfg.FontDataOwnedByAtlas = true;
  1019. memcpy(new_font_cfg.FontData, font_cfg->FontData, (size_t)new_font_cfg.FontDataSize);
  1020. }
  1021. // Invalidate texture
  1022. ClearTexData();
  1023. return new_font_cfg.DstFont;
  1024. }
  1025. // Default font TTF is compressed with stb_compress then base85 encoded (see extra_fonts/binary_to_compressed_c.cpp for encoder)
  1026. static unsigned int stb_decompress_length(unsigned char *input);
  1027. static unsigned int stb_decompress(unsigned char *output, unsigned char *i, unsigned int length);
  1028. static const char* GetDefaultCompressedFontDataTTFBase85();
  1029. static unsigned int Decode85Byte(char c) { return c >= '\\' ? c-36 : c-35; }
  1030. static void Decode85(const unsigned char* src, unsigned char* dst)
  1031. {
  1032. while (*src)
  1033. {
  1034. unsigned int tmp = Decode85Byte(src[0]) + 85*(Decode85Byte(src[1]) + 85*(Decode85Byte(src[2]) + 85*(Decode85Byte(src[3]) + 85*Decode85Byte(src[4]))));
  1035. dst[0] = ((tmp >> 0) & 0xFF); dst[1] = ((tmp >> 8) & 0xFF); dst[2] = ((tmp >> 16) & 0xFF); dst[3] = ((tmp >> 24) & 0xFF); // We can't assume little-endianness.
  1036. src += 5;
  1037. dst += 4;
  1038. }
  1039. }
  1040. // Load embedded ProggyClean.ttf at size 13, disable oversampling
  1041. ImFont* ImFontAtlas::AddFontDefault(const ImFontConfig* font_cfg_template)
  1042. {
  1043. ImFontConfig font_cfg = font_cfg_template ? *font_cfg_template : ImFontConfig();
  1044. if (!font_cfg_template)
  1045. {
  1046. font_cfg.OversampleH = font_cfg.OversampleV = 1;
  1047. font_cfg.PixelSnapH = true;
  1048. }
  1049. if (font_cfg.Name[0] == '\0') strcpy(font_cfg.Name, "<default>");
  1050. const char* ttf_compressed_base85 = GetDefaultCompressedFontDataTTFBase85();
  1051. ImFont* font = AddFontFromMemoryCompressedBase85TTF(ttf_compressed_base85, 13.0f, &font_cfg, GetGlyphRangesDefault());
  1052. return font;
  1053. }
  1054. ImFont* ImFontAtlas::AddFontFromFileTTF(const char* filename, float size_pixels, const ImFontConfig* font_cfg_template, const ImWchar* glyph_ranges)
  1055. {
  1056. int data_size = 0;
  1057. void* data = ImLoadFileToMemory(filename, "rb", &data_size, 0);
  1058. if (!data)
  1059. {
  1060. IM_ASSERT(0); // Could not load file.
  1061. return NULL;
  1062. }
  1063. ImFontConfig font_cfg = font_cfg_template ? *font_cfg_template : ImFontConfig();
  1064. if (font_cfg.Name[0] == '\0')
  1065. {
  1066. // Store a short copy of filename into into the font name for convenience
  1067. const char* p;
  1068. for (p = filename + strlen(filename); p > filename && p[-1] != '/' && p[-1] != '\\'; p--) {}
  1069. snprintf(font_cfg.Name, IM_ARRAYSIZE(font_cfg.Name), "%s", p);
  1070. }
  1071. return AddFontFromMemoryTTF(data, data_size, size_pixels, &font_cfg, glyph_ranges);
  1072. }
  1073. // NBM Transfer ownership of 'ttf_data' to ImFontAtlas, unless font_cfg_template->FontDataOwnedByAtlas == false. Owned TTF buffer will be deleted after Build().
  1074. ImFont* ImFontAtlas::AddFontFromMemoryTTF(void* ttf_data, int ttf_size, float size_pixels, const ImFontConfig* font_cfg_template, const ImWchar* glyph_ranges)
  1075. {
  1076. ImFontConfig font_cfg = font_cfg_template ? *font_cfg_template : ImFontConfig();
  1077. IM_ASSERT(font_cfg.FontData == NULL);
  1078. font_cfg.FontData = ttf_data;
  1079. font_cfg.FontDataSize = ttf_size;
  1080. font_cfg.SizePixels = size_pixels;
  1081. if (glyph_ranges)
  1082. font_cfg.GlyphRanges = glyph_ranges;
  1083. return AddFont(&font_cfg);
  1084. }
  1085. ImFont* ImFontAtlas::AddFontFromMemoryCompressedTTF(const void* compressed_ttf_data, int compressed_ttf_size, float size_pixels, const ImFontConfig* font_cfg_template, const ImWchar* glyph_ranges)
  1086. {
  1087. const unsigned int buf_decompressed_size = stb_decompress_length((unsigned char*)compressed_ttf_data);
  1088. unsigned char* buf_decompressed_data = (unsigned char *)ImGui::MemAlloc(buf_decompressed_size);
  1089. stb_decompress(buf_decompressed_data, (unsigned char*)compressed_ttf_data, (unsigned int)compressed_ttf_size);
  1090. ImFontConfig font_cfg = font_cfg_template ? *font_cfg_template : ImFontConfig();
  1091. IM_ASSERT(font_cfg.FontData == NULL);
  1092. font_cfg.FontDataOwnedByAtlas = true;
  1093. return AddFontFromMemoryTTF(buf_decompressed_data, (int)buf_decompressed_size, size_pixels, &font_cfg, glyph_ranges);
  1094. }
  1095. ImFont* ImFontAtlas::AddFontFromMemoryCompressedBase85TTF(const char* compressed_ttf_data_base85, float size_pixels, const ImFontConfig* font_cfg, const ImWchar* glyph_ranges)
  1096. {
  1097. int compressed_ttf_size = (((int)strlen(compressed_ttf_data_base85) + 4) / 5) * 4;
  1098. void* compressed_ttf = ImGui::MemAlloc((size_t)compressed_ttf_size);
  1099. Decode85((const unsigned char*)compressed_ttf_data_base85, (unsigned char*)compressed_ttf);
  1100. ImFont* font = AddFontFromMemoryCompressedTTF(compressed_ttf, compressed_ttf_size, size_pixels, font_cfg, glyph_ranges);
  1101. ImGui::MemFree(compressed_ttf);
  1102. return font;
  1103. }
  1104. bool ImFontAt