2023-05-30 20:11:08 +02:00
// dear imgui: Renderer Backend for SDL_Renderer for SDL2
2021-09-21 17:12:30 +02:00
// (Requires: SDL 2.0.17+)
2023-05-30 20:11:08 +02:00
// Note how SDL_Renderer is an _optional_ component of SDL2.
2021-09-21 19:29:39 +02:00
// For a multi-platform app consider using e.g. SDL+DirectX on Windows and SDL+OpenGL on Linux/OSX.
2021-11-29 12:18:25 +01:00
// If your application will want to render any non trivial amount of graphics other than UI,
2023-05-30 20:11:08 +02:00
// please be aware that SDL_Renderer currently offers a limited graphic API to the end-user and
// it might be difficult to step out of those boundaries.
2021-09-21 19:29:39 +02:00
2021-09-21 17:12:30 +02:00
// Implemented features:
2021-09-21 19:29:39 +02:00
// [X] Renderer: User texture binding. Use 'SDL_Texture*' as ImTextureID. Read the FAQ about ImTextureID!
2023-02-07 12:24:42 +01:00
// [X] Renderer: Large meshes support (64k+ vertices) with 16-bit indices.
2021-09-21 17:12:30 +02:00
// You can copy and use unmodified imgui_impl_* files in your project. See examples/ folder for examples of using this.
2023-09-11 13:47:08 +02:00
// Prefer including the entire imgui/ repository into your project (either as a copy or as a submodule), and only build the backends you need.
// Learn about Dear ImGui:
// - FAQ https://dearimgui.com/faq
// - Getting Started https://dearimgui.com/getting-started
// - Documentation https://dearimgui.com/docs (same as your local docs/ folder).
// - Introduction, links and more at the top of imgui.cpp
2021-09-21 17:12:30 +02:00
// CHANGELOG
2024-05-14 19:10:31 +02:00
// 2024-05-14: *BREAKING CHANGE* ImGui_ImplSDLRenderer3_RenderDrawData() requires SDL_Renderer* passed as parameter.
2023-05-30 20:11:08 +02:00
// 2023-05-30: Renamed imgui_impl_sdlrenderer.h/.cpp to imgui_impl_sdlrenderer2.h/.cpp to accommodate for upcoming SDL3.
2022-10-11 12:22:29 +02:00
// 2022-10-11: Using 'nullptr' instead of 'NULL' as per our switch to C++11.
2021-12-21 05:27:48 +01:00
// 2021-12-21: Update SDL_RenderGeometryRaw() format to work with SDL 2.0.19.
2021-12-03 11:03:05 +01:00
// 2021-12-03: Added support for large mesh (64K+ vertices), enable ImGuiBackendFlags_RendererHasVtxOffset flag.
2021-10-06 13:41:29 +02:00
// 2021-10-06: Backup and restore modified ClipRect/Viewport.
2021-09-21 19:29:39 +02:00
// 2021-09-21: Initial version.
2021-09-21 17:12:30 +02:00
# include "imgui.h"
2023-07-13 11:27:52 +02:00
# ifndef IMGUI_DISABLE
2023-05-30 20:11:08 +02:00
# include "imgui_impl_sdlrenderer2.h"
2021-09-21 17:12:30 +02:00
# include <stdint.h> // intptr_t
2023-03-23 18:17:45 +01:00
// Clang warnings with -Weverything
# if defined(__clang__)
# pragma clang diagnostic push
# pragma clang diagnostic ignored "-Wsign-conversion" // warning: implicit conversion changes signedness
# endif
2021-09-21 19:29:39 +02:00
// SDL
# include <SDL.h>
# if !SDL_VERSION_ATLEAST(2,0,17)
# error This backend requires SDL 2.0.17+ because of SDL_RenderGeometry() function
2021-09-21 17:12:30 +02:00
# endif
2021-09-21 19:29:39 +02:00
// SDL_Renderer data
2023-05-30 20:15:31 +02:00
struct ImGui_ImplSDLRenderer2_Data
2021-09-21 17:12:30 +02:00
{
2024-05-14 19:10:31 +02:00
SDL_Renderer * Renderer ; // Main viewport's renderer
2021-09-21 19:29:39 +02:00
SDL_Texture * FontTexture ;
2024-05-14 19:10:31 +02:00
ImGui_ImplSDLRenderer2_Data ( ) { memset ( ( void * ) this , 0 , sizeof ( * this ) ) ; }
2021-09-21 17:12:30 +02:00
} ;
// Backend data stored in io.BackendRendererUserData to allow support for multiple Dear ImGui contexts
// It is STRONGLY preferred that you use docking branch with multi-viewports (== single Dear ImGui context + multiple windows) instead of multiple Dear ImGui contexts.
2023-05-30 20:15:31 +02:00
static ImGui_ImplSDLRenderer2_Data * ImGui_ImplSDLRenderer2_GetBackendData ( )
2021-09-21 17:12:30 +02:00
{
2023-05-30 20:15:31 +02:00
return ImGui : : GetCurrentContext ( ) ? ( ImGui_ImplSDLRenderer2_Data * ) ImGui : : GetIO ( ) . BackendRendererUserData : nullptr ;
2021-09-21 17:12:30 +02:00
}
// Functions
2023-05-30 20:15:31 +02:00
bool ImGui_ImplSDLRenderer2_Init ( SDL_Renderer * renderer )
2021-09-21 17:12:30 +02:00
{
ImGuiIO & io = ImGui : : GetIO ( ) ;
2024-05-13 15:07:13 +02:00
IMGUI_CHECKVERSION ( ) ;
2022-10-11 12:22:29 +02:00
IM_ASSERT ( io . BackendRendererUserData = = nullptr & & " Already initialized a renderer backend! " ) ;
IM_ASSERT ( renderer ! = nullptr & & " SDL_Renderer not initialized! " ) ;
2021-09-21 17:12:30 +02:00
// Setup backend capabilities flags
2023-05-30 20:15:31 +02:00
ImGui_ImplSDLRenderer2_Data * bd = IM_NEW ( ImGui_ImplSDLRenderer2_Data ) ( ) ;
2021-09-21 17:12:30 +02:00
io . BackendRendererUserData = ( void * ) bd ;
2023-05-30 20:11:08 +02:00
io . BackendRendererName = " imgui_impl_sdlrenderer2 " ;
2021-12-03 11:03:05 +01:00
io . BackendFlags | = ImGuiBackendFlags_RendererHasVtxOffset ; // We can honor the ImDrawCmd::VtxOffset field, allowing for large meshes.
2021-09-21 17:12:30 +02:00
2024-05-14 19:10:31 +02:00
bd - > Renderer = renderer ;
2021-09-21 19:29:39 +02:00
2021-09-21 17:12:30 +02:00
return true ;
}
2023-05-30 20:15:31 +02:00
void ImGui_ImplSDLRenderer2_Shutdown ( )
2021-09-21 17:12:30 +02:00
{
2023-05-30 20:15:31 +02:00
ImGui_ImplSDLRenderer2_Data * bd = ImGui_ImplSDLRenderer2_GetBackendData ( ) ;
2022-10-11 12:22:29 +02:00
IM_ASSERT ( bd ! = nullptr & & " No renderer backend to shutdown, or already shutdown? " ) ;
2021-09-21 19:29:39 +02:00
ImGuiIO & io = ImGui : : GetIO ( ) ;
2021-09-21 17:12:30 +02:00
2023-05-30 20:15:31 +02:00
ImGui_ImplSDLRenderer2_DestroyDeviceObjects ( ) ;
2021-09-21 17:12:30 +02:00
2022-10-11 12:22:29 +02:00
io . BackendRendererName = nullptr ;
io . BackendRendererUserData = nullptr ;
2023-04-17 14:47:15 +02:00
io . BackendFlags & = ~ ImGuiBackendFlags_RendererHasVtxOffset ;
2021-09-21 17:12:30 +02:00
IM_DELETE ( bd ) ;
}
2024-05-14 19:10:31 +02:00
static void ImGui_ImplSDLRenderer2_SetupRenderState ( SDL_Renderer * renderer )
2021-09-21 17:12:30 +02:00
{
2021-09-21 19:29:39 +02:00
// Clear out any viewports and cliprect set by the user
// FIXME: Technically speaking there are lots of other things we could backup/setup/restore during our render process.
2024-05-14 19:10:31 +02:00
SDL_RenderSetViewport ( renderer , nullptr ) ;
SDL_RenderSetClipRect ( renderer , nullptr ) ;
2021-09-21 17:12:30 +02:00
}
2023-05-30 20:15:31 +02:00
void ImGui_ImplSDLRenderer2_NewFrame ( )
2021-09-21 17:12:30 +02:00
{
2023-05-30 20:15:31 +02:00
ImGui_ImplSDLRenderer2_Data * bd = ImGui_ImplSDLRenderer2_GetBackendData ( ) ;
2024-05-07 16:53:03 +02:00
IM_ASSERT ( bd ! = nullptr & & " Context or backend not initialized! Did you call ImGui_ImplSDLRenderer2_Init()? " ) ;
2021-09-21 17:12:30 +02:00
2021-09-21 19:29:39 +02:00
if ( ! bd - > FontTexture )
2023-05-30 20:15:31 +02:00
ImGui_ImplSDLRenderer2_CreateDeviceObjects ( ) ;
2021-09-21 17:12:30 +02:00
}
2024-05-14 19:10:31 +02:00
void ImGui_ImplSDLRenderer2_RenderDrawData ( ImDrawData * draw_data , SDL_Renderer * renderer )
2021-09-21 17:12:30 +02:00
{
// If there's a scale factor set by the user, use that instead
2021-09-21 19:29:39 +02:00
// If the user has specified a scale factor to SDL_Renderer already via SDL_RenderSetScale(), SDL will scale whatever we pass
// to SDL_RenderGeometryRaw() by that scale factor. In that case we don't want to be also scaling it ourselves here.
float rsx = 1.0f ;
float rsy = 1.0f ;
2024-05-14 19:10:31 +02:00
SDL_RenderGetScale ( renderer , & rsx , & rsy ) ;
2021-09-21 19:29:39 +02:00
ImVec2 render_scale ;
render_scale . x = ( rsx = = 1.0f ) ? draw_data - > FramebufferScale . x : 1.0f ;
render_scale . y = ( rsy = = 1.0f ) ? draw_data - > FramebufferScale . y : 1.0f ;
2021-09-21 17:12:30 +02:00
// Avoid rendering when minimized, scale coordinates for retina displays (screen coordinates != framebuffer coordinates)
2021-09-21 19:29:39 +02:00
int fb_width = ( int ) ( draw_data - > DisplaySize . x * render_scale . x ) ;
int fb_height = ( int ) ( draw_data - > DisplaySize . y * render_scale . y ) ;
2021-09-21 17:12:30 +02:00
if ( fb_width = = 0 | | fb_height = = 0 )
return ;
2021-10-06 13:41:29 +02:00
// Backup SDL_Renderer state that will be modified to restore it afterwards
struct BackupSDLRendererState
{
SDL_Rect Viewport ;
2021-10-07 15:07:43 +02:00
bool ClipEnabled ;
2021-10-06 13:41:29 +02:00
SDL_Rect ClipRect ;
} ;
BackupSDLRendererState old = { } ;
2024-05-14 19:10:31 +02:00
old . ClipEnabled = SDL_RenderIsClipEnabled ( renderer ) = = SDL_TRUE ;
SDL_RenderGetViewport ( renderer , & old . Viewport ) ;
SDL_RenderGetClipRect ( renderer , & old . ClipRect ) ;
2021-10-06 13:41:29 +02:00
2021-09-21 17:12:30 +02:00
// Will project scissor/clipping rectangles into framebuffer space
ImVec2 clip_off = draw_data - > DisplayPos ; // (0,0) unless using multi-viewports
2021-09-21 19:29:39 +02:00
ImVec2 clip_scale = render_scale ;
2021-09-21 17:12:30 +02:00
2021-09-21 19:29:39 +02:00
// Render command lists
2024-05-14 19:10:31 +02:00
ImGui_ImplSDLRenderer2_SetupRenderState ( renderer ) ;
2021-09-21 19:29:39 +02:00
for ( int n = 0 ; n < draw_data - > CmdListsCount ; n + + )
{
2021-09-21 17:12:30 +02:00
const ImDrawList * cmd_list = draw_data - > CmdLists [ n ] ;
const ImDrawVert * vtx_buffer = cmd_list - > VtxBuffer . Data ;
const ImDrawIdx * idx_buffer = cmd_list - > IdxBuffer . Data ;
2021-09-21 19:29:39 +02:00
2021-09-21 17:12:30 +02:00
for ( int cmd_i = 0 ; cmd_i < cmd_list - > CmdBuffer . Size ; cmd_i + + )
{
const ImDrawCmd * pcmd = & cmd_list - > CmdBuffer [ cmd_i ] ;
if ( pcmd - > UserCallback )
{
// User callback, registered via ImDrawList::AddCallback()
// (ImDrawCallback_ResetRenderState is a special callback value used by the user to request the renderer to reset render state.)
2021-09-21 19:29:39 +02:00
if ( pcmd - > UserCallback = = ImDrawCallback_ResetRenderState )
2024-05-14 19:10:31 +02:00
ImGui_ImplSDLRenderer2_SetupRenderState ( renderer ) ;
2021-09-21 19:29:39 +02:00
else
2021-09-21 17:12:30 +02:00
pcmd - > UserCallback ( cmd_list , pcmd ) ;
}
else
{
// Project scissor/clipping rectangles into framebuffer space
2021-09-21 19:29:39 +02:00
ImVec2 clip_min ( ( pcmd - > ClipRect . x - clip_off . x ) * clip_scale . x , ( pcmd - > ClipRect . y - clip_off . y ) * clip_scale . y ) ;
ImVec2 clip_max ( ( pcmd - > ClipRect . z - clip_off . x ) * clip_scale . x , ( pcmd - > ClipRect . w - clip_off . y ) * clip_scale . y ) ;
if ( clip_min . x < 0.0f ) { clip_min . x = 0.0f ; }
if ( clip_min . y < 0.0f ) { clip_min . y = 0.0f ; }
2022-10-11 12:22:29 +02:00
if ( clip_max . x > ( float ) fb_width ) { clip_max . x = ( float ) fb_width ; }
if ( clip_max . y > ( float ) fb_height ) { clip_max . y = ( float ) fb_height ; }
2021-11-30 21:48:29 +01:00
if ( clip_max . x < = clip_min . x | | clip_max . y < = clip_min . y )
2021-09-21 19:29:39 +02:00
continue ;
SDL_Rect r = { ( int ) ( clip_min . x ) , ( int ) ( clip_min . y ) , ( int ) ( clip_max . x - clip_min . x ) , ( int ) ( clip_max . y - clip_min . y ) } ;
2024-05-14 19:10:31 +02:00
SDL_RenderSetClipRect ( renderer , & r ) ;
2021-09-21 19:29:39 +02:00
2023-11-09 16:44:39 +01:00
const float * xy = ( const float * ) ( const void * ) ( ( const char * ) ( vtx_buffer + pcmd - > VtxOffset ) + offsetof ( ImDrawVert , pos ) ) ;
const float * uv = ( const float * ) ( const void * ) ( ( const char * ) ( vtx_buffer + pcmd - > VtxOffset ) + offsetof ( ImDrawVert , uv ) ) ;
2021-12-21 05:27:48 +01:00
# if SDL_VERSION_ATLEAST(2,0,19)
2023-11-09 16:44:39 +01:00
const SDL_Color * color = ( const SDL_Color * ) ( const void * ) ( ( const char * ) ( vtx_buffer + pcmd - > VtxOffset ) + offsetof ( ImDrawVert , col ) ) ; // SDL 2.0.19+
2021-12-21 05:27:48 +01:00
# else
2023-11-09 16:44:39 +01:00
const int * color = ( const int * ) ( const void * ) ( ( const char * ) ( vtx_buffer + pcmd - > VtxOffset ) + offsetof ( ImDrawVert , col ) ) ; // SDL 2.0.17 and 2.0.18
2021-12-21 05:27:48 +01:00
# endif
2021-09-21 19:29:39 +02:00
// Bind texture, Draw
SDL_Texture * tex = ( SDL_Texture * ) pcmd - > GetTexID ( ) ;
2024-05-14 19:10:31 +02:00
SDL_RenderGeometryRaw ( renderer , tex ,
2021-09-21 19:29:39 +02:00
xy , ( int ) sizeof ( ImDrawVert ) ,
color , ( int ) sizeof ( ImDrawVert ) ,
uv , ( int ) sizeof ( ImDrawVert ) ,
2021-12-03 11:03:05 +01:00
cmd_list - > VtxBuffer . Size - pcmd - > VtxOffset ,
2021-09-21 19:29:39 +02:00
idx_buffer + pcmd - > IdxOffset , pcmd - > ElemCount , sizeof ( ImDrawIdx ) ) ;
2021-09-21 17:12:30 +02:00
}
}
}
2021-10-06 13:41:29 +02:00
// Restore modified SDL_Renderer state
2024-05-14 19:10:31 +02:00
SDL_RenderSetViewport ( renderer , & old . Viewport ) ;
SDL_RenderSetClipRect ( renderer , old . ClipEnabled ? & old . ClipRect : nullptr ) ;
2021-09-21 17:12:30 +02:00
}
// Called by Init/NewFrame/Shutdown
2023-05-30 20:15:31 +02:00
bool ImGui_ImplSDLRenderer2_CreateFontsTexture ( )
2021-09-21 17:12:30 +02:00
{
ImGuiIO & io = ImGui : : GetIO ( ) ;
2023-05-30 20:15:31 +02:00
ImGui_ImplSDLRenderer2_Data * bd = ImGui_ImplSDLRenderer2_GetBackendData ( ) ;
2021-09-21 19:29:39 +02:00
// Build texture atlas
2021-09-21 17:12:30 +02:00
unsigned char * pixels ;
int width , height ;
io . Fonts - > GetTexDataAsRGBA32 ( & pixels , & width , & height ) ; // Load as RGBA 32-bit (75% of the memory is wasted, but default font is so small) because it is more likely to be compatible with user's existing shaders. If your ImTextureId represent a higher-level concept than just a GL texture id, consider calling GetTexDataAsAlpha8() instead to save on GPU memory.
2021-09-21 19:29:39 +02:00
// Upload texture to graphics system
2022-04-07 14:28:08 +02:00
// (Bilinear sampling is required by default. Set 'io.Fonts->Flags |= ImFontAtlasFlags_NoBakedLines' or 'style.AntiAliasedLinesUseTex = false' to allow point/nearest sampling)
2024-05-14 19:10:31 +02:00
bd - > FontTexture = SDL_CreateTexture ( bd - > Renderer , SDL_PIXELFORMAT_ABGR8888 , SDL_TEXTUREACCESS_STATIC , width , height ) ;
2022-10-11 12:22:29 +02:00
if ( bd - > FontTexture = = nullptr )
2021-09-21 19:29:39 +02:00
{
2021-09-21 17:12:30 +02:00
SDL_Log ( " error creating texture " ) ;
return false ;
}
2022-10-11 12:22:29 +02:00
SDL_UpdateTexture ( bd - > FontTexture , nullptr , pixels , 4 * width ) ;
2021-09-21 17:12:30 +02:00
SDL_SetTextureBlendMode ( bd - > FontTexture , SDL_BLENDMODE_BLEND ) ;
2022-04-07 14:28:08 +02:00
SDL_SetTextureScaleMode ( bd - > FontTexture , SDL_ScaleModeLinear ) ;
2021-09-21 19:29:39 +02:00
2021-09-21 17:12:30 +02:00
// Store our identifier
io . Fonts - > SetTexID ( ( ImTextureID ) ( intptr_t ) bd - > FontTexture ) ;
return true ;
}
2023-05-30 20:15:31 +02:00
void ImGui_ImplSDLRenderer2_DestroyFontsTexture ( )
2021-09-21 17:12:30 +02:00
{
ImGuiIO & io = ImGui : : GetIO ( ) ;
2023-05-30 20:15:31 +02:00
ImGui_ImplSDLRenderer2_Data * bd = ImGui_ImplSDLRenderer2_GetBackendData ( ) ;
2021-09-21 19:29:39 +02:00
if ( bd - > FontTexture )
{
2021-09-21 17:12:30 +02:00
io . Fonts - > SetTexID ( 0 ) ;
SDL_DestroyTexture ( bd - > FontTexture ) ;
2022-10-11 12:22:29 +02:00
bd - > FontTexture = nullptr ;
2021-09-21 17:12:30 +02:00
}
}
2023-05-30 20:15:31 +02:00
bool ImGui_ImplSDLRenderer2_CreateDeviceObjects ( )
2021-09-21 17:12:30 +02:00
{
2023-05-30 20:15:31 +02:00
return ImGui_ImplSDLRenderer2_CreateFontsTexture ( ) ;
2021-09-21 17:12:30 +02:00
}
2023-05-30 20:15:31 +02:00
void ImGui_ImplSDLRenderer2_DestroyDeviceObjects ( )
2021-09-21 17:12:30 +02:00
{
2023-05-30 20:15:31 +02:00
ImGui_ImplSDLRenderer2_DestroyFontsTexture ( ) ;
2021-09-21 17:12:30 +02:00
}
2023-03-23 18:17:45 +01:00
2023-07-13 11:27:52 +02:00
//-----------------------------------------------------------------------------
2023-03-23 18:17:45 +01:00
# if defined(__clang__)
# pragma clang diagnostic pop
# endif
2023-07-13 11:27:52 +02:00
# endif // #ifndef IMGUI_DISABLE