# How ImGui Handles DPI Scaling for High-DPI Displays

> Learn how ImGui handles DPI scaling for high-DPI displays. Discover how ImGuiIO::DisplayFramebufferScale automatically adjusts fonts and coordinates for crisp visuals.

- Repository: [omar/imgui](https://github.com/ocornut/imgui)
- Tags: deep-dive
- Published: 2026-07-16

---

**ImGui handles DPI scaling by separating logical UI coordinates from physical pixel density through the `ImGuiIO::DisplayFramebufferScale` field, which backends populate by querying OS-specific DPI APIs to automatically scale fonts, geometry, and input coordinates.**

The Dear ImGui library (`ocornut/imgui`) delegates high-DPI support to platform backends while providing the core infrastructure to scale the entire rendering pipeline. When the framebuffer scale is greater than 1.0, the library multiplies font atlas sizes, widget geometry, and divides mouse input to maintain consistent visual sizing across Retina displays and other high-density monitors.

## Core DPI Scaling Architecture

The central mechanism lives in `ImGuiIO::DisplayFramebufferScale` (defined in **[imgui.h](https://github.com/ocornut/imgui/blob/master/imgui.h#L2420)**). This `ImVec2` field tells ImGui the ratio between logical pixels and physical framebuffer pixels. When the scale exceeds 1.0 (e.g., 2.0 on a Retina display), ImGui automatically performs three transformations:

1. **Font Scaling** – The font atlas is generated at a size multiplied by the scale factor, ensuring text remains crisp rather than bitmap-stretched.
2. **Geometry Scaling** – All widget coordinates and sizes are multiplied by the scale during the rendering phase, keeping UI elements the same physical size regardless of pixel density.
3. **Input Adjustment** – Mouse positions are divided by the scale factor so that the logical cursor position matches the scaled UI coordinates.

## Official Backend Implementations

ImGui itself does not detect monitor DPI; instead, official platform backends query native OS APIs and populate `io.DisplayFramebufferScale` before `NewFrame()` is called.

### Win32 Implementation

The Win32 backend ([`imgui_impl_win32.cpp`](https://github.com/ocornut/imgui/blob/main/imgui_impl_win32.cpp)) uses `ImGui_ImplWin32_GetDpiScaleForMonitor()` to detect DPI. According to the source code at [lines 27-40](https://github.com/ocornut/imgui/blob/master/backends/imgui_impl_win32.cpp#L27-L40), it calls `GetDpiForMonitor` on Windows 8.1 and later, falling back to `GetDeviceCaps(LOGPIXELSX)` on older systems. The result is stored in `io.DisplayFramebufferScale` via `ImGui_ImplWin32_NewFrame()`.

### SDL2 Implementation

The SDL2 backend ([`imgui_impl_sdl2.cpp`](https://github.com/ocornut/imgui/blob/main/imgui_impl_sdl2.cpp)) queries the display DPI using `SDL_GetDisplayDPI()`. As implemented at [lines 75-81](https://github.com/ocornut/imgui/blob/master/backends/imgui_impl_sdl2.cpp#L75-L81), if the returned value is less than 96 (the baseline DPI), it clamps to 96 to prevent division by zero or negative scales. It then calculates the scale as `dpi / 96.0f` and assigns it to the IO structure.

### GLFW Implementation

The GLFW backend ([`imgui_impl_glfw.cpp`](https://github.com/ocornut/imgui/blob/main/imgui_impl_glfw.cpp)) handles Windows-specific DPI through the legacy helper `ImGui_ImplWin32_GetLegacyDpiScale()`, which reads `LOGPIXELSX` from a device context (see [lines 93-95](https://github.com/ocornut/imgui/blob/master/backends/imgui_impl_glfw.cpp#L93-L95)). On other platforms, it typically uses `glfwGetMonitorContentScale` to obtain the necessary values.

## Implementing DPI Scaling in Custom Backends

If you are writing a custom backend for a platform not covered by the official implementations, you must manually detect and set the DPI scale:

```cpp
// 1. Detect native DPI using OS-specific APIs
//    Windows: GetDpiForMonitor or GetDeviceCaps
//    macOS:   [NSScreen backingScaleFactor]
//    SDL:     SDL_GetDisplayDPI
//    GLFW:    glfwGetMonitorContentScale

// 2. Calculate scale (96 DPI is the baseline)
float scale = dpi / 96.0f;

// 3. Assign to ImGuiIO
ImGuiIO& io = ImGui::GetIO();
io.DisplayFramebufferScale = ImVec2(scale, scale);

```

Additionally, you must transform incoming mouse coordinates by dividing them by `scale` before passing them to ImGui, unless you utilize the built-in input helpers that handle this automatically.

## Handling DPI Changes and Multi-Viewport

DPI scaling is not a static initialization value. Applications must respond to monitor changes and per-window density variations.

### Dynamic DPI Updates

Update the scale in the following scenarios:

- **On window creation** – Initialize the scale once the OS window is associated with a monitor.
- **On monitor change** – Listen for `WM_DPICHANGED` (Win32), `SDL_EVENT_DISPLAY_ORIENTATION` (SDL), or similar events and recompute `DisplayFramebufferScale`.
- **On window movement** – When dragging a window between monitors with different densities, the backend must detect the change and update the IO structure accordingly.

### Multi-Viewport Support

When using `ImGuiConfigFlags_ViewportsEnable`, each `ImGuiViewport` may exist on a monitor with a different DPI. According to the `ocornut/imgui` source, each viewport stores its own `FramebufferScale` field. The platform backend must set `viewport->Flags |= ImGuiViewportFlags_IsPlatformWindow;` and update `viewport->FramebufferScale` during the `NewFrame` processing. The official backends handle this automatically by querying the monitor associated with each platform window.

## Summary

- **Backend-driven architecture** – ImGui consumes `DisplayFramebufferScale` but relies on the platform backend to query native DPI via OS APIs.
- **Automatic scaling** – When properly configured, ImGui scales fonts, geometry, and input coordinates automatically based on the framebuffer scale value.
- **Baseline calculation** – Always compute scale as `dpi / 96.0f`, where 96 represents the standard 100% DPI baseline.
- **Dynamic updates** – Handle `WM_DPICHANGED` and monitor change events to update scaling when windows move between displays.
- **Per-viewport scaling** – In multi-viewport mode, each viewport maintains its own `FramebufferScale` to support mixed-DPI environments.

## Frequently Asked Questions

### How do I enable DPI scaling in my ImGui application?

You do not need to enable a specific flag in the core library. Instead, ensure your platform backend (Win32, SDL2, GLFW, etc.) is correctly detecting the monitor DPI and writing to `ImGuiIO::DisplayFramebufferScale`. The official backends do this automatically in their `NewFrame()` functions. If you are not using an official backend, implement the OS-specific DPI detection yourself and set the scale value before calling `ImGui::NewFrame()`.

### Why do my fonts appear blurry on high-DPI displays?

Blurry fonts indicate that `DisplayFramebufferScale` is set to 1.0 on a high-DPI monitor, causing the font atlas to be generated at logical resolution rather than physical pixel resolution. Verify that your backend is correctly detecting the DPI and that you are regenerating the font atlas after setting the scale. The font size should be specified in logical pixels; ImGui multiplies this by the framebuffer scale internally to create a high-resolution atlas.

### What is the difference between `DisplayFramebufferScale` and `DisplaySize`?

`DisplaySize` (measured in logical pixels) represents the size of the display area available for the UI, while `DisplayFramebufferScale` is a multiplier that converts logical coordinates to physical framebuffer pixels. Prior to ImGui 1.87, some backends used `DisplaySize` to indirectly handle DPI by reporting the framebuffer size directly. Modern backends should keep `DisplaySize` as the logical size and use `DisplayFramebufferScale` to communicate the pixel density ratio.

### How do I handle different DPIs for multiple monitors?

When using multi-viewport support, each `ImGuiViewport` structure contains its own `FramebufferScale` field. As the platform backend creates and manages OS windows, it must detect the DPI of the monitor hosting each window and update that viewport's `FramebufferScale` accordingly. The core library uses this per-viewport scale when generating the draw data for that specific window, ensuring correct scaling even when windows span monitors with different densities.