mirror of
https://github.com/ocornut/imgui.git
synced 2026-03-30 13:05:23 -05:00
SDL_Renderer update code to match style of others and add LoadTextureFromMemory()
@@ -72,7 +72,7 @@ From the FAQ: "How can I display an image? What is ImTextureID, how does it work
|
||||
|
||||
Short explanation:
|
||||
- You may use functions such as `ImGui::Image()`, `ImGui::ImageButton()` or lower-level `ImDrawList::AddImage()` to emit draw calls that will use your own textures. Using `ImGui::GetBackgroundDrawList()` you may submit AddImage() calls that are not part of a specific ImGui window but displayed between your background contents and ImGui windows.
|
||||
- Actual textures are identified in a way that is up to the user/engine. Those identifiers are stored and passed as a `ImTextureID` value (which is no other than an opaque u64/void* type).
|
||||
- Actual textures are identified in a way that is up to the user/engine. Those identifiers are stored and passed as a `ImTextureID` value (which is no other than an opaque u64).
|
||||
- Loading image files from the disk and turning them into a texture is not within the scope of Dear ImGui (for a good reason). You can read documentations or tutorials on your graphics API to understand how to upload textures. Onward in this document you'll find examples.
|
||||
|
||||
Long explanation:
|
||||
@@ -80,12 +80,13 @@ Long explanation:
|
||||
- Each rendering function decides on a data type to represent "textures". The concept of what is a "texture" is entirely tied to your underlying engine/graphics API. We carry the information to identify a "texture" in the ImTextureID type. ImTextureID default to ImU64, aka 8 bytes worth of data: just enough to store 1 pointer or 1 integer of your choice (note: before 1.91.3, ImTextureID defaulted to being a void*, which couldn't fit e.g. a 64-bit descriptor in 32-bits builds). Dear ImGui doesn't know or understand what you are storing in ImTextureID, it merely pass ImTextureID values until they reach your rendering function.
|
||||
- In the examples/ bindings, for each graphics API binding we decided on a type that is likely to be a good representation for specifying an image from the end-user perspective. This is what the _examples_ rendering functions are using:
|
||||
```
|
||||
OpenGL: ImTextureID = GLuint (see ImGui_ImplOpenGL3_RenderDrawData() in imgui_impl_opengl3.cpp)
|
||||
DirectX9: ImTextureID = LPDIRECT3DTEXTURE9 (see ImGui_ImplDX9_RenderDrawData() in imgui_impl_dx9.cpp)
|
||||
DirectX11: ImTextureID = ID3D11ShaderResourceView* (see ImGui_ImplDX11_RenderDrawData() in imgui_impl_dx11.cpp)
|
||||
DirectX12: ImTextureID = D3D12_GPU_DESCRIPTOR_HANDLE (see ImGui_ImplDX12_RenderDrawData() in imgui_impl_dx12.cpp)
|
||||
Vulkan: ImTextureID = VkDescriptorSet (see ImGui_ImplVulkan_RenderDrawData() in imgui_impl_vulkan.cpp)
|
||||
WebGPU: ImTextureID = WGPUTextureView (see ImGui_ImplWGPU_RenderDrawData() in imgui_impl_wgpu.cpp)
|
||||
OpenGL: ImTextureID = GLuint (see ImGui_ImplOpenGL3_RenderDrawData() in imgui_impl_opengl3.cpp)
|
||||
DirectX9: ImTextureID = LPDIRECT3DTEXTURE9 (see ImGui_ImplDX9_RenderDrawData() in imgui_impl_dx9.cpp)
|
||||
DirectX11: ImTextureID = ID3D11ShaderResourceView* (see ImGui_ImplDX11_RenderDrawData() in imgui_impl_dx11.cpp)
|
||||
DirectX12: ImTextureID = D3D12_GPU_DESCRIPTOR_HANDLE (see ImGui_ImplDX12_RenderDrawData() in imgui_impl_dx12.cpp)
|
||||
SDL_Renderer: ImTextureID = SDL_Texture* (see ImGui_ImplSDLRenderer2_RenderDrawData() in imgui_impl_sdlrenderer2.cpp)
|
||||
Vulkan: ImTextureID = VkDescriptorSet (see ImGui_ImplVulkan_RenderDrawData() in imgui_impl_vulkan.cpp)
|
||||
WebGPU: ImTextureID = WGPUTextureView (see ImGui_ImplWGPU_RenderDrawData() in imgui_impl_wgpu.cpp)
|
||||
```
|
||||
For example, in the OpenGL example binding we store raw OpenGL texture identifier (GLuint) inside ImTextureID. Whereas in the DirectX11 example binding we store a pointer to ID3D11ShaderResourceView inside ImTextureID, which is a higher-level structure tying together both the texture and information about its format and how to read it.
|
||||
- If you have a custom engine built over e.g. OpenGL, instead of passing GLuint around you may decide to use a high-level data type to carry information about the texture as well as how to display it (shaders, etc.). The decision of what to use as ImTextureID can always be made better knowing how your codebase is designed. If your engine has high-level data types for "textures" and "material" then you may want to use them. If you are starting with OpenGL or DirectX or Vulkan and haven't built much of a rendering engine over them, keeping the default ImTextureID representation suggested by the example bindings is probably the best choice. (Advanced users may also decide to keep a low-level type in ImTextureID, and use ImDrawList callback and pass information to their renderer)
|
||||
@@ -597,60 +598,85 @@ ImGui::End();
|
||||
|
||||
## Example for SDL_Renderer users
|
||||
|
||||
ImTextureID has the type SDL_Texture* when we use the SDL_Renderer backend.
|
||||
|
||||
We will here use [stb_image.h](https://github.com/nothings/stb/blob/master/stb_image.h) to load images from disk.
|
||||
|
||||
Add at the top of one of your source files:
|
||||
|
||||
```cpp
|
||||
bool LoadTextureFromFile(const char* filename, SDL_Texture** texture_ptr, int& width, int& height, SDL_Renderer* renderer) {
|
||||
int channels;
|
||||
unsigned char* data = stbi_load(filename, &width, &height, &channels, 0);
|
||||
#define _CRT_SECURE_NO_WARNINGS
|
||||
#define STB_IMAGE_IMPLEMENTATION
|
||||
#include "stb_image.h"
|
||||
|
||||
if (data == nullptr) {
|
||||
bool LoadTextureFromMemory(const void* data, size_t data_size, SDL_Renderer* renderer, SDL_Texture** out_texture, int* out_width, int* out_height)
|
||||
{
|
||||
int image_width = 0;
|
||||
int image_height = 0;
|
||||
int channels = 4;
|
||||
unsigned char* image_data = stbi_load_from_memory((const unsigned char*)data, (int)data_size, &image_width, &image_height, NULL, 4);
|
||||
if (image_data == nullptr)
|
||||
{
|
||||
fprintf(stderr, "Failed to load image: %s\n", stbi_failure_reason());
|
||||
return false;
|
||||
}
|
||||
|
||||
SDL_Surface* surface = SDL_CreateRGBSurfaceFrom((void*)data, width, height, channels * 8, channels * width,
|
||||
0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000);
|
||||
|
||||
if (surface == nullptr) {
|
||||
SDL_Surface* surface = SDL_CreateRGBSurfaceFrom((void*)image_data, image_width, image_height, channels * 8, channels * image_width, 0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000);
|
||||
if (surface == nullptr)
|
||||
{
|
||||
fprintf(stderr, "Failed to create SDL surface: %s\n", SDL_GetError());
|
||||
return false;
|
||||
}
|
||||
|
||||
*texture_ptr = SDL_CreateTextureFromSurface(renderer, surface);
|
||||
|
||||
if ((*texture_ptr) == nullptr) {
|
||||
SDL_Texture* texture = SDL_CreateTextureFromSurface(renderer, surface);
|
||||
if (texture == nullptr)
|
||||
fprintf(stderr, "Failed to create SDL texture: %s\n", SDL_GetError());
|
||||
}
|
||||
|
||||
*out_texture = texture;
|
||||
*out_width = image_width;
|
||||
*out_height = image_height;
|
||||
|
||||
SDL_FreeSurface(surface);
|
||||
stbi_image_free(data);
|
||||
stbi_image_free(image_data);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Open and read a file, then forward to LoadTextureFromMemory()
|
||||
bool LoadTextureFromFile(const char* file_name, SDL_Renderer* renderer, SDL_Texture** out_texture, int* out_width, int* out_height)
|
||||
{
|
||||
FILE* f = fopen(file_name, "rb");
|
||||
if (f == NULL)
|
||||
return false;
|
||||
fseek(f, 0, SEEK_END);
|
||||
size_t file_size = (size_t)ftell(f);
|
||||
if (file_size == -1)
|
||||
return false;
|
||||
fseek(f, 0, SEEK_SET);
|
||||
void* file_data = IM_ALLOC(file_size);
|
||||
fread(file_data, 1, file_size, f);
|
||||
bool ret = LoadTextureFromMemory(file_data, file_size, renderer, out_texture, out_width, out_height);
|
||||
IM_FREE(file_data);
|
||||
return ret;
|
||||
}
|
||||
```
|
||||
|
||||
We can then load our texture after initialising SDL:
|
||||
Load our texture after initialising SDL:
|
||||
```cpp
|
||||
SDL_Texture* my_texture;
|
||||
int my_image_width, my_image_height;
|
||||
bool ret = LoadTextureFromFile("MyImage01.jpg", &my_texture, my_image_width, my_image_height, renderer);
|
||||
bool ret = LoadTextureFromFile("MyImage01.jpg", renderer, &my_texture, &my_image_width, &my_image_height);
|
||||
IM_ASSERT(ret);
|
||||
```
|
||||
|
||||
And finally, use the image with Dear ImGui:
|
||||
Now that we have a SDL_Texture and its dimensions, we can display it in our main loop:
|
||||
```cpp
|
||||
ImGui::Begin("SDL2/SDL_Renderer Texture Test");
|
||||
ImGui::Text("pointer = %p", my_texture);
|
||||
ImGui::Text("size = %d x %d", my_image_width, my_image_height);
|
||||
ImGui::Image((ImTextureID)(intptr_t)my_texture, ImVec2(my_image_width, my_image_height));
|
||||
ImGui::Image((ImTextureID)(intptr_t)my_texture, ImVec2((float)my_image_width, (float)my_image_height));
|
||||
ImGui::End();
|
||||
```
|
||||
|
||||

|
||||

|
||||
|
||||
##### [Return to Index](#index)
|
||||
|
||||
@@ -1059,7 +1085,7 @@ Now that we have a WebGPU texture view and its dimensions, we can display it in
|
||||
ImGui::Begin("WebGPU Texture Test");
|
||||
ImGui::Text("pointer = %p", my_texture_view);
|
||||
ImGui::Text("size = %d x %d", my_image_width, my_image_height);
|
||||
ImGui::Image((void *)my_texture_view, ImVec2(my_image_width, my_image_height));
|
||||
ImGui::Image((ImTextureID)(intptr_t)my_texture_view, ImVec2(my_image_width, my_image_height));
|
||||
ImGui::End();
|
||||
```
|
||||
|
||||
|
||||
Reference in New Issue
Block a user