// DragonX Wallet - ImGui Edition // Copyright 2024-2026 The Hush Developers // Released under the GPLv3 #include "texture_loader.h" // stb_image — single-file image loader (public domain) // Only compiled here; all other files just include the header. #define STB_IMAGE_IMPLEMENTATION #define STBI_ONLY_PNG #define STBI_NO_STDIO // we do our own fread for portability #include "stb_image.h" #include #include #include #ifdef DRAGONX_USE_DX11 #include // Get D3D11 device from ImGui backend (same pattern as qr_code.cpp) static ID3D11Device* GetImGuiD3D11Device() { ImGuiIO& io = ImGui::GetIO(); if (!io.BackendRendererUserData) return nullptr; return *reinterpret_cast(io.BackendRendererUserData); } #else #ifdef DRAGONX_HAS_GLAD #include #else #include #endif #endif #include "../util/logger.h" namespace dragonx { namespace util { // Read entire file into memory static bool ReadFileToBuffer(const char* path, std::vector& buf) { FILE* f = fopen(path, "rb"); if (!f) return false; fseek(f, 0, SEEK_END); long sz = ftell(f); fseek(f, 0, SEEK_SET); if (sz <= 0) { fclose(f); return false; } buf.resize((size_t)sz); size_t rd = fread(buf.data(), 1, (size_t)sz, f); fclose(f); return rd == (size_t)sz; } bool LoadTextureFromFile(const char* path, ImTextureID* outTexID, int* outW, int* outH) { std::vector fileData; if (!ReadFileToBuffer(path, fileData)) { DEBUG_LOGF("LoadTextureFromFile: failed to read '%s'\n", path); return false; } int w = 0, h = 0, channels = 0; unsigned char* pixels = stbi_load_from_memory( fileData.data(), (int)fileData.size(), &w, &h, &channels, 4); if (!pixels) { DEBUG_LOGF("LoadTextureFromFile: stbi failed for '%s'\n", path); return false; } #ifdef DRAGONX_USE_DX11 ID3D11Device* device = GetImGuiD3D11Device(); if (!device) { stbi_image_free(pixels); DEBUG_LOGF("LoadTextureFromFile: no D3D11 device available\n"); return false; } D3D11_TEXTURE2D_DESC desc = {}; desc.Width = w; desc.Height = h; desc.MipLevels = 1; desc.ArraySize = 1; desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; desc.SampleDesc.Count = 1; desc.Usage = D3D11_USAGE_DEFAULT; desc.BindFlags = D3D11_BIND_SHADER_RESOURCE; D3D11_SUBRESOURCE_DATA initData = {}; initData.pSysMem = pixels; initData.SysMemPitch = w * 4; ID3D11Texture2D* texture = nullptr; HRESULT hr = device->CreateTexture2D(&desc, &initData, &texture); stbi_image_free(pixels); if (FAILED(hr) || !texture) { DEBUG_LOGF("LoadTextureFromFile: CreateTexture2D failed for '%s'\n", path); return false; } ID3D11ShaderResourceView* srv = nullptr; D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc = {}; srvDesc.Format = desc.Format; srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D; srvDesc.Texture2D.MipLevels = 1; hr = device->CreateShaderResourceView(texture, &srvDesc, &srv); texture->Release(); if (FAILED(hr) || !srv) { DEBUG_LOGF("LoadTextureFromFile: CreateSRV failed for '%s'\n", path); return false; } *outTexID = (ImTextureID)(intptr_t)srv; *outW = w; *outH = h; DEBUG_LOGF("LoadTextureFromFile: loaded '%s' (%dx%d) -> DX11 SRV %p\n", path, w, h, (void*)srv); return true; #else GLuint tex = 0; glGenTextures(1, &tex); glBindTexture(GL_TEXTURE_2D, tex); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels); glGenerateMipmap(GL_TEXTURE_2D); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glBindTexture(GL_TEXTURE_2D, 0); stbi_image_free(pixels); *outTexID = (ImTextureID)(intptr_t)tex; *outW = w; *outH = h; DEBUG_LOGF("LoadTextureFromFile: loaded '%s' (%dx%d) -> texture %u\n", path, w, h, tex); return true; #endif } bool LoadTextureFromMemory(const unsigned char* data, size_t dataSize, ImTextureID* outTexID, int* outW, int* outH) { if (!data || dataSize == 0) { DEBUG_LOGF("LoadTextureFromMemory: null/empty data\n"); return false; } int w = 0, h = 0, channels = 0; unsigned char* pixels = stbi_load_from_memory( data, (int)dataSize, &w, &h, &channels, 4); if (!pixels) { DEBUG_LOGF("LoadTextureFromMemory: stbi decode failed\n"); return false; } #ifdef DRAGONX_USE_DX11 ID3D11Device* device = GetImGuiD3D11Device(); if (!device) { stbi_image_free(pixels); DEBUG_LOGF("LoadTextureFromMemory: no D3D11 device available\n"); return false; } D3D11_TEXTURE2D_DESC desc = {}; desc.Width = w; desc.Height = h; desc.MipLevels = 1; desc.ArraySize = 1; desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; desc.SampleDesc.Count = 1; desc.Usage = D3D11_USAGE_DEFAULT; desc.BindFlags = D3D11_BIND_SHADER_RESOURCE; D3D11_SUBRESOURCE_DATA initData = {}; initData.pSysMem = pixels; initData.SysMemPitch = w * 4; ID3D11Texture2D* texture = nullptr; HRESULT hr = device->CreateTexture2D(&desc, &initData, &texture); stbi_image_free(pixels); if (FAILED(hr) || !texture) { DEBUG_LOGF("LoadTextureFromMemory: CreateTexture2D failed\n"); return false; } ID3D11ShaderResourceView* srv = nullptr; D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc = {}; srvDesc.Format = desc.Format; srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D; srvDesc.Texture2D.MipLevels = 1; hr = device->CreateShaderResourceView(texture, &srvDesc, &srv); texture->Release(); if (FAILED(hr) || !srv) { DEBUG_LOGF("LoadTextureFromMemory: CreateSRV failed\n"); return false; } *outTexID = (ImTextureID)(intptr_t)srv; *outW = w; *outH = h; DEBUG_LOGF("LoadTextureFromMemory: %dx%d -> DX11 SRV %p\n", w, h, (void*)srv); return true; #else GLuint tex = 0; glGenTextures(1, &tex); glBindTexture(GL_TEXTURE_2D, tex); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels); glGenerateMipmap(GL_TEXTURE_2D); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glBindTexture(GL_TEXTURE_2D, 0); stbi_image_free(pixels); *outTexID = (ImTextureID)(intptr_t)tex; *outW = w; *outH = h; DEBUG_LOGF("LoadTextureFromMemory: %dx%d -> texture %u\n", w, h, tex); return true; #endif } bool CreateRawTexture(const unsigned char* pixels, int w, int h, bool repeat, ImTextureID* outTexID) { if (!pixels || w <= 0 || h <= 0) return false; #ifdef DRAGONX_USE_DX11 ID3D11Device* device = GetImGuiD3D11Device(); if (!device) return false; D3D11_TEXTURE2D_DESC desc = {}; desc.Width = w; desc.Height = h; desc.MipLevels = 1; desc.ArraySize = 1; desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; desc.SampleDesc.Count = 1; desc.Usage = D3D11_USAGE_DEFAULT; desc.BindFlags = D3D11_BIND_SHADER_RESOURCE; D3D11_SUBRESOURCE_DATA initData = {}; initData.pSysMem = pixels; initData.SysMemPitch = w * 4; ID3D11Texture2D* texture = nullptr; HRESULT hr = device->CreateTexture2D(&desc, &initData, &texture); if (FAILED(hr) || !texture) return false; D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc = {}; srvDesc.Format = desc.Format; srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D; srvDesc.Texture2D.MipLevels = 1; ID3D11ShaderResourceView* srv = nullptr; hr = device->CreateShaderResourceView(texture, &srvDesc, &srv); texture->Release(); if (FAILED(hr) || !srv) return false; *outTexID = (ImTextureID)(intptr_t)srv; return true; #else GLuint tex = 0; glGenTextures(1, &tex); glBindTexture(GL_TEXTURE_2D, tex); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, pixels); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); if (repeat) { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); } else { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); } glBindTexture(GL_TEXTURE_2D, 0); *outTexID = (ImTextureID)(intptr_t)tex; return true; #endif } unsigned char* LoadRawPixelsFromFile(const char* path, int* outW, int* outH) { std::vector fileData; if (!ReadFileToBuffer(path, fileData)) return nullptr; int channels = 0; return stbi_load_from_memory(fileData.data(), (int)fileData.size(), outW, outH, &channels, 4); } unsigned char* LoadRawPixelsFromMemory(const unsigned char* data, size_t dataSize, int* outW, int* outH) { if (!data || dataSize == 0) return nullptr; int channels = 0; return stbi_load_from_memory(data, (int)dataSize, outW, outH, &channels, 4); } void FreeRawPixels(unsigned char* pixels) { if (pixels) stbi_image_free(pixels); } void DestroyTexture(ImTextureID texID) { if (!texID) return; #ifdef DRAGONX_USE_DX11 auto* srv = (ID3D11ShaderResourceView*)(intptr_t)texID; srv->Release(); #else GLuint tex = (GLuint)(intptr_t)texID; glDeleteTextures(1, &tex); #endif } } // namespace util } // namespace dragonx