// main.cpp // Example program using imgui_impl_cpu backend #include "imgui.h" #include "imgui_impl_cpu.h" #include "imgui_impl_sdl2.h" #include #include #include #include #ifdef __EMSCRIPTEN__ #include "../libs/emscripten/emscripten_mainloop_stub.h" #endif struct ExampleTimingHistory { double Samples[120]; int Count; int Offset; ExampleTimingHistory() : Count(0), Offset(0) { memset(Samples, 0, sizeof(Samples)); } void AddSample(double value_ms) { Samples[Offset] = value_ms; Offset = (Offset + 1) % IM_ARRAYSIZE(Samples); if (Count < IM_ARRAYSIZE(Samples)) Count++; } double GetAverageMs() const { if (Count == 0) return 0.0; double total = 0.0; for (int i = 0; i < Count; i++) total += Samples[i]; return total / (double)Count; } }; struct ExampleFramebuffer { ImVector Storage; ImVector Palette; ImGui_ImplCPU_PixelFormat Format; ImGui_ImplCPU_Framebuffer CPU; SDL_Surface* Surface; Uint32 SDLFormat; ExampleFramebuffer() { memset((void*)this, 0, sizeof(*this)); } }; static double ExampleGetTimeMs() { typedef std::chrono::steady_clock Clock; return std::chrono::duration(Clock::now().time_since_epoch()).count(); } static void InitCPUFormatFromSDL(ImGui_ImplCPU_PixelFormat* out, ImVector* out_palette, const SDL_PixelFormat* format) { memset(out, 0, sizeof(*out)); out->BitsPerPixel = format->BitsPerPixel; out->BytesPerPixel = format->BytesPerPixel; out->Rmask = format->Rmask; out->Gmask = format->Gmask; out->Bmask = format->Bmask; out->Amask = format->Amask; out->Rshift = (ImU8)format->Rshift; out->Gshift = (ImU8)format->Gshift; out->Bshift = (ImU8)format->Bshift; out->Ashift = (ImU8)format->Ashift; out->Rloss = (ImU8)format->Rloss; out->Gloss = (ImU8)format->Gloss; out->Bloss = (ImU8)format->Bloss; out->Aloss = (ImU8)format->Aloss; out->Palette = nullptr; out->PaletteSize = 0; out->MapColorFn = nullptr; out_palette->clear(); if (format->palette != nullptr && format->palette->colors != nullptr && format->palette->ncolors > 0) { out_palette->resize(format->palette->ncolors); for (int i = 0; i < format->palette->ncolors; i++) { const SDL_Color& c = format->palette->colors[i]; (*out_palette)[i] = IM_COL32(c.r, c.g, c.b, c.a); } out->Palette = out_palette->Data; out->PaletteSize = out_palette->Size; } } static bool RecreateFramebuffer(ExampleFramebuffer* framebuffer, SDL_Surface* window_surface) { if (framebuffer->Surface != nullptr) { SDL_FreeSurface(framebuffer->Surface); framebuffer->Surface = nullptr; } const int width = window_surface->w; const int height = window_surface->h; const int bytes_per_pixel = window_surface->format->BytesPerPixel; if (width <= 0 || height <= 0 || bytes_per_pixel <= 0) return false; InitCPUFormatFromSDL(&framebuffer->Format, &framebuffer->Palette, window_surface->format); framebuffer->Storage.resize(width * height * bytes_per_pixel); framebuffer->CPU.Pixels = framebuffer->Storage.Data; framebuffer->CPU.Width = width; framebuffer->CPU.Height = height; framebuffer->CPU.Pitch = width * bytes_per_pixel; framebuffer->CPU.Format = &framebuffer->Format; framebuffer->SDLFormat = window_surface->format->format; framebuffer->Surface = SDL_CreateRGBSurfaceWithFormatFrom(framebuffer->CPU.Pixels, width, height, window_surface->format->BitsPerPixel, framebuffer->CPU.Pitch, window_surface->format->format); return framebuffer->Surface != nullptr; } int main(int, char**) { // Setup SDL if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER | SDL_INIT_GAMECONTROLLER) != 0) { printf("Error: %s\n", SDL_GetError()); return -1; } // From 2.0.18: Enable native IME. #ifdef SDL_HINT_IME_SHOW_UI SDL_SetHint(SDL_HINT_IME_SHOW_UI, "1"); #endif // Create window float main_scale = ImGui_ImplSDL2_GetContentScaleForDisplay(0); SDL_WindowFlags window_flags = (SDL_WindowFlags)(SDL_WINDOW_RESIZABLE | SDL_WINDOW_ALLOW_HIGHDPI | SDL_WINDOW_SHOWN); SDL_Window* window = SDL_CreateWindow("Dear ImGui SDL2+CPU Example", SDL_WINDOWPOS_CENTERED, SDL_WINDOWPOS_CENTERED, (int)(1280 * main_scale), (int)(720 * main_scale), window_flags); if (!window) { printf("Error creating window: %s\n", SDL_GetError()); return -1; } SDL_Surface* window_surface = SDL_GetWindowSurface(window); if (!window_surface) { printf("Error getting window surface: %s\n", SDL_GetError()); return -1; } int win_w = window_surface->w; int win_h = window_surface->h; ExampleFramebuffer framebuffer; if (!RecreateFramebuffer(&framebuffer, window_surface)) { printf("Error creating framebuffer: %s\n", SDL_GetError()); return -1; } // Setup Dear ImGui context IMGUI_CHECKVERSION(); ImGui::CreateContext(); ImGuiIO& io = ImGui::GetIO(); (void)io; io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls io.ConfigFlags |= ImGuiConfigFlags_NavEnableGamepad; // Enable Gamepad Controls #ifdef __EMSCRIPTEN__ io.IniFilename = nullptr; #endif // Setup Dear ImGui style ImGui::StyleColorsDark(); // Setup scaling ImGuiStyle& style = ImGui::GetStyle(); style.ScaleAllSizes(main_scale); // Bake a fixed style scale. style.FontScaleDpi = main_scale; // Setup Platform/Renderer backends ImGui_ImplSDL2_InitForOther(window); ImGui_ImplSDL2_SetGamepadMode(ImGui_ImplSDL2_GamepadMode_AutoFirst, nullptr, 0); ImGui_ImplCPU_Init(); // Our state bool show_demo_window = true; bool show_another_window = false; ImVec4 clear_color = ImVec4(0.45f, 0.55f, 0.60f, 1.00f); ExampleTimingHistory raster_times; ExampleTimingHistory present_times; // Main loop bool done = false; #ifdef __EMSCRIPTEN__ EMSCRIPTEN_MAINLOOP_BEGIN #else while (!done) #endif { // Poll and handle events (inputs, window resize, etc.) SDL_Event event; while (SDL_PollEvent(&event)) { ImGui_ImplSDL2_ProcessEvent(&event); if (event.type == SDL_QUIT) done = true; if (event.type == SDL_WINDOWEVENT) { if (event.window.event == SDL_WINDOWEVENT_CLOSE && event.window.windowID == SDL_GetWindowID(window)) done = true; if (event.window.event == SDL_WINDOWEVENT_SIZE_CHANGED || event.window.event == SDL_WINDOWEVENT_RESIZED) { // Recreate framebuffer at new size window_surface = SDL_GetWindowSurface(window); int new_w = window_surface->w; int new_h = window_surface->h; if (new_w != win_w || new_h != win_h || window_surface->format->format != framebuffer.SDLFormat) { if (!RecreateFramebuffer(&framebuffer, window_surface)) { printf("Error creating framebuffer after resize: %s\n", SDL_GetError()); return -1; } win_w = new_w; win_h = new_h; } } } } // Skip rendering when minimized to avoid busy-looping if (SDL_GetWindowFlags(window) & SDL_WINDOW_MINIMIZED) { SDL_Delay(10); continue; } // Start the Dear ImGui frame ImGui_ImplCPU_NewFrame(); ImGui_ImplSDL2_NewFrame(); ImGui::NewFrame(); int logical_w = 0; int logical_h = 0; SDL_GetWindowSize(window, &logical_w, &logical_h); // 1. Show the big demo window if (show_demo_window) ImGui::ShowDemoWindow(&show_demo_window); // 2. Show a simple window that we create ourselves. Mirror the SDL_Renderer example. { static float f = 0.0f; static int counter = 0; ImGui::Begin("Hello, world!"); // Create a window called "Hello, world!" and append into it. ImGui::Text("This is some useful text."); // Display some text ImGui::Checkbox("Demo Window", &show_demo_window); // Edit bools storing our window open/close state ImGui::Checkbox("Another Window", &show_another_window); ImGui::SliderFloat("float", &f, 0.0f, 1.0f); // Edit 1 float using a slider from 0.0f to 1.0f ImGui::ColorEdit3("clear color", (float*)&clear_color); // Edit 3 floats representing a color if (ImGui::Button("Button")) // Buttons return true when clicked counter++; ImGui::SameLine(); ImGui::Text("counter = %d", counter); ImGui::Text("Raster %.3f ms | Present %.3f ms", raster_times.GetAverageMs(), present_times.GetAverageMs()); ImGui::Text("Window %d x %d | Framebuffer %d x %d", logical_w, logical_h, framebuffer.CPU.Width, framebuffer.CPU.Height); ImGui::Text("Application average %.3f ms/frame (%.1f FPS)", 1000.0f / io.Framerate, io.Framerate); ImGui::End(); } // 3. Show another simple window. if (show_another_window) { ImGui::Begin("Another Window", &show_another_window); // Pass a pointer to our bool variable (the window will have a closing button that will clear the bool when clicked) ImGui::Text("Hello from another window!"); if (ImGui::Button("Close Me")) show_another_window = false; ImGui::End(); } // Rendering ImGui::Render(); const double raster_start_ms = ExampleGetTimeMs(); SDL_FillRect(framebuffer.Surface, nullptr, SDL_MapRGBA(framebuffer.Surface->format, (Uint8)(clear_color.x * 255), (Uint8)(clear_color.y * 255), (Uint8)(clear_color.z * 255), (Uint8)(clear_color.w * 255))); ImGui_ImplCPU_RenderDrawData(ImGui::GetDrawData(), &framebuffer.CPU); raster_times.AddSample(ExampleGetTimeMs() - raster_start_ms); const double present_start_ms = ExampleGetTimeMs(); SDL_BlitSurface(framebuffer.Surface, nullptr, window_surface, nullptr); SDL_UpdateWindowSurface(window); present_times.AddSample(ExampleGetTimeMs() - present_start_ms); } #ifdef __EMSCRIPTEN__ EMSCRIPTEN_MAINLOOP_END; #endif ImGui_ImplCPU_Shutdown(); ImGui_ImplSDL2_Shutdown(); ImGui::DestroyContext(); if (framebuffer.Surface != nullptr) SDL_FreeSurface(framebuffer.Surface); SDL_DestroyWindow(window); SDL_Quit(); return 0; }