ImageViewerUi.cpp 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287
  1. // Copyright (C) 2009-present, Panagiotis Christopoulos Charitos and contributors.
  2. // All rights reserved.
  3. // Code licensed under the BSD License.
  4. // http://www.anki3d.org/LICENSE
  5. #include <AnKi/Editor/ImageViewerUi.h>
  6. #include <AnKi/Resource/ResourceManager.h>
  7. #include <AnKi/Resource/ImageResource.h>
  8. #include <AnKi/Window/Input.h>
  9. #include <ThirdParty/ImGui/Extra/IconsMaterialDesignIcons.h> // See all icons in https://pictogrammers.com/library/mdi/
  10. namespace anki {
  11. ImageViewerUi::ImageViewerUi()
  12. {
  13. ANKI_CHECKF(ResourceManager::getSingleton().loadResource("ShaderBinaries/UiVisualizeImage.ankiprogbin", m_imageProgram));
  14. for(U32 i = 0; i < 2; i++)
  15. {
  16. ShaderProgramResourceVariantInitInfo variantInit(m_imageProgram);
  17. variantInit.addMutation("TEXTURE_TYPE", i);
  18. const ShaderProgramResourceVariant* variant;
  19. m_imageProgram->getOrCreateVariant(variantInit, variant);
  20. m_imageGrPrograms[i].reset(&variant->getProgram());
  21. }
  22. }
  23. void ImageViewerUi::drawWindow(UiCanvas& canvas, Vec2 initialPos, Vec2 initialSize, ImGuiWindowFlags windowFlags)
  24. {
  25. if(!m_open)
  26. {
  27. return;
  28. }
  29. const Bool imageChanged = !!m_image && (m_imageUuid != m_image->getUuid());
  30. if(imageChanged)
  31. {
  32. m_crntMip = 0;
  33. m_zoom = 1.0f;
  34. m_depth = 0.0f;
  35. m_pointSampling = true;
  36. m_colorChannel = {true, true, true, true};
  37. m_maxColorValue = 1.0f;
  38. m_imageUuid = m_image->getUuid();
  39. }
  40. if(ImGui::GetFrameCount() > 1)
  41. {
  42. ImGui::SetNextWindowPos(initialPos, ImGuiCond_FirstUseEver);
  43. ImGui::SetNextWindowSize(initialSize, ImGuiCond_FirstUseEver);
  44. }
  45. if(ImGui::Begin("Image Viewer", &m_open, windowFlags))
  46. {
  47. if(ImGui::BeginChild("Toolbox", Vec2(0.0f), ImGuiChildFlags_AlwaysAutoResize | ImGuiChildFlags_AutoResizeX | ImGuiChildFlags_AutoResizeY,
  48. ImGuiWindowFlags_None))
  49. {
  50. // Texture info
  51. {
  52. ImGui::TextUnformatted(ICON_MDI_INFORMATION_SLAB_BOX);
  53. if(m_image)
  54. {
  55. const Texture& tex = m_image->getTexture();
  56. ImGui::SetItemTooltip("%u x %u x %u\nMips %u\nFormat %s", tex.getWidth(), tex.getHeight(), tex.getDepth(), tex.getMipmapCount(),
  57. getFormatInfo(tex.getFormat()).m_name);
  58. }
  59. ImGui::SameLine();
  60. }
  61. // Zoom
  62. {
  63. if(ImGui::Button(ICON_MDI_MINUS))
  64. {
  65. m_zoom -= 0.1f;
  66. }
  67. ImGui::SameLine();
  68. ImGui::DragFloat("##Zoom", &m_zoom, 0.01f, 0.1f, 20.0f, "Zoom %.3f");
  69. ImGui::SameLine();
  70. if(ImGui::Button(ICON_MDI_PLUS))
  71. {
  72. m_zoom += 0.1f;
  73. }
  74. ImGui::SameLine();
  75. ImGui::Spacing();
  76. ImGui::SameLine();
  77. }
  78. // Sampling
  79. {
  80. ImGui::Checkbox("Point Sampling", &m_pointSampling);
  81. ImGui::SameLine();
  82. ImGui::Spacing();
  83. ImGui::SameLine();
  84. }
  85. // Colors
  86. {
  87. ImGui::Checkbox("Red", &m_colorChannel[0]);
  88. ImGui::SameLine();
  89. ImGui::Checkbox("Green", &m_colorChannel[1]);
  90. ImGui::SameLine();
  91. ImGui::Checkbox("Blue", &m_colorChannel[2]);
  92. ImGui::SameLine();
  93. const U32 colorComponentCount = (!!m_image) ? getFormatInfo(m_image->getTexture().getFormat()).m_componentCount : 4;
  94. if(colorComponentCount == 4)
  95. {
  96. ImGui::Checkbox("Alpha", &m_colorChannel[3]);
  97. ImGui::SameLine();
  98. }
  99. ImGui::Spacing();
  100. }
  101. // Mips combo
  102. if(m_image)
  103. {
  104. const U32 mipCount = m_image->getTexture().getMipmapCount();
  105. UiStringList mipLabels;
  106. for(U32 mip = 0; mip < mipCount; ++mip)
  107. {
  108. mipLabels.pushBackSprintf("Mip %u (%u x %u)", mip, m_image->getTexture().getWidth() >> mip,
  109. m_image->getTexture().getHeight() >> mip);
  110. }
  111. if(ImGui::BeginCombo("##Mipmap", (mipLabels.getBegin() + m_crntMip)->cstr(), ImGuiComboFlags_HeightLarge))
  112. {
  113. for(U32 mip = 0; mip < mipCount; ++mip)
  114. {
  115. const Bool isSelected = (m_crntMip == mip);
  116. if(ImGui::Selectable((mipLabels.getBegin() + mip)->cstr(), isSelected))
  117. {
  118. m_crntMip = mip;
  119. }
  120. if(isSelected)
  121. {
  122. ImGui::SetItemDefaultFocus();
  123. }
  124. }
  125. ImGui::EndCombo();
  126. }
  127. ImGui::SameLine();
  128. }
  129. // Depth
  130. if(m_image && m_image->getTexture().getTextureType() == TextureType::k3D)
  131. {
  132. UiStringList labels;
  133. for(U32 d = 0; d < m_image->getTexture().getDepth(); ++d)
  134. {
  135. labels.pushBackSprintf("Depth %u", d);
  136. }
  137. if(ImGui::BeginCombo("##Depth", (labels.getBegin() + U32(m_depth))->cstr(), ImGuiComboFlags_HeightLarge))
  138. {
  139. for(U32 d = 0; d < m_image->getTexture().getDepth(); ++d)
  140. {
  141. const Bool isSelected = (m_depth == F32(d));
  142. if(ImGui::Selectable((labels.getBegin() + d)->cstr(), isSelected))
  143. {
  144. m_depth = F32(d);
  145. }
  146. if(isSelected)
  147. {
  148. ImGui::SetItemDefaultFocus();
  149. }
  150. }
  151. ImGui::EndCombo();
  152. }
  153. ImGui::SameLine();
  154. }
  155. // Avg color
  156. {
  157. const Vec4 avgColor = (m_image) ? m_image->getAverageColor() : Vec4(0.0f);
  158. ImGui::Text("Average Color %.2f %.2f %.2f %.2f", avgColor.x, avgColor.y, avgColor.z, avgColor.w);
  159. ImGui::SameLine();
  160. ImGui::ColorButton("Average Color", avgColor);
  161. ImGui::SameLine();
  162. }
  163. // Max color slider
  164. ImGui::SliderFloat("##Max color", &m_maxColorValue, 0.0f, 5.0f, "Max Color = %.3f");
  165. }
  166. ImGui::EndChild();
  167. ImGuiWindowFlags windowFlags = ImGuiWindowFlags_HorizontalScrollbar;
  168. if(Input::getSingleton().getKey(KeyCode::kLeftCtrl) > 0)
  169. {
  170. windowFlags |= ImGuiWindowFlags_NoScrollWithMouse | ImGuiWindowFlags_NoMove;
  171. }
  172. if(ImGui::BeginChild("Image", Vec2(-1.0f, -1.0f), ImGuiChildFlags_AlwaysAutoResize | ImGuiChildFlags_AutoResizeX, windowFlags))
  173. {
  174. if(m_image)
  175. {
  176. Texture& tex = m_image->getTexture();
  177. // Center image
  178. const Vec2 imageSize = Vec2(F32(tex.getWidth()), F32(tex.getHeight())) * m_zoom;
  179. class ExtraPushConstants
  180. {
  181. public:
  182. Vec4 m_colorScale;
  183. Vec4 m_depth;
  184. } pc;
  185. pc.m_colorScale.x = F32(m_colorChannel[0]) / m_maxColorValue;
  186. pc.m_colorScale.y = F32(m_colorChannel[1]) / m_maxColorValue;
  187. pc.m_colorScale.z = F32(m_colorChannel[2]) / m_maxColorValue;
  188. pc.m_colorScale.w = F32(m_colorChannel[3]);
  189. pc.m_depth = Vec4((m_depth + 0.5f) / F32(tex.getDepth()));
  190. ImTextureID texid;
  191. texid.m_texture = &tex;
  192. texid.m_textureSubresource = TextureSubresourceDesc::surface(m_crntMip, 0, 0, DepthStencilAspectBit::kNone);
  193. texid.m_customProgram = m_imageGrPrograms[tex.getTextureType() != TextureType::k2D].get();
  194. texid.m_extraFastConstantsSize = U8(sizeof(pc));
  195. texid.setExtraFastConstants(&pc, sizeof(pc));
  196. texid.m_pointSampling = m_pointSampling;
  197. ImGui::Image(texid, imageSize);
  198. if(ImGui::IsItemHovered())
  199. {
  200. if(Input::getSingleton().getKey(KeyCode::kLeftCtrl) > 0)
  201. {
  202. // Zoom
  203. const F32 zoomSpeed = 0.05f;
  204. if(Input::getSingleton().getMouseButton(MouseButton::kScrollDown) > 0)
  205. {
  206. m_zoom *= 1.0f - zoomSpeed;
  207. }
  208. else if(Input::getSingleton().getMouseButton(MouseButton::kScrollUp) > 0)
  209. {
  210. m_zoom *= 1.0f + zoomSpeed;
  211. }
  212. // Pan
  213. if(Input::getSingleton().getMouseButton(MouseButton::kLeft) > 0)
  214. {
  215. auto toWindow = [&](Vec2 in) {
  216. in = in * 0.5f + 0.5f;
  217. in.y = 1.0f - in.y;
  218. in *= canvas.getSizef();
  219. return in;
  220. };
  221. const Vec2 delta =
  222. toWindow(Input::getSingleton().getMousePositionNdc()) - toWindow(Input::getSingleton().getMousePreviousPositionNdc());
  223. if(delta.x != 0.0f)
  224. {
  225. ImGui::SetScrollX(ImGui::GetScrollX() - delta.x);
  226. }
  227. if(delta.y != 0.0f)
  228. {
  229. ImGui::SetScrollY(ImGui::GetScrollY() - delta.y);
  230. }
  231. }
  232. }
  233. }
  234. }
  235. else
  236. {
  237. ImGui::TextUnformatted("No image");
  238. }
  239. }
  240. ImGui::EndChild();
  241. }
  242. ImGui::End();
  243. if(!m_open)
  244. {
  245. // It was closed
  246. m_image.reset(nullptr);
  247. }
  248. }
  249. } // end namespace anki