imgui_tex_inspect.cpp 40 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182
  1. // ImGuiTexInspect, a texture inspector widget for dear imgui
  2. //-------------------------------------------------------------------------
  3. // [SECTION] INCLUDES
  4. //-------------------------------------------------------------------------
  5. #define IMGUI_DEFINE_MATH_OPERATORS
  6. #include "imgui_tex_inspect.h"
  7. #include "imgui_tex_inspect_internal.h"
  8. #include "imgui.h"
  9. #include "imgui_internal.h"
  10. #if defined(_MSC_VER)
  11. #pragma warning(disable : 4996) // 'sprintf' considered unsafe
  12. #endif
  13. namespace ImGuiTexInspect
  14. {
  15. //-------------------------------------------------------------------------
  16. // [SECTION] FORWARD DECLARATIONS
  17. //-------------------------------------------------------------------------
  18. void UpdateShaderOptions(Inspector *inspector);
  19. void InspectorDrawCallback(const ImDrawList *parent_list, const ImDrawCmd *cmd);
  20. bool GetVisibleTexelRegionAndGetData(Inspector *inspector, ImVec2 &texelTL, ImVec2 &texelBR);
  21. //-------------------------------------------------------------------------
  22. // [SECTION] GLOBAL STATE
  23. //-------------------------------------------------------------------------
  24. // Input mapping structure, default values listed in the comments.
  25. struct InputMap
  26. {
  27. ImGuiMouseButton PanButton; // LMB enables panning when held
  28. InputMap();
  29. };
  30. InputMap::InputMap()
  31. {
  32. PanButton = ImGuiMouseButton_Left;
  33. }
  34. // Settings configured via SetNextPanelOptions etc.
  35. struct NextPanelSettings
  36. {
  37. InspectorFlags ToSet = 0;
  38. InspectorFlags ToClear = 0;
  39. };
  40. // Main context / configuration structure for imgui_tex_inspect
  41. struct Context
  42. {
  43. InputMap Input; // Input mapping config
  44. ImGuiStorage Inspectors; // All the inspectors we've seen
  45. Inspector * CurrentInspector; // Inspector currently being processed
  46. NextPanelSettings NextPanelOptions; // Options configured for next inspector panel
  47. float ZoomRate = 1.3f; // How fast mouse wheel affects zoom
  48. float DefaultPanelHeight = 600; // Height of panel in pixels
  49. float DefaultInitialPanelWidth = 600; // Only applies when window first appears
  50. int MaxAnnotations = 1000; // Limit number of texel annotations for performance
  51. };
  52. Context *GContext = nullptr;
  53. //-------------------------------------------------------------------------
  54. // [SECTION] USER FUNCTIONS
  55. //-------------------------------------------------------------------------
  56. void Init()
  57. {
  58. // Nothing to do here. But there might be in a later version. So client code should still call it!
  59. }
  60. void Shutdown()
  61. {
  62. // Nothing to do here. But there might be in a later version. So client code should still call it!
  63. }
  64. Context *CreateContext()
  65. {
  66. GContext = IM_NEW(Context);
  67. SetCurrentContext(GContext);
  68. return GContext;
  69. }
  70. void DestroyContext(Context *ctx)
  71. {
  72. if (ctx == NULL)
  73. {
  74. ctx = GContext;
  75. }
  76. if (ctx == GContext)
  77. {
  78. GContext = NULL;
  79. }
  80. for (ImGuiStorage::ImGuiStoragePair &pair : ctx->Inspectors.Data)
  81. {
  82. Inspector *inspector = (Inspector *)pair.val_p;
  83. if (inspector)
  84. {
  85. IM_DELETE(inspector);
  86. }
  87. }
  88. IM_DELETE(ctx);
  89. }
  90. void SetCurrentContext(Context *context)
  91. {
  92. ImGuiTexInspect::GContext = context;
  93. }
  94. void SetNextPanelFlags(InspectorFlags setFlags, InspectorFlags clearFlags)
  95. {
  96. SetFlag(GContext->NextPanelOptions.ToSet, setFlags);
  97. SetFlag(GContext->NextPanelOptions.ToClear, clearFlags);
  98. }
  99. bool BeginInspectorPanel(const char *title, ImTextureID texture, ImVec2 textureSize, InspectorFlags flags,
  100. SizeIncludingBorder sizeIncludingBorder)
  101. {
  102. const int borderWidth = 1;
  103. // Unpack size param. It's in the SizeIncludingBorder structure just to make sure users know what they're requesting
  104. ImVec2 size = sizeIncludingBorder.Size;
  105. ImGuiWindow *window = ImGui::GetCurrentWindow();
  106. Context *ctx = GContext;
  107. const ImGuiID ID = window->GetID(title);
  108. const ImGuiIO &IO = ImGui::GetIO();
  109. // Create or find inspector
  110. bool justCreated = GetByKey(ctx, ID) == NULL;
  111. ctx->CurrentInspector = GetOrAddByKey(ctx, ID);
  112. Inspector *inspector = ctx->CurrentInspector;
  113. justCreated |= !inspector->Initialized;
  114. // Cache the basics
  115. inspector->ID = ID;
  116. inspector->Texture = texture;
  117. inspector->TextureSize = textureSize;
  118. inspector->Initialized = true;
  119. // Handle incoming flags. We keep special track of the
  120. // newly set flags because somethings only take effect
  121. // the first time the flag is set.
  122. InspectorFlags newlySetFlags = ctx->NextPanelOptions.ToSet;
  123. if (justCreated)
  124. {
  125. SetFlag(newlySetFlags, flags);
  126. inspector->MaxAnnotatedTexels = ctx->MaxAnnotations;
  127. }
  128. SetFlag(inspector->Flags, newlySetFlags);
  129. ClearFlag(inspector->Flags, ctx->NextPanelOptions.ToClear);
  130. ClearFlag(newlySetFlags, ctx->NextPanelOptions.ToClear);
  131. ctx->NextPanelOptions = NextPanelSettings();
  132. // Calculate panel size
  133. ImVec2 contentRegionAvail = ImGui::GetContentRegionAvail();
  134. ImVec2 panelSize;
  135. // A size value of zero indicates we should use defaults
  136. if (justCreated)
  137. {
  138. panelSize = {size.x == 0 ? ImMax(ctx->DefaultInitialPanelWidth, contentRegionAvail.x) : size.x,
  139. size.y == 0 ? ctx->DefaultPanelHeight : size.y};
  140. }
  141. else
  142. {
  143. panelSize = {size.x == 0 ? contentRegionAvail.x : size.x, size.y == 0 ? ctx->DefaultPanelHeight : size.y};
  144. }
  145. inspector->PanelSize = panelSize;
  146. ImVec2 availablePanelSize = panelSize - ImVec2(borderWidth, borderWidth) * 2;
  147. {
  148. // Possibly update scale
  149. float newScale = -1;
  150. if (HasFlag(newlySetFlags, InspectorFlags_FillVertical))
  151. {
  152. newScale = availablePanelSize.y / textureSize.y;
  153. }
  154. else if (HasFlag(newlySetFlags, InspectorFlags_FillHorizontal))
  155. {
  156. newScale = availablePanelSize.x / textureSize.x;
  157. }
  158. else if (justCreated)
  159. {
  160. newScale = 1;
  161. }
  162. if (newScale != -1)
  163. {
  164. inspector->Scale = ImVec2(newScale, newScale);
  165. SetPanPos(inspector, ImVec2(0.5f, 0.5f));
  166. }
  167. }
  168. RoundPanPos(inspector);
  169. ImVec2 textureSizePixels = inspector->Scale * textureSize; // Size whole texture would appear on screen
  170. ImVec2 viewSizeUV = availablePanelSize / textureSizePixels; // Cropped size in terms of UV
  171. ImVec2 uv0 = inspector->PanPos - viewSizeUV * 0.5;
  172. ImVec2 uv1 = inspector->PanPos + viewSizeUV * 0.5;
  173. ImVec2 drawImageOffset{borderWidth, borderWidth};
  174. ImVec2 viewSize = availablePanelSize;
  175. if ((inspector->Flags & InspectorFlags_ShowWrap) == 0)
  176. {
  177. /* Don't crop the texture to UV [0,1] range. What you see outside this
  178. * range will depend on API and texture properties */
  179. if (textureSizePixels.x < availablePanelSize.x)
  180. {
  181. // Not big enough to horizontally fill view
  182. viewSize.x = ImFloor(textureSizePixels.x);
  183. drawImageOffset.x += ImFloor((availablePanelSize.x - textureSizePixels.x) / 2);
  184. uv0.x = 0;
  185. uv1.x = 1;
  186. viewSizeUV.x = 1;
  187. inspector->PanPos.x = 0.5f;
  188. }
  189. if (textureSizePixels.y < availablePanelSize.y)
  190. {
  191. // Not big enough to vertically fill view
  192. viewSize.y = ImFloor(textureSizePixels.y);
  193. drawImageOffset.y += ImFloor((availablePanelSize.y - textureSizePixels.y) / 2);
  194. uv0.y = 0;
  195. uv1.y = 1;
  196. viewSizeUV.y = 1;
  197. inspector->PanPos.y = 0.5;
  198. }
  199. }
  200. if (HasFlag(flags,InspectorFlags_FlipX))
  201. {
  202. ImSwap(uv0.x, uv1.x);
  203. viewSizeUV.x *= -1;
  204. }
  205. if (HasFlag(flags,InspectorFlags_FlipY))
  206. {
  207. ImSwap(uv0.y, uv1.y);
  208. viewSizeUV.y *= -1;
  209. }
  210. inspector->ViewSize = viewSize;
  211. inspector->ViewSizeUV = viewSizeUV;
  212. /* We use mouse scroll to zoom so we don't want scroll to propagate to
  213. * parent window. For this to happen we must NOT set
  214. * ImGuiWindowFlags_NoScrollWithMouse. This seems strange but it's the way
  215. * ImGui works. Also we must ensure the ScrollMax.y is not zero for the
  216. * child window. */
  217. if (ImGui::BeginChild(title, panelSize, false, ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoMove))
  218. {
  219. // See comment above
  220. ImGui::GetCurrentWindow()->ScrollMax.y = 1.0f;
  221. // Callback for using our own image shader
  222. ImGui::GetWindowDrawList()->AddCallback(InspectorDrawCallback, inspector);
  223. // Keep track of size of area that we draw for borders later
  224. inspector->PanelTopLeftPixel = ImGui::GetCursorScreenPos();
  225. ImGui::SetCursorPos(ImGui::GetCursorPos() + drawImageOffset);
  226. inspector->ViewTopLeftPixel = ImGui::GetCursorScreenPos();
  227. UpdateShaderOptions(inspector);
  228. inspector->CachedShaderOptions = inspector->ActiveShaderOptions;
  229. ImGui::Image(texture, viewSize, uv0, uv1);
  230. ImGui::GetWindowDrawList()->AddCallback(ImDrawCallback_ResetRenderState, nullptr);
  231. /* Matrices for going back and forth between texel coordinates in the
  232. * texture and screen coordinates based on where texture is drawn.
  233. * Useful for annotations and mouse hover etc. */
  234. inspector->TexelsToPixels = GetTexelsToPixels(inspector->ViewTopLeftPixel, viewSize, uv0, viewSizeUV, inspector->TextureSize);
  235. inspector->PixelsToTexels = inspector->TexelsToPixels.Inverse();
  236. ImVec2 mousePos = ImGui::GetMousePos();
  237. ImVec2 mousePosTexel = inspector->PixelsToTexels * mousePos;
  238. ImVec2 mouseUV = mousePosTexel / textureSize;
  239. mousePosTexel.x = Modulus(mousePosTexel.x, textureSize.x);
  240. mousePosTexel.y = Modulus(mousePosTexel.y, textureSize.y);
  241. if (ImGui::IsItemHovered() && (inspector->Flags & ImGuiTexInspect::InspectorFlags_NoTooltip) == 0)
  242. {
  243. // Show a tooltip for currently hovered texel
  244. ImVec2 texelTL;
  245. ImVec2 texelBR;
  246. if (GetVisibleTexelRegionAndGetData(inspector, texelTL, texelBR))
  247. {
  248. ImVec4 color = GetTexel(&inspector->Buffer, (int)mousePosTexel.x, (int)mousePosTexel.y);
  249. char buffer[128];
  250. sprintf(buffer, "UV: (%.5f, %.5f)\nTexel: (%d, %d)", mouseUV.x, mouseUV.y, (int)mousePosTexel.x, (int)mousePosTexel.y);
  251. ImGui::ColorTooltip(buffer, &color.x, 0);
  252. }
  253. }
  254. bool hovered = ImGui::IsWindowHovered();
  255. { //DRAGGING
  256. // start drag
  257. if (!inspector->IsDragging && hovered && IO.MouseClicked[ctx->Input.PanButton])
  258. {
  259. inspector->IsDragging = true;
  260. }
  261. // carry on dragging
  262. else if (inspector->IsDragging)
  263. {
  264. ImVec2 uvDelta = IO.MouseDelta * viewSizeUV / viewSize;
  265. inspector->PanPos -= uvDelta;
  266. RoundPanPos(inspector);
  267. }
  268. // end drag
  269. if (inspector->IsDragging && (IO.MouseReleased[ctx->Input.PanButton] || !IO.MouseDown[ctx->Input.PanButton]))
  270. {
  271. inspector->IsDragging = false;
  272. }
  273. }
  274. // ZOOM
  275. if (hovered && IO.MouseWheel != 0)
  276. {
  277. float zoomRate = ctx->ZoomRate;
  278. float scale = inspector->Scale.y;
  279. float prevScale = scale;
  280. bool keepTexelSizeRegular = scale > inspector->MinimumGridSize && !HasFlag(inspector->Flags, InspectorFlags_NoGrid);
  281. if (IO.MouseWheel > 0)
  282. {
  283. scale *= zoomRate;
  284. if (keepTexelSizeRegular)
  285. {
  286. // It looks nicer when all the grid cells are the same size
  287. // so keep scale integer when zoomed in
  288. scale = ImCeil(scale);
  289. }
  290. }
  291. else
  292. {
  293. scale /= zoomRate;
  294. if (keepTexelSizeRegular)
  295. {
  296. // See comment above. We're doing a floor this time to make
  297. // sure the scale always changes when scrolling
  298. scale = ImFloorSigned(scale);
  299. }
  300. }
  301. /* To make it easy to get back to 1:1 size we ensure that we stop
  302. * here without going straight past it*/
  303. if ((prevScale < 1 && scale > 1) || (prevScale > 1 && scale < 1))
  304. {
  305. scale = 1;
  306. }
  307. SetScale(inspector, ImVec2(inspector->PixelAspectRatio * scale, scale));
  308. SetPanPos(inspector, inspector->PanPos + (mouseUV - inspector->PanPos) * (1 - prevScale / scale));
  309. }
  310. return true;
  311. }
  312. else
  313. {
  314. return false;
  315. }
  316. }
  317. bool BeginInspectorPanel(const char *name, ImTextureID texture, ImVec2 textureSize, InspectorFlags flags)
  318. {
  319. return BeginInspectorPanel(name, texture, textureSize, flags, SizeIncludingBorder{{0, 0}});
  320. }
  321. bool BeginInspectorPanel(const char *name, ImTextureID texture, ImVec2 textureSize, InspectorFlags flags, SizeExcludingBorder size)
  322. {
  323. // Correct the size to include the border, but preserve 0 which has a special meaning
  324. return BeginInspectorPanel(name, texture, textureSize, flags,
  325. SizeIncludingBorder{ImVec2{size.size.x == 0 ? 0 : size.size.x + 2,
  326. size.size.y == 0 ? 0 : size.size.y + 2}});
  327. }
  328. void EndInspectorPanel()
  329. {
  330. const ImU32 innerBorderColour = 0xFFFFFFFF;
  331. const ImU32 outerBorderColour = 0xFF888888;
  332. Inspector *inspector = GContext->CurrentInspector;
  333. // Draw out border around whole inspector panel
  334. ImGui::GetWindowDrawList()->AddRect(inspector->PanelTopLeftPixel, inspector->PanelTopLeftPixel + inspector->PanelSize,
  335. outerBorderColour);
  336. // Draw innder border around texture. If zoomed in this will completely cover the outer border
  337. ImGui::GetWindowDrawList()->AddRect(inspector->ViewTopLeftPixel - ImVec2(1, 1),
  338. inspector->ViewTopLeftPixel + inspector->ViewSize + ImVec2(1, 1), innerBorderColour);
  339. ImGui::EndChild();
  340. // We set this back to false every frame in case the texture is dynamic
  341. if (!HasFlag(inspector->Flags, InspectorFlags_NoAutoReadTexture))
  342. {
  343. inspector->HaveCurrentTexelData = false;
  344. }
  345. }
  346. void ReleaseInspectorData(ImGuiID ID)
  347. {
  348. Inspector *inspector = GetByKey(GContext, ID);
  349. if (inspector == NULL)
  350. return;
  351. if (inspector->DataBuffer)
  352. {
  353. IM_FREE(inspector->DataBuffer);
  354. inspector->DataBuffer = NULL;
  355. inspector->DataBufferSize = 0;
  356. }
  357. /* In a later version we will remove inspector from the inspector table
  358. * altogether. For now we reset the whole inspector structure to prevent
  359. * clients relying on persisted data.
  360. */
  361. *inspector = Inspector();
  362. }
  363. ImGuiID CurrentInspector_GetID()
  364. {
  365. return GContext->CurrentInspector->ID;
  366. }
  367. void CurrentInspector_SetColorMatrix(const float (&matrix)[16], const float (&colorOffset)[4])
  368. {
  369. Inspector *inspector = GContext->CurrentInspector;
  370. ShaderOptions *shaderOptions = &inspector->ActiveShaderOptions;
  371. memcpy(shaderOptions->ColorTransform, matrix, sizeof(matrix));
  372. memcpy(shaderOptions->ColorOffset, colorOffset, sizeof(colorOffset));
  373. }
  374. void CurrentInspector_ResetColorMatrix()
  375. {
  376. Inspector *inspector = GContext->CurrentInspector;
  377. ShaderOptions *shaderOptions = &inspector->ActiveShaderOptions;
  378. shaderOptions->ResetColorTransform();
  379. }
  380. void CurrentInspector_SetAlphaMode(InspectorAlphaMode mode)
  381. {
  382. Inspector *inspector = GContext->CurrentInspector;
  383. ShaderOptions *shaderOptions = &inspector->ActiveShaderOptions;
  384. inspector->AlphaMode = mode;
  385. switch (mode)
  386. {
  387. case InspectorAlphaMode_Black:
  388. shaderOptions->BackgroundColor = ImVec4(0, 0, 0, 1);
  389. shaderOptions->DisableFinalAlpha = 1;
  390. shaderOptions->PremultiplyAlpha = 1;
  391. break;
  392. case InspectorAlphaMode_White:
  393. shaderOptions->BackgroundColor = ImVec4(1, 1, 1, 1);
  394. shaderOptions->DisableFinalAlpha = 1;
  395. shaderOptions->PremultiplyAlpha = 1;
  396. break;
  397. case InspectorAlphaMode_ImGui:
  398. shaderOptions->BackgroundColor = ImVec4(0, 0, 0, 0);
  399. shaderOptions->DisableFinalAlpha = 0;
  400. shaderOptions->PremultiplyAlpha = 0;
  401. break;
  402. case InspectorAlphaMode_CustomColor:
  403. shaderOptions->BackgroundColor = inspector->CustomBackgroundColor;
  404. shaderOptions->DisableFinalAlpha = 1;
  405. shaderOptions->PremultiplyAlpha = 1;
  406. break;
  407. }
  408. }
  409. void CurrentInspector_SetFlags(InspectorFlags toSet, InspectorFlags toClear)
  410. {
  411. Inspector *inspector = GContext->CurrentInspector;
  412. SetFlag(inspector->Flags, toSet);
  413. ClearFlag(inspector->Flags, toClear);
  414. }
  415. void CurrentInspector_SetGridColor(ImU32 color)
  416. {
  417. Inspector *inspector = GContext->CurrentInspector;
  418. float alpha = inspector->ActiveShaderOptions.GridColor.w;
  419. inspector->ActiveShaderOptions.GridColor = ImColor(color);
  420. inspector->ActiveShaderOptions.GridColor.w = alpha;
  421. }
  422. void CurrentInspector_SetMaxAnnotations(int maxAnnotations)
  423. {
  424. Inspector *inspector = GContext->CurrentInspector;
  425. inspector->MaxAnnotatedTexels = maxAnnotations;
  426. }
  427. void CurrentInspector_InvalidateTextureCache()
  428. {
  429. Inspector *inspector = GContext->CurrentInspector;
  430. inspector->HaveCurrentTexelData = false;
  431. }
  432. void CurrentInspector_SetCustomBackgroundColor(ImVec4 color)
  433. {
  434. Inspector *inspector = GContext->CurrentInspector;
  435. inspector->CustomBackgroundColor = color;
  436. if (inspector->AlphaMode == InspectorAlphaMode_CustomColor)
  437. {
  438. inspector->ActiveShaderOptions.BackgroundColor = color;
  439. }
  440. }
  441. void CurrentInspector_SetCustomBackgroundColor(ImU32 color)
  442. {
  443. CurrentInspector_SetCustomBackgroundColor(ImGui::ColorConvertU32ToFloat4(color));
  444. }
  445. void DrawColorMatrixEditor()
  446. {
  447. const char *colorVectorNames[] = {"R", "G", "B", "A", "1"};
  448. const char *finalColorVectorNames[] = {"R'", "G'", "B'", "A'"};
  449. const float dragSpeed = 0.02f;
  450. Inspector *inspector = GContext->CurrentInspector;
  451. ShaderOptions *shaderOptions = &inspector->ActiveShaderOptions;
  452. // Left hand side of equation. The final color vector which is the actual drawn color
  453. TextVector("FinalColorVector", finalColorVectorNames, IM_ARRAYSIZE(finalColorVectorNames));
  454. ImGui::SameLine();
  455. ImGui::TextUnformatted("=");
  456. ImGui::SameLine();
  457. // Right hand side of the equation: the Matrix. This is the editable part
  458. ImGui::BeginGroup();
  459. for (int i = 0; i < 4; ++i)
  460. {
  461. ImGui::PushID(i);
  462. for (int j = 0; j < 4; ++j)
  463. {
  464. ImGui::PushID(j);
  465. ImGui::SetNextItemWidth(50);
  466. ImGui::DragFloat("##f", &shaderOptions->ColorTransform[j * 4 + i], dragSpeed);
  467. ImGui::PopID();
  468. ImGui::SameLine();
  469. }
  470. ImGui::SetNextItemWidth(50);
  471. ImGui::DragFloat("##offset", &shaderOptions->ColorOffset[i], dragSpeed);
  472. ImGui::PopID();
  473. }
  474. ImGui::EndGroup();
  475. ImGui::SameLine();
  476. ImGui::TextUnformatted("*");
  477. ImGui::SameLine();
  478. // Right hand side of equation. The input vector, the source color of the texel.
  479. TextVector("ColorVector", colorVectorNames, IM_ARRAYSIZE(colorVectorNames));
  480. }
  481. void DrawGridEditor()
  482. {
  483. Inspector *inspector = GContext->CurrentInspector;
  484. ImGui::BeginGroup();
  485. bool gridEnabled = !HasFlag(inspector->Flags, InspectorFlags_NoGrid);
  486. if (ImGui::Checkbox("Grid", &gridEnabled))
  487. {
  488. if (gridEnabled)
  489. {
  490. CurrentInspector_ClearFlags(InspectorFlags_NoGrid);
  491. }
  492. else
  493. {
  494. CurrentInspector_SetFlags(InspectorFlags_NoGrid);
  495. }
  496. }
  497. if (gridEnabled)
  498. {
  499. ImGui::SameLine();
  500. ImGui::ColorEdit3("Grid Color", (float *)&inspector->ActiveShaderOptions.GridColor, ImGuiColorEditFlags_NoInputs);
  501. }
  502. ImGui::EndGroup();
  503. }
  504. void DrawColorChannelSelector()
  505. {
  506. Inspector *inspector = GContext->CurrentInspector;
  507. ShaderOptions *shaderOptions = &inspector->ActiveShaderOptions;
  508. ImGuiStorage *storage = ImGui::GetStateStorage();
  509. const ImGuiID greyScaleID = ImGui::GetID("greyScale");
  510. bool greyScale = storage->GetBool(greyScaleID, false);
  511. bool red = shaderOptions->ColorTransform[0] > 0;
  512. bool green = shaderOptions->ColorTransform[5] > 0;
  513. bool blue = shaderOptions->ColorTransform[10] > 0;
  514. bool changed = false;
  515. // In greyScale made we draw the red, green, blue checkboxes as disabled
  516. if (greyScale)
  517. {
  518. PushDisabled();
  519. }
  520. ImGui::BeginGroup();
  521. changed |= ImGui::Checkbox("Red", &red);
  522. changed |= ImGui::Checkbox("Green", &green);
  523. changed |= ImGui::Checkbox("Blue", &blue);
  524. ImGui::EndGroup();
  525. ImGui::SameLine();
  526. if (greyScale)
  527. {
  528. PopDisabled();
  529. }
  530. if (changed)
  531. {
  532. // Overwrite the color transform matrix with one based on the settings
  533. shaderOptions->ResetColorTransform();
  534. shaderOptions->ColorTransform[0] = red ? 1.0f : 0.0f;
  535. shaderOptions->ColorTransform[5] = green ? 1.0f : 0.0f;
  536. shaderOptions->ColorTransform[10] = blue ? 1.0f : 0.0f;
  537. }
  538. ImGui::BeginGroup();
  539. if (ImGui::Checkbox("Grey", &greyScale))
  540. {
  541. shaderOptions->ResetColorTransform();
  542. storage->SetBool(greyScaleID, greyScale);
  543. if (greyScale)
  544. {
  545. for (int i = 0; i < 3; i++)
  546. {
  547. for (int j = 0; j < 3; j++)
  548. {
  549. shaderOptions->ColorTransform[i * 4 + j] = 0.333f;
  550. }
  551. }
  552. }
  553. }
  554. ImGui::EndGroup();
  555. }
  556. void DrawAlphaModeSelector()
  557. {
  558. Inspector *inspector = GContext->CurrentInspector;
  559. const char *alphaModes[] = {"ImGui Background", "Black", "White", "Custom Color"};
  560. ImGui::SetNextItemWidth(200);
  561. InspectorAlphaMode currentAlphaMode = inspector->AlphaMode;
  562. ImGui::Combo("Alpha Modes", (int *)&currentAlphaMode, alphaModes, IM_ARRAYSIZE(alphaModes));
  563. CurrentInspector_SetAlphaMode(currentAlphaMode);
  564. if (inspector->AlphaMode == InspectorAlphaMode_CustomColor)
  565. {
  566. ImVec4 backgroundColor = inspector->CustomBackgroundColor;
  567. if (ImGui::ColorEdit3("Background Color", (float *)&backgroundColor, 0))
  568. {
  569. CurrentInspector_SetCustomBackgroundColor(backgroundColor);
  570. }
  571. }
  572. }
  573. void SetZoomRate(float rate)
  574. {
  575. GContext->ZoomRate = rate;
  576. }
  577. //-------------------------------------------------------------------------
  578. // [SECTION] Life Cycle
  579. //-------------------------------------------------------------------------
  580. Inspector::~Inspector()
  581. {
  582. if (DataBuffer)
  583. {
  584. IM_FREE(DataBuffer);
  585. }
  586. }
  587. //-------------------------------------------------------------------------
  588. // [SECTION] Scaling and Panning
  589. //-------------------------------------------------------------------------
  590. void RoundPanPos(Inspector *inspector)
  591. {
  592. if ((inspector->Flags & InspectorFlags_ShowWrap) > 0)
  593. {
  594. /* PanPos is the point in the center of the current view. Allow the
  595. * user to pan anywhere as long as the view center is inside the
  596. * texture.*/
  597. inspector->PanPos = ImClamp(inspector->PanPos, ImVec2(0, 0), ImVec2(1, 1));
  598. }
  599. else
  600. {
  601. /* When ShowWrap mode is disabled the limits are a bit more strict. We
  602. * try to keep it so that the user cannot pan past the edge of the
  603. * texture at all.*/
  604. ImVec2 absViewSizeUV = Abs(inspector->ViewSizeUV);
  605. inspector->PanPos = ImMax(inspector->PanPos - absViewSizeUV / 2, ImVec2(0, 0)) + absViewSizeUV / 2;
  606. inspector->PanPos = ImMin(inspector->PanPos + absViewSizeUV / 2, ImVec2(1, 1)) - absViewSizeUV / 2;
  607. }
  608. /* If inspector->scale is 1 then we should ensure that pixels are aligned
  609. * with texel centers to get pixel-perfect texture rendering*/
  610. ImVec2 topLeftSubTexel = inspector->PanPos * inspector->Scale * inspector->TextureSize - inspector->ViewSize * 0.5f;
  611. if (inspector->Scale.x >= 1)
  612. {
  613. topLeftSubTexel.x = Round(topLeftSubTexel.x);
  614. }
  615. if (inspector->Scale.y >= 1)
  616. {
  617. topLeftSubTexel.y = Round(topLeftSubTexel.y);
  618. }
  619. inspector->PanPos = (topLeftSubTexel + inspector->ViewSize * 0.5f) / (inspector->Scale * inspector->TextureSize);
  620. }
  621. void SetPanPos(Inspector *inspector, ImVec2 pos)
  622. {
  623. inspector->PanPos = pos;
  624. RoundPanPos(inspector);
  625. }
  626. void SetScale(Inspector *inspector, ImVec2 scale)
  627. {
  628. scale = ImClamp(scale, inspector->ScaleMin, inspector->ScaleMax);
  629. inspector->ViewSizeUV *= inspector->Scale / scale;
  630. inspector->Scale = scale;
  631. // Only force nearest sampling if zoomed in
  632. inspector->ActiveShaderOptions.ForceNearestSampling =
  633. (inspector->Scale.x > 1.0f || inspector->Scale.y > 1.0f) && !HasFlag(inspector->Flags, InspectorFlags_NoForceFilterNearest);
  634. inspector->ActiveShaderOptions.GridWidth = ImVec2(1.0f / inspector->Scale.x, 1.0f / inspector->Scale.y);
  635. }
  636. void SetScale(Inspector *inspector, float scaleY)
  637. {
  638. SetScale(inspector, ImVec2(scaleY * inspector->PixelAspectRatio, scaleY));
  639. }
  640. //-------------------------------------------------------------------------
  641. // [SECTION] INSPECTOR MAP
  642. //-------------------------------------------------------------------------
  643. Inspector *GetByKey(const Context *ctx, ImGuiID key)
  644. {
  645. return (Inspector *)ctx->Inspectors.GetVoidPtr(key);
  646. }
  647. Inspector *GetOrAddByKey(Context *ctx, ImGuiID key)
  648. {
  649. Inspector *inspector = GetByKey(ctx, key);
  650. if (inspector)
  651. {
  652. return inspector;
  653. }
  654. else
  655. {
  656. inspector = IM_NEW(Inspector);
  657. ctx->Inspectors.SetVoidPtr(key, inspector);
  658. return inspector;
  659. }
  660. }
  661. //-------------------------------------------------------------------------
  662. // [SECTION] TextureConversion class
  663. //-------------------------------------------------------------------------
  664. void ShaderOptions::ResetColorTransform()
  665. {
  666. memset(ColorTransform, 0, sizeof(ColorTransform));
  667. for (int i = 0; i < 4; ++i)
  668. {
  669. ColorTransform[i * 4 + i] = 1;
  670. }
  671. }
  672. ShaderOptions::ShaderOptions()
  673. {
  674. ResetColorTransform();
  675. memset(ColorOffset, 0, sizeof(ColorOffset));
  676. }
  677. //-------------------------------------------------------------------------
  678. // [SECTION] UI and CONFIG
  679. //-------------------------------------------------------------------------
  680. void UpdateShaderOptions(Inspector *inspector)
  681. {
  682. if (HasFlag(inspector->Flags, InspectorFlags_NoGrid) == false && inspector->Scale.y > inspector->MinimumGridSize)
  683. {
  684. // Enable grid in shader
  685. inspector->ActiveShaderOptions.GridColor.w = 1;
  686. SetScale(inspector, Round(inspector->Scale.y));
  687. }
  688. else
  689. {
  690. // Disable grid in shader
  691. inspector->ActiveShaderOptions.GridColor.w = 0;
  692. }
  693. inspector->ActiveShaderOptions.ForceNearestSampling =
  694. (inspector->Scale.x > 1.0f || inspector->Scale.y > 1.0f) && !HasFlag(inspector->Flags, InspectorFlags_NoForceFilterNearest);
  695. }
  696. // Draws a single column ImGui table with one row for each provided string
  697. void TextVector(const char *title, const char *const *strings, int stringCount)
  698. {
  699. ImGui::BeginGroup();
  700. ImGui::SetNextItemWidth(50);
  701. if (ImGui::BeginTable(title, 1, ImGuiTableFlags_BordersOuter | ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_NoHostExtendX))
  702. {
  703. for (int i = 0; i < stringCount; ++i)
  704. {
  705. ImGui::TableNextRow();
  706. ImGui::TableSetColumnIndex(0);
  707. ImGui::SetNextItemWidth(50);
  708. ImGui::Text("%s", strings[i]);
  709. }
  710. ImGui::EndTable();
  711. }
  712. ImGui::EndGroup();
  713. }
  714. const ImGuiCol disabledUIColorIds[] = {ImGuiCol_FrameBg,
  715. ImGuiCol_FrameBgActive,
  716. ImGuiCol_FrameBgHovered,
  717. ImGuiCol_Text,
  718. ImGuiCol_CheckMark};
  719. // Push disabled style for ImGui elements
  720. void PushDisabled()
  721. {
  722. for (ImGuiCol colorId : disabledUIColorIds)
  723. {
  724. ImVec4 color = ImGui::GetStyleColorVec4(colorId);
  725. color = color * ImVec4(0.5f, 0.5f, 0.5f, 0.5f);
  726. ImGui::PushStyleColor(colorId, color);
  727. }
  728. ImGui::PushItemFlag(ImGuiItemFlags_Disabled, true);
  729. }
  730. // Pop disabled style for ImGui elements
  731. void PopDisabled()
  732. {
  733. for (ImGuiCol colorId : disabledUIColorIds)
  734. {
  735. (void)colorId;
  736. ImGui::PopStyleColor();
  737. }
  738. ImGui::PopItemFlag();
  739. }
  740. //-------------------------------------------------------------------------
  741. // [SECTION] Rendering & Buffer Management
  742. //-------------------------------------------------------------------------
  743. void InspectorDrawCallback(const ImDrawList *parent_list, const ImDrawCmd *cmd)
  744. {
  745. // Forward call to API-specific backend
  746. Inspector *inspector = (Inspector *)cmd->UserCallbackData;
  747. BackEnd_SetShader(parent_list, cmd, inspector);
  748. }
  749. // Calculate a transform to convert from texel coordinates to screen pixel coordinates
  750. Transform2D GetTexelsToPixels(ImVec2 screenTopLeft, ImVec2 screenViewSize, ImVec2 uvTopLeft, ImVec2 uvViewSize, ImVec2 textureSize)
  751. {
  752. ImVec2 uvToPixel = screenViewSize / uvViewSize;
  753. Transform2D transform;
  754. transform.Scale = uvToPixel / textureSize;
  755. transform.Translate.x = screenTopLeft.x - uvTopLeft.x * uvToPixel.x;
  756. transform.Translate.y = screenTopLeft.y - uvTopLeft.y * uvToPixel.y;
  757. return transform;
  758. }
  759. /* Fills in the AnnotationsDesc structure which provides all necessary
  760. * information for code which draw annoations. Returns false if no annoations
  761. * should be drawn. The maxAnnotatedTexels argument provides a way to override
  762. * the default maxAnnotatedTexels.
  763. */
  764. bool GetAnnotationDesc(AnnotationsDesc *ad, ImU64 maxAnnotatedTexels)
  765. {
  766. Inspector *inspector = GContext->CurrentInspector;
  767. if (maxAnnotatedTexels == 0)
  768. {
  769. maxAnnotatedTexels = inspector->MaxAnnotatedTexels;
  770. }
  771. if (maxAnnotatedTexels != 0)
  772. {
  773. /* Check if we would draw too many annotations. This is to avoid poor
  774. * frame rate when too zoomed out. Increase MaxAnnotatedTexels if you
  775. * want to draw more annotations. Note that we don't use texelTL &
  776. * texelBR to get total visible texels as this would cause flickering
  777. * while panning as the exact number of visible texels changes.
  778. */
  779. ImVec2 screenViewSizeTexels = Abs(inspector->PixelsToTexels.Scale) * inspector->ViewSize;
  780. ImU64 approxVisibleTexelCount = (ImU64)screenViewSizeTexels.x * (ImU64)screenViewSizeTexels.y;
  781. if (approxVisibleTexelCount > maxAnnotatedTexels)
  782. {
  783. return false;
  784. }
  785. }
  786. // texelTL & texelBL will describe the currently visible texel region
  787. ImVec2 texelTL;
  788. ImVec2 texelBR;
  789. if (GetVisibleTexelRegionAndGetData(inspector, texelTL, texelBR))
  790. {
  791. ad->Buffer= inspector->Buffer;
  792. ad->DrawList = ImGui::GetWindowDrawList();
  793. ad->TexelsToPixels = inspector->TexelsToPixels;
  794. ad->TexelTopLeft = texelTL;
  795. ad->TexelViewSize = texelBR - texelTL;
  796. return true;
  797. }
  798. return false;
  799. }
  800. /* Calculates currently visible region of texture (which is returned in texelTL
  801. * and texelBR) then also actually ensure that that data is in memory. Returns
  802. * false if fetching data failed.
  803. */
  804. bool GetVisibleTexelRegionAndGetData(Inspector *inspector, ImVec2 &texelTL, ImVec2 &texelBR)
  805. {
  806. /* Figure out which texels correspond to the top left and bottom right
  807. * corners of the texture view. The plus + ImVec2(1,1) is because we
  808. * want to draw partially visible texels on the bottom and right edges.
  809. */
  810. texelTL = ImFloor(inspector->PixelsToTexels * inspector->ViewTopLeftPixel);
  811. texelBR = ImFloor(inspector->PixelsToTexels * (inspector->ViewTopLeftPixel + inspector->ViewSize));
  812. if (texelTL.x > texelBR.x)
  813. {
  814. ImSwap(texelTL.x, texelBR.x);
  815. }
  816. if (texelTL.y > texelBR.y)
  817. {
  818. ImSwap(texelTL.y, texelBR.y);
  819. }
  820. /* Add ImVec2(1,1) because we want to draw partially visible texels on the
  821. * bottom and right edges.*/
  822. texelBR += ImVec2(1,1);
  823. texelTL = ImClamp(texelTL, ImVec2(0, 0), inspector->TextureSize);
  824. texelBR = ImClamp(texelBR, ImVec2(0, 0), inspector->TextureSize);
  825. if (inspector->HaveCurrentTexelData)
  826. {
  827. return true;
  828. }
  829. // Now request pixel data for this region from backend
  830. ImVec2 texelViewSize = texelBR - texelTL;
  831. if (ImMin(texelViewSize.x, texelViewSize.y) > 0)
  832. {
  833. if (BackEnd_GetData(inspector, inspector->Texture, (int)texelTL.x, (int)texelTL.y, (int)texelViewSize.x, (int)texelViewSize.y,
  834. &inspector->Buffer))
  835. {
  836. inspector->HaveCurrentTexelData = true;
  837. return true;
  838. }
  839. }
  840. return false;
  841. }
  842. /* This is a function the backends can use to allocate a buffer for storing
  843. * texture texel data. The buffer is owned by the inpsector so the backend
  844. * code doesn't need to worry about freeing it.
  845. */
  846. ImU8 *GetBuffer(Inspector *inspector, size_t bytes)
  847. {
  848. if (inspector->DataBufferSize < bytes || inspector->DataBuffer == nullptr)
  849. {
  850. // We need to allocate a buffer
  851. if (inspector->DataBuffer)
  852. {
  853. IM_FREE(inspector->DataBuffer);
  854. }
  855. // Allocate slightly more than we need to avoid reallocating
  856. // very frequently in the case that size is increasing.
  857. size_t size = bytes * 5 / 4;
  858. inspector->DataBuffer = (ImU8 *)IM_ALLOC(size);
  859. inspector->DataBufferSize = size;
  860. }
  861. return inspector->DataBuffer;
  862. }
  863. ImVec4 GetTexel(const BufferDesc *bd, int x, int y)
  864. {
  865. if (x < bd->StartX || x >= bd->StartX + bd->Width || y < bd->StartY || y >= bd->StartY + bd->Height)
  866. {
  867. // Outside the range of data in the buffer.
  868. return ImVec4();
  869. }
  870. // Calculate position in array
  871. size_t offset = ((size_t)bd->LineStride * (y - bd->StartY) + bd->Stride * (x - bd->StartX));
  872. if (bd->Data_float)
  873. {
  874. const float *texel = bd->Data_float + offset;
  875. // It's possible our buffer doesn't have all 4 channels so fill gaps in with zeros
  876. return ImVec4( texel[bd->Red],
  877. bd->ChannelCount >= 2 ? texel[bd->Green] : 0,
  878. bd->ChannelCount >= 3 ? texel[bd->Blue] : 0,
  879. bd->ChannelCount >= 4 ? texel[bd->Alpha] : 0);
  880. }
  881. else if (bd->Data_uint8_t)
  882. {
  883. const ImU8 *texel = bd->Data_uint8_t + offset;
  884. // It's possible our buffer doesn't have all 4 channels so fill gaps in with zeros.
  885. // Also map from [0,255] to [0,1]
  886. return ImVec4( (float)texel[bd->Red] / 255.0f,
  887. bd->ChannelCount >= 2 ? (float)texel[bd->Green] / 255.0f : 0,
  888. bd->ChannelCount >= 3 ? (float)texel[bd->Blue] / 255.0f : 0,
  889. bd->ChannelCount >= 4 ? (float)texel[bd->Alpha] / 255.0f : 0);
  890. }
  891. else
  892. {
  893. return ImVec4();
  894. }
  895. }
  896. //-------------------------------------------------------------------------
  897. // [SECTION] Annotations
  898. //-------------------------------------------------------------------------
  899. ValueText::ValueText(Format format)
  900. {
  901. /* The ValueText annotation draws a string inside each texel displaying the
  902. * values of each channel. We now select a format string based on the enum
  903. * parameter*/
  904. switch (format)
  905. {
  906. case Format::HexString:
  907. TextFormatString = "#%02X%02X%02X%02X";
  908. TextColumnCount = 9;
  909. TextRowCount = 1;
  910. FormatAsFloats = false;
  911. break;
  912. case Format::BytesHex:
  913. TextFormatString = "R:#%02X\nG:#%02X\nB:#%02X\nA:#%02X";
  914. TextColumnCount = 5;
  915. TextRowCount = 4;
  916. FormatAsFloats = false;
  917. break;
  918. case Format::BytesDec:
  919. TextFormatString = "R:%3d\nG:%3d\nB:%3d\nA:%3d";
  920. TextColumnCount = 5;
  921. TextRowCount = 4;
  922. FormatAsFloats = false;
  923. break;
  924. case Format::Floats:
  925. TextFormatString = "%5.3f\n%5.3f\n%5.3f\n%5.3f";
  926. TextColumnCount = 5;
  927. TextRowCount = 4;
  928. FormatAsFloats = true;
  929. break;
  930. }
  931. }
  932. void ValueText::DrawAnnotation(ImDrawList *drawList, ImVec2 texel, Transform2D texelsToPixels, ImVec4 value)
  933. {
  934. char buffer[64];
  935. float fontHeight = ImGui::GetFontSize();
  936. float fontWidth = fontHeight / 2; /* WARNING this is a hack that gets a constant
  937. * character width from half the height. This work for the default font but
  938. * won't work on other fonts which may even not be monospace.*/
  939. // Calculate size of text and check if it fits
  940. ImVec2 textSize = ImVec2((float)TextColumnCount * fontWidth, (float)TextRowCount * fontHeight);
  941. if (textSize.x > ImAbs(texelsToPixels.Scale.x) || textSize.y > ImAbs(texelsToPixels.Scale.y))
  942. {
  943. // Not enough room in texel to fit the text. Don't draw it.
  944. return;
  945. }
  946. /* Choose black or white text based on how bright the texel. I.e. don't
  947. * draw black text on a dark background or vice versa. */
  948. float brightness = (value.x + value.y + value.z) * value.w / 3;
  949. ImU32 lineColor = brightness > 0.5 ? 0xFF000000 : 0xFFFFFFFF;
  950. if (FormatAsFloats)
  951. {
  952. sprintf(buffer, TextFormatString, value.x, value.y, value.z, value.w);
  953. }
  954. else
  955. {
  956. /* Map [0,1] to [0,255]. Also clamp it since input data wasn't
  957. * necessarily in [0,1] range. */
  958. ImU8 r = (ImU8)Round((ImClamp(value.x, 0.0f, 1.0f)) * 255);
  959. ImU8 g = (ImU8)Round((ImClamp(value.y, 0.0f, 1.0f)) * 255);
  960. ImU8 b = (ImU8)Round((ImClamp(value.z, 0.0f, 1.0f)) * 255);
  961. ImU8 a = (ImU8)Round((ImClamp(value.w, 0.0f, 1.0f)) * 255);
  962. sprintf(buffer, TextFormatString, r, g, b, a);
  963. }
  964. // Add text to drawlist!
  965. ImVec2 pixelCenter = texelsToPixels * texel;
  966. drawList->AddText(pixelCenter - textSize * 0.5f, lineColor, buffer);
  967. }
  968. Arrow::Arrow(int xVectorIndex, int yVectorIndex, ImVec2 lineScale)
  969. : VectorIndex_x(xVectorIndex), VectorIndex_y(yVectorIndex), LineScale(lineScale)
  970. {
  971. }
  972. Arrow &Arrow::UsePreset(Preset preset)
  973. {
  974. switch (preset)
  975. {
  976. default:
  977. case Preset::NormalMap:
  978. VectorIndex_x = 0;
  979. VectorIndex_y = 1;
  980. LineScale = ImVec2(1, -1);
  981. ZeroPoint = ImVec2(128.0f / 255, 128.0f / 255);
  982. break;
  983. case Preset::NormalizedFloat:
  984. VectorIndex_x = 0;
  985. VectorIndex_y = 1;
  986. LineScale = ImVec2(0.5f, -0.5f);
  987. ZeroPoint = ImVec2(0, 0);
  988. break;
  989. }
  990. return *this;
  991. }
  992. void Arrow::DrawAnnotation(ImDrawList *drawList, ImVec2 texel, Transform2D texelsToPixels, ImVec4 value)
  993. {
  994. const float arrowHeadScale = 0.35f;
  995. const ImU32 lineColor = 0xFFFFFFFF;
  996. float *vecPtr = &value.x;
  997. // Draw an arrow!
  998. ImVec2 lineDir = (ImVec2(vecPtr[VectorIndex_x], vecPtr[VectorIndex_y]) - ZeroPoint) * LineScale;
  999. ImVec2 lineStart = texel;
  1000. ImVec2 lineEnd = lineStart + lineDir;
  1001. ImVec2 arrowHead1 = ImVec2(lineDir.x - lineDir.y, lineDir.x + lineDir.y) * -arrowHeadScale;
  1002. ImVec2 arrowHead2 = ImVec2(lineDir.x + lineDir.y, -lineDir.x + lineDir.y) * -arrowHeadScale;
  1003. DrawAnnotationLine(drawList, lineStart, lineEnd, texelsToPixels, lineColor);
  1004. DrawAnnotationLine(drawList, lineEnd, lineEnd + arrowHead1, texelsToPixels, lineColor);
  1005. DrawAnnotationLine(drawList, lineEnd, lineEnd + arrowHead2, texelsToPixels, lineColor);
  1006. }
  1007. void DrawAnnotationLine(ImDrawList *drawList, ImVec2 fromTexel, ImVec2 toTexel, Transform2D texelsToPixels, ImU32 color)
  1008. {
  1009. ImVec2 lineFrom = texelsToPixels * fromTexel;
  1010. ImVec2 lineTo = texelsToPixels * toTexel;
  1011. drawList->AddLine(lineFrom, lineTo, color, 1.0f);
  1012. }
  1013. } // namespace ImGuiTexInspect