| 1 | |
| 2 | |
| 3 | |
| 4 | |
| 5 | |
| 6 | |
| 7 | |
| 8 | |
| 9 | |
| 10 | |
| 11 | |
| 12 | |
| 13 | |
| 14 | |
| 15 | |
| 16 | |
| 17 | |
| 18 | |
| 19 | |
| 20 | |
| 21 | |
| 22 | |
| 23 | |
| 24 | |
| 25 | |
| 26 | |
| 27 | |
| 28 | |
| 29 | |
| 30 | |
| 31 | |
| 32 | |
| 33 | |
| 34 | |
| 35 | |
| 36 | |
| 37 | |
| 38 | |
| 39 | |
| 40 | |
| 41 | #include "imgui.h" |
| 42 | #ifndef IMGUI_DISABLE |
| 43 | #include "imgui_impl_sdlrenderer3.h" |
| 44 | #include <stdint.h> |
| 45 | |
| 46 | |
| 47 | #if defined(__clang__) |
| 48 | #pragma clang diagnostic push |
| 49 | #pragma clang diagnostic ignored "-Wsign-conversion" |
| 50 | #elif defined(__GNUC__) |
| 51 | #pragma GCC diagnostic ignored "-Wfloat-equal" |
| 52 | #endif |
| 53 | |
| 54 | |
| 55 | #include <SDL3/SDL.h> |
| 56 | #if !SDL_VERSION_ATLEAST(3,0,0) |
| 57 | #error This backend requires SDL 3.0.0+ |
| 58 | #endif |
| 59 | |
| 60 | |
| 61 | struct ImGui_ImplSDLRenderer3_Data |
| 62 | { |
| 63 | SDL_Renderer* Renderer; |
| 64 | ImVector<SDL_FColor> ColorBuffer; |
| 65 | |
| 66 | |
| 67 | SDL_ScaleMode CurrentScaleMode; |
| 68 | |
| 69 | ImGui_ImplSDLRenderer3_Data() { memset((void*)this, 0, sizeof(*this)); } |
| 70 | }; |
| 71 | |
| 72 | |
| 73 | |
| 74 | static ImGui_ImplSDLRenderer3_Data* ImGui_ImplSDLRenderer3_GetBackendData() |
| 75 | { |
| 76 | return ImGui::GetCurrentContext() ? (ImGui_ImplSDLRenderer3_Data*)ImGui::GetIO().BackendRendererUserData : nullptr; |
| 77 | } |
| 78 | |
| 79 | |
| 80 | static void ImGui_ImplSDLRenderer3_SetupRenderState(SDL_Renderer* renderer) |
| 81 | { |
| 82 | |
| 83 | |
| 84 | SDL_SetRenderViewport(renderer, nullptr); |
| 85 | SDL_SetRenderClipRect(renderer, nullptr); |
| 86 | } |
| 87 | |
| 88 | void ImGui_ImplSDLRenderer3_NewFrame() |
| 89 | { |
| 90 | ImGui_ImplSDLRenderer3_Data* bd = ImGui_ImplSDLRenderer3_GetBackendData(); |
| 91 | IM_ASSERT(bd != nullptr && "Context or backend not initialized! Did you call ImGui_ImplSDLRenderer3_Init()?"); |
| 92 | IM_UNUSED(bd); |
| 93 | } |
| 94 | |
| 95 | |
| 96 | static int SDL_RenderGeometryRaw8BitColor(SDL_Renderer* renderer, ImVector<SDL_FColor>& colors_out, SDL_Texture* texture, const float* xy, int xy_stride, const SDL_Color* color, int color_stride, const float* uv, int uv_stride, int num_vertices, const void* indices, int num_indices, int size_indices) |
| 97 | { |
| 98 | const Uint8* color2 = (const Uint8*)color; |
| 99 | colors_out.resize(num_vertices); |
| 100 | SDL_FColor* color3 = colors_out.Data; |
| 101 | for (int i = 0; i < num_vertices; i++) |
| 102 | { |
| 103 | color3[i].r = color->r / 255.0f; |
| 104 | color3[i].g = color->g / 255.0f; |
| 105 | color3[i].b = color->b / 255.0f; |
| 106 | color3[i].a = color->a / 255.0f; |
| 107 | color2 += color_stride; |
| 108 | color = (const SDL_Color*)color2; |
| 109 | } |
| 110 | return SDL_RenderGeometryRaw(renderer, texture, xy, xy_stride, color3, sizeof(*color3), uv, uv_stride, num_vertices, indices, num_indices, size_indices); |
| 111 | } |
| 112 | |
| 113 | |
| 114 | static void ImGui_ImplSDLRenderer3_DrawCallback_ResetRenderState(const ImDrawList*, const ImDrawCmd*) {} |
| 115 | static void ImGui_ImplSDLRenderer3_DrawCallback_SetSamplerLinear(const ImDrawList*, const ImDrawCmd*) { ImGui_ImplSDLRenderer3_Data* bd = ImGui_ImplSDLRenderer3_GetBackendData(); bd->CurrentScaleMode = SDL_SCALEMODE_LINEAR; } |
| 116 | static void ImGui_ImplSDLRenderer3_DrawCallback_SetSamplerNearest(const ImDrawList*, const ImDrawCmd*) { ImGui_ImplSDLRenderer3_Data* bd = ImGui_ImplSDLRenderer3_GetBackendData(); bd->CurrentScaleMode = SDL_SCALEMODE_NEAREST; } |
| 117 | |
| 118 | void ImGui_ImplSDLRenderer3_RenderDrawData(ImDrawData* draw_data, SDL_Renderer* renderer) |
| 119 | { |
| 120 | ImGui_ImplSDLRenderer3_Data* bd = ImGui_ImplSDLRenderer3_GetBackendData(); |
| 121 | |
| 122 | |
| 123 | |
| 124 | |
| 125 | float rsx = 1.0f; |
| 126 | float rsy = 1.0f; |
| 127 | SDL_GetRenderScale(renderer, &rsx, &rsy); |
| 128 | ImVec2 render_scale; |
| 129 | render_scale.x = (rsx == 1.0f) ? draw_data->FramebufferScale.x : 1.0f; |
| 130 | render_scale.y = (rsy == 1.0f) ? draw_data->FramebufferScale.y : 1.0f; |
| 131 | |
| 132 | |
| 133 | int fb_width = (int)(draw_data->DisplaySize.x * render_scale.x); |
| 134 | int fb_height = (int)(draw_data->DisplaySize.y * render_scale.y); |
| 135 | if (fb_width == 0 || fb_height == 0) |
| 136 | return; |
| 137 | |
| 138 | |
| 139 | |
| 140 | if (draw_data->Textures != nullptr) |
| 141 | for (ImTextureData* tex : *draw_data->Textures) |
| 142 | if (tex->Status != ImTextureStatus_OK) |
| 143 | ImGui_ImplSDLRenderer3_UpdateTexture(tex); |
| 144 | |
| 145 | |
| 146 | struct BackupSDLRendererState |
| 147 | { |
| 148 | SDL_Rect Viewport; |
| 149 | bool ViewportEnabled; |
| 150 | bool ClipEnabled; |
| 151 | SDL_Rect ClipRect; |
| 152 | }; |
| 153 | BackupSDLRendererState old = {}; |
| 154 | old.ViewportEnabled = SDL_RenderViewportSet(renderer); |
| 155 | old.ClipEnabled = SDL_RenderClipEnabled(renderer); |
| 156 | SDL_GetRenderViewport(renderer, &old.Viewport); |
| 157 | SDL_GetRenderClipRect(renderer, &old.ClipRect); |
| 158 | |
| 159 | |
| 160 | ImGui_ImplSDLRenderer3_SetupRenderState(renderer); |
| 161 | |
| 162 | |
| 163 | ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); |
| 164 | ImGui_ImplSDLRenderer3_RenderState render_state; |
| 165 | render_state.Renderer = renderer; |
| 166 | platform_io.Renderer_RenderState = &render_state; |
| 167 | |
| 168 | |
| 169 | ImVec2 clip_off = draw_data->DisplayPos; |
| 170 | ImVec2 clip_scale = render_scale; |
| 171 | |
| 172 | |
| 173 | for (const ImDrawList* draw_list : draw_data->CmdLists) |
| 174 | { |
| 175 | const ImDrawVert* vtx_buffer = draw_list->VtxBuffer.Data; |
| 176 | const ImDrawIdx* idx_buffer = draw_list->IdxBuffer.Data; |
| 177 | |
| 178 | for (int cmd_i = 0; cmd_i < draw_list->CmdBuffer.Size; cmd_i++) |
| 179 | { |
| 180 | const ImDrawCmd* pcmd = &draw_list->CmdBuffer[cmd_i]; |
| 181 | if (pcmd->UserCallback) |
| 182 | { |
| 183 | |
| 184 | if (pcmd->UserCallback == ImGui_ImplSDLRenderer3_DrawCallback_ResetRenderState) |
| 185 | ImGui_ImplSDLRenderer3_SetupRenderState(renderer); |
| 186 | else |
| 187 | pcmd->UserCallback(draw_list, pcmd); |
| 188 | } |
| 189 | else |
| 190 | { |
| 191 | |
| 192 | ImVec2 clip_min((pcmd->ClipRect.x - clip_off.x) * clip_scale.x, (pcmd->ClipRect.y - clip_off.y) * clip_scale.y); |
| 193 | ImVec2 clip_max((pcmd->ClipRect.z - clip_off.x) * clip_scale.x, (pcmd->ClipRect.w - clip_off.y) * clip_scale.y); |
| 194 | if (clip_min.x < 0.0f) { clip_min.x = 0.0f; } |
| 195 | if (clip_min.y < 0.0f) { clip_min.y = 0.0f; } |
| 196 | if (clip_max.x > (float)fb_width) { clip_max.x = (float)fb_width; } |
| 197 | if (clip_max.y > (float)fb_height) { clip_max.y = (float)fb_height; } |
| 198 | if (clip_max.x <= clip_min.x || clip_max.y <= clip_min.y) |
| 199 | continue; |
| 200 | |
| 201 | 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) }; |
| 202 | SDL_SetRenderClipRect(renderer, &r); |
| 203 | |
| 204 | const float* xy = (const float*)(const void*)((const char*)(vtx_buffer + pcmd->VtxOffset) + offsetof(ImDrawVert, pos)); |
| 205 | const float* uv = (const float*)(const void*)((const char*)(vtx_buffer + pcmd->VtxOffset) + offsetof(ImDrawVert, uv)); |
| 206 | const SDL_Color* color = (const SDL_Color*)(const void*)((const char*)(vtx_buffer + pcmd->VtxOffset) + offsetof(ImDrawVert, col)); |
| 207 | |
| 208 | |
| 209 | SDL_Texture* tex = (SDL_Texture*)pcmd->GetTexID(); |
| 210 | SDL_SetTextureScaleMode(tex, bd->CurrentScaleMode); |
| 211 | SDL_RenderGeometryRaw8BitColor(renderer, bd->ColorBuffer, tex, |
| 212 | xy, (int)sizeof(ImDrawVert), |
| 213 | color, (int)sizeof(ImDrawVert), |
| 214 | uv, (int)sizeof(ImDrawVert), |
| 215 | draw_list->VtxBuffer.Size - pcmd->VtxOffset, |
| 216 | idx_buffer + pcmd->IdxOffset, pcmd->ElemCount, sizeof(ImDrawIdx)); |
| 217 | } |
| 218 | } |
| 219 | } |
| 220 | platform_io.Renderer_RenderState = nullptr; |
| 221 | |
| 222 | |
| 223 | SDL_SetRenderViewport(renderer, old.ViewportEnabled ? &old.Viewport : nullptr); |
| 224 | SDL_SetRenderClipRect(renderer, old.ClipEnabled ? &old.ClipRect : nullptr); |
| 225 | } |
| 226 | |
| 227 | void ImGui_ImplSDLRenderer3_UpdateTexture(ImTextureData* tex) |
| 228 | { |
| 229 | ImGui_ImplSDLRenderer3_Data* bd = ImGui_ImplSDLRenderer3_GetBackendData(); |
| 230 | |
| 231 | if (tex->Status == ImTextureStatus_WantCreate) |
| 232 | { |
| 233 | |
| 234 | |
| 235 | IM_ASSERT(tex->TexID == ImTextureID_Invalid && tex->BackendUserData == nullptr); |
| 236 | IM_ASSERT(tex->Format == ImTextureFormat_RGBA32); |
| 237 | |
| 238 | |
| 239 | |
| 240 | SDL_Texture* sdl_texture = SDL_CreateTexture(bd->Renderer, SDL_PIXELFORMAT_RGBA32, SDL_TEXTUREACCESS_STATIC, tex->Width, tex->Height); |
| 241 | IM_ASSERT(sdl_texture != nullptr && "Backend failed to create texture!"); |
| 242 | SDL_UpdateTexture(sdl_texture, nullptr, tex->GetPixels(), tex->GetPitch()); |
| 243 | SDL_SetTextureBlendMode(sdl_texture, SDL_BLENDMODE_BLEND); |
| 244 | SDL_SetTextureScaleMode(sdl_texture, SDL_SCALEMODE_LINEAR); |
| 245 | |
| 246 | |
| 247 | tex->SetTexID((ImTextureID)(intptr_t)sdl_texture); |
| 248 | tex->SetStatus(ImTextureStatus_OK); |
| 249 | } |
| 250 | else if (tex->Status == ImTextureStatus_WantUpdates) |
| 251 | { |
| 252 | |
| 253 | |
| 254 | SDL_Texture* sdl_texture = (SDL_Texture*)(intptr_t)tex->TexID; |
| 255 | for (ImTextureRect& r : tex->Updates) |
| 256 | { |
| 257 | SDL_Rect sdl_r = { r.x, r.y, r.w, r.h }; |
| 258 | SDL_UpdateTexture(sdl_texture, &sdl_r, tex->GetPixelsAt(r.x, r.y), tex->GetPitch()); |
| 259 | } |
| 260 | tex->SetStatus(ImTextureStatus_OK); |
| 261 | } |
| 262 | else if (tex->Status == ImTextureStatus_WantDestroy) |
| 263 | { |
| 264 | if (tex->TexID != ImTextureID_Invalid) |
| 265 | if (SDL_Texture* sdl_texture = (SDL_Texture*)(intptr_t)tex->TexID) |
| 266 | SDL_DestroyTexture(sdl_texture); |
| 267 | |
| 268 | |
| 269 | tex->SetTexID(ImTextureID_Invalid); |
| 270 | tex->SetStatus(ImTextureStatus_Destroyed); |
| 271 | } |
| 272 | } |
| 273 | |
| 274 | void ImGui_ImplSDLRenderer3_CreateDeviceObjects() |
| 275 | { |
| 276 | } |
| 277 | |
| 278 | void ImGui_ImplSDLRenderer3_DestroyDeviceObjects() |
| 279 | { |
| 280 | |
| 281 | for (ImTextureData* tex : ImGui::GetPlatformIO().Textures) |
| 282 | if (tex->RefCount == 1) |
| 283 | { |
| 284 | tex->SetStatus(ImTextureStatus_WantDestroy); |
| 285 | ImGui_ImplSDLRenderer3_UpdateTexture(tex); |
| 286 | } |
| 287 | } |
| 288 | |
| 289 | bool ImGui_ImplSDLRenderer3_Init(SDL_Renderer* renderer) |
| 290 | { |
| 291 | ImGuiIO& io = ImGui::GetIO(); |
| 292 | IMGUI_CHECKVERSION(); |
| 293 | IM_ASSERT(io.BackendRendererUserData == nullptr && "Already initialized a renderer backend!"); |
| 294 | IM_ASSERT(renderer != nullptr && "SDL_Renderer not initialized!"); |
| 295 | |
| 296 | |
| 297 | ImGui_ImplSDLRenderer3_Data* bd = IM_NEW(ImGui_ImplSDLRenderer3_Data)(); |
| 298 | io.BackendRendererUserData = (void*)bd; |
| 299 | io.BackendRendererName = "imgui_impl_sdlrenderer3"; |
| 300 | io.BackendFlags |= ImGuiBackendFlags_RendererHasVtxOffset; |
| 301 | io.BackendFlags |= ImGuiBackendFlags_RendererHasTextures; |
| 302 | |
| 303 | ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); |
| 304 | platform_io.DrawCallback_ResetRenderState = ImGui_ImplSDLRenderer3_DrawCallback_ResetRenderState; |
| 305 | platform_io.DrawCallback_SetSamplerLinear = ImGui_ImplSDLRenderer3_DrawCallback_SetSamplerLinear; |
| 306 | platform_io.DrawCallback_SetSamplerNearest = ImGui_ImplSDLRenderer3_DrawCallback_SetSamplerNearest; |
| 307 | |
| 308 | bd->Renderer = renderer; |
| 309 | |
| 310 | return true; |
| 311 | } |
| 312 | |
| 313 | void ImGui_ImplSDLRenderer3_Shutdown() |
| 314 | { |
| 315 | ImGui_ImplSDLRenderer3_Data* bd = ImGui_ImplSDLRenderer3_GetBackendData(); |
| 316 | IM_ASSERT(bd != nullptr && "No renderer backend to shutdown, or already shutdown?"); |
| 317 | ImGuiIO& io = ImGui::GetIO(); |
| 318 | ImGuiPlatformIO& platform_io = ImGui::GetPlatformIO(); |
| 319 | |
| 320 | ImGui_ImplSDLRenderer3_DestroyDeviceObjects(); |
| 321 | |
| 322 | io.BackendRendererName = nullptr; |
| 323 | io.BackendRendererUserData = nullptr; |
| 324 | io.BackendFlags &= ~(ImGuiBackendFlags_RendererHasVtxOffset | ImGuiBackendFlags_RendererHasTextures); |
| 325 | platform_io.ClearRendererHandlers(); |
| 326 | IM_DELETE(bd); |
| 327 | } |
| 328 | |
| 329 | |
| 330 | |
| 331 | #if defined(__clang__) |
| 332 | #pragma clang diagnostic pop |
| 333 | #endif |
| 334 | |
| 335 | #endif |