ImageViewerMain.cpp 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344
  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/AnKi.h>
  6. using namespace anki;
  7. class TextureViewerUiNode : public SceneNode
  8. {
  9. public:
  10. ImageResourcePtr m_imageResource;
  11. TextureViewerUiNode(CString name)
  12. : SceneNode(name)
  13. {
  14. UiComponent* uic = newComponent<UiComponent>();
  15. uic->init(
  16. [](UiCanvas& canvas, void* ud) {
  17. static_cast<TextureViewerUiNode*>(ud)->draw(canvas);
  18. },
  19. this);
  20. ANKI_CHECK_AND_IGNORE(ResourceManager::getSingleton().loadResource("ShaderBinaries/UiVisualizeImage.ankiprogbin", m_imageProgram));
  21. }
  22. void frameUpdate([[maybe_unused]] Second prevUpdateTime, [[maybe_unused]] Second crntTime) override
  23. {
  24. }
  25. private:
  26. ImFont* m_font = nullptr;
  27. ShaderProgramResourcePtr m_imageProgram;
  28. ShaderProgramPtr m_imageGrProgram;
  29. U32 m_crntMip = 0;
  30. F32 m_zoom = 1.0f;
  31. F32 m_depth = 0.0f;
  32. Bool m_pointSampling = true;
  33. Array<Bool, 4> m_colorChannel = {true, true, true, true};
  34. F32 m_maxColorValue = 1.0f;
  35. void draw(UiCanvas& canvas)
  36. {
  37. if(!m_font)
  38. {
  39. m_font = canvas.addFont("EngineAssets/UbuntuMonoRegular.ttf");
  40. }
  41. const Texture& grTex = m_imageResource->getTexture();
  42. const U32 colorComponentCount = getFormatInfo(grTex.getFormat()).m_componentCount;
  43. ANKI_ASSERT(grTex.getTextureType() == TextureType::k2D || grTex.getTextureType() == TextureType::k3D);
  44. if(!m_imageGrProgram.isCreated())
  45. {
  46. ShaderProgramResourceVariantInitInfo variantInit(m_imageProgram);
  47. variantInit.addMutation("TEXTURE_TYPE", (grTex.getTextureType() == TextureType::k2D) ? 0 : 1);
  48. const ShaderProgramResourceVariant* variant;
  49. m_imageProgram->getOrCreateVariant(variantInit, variant);
  50. m_imageGrProgram.reset(&variant->getProgram());
  51. }
  52. const ImGuiWindowFlags windowFlags = ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoMove;
  53. ImGui::Begin("Console", nullptr, windowFlags);
  54. ImGui::PushFont(m_font, 16.0f);
  55. ImGui::SetWindowPos(Vec2(0.0f, 0.0f));
  56. ImGui::SetWindowSize(canvas.getSizef());
  57. ImGui::BeginChild("Tools", Vec2(-1.0f, 30.0f), ImGuiChildFlags_AlwaysAutoResize | ImGuiChildFlags_AutoResizeX, ImGuiWindowFlags_None);
  58. // Zoom
  59. if(ImGui::Button("-"))
  60. {
  61. m_zoom -= 0.1f;
  62. }
  63. ImGui::SameLine();
  64. ImGui::DragFloat("##Zoom", &m_zoom, 0.01f, 0.1f, 20.0f, "Zoom %.3f");
  65. ImGui::SameLine();
  66. if(ImGui::Button("+"))
  67. {
  68. m_zoom += 0.1f;
  69. }
  70. ImGui::SameLine();
  71. ImGui::Spacing();
  72. ImGui::SameLine();
  73. // Sampling
  74. ImGui::Checkbox("Point sampling", &m_pointSampling);
  75. ImGui::SameLine();
  76. ImGui::Spacing();
  77. ImGui::SameLine();
  78. // Colors
  79. ImGui::Checkbox("Red", &m_colorChannel[0]);
  80. ImGui::SameLine();
  81. ImGui::Checkbox("Green", &m_colorChannel[1]);
  82. ImGui::SameLine();
  83. ImGui::Checkbox("Blue", &m_colorChannel[2]);
  84. ImGui::SameLine();
  85. if(colorComponentCount == 4)
  86. {
  87. ImGui::Checkbox("Alpha", &m_colorChannel[3]);
  88. ImGui::SameLine();
  89. }
  90. ImGui::Spacing();
  91. ImGui::SameLine();
  92. // Mips combo
  93. {
  94. UiStringList mipLabels;
  95. for(U32 mip = 0; mip < grTex.getMipmapCount(); ++mip)
  96. {
  97. mipLabels.pushBackSprintf("Mip %u (%u x %u)", mip, grTex.getWidth() >> mip, grTex.getHeight() >> mip);
  98. }
  99. if(ImGui::BeginCombo("##Mipmap", (mipLabels.getBegin() + m_crntMip)->cstr(), ImGuiComboFlags_HeightLarge))
  100. {
  101. for(U32 mip = 0; mip < grTex.getMipmapCount(); ++mip)
  102. {
  103. const Bool isSelected = (m_crntMip == mip);
  104. if(ImGui::Selectable((mipLabels.getBegin() + mip)->cstr(), isSelected))
  105. {
  106. m_crntMip = mip;
  107. }
  108. if(isSelected)
  109. {
  110. ImGui::SetItemDefaultFocus();
  111. }
  112. }
  113. ImGui::EndCombo();
  114. }
  115. ImGui::SameLine();
  116. }
  117. // Depth
  118. if(grTex.getTextureType() == TextureType::k3D)
  119. {
  120. UiStringList labels;
  121. for(U32 d = 0; d < grTex.getDepth(); ++d)
  122. {
  123. labels.pushBackSprintf("Depth %u", d);
  124. }
  125. if(ImGui::BeginCombo("##Depth", (labels.getBegin() + U32(m_depth))->cstr(), ImGuiComboFlags_HeightLarge))
  126. {
  127. for(U32 d = 0; d < grTex.getDepth(); ++d)
  128. {
  129. const Bool isSelected = (m_depth == F32(d));
  130. if(ImGui::Selectable((labels.getBegin() + d)->cstr(), isSelected))
  131. {
  132. m_depth = F32(d);
  133. }
  134. if(isSelected)
  135. {
  136. ImGui::SetItemDefaultFocus();
  137. }
  138. }
  139. ImGui::EndCombo();
  140. }
  141. ImGui::SameLine();
  142. }
  143. // Max color slider
  144. ImGui::SliderFloat("##Max color", &m_maxColorValue, 0.0f, 5.0f, "Max color = %.3f");
  145. ImGui::SameLine();
  146. // Avg color
  147. {
  148. const Vec4 avgColor = m_imageResource->getAverageColor();
  149. ImGui::Text("Average Color %.2f %.2f %.2f %.2f", avgColor.x(), avgColor.y(), avgColor.z(), avgColor.w());
  150. ImGui::SameLine();
  151. ImGui::ColorButton("Average color", avgColor);
  152. }
  153. // Next
  154. ImGui::EndChild();
  155. ImGui::BeginChild("Image", Vec2(-1.0f, -1.0f), ImGuiChildFlags_AlwaysAutoResize | ImGuiChildFlags_AutoResizeX,
  156. ImGuiWindowFlags_HorizontalScrollbar
  157. | (Input::getSingleton().getKey(KeyCode::kLeftCtrl) > 0 ? ImGuiWindowFlags_NoScrollWithMouse : 0));
  158. // Image
  159. {
  160. // Center image
  161. const Vec2 imageSize = Vec2(F32(grTex.getWidth()), F32(grTex.getHeight())) * m_zoom;
  162. class ExtraPushConstants
  163. {
  164. public:
  165. Vec4 m_colorScale;
  166. Vec4 m_depth;
  167. } pc;
  168. pc.m_colorScale.x() = F32(m_colorChannel[0]) / m_maxColorValue;
  169. pc.m_colorScale.y() = F32(m_colorChannel[1]) / m_maxColorValue;
  170. pc.m_colorScale.z() = F32(m_colorChannel[2]) / m_maxColorValue;
  171. pc.m_colorScale.w() = F32(m_colorChannel[3]);
  172. pc.m_depth = Vec4((m_depth + 0.5f) / F32(grTex.getDepth()));
  173. ImTextureID texid;
  174. texid.m_texture = &m_imageResource->getTexture();
  175. texid.m_textureSubresource = TextureSubresourceDesc::surface(m_crntMip, 0, 0, DepthStencilAspectBit::kNone);
  176. texid.m_customProgram = m_imageGrProgram.get();
  177. texid.m_extraFastConstantsSize = U8(sizeof(pc));
  178. texid.setExtraFastConstants(&pc, sizeof(pc));
  179. texid.m_pointSampling = m_pointSampling;
  180. ImGui::Image(texid, imageSize);
  181. if(ImGui::IsItemHovered())
  182. {
  183. if(Input::getSingleton().getKey(KeyCode::kLeftCtrl) > 0)
  184. {
  185. // Zoom
  186. const F32 zoomSpeed = 0.05f;
  187. if(Input::getSingleton().getMouseButton(MouseButton::kScrollDown) > 0)
  188. {
  189. m_zoom *= 1.0f - zoomSpeed;
  190. }
  191. else if(Input::getSingleton().getMouseButton(MouseButton::kScrollUp) > 0)
  192. {
  193. m_zoom *= 1.0f + zoomSpeed;
  194. }
  195. // Pan
  196. if(Input::getSingleton().getMouseButton(MouseButton::kLeft) > 0)
  197. {
  198. auto toWindow = [](Vec2 in) {
  199. in = in * 0.5f + 0.5f;
  200. in.y() = 1.0f - in.y();
  201. in *= Vec2(UVec2(NativeWindow::getSingleton().getWidth(), NativeWindow::getSingleton().getHeight()));
  202. return in;
  203. };
  204. const Vec2 delta =
  205. toWindow(Input::getSingleton().getMousePositionNdc()) - toWindow(Input::getSingleton().getMousePreviousPositionNdc());
  206. if(delta.x() != 0.0f)
  207. {
  208. ImGui::SetScrollX(ImGui::GetScrollX() - delta.x());
  209. }
  210. if(delta.y() != 0.0f)
  211. {
  212. ImGui::SetScrollY(ImGui::GetScrollY() - delta.y());
  213. }
  214. }
  215. }
  216. }
  217. }
  218. ImGui::EndChild();
  219. ImGui::PopFont();
  220. ImGui::End();
  221. }
  222. };
  223. class MyApp : public App
  224. {
  225. public:
  226. U32 m_argc = 0;
  227. Char** m_argv = nullptr;
  228. MyApp(U32 argc, Char** argv)
  229. : App("ImageViewer")
  230. , m_argc(argc)
  231. , m_argv(argv)
  232. {
  233. }
  234. Error userPreInit() override
  235. {
  236. if(m_argc < 2)
  237. {
  238. ANKI_LOGE("Wrong number of arguments");
  239. return Error::kUserData;
  240. }
  241. g_cvarWindowFullscreen = 0;
  242. g_cvarRsrcDataPaths = ANKI_SOURCE_DIRECTORY;
  243. ANKI_CHECK(CVarSet::getSingleton().setFromCommandLineArguments(m_argc - 2, m_argv + 2));
  244. return Error::kNone;
  245. }
  246. Error userPostInit() override
  247. {
  248. // Load the texture
  249. ImageResourcePtr image;
  250. ANKI_CHECK(ResourceManager::getSingleton().loadResource(m_argv[1], image, false));
  251. // Change window name
  252. String title;
  253. title.sprintf("%s %u x %u Mips %u Format %s", m_argv[1], image->getTexture().getWidth(), image->getTexture().getHeight(),
  254. image->getTexture().getMipmapCount(), getFormatInfo(image->getTexture().getFormat()).m_name);
  255. NativeWindow::getSingleton().setWindowTitle(title);
  256. // Create the node
  257. TextureViewerUiNode* node = SceneGraph::getSingleton().newSceneNode<TextureViewerUiNode>("TextureViewer");
  258. node->m_imageResource = std::move(image);
  259. return Error::kNone;
  260. }
  261. Error userMainLoop(Bool& quit, [[maybe_unused]] Second elapsedTime) override
  262. {
  263. Input& input = Input::getSingleton();
  264. if(input.getKey(KeyCode::kEscape) > 0)
  265. {
  266. quit = true;
  267. }
  268. return Error::kNone;
  269. }
  270. };
  271. ANKI_MAIN_FUNCTION(myMain)
  272. int myMain(int argc, char* argv[])
  273. {
  274. MyApp* app = new MyApp(argc, argv);
  275. const Error err = app->mainLoop();
  276. delete app;
  277. if(err)
  278. {
  279. ANKI_LOGE("Error reported. Bye!!");
  280. }
  281. else
  282. {
  283. ANKI_LOGI("Bye!!");
  284. }
  285. return 0;
  286. }