# How to Use the Dear ImGui ImDrawList API to Render Custom Shapes

> Learn to draw custom shapes like lines, rectangles, and circles in Dear ImGui using the ImDrawList API. Access the vertex buffer and leverage Add methods for direct rendering.

- Repository: [omar/imgui](https://github.com/ocornut/imgui)
- Tags: how-to-guide
- Published: 2026-07-29

---

**The ImDrawList API provides direct access to Dear ImGui's vertex buffer, allowing you to render custom lines, rectangles, circles, and images by obtaining a draw list pointer and calling its `Add*` methods.**

Dear ImGui (ocornut/imgui) exposes a low-level **ImDrawList API** that sits just above the renderer-agnostic `ImDrawData` structure, enabling direct manipulation of vertex buffers for custom rendering. Unlike high-level widgets such as `ImGui::Button()`, this interface lets you issue raw drawing commands using screen-space coordinates. All primitive functions are implemented in [`imgui_draw.cpp`](https://github.com/ocornut/imgui/blob/main/imgui_draw.cpp) and operate by pushing vertices into `ImDrawList::VtxBuffer` while creating corresponding `ImDrawCmd` entries in `ImDrawList::CmdBuffer`.

## Obtaining a Draw List Context

Before issuing draw commands, you must obtain a pointer to an `ImDrawList` instance. Dear ImGui provides three primary helper functions that return properly initialized lists tied to the current frame context:

- **`ImGui::GetWindowDrawList()`** – Retrieves the draw list for the current window, automatically applying the window's scroll offset and clipping rectangle.
- **`ImGui::GetBackgroundDrawList()`** – Returns a draw list rendered before all ImGui windows, useful for full-screen backgrounds or effects behind the UI.
- **`ImGui::GetForegroundDrawList()`** – Returns a draw list rendered after all ImGui windows, suitable for overlays, selection boxes, or debug visualization on top of everything.

For advanced use cases involving standalone `ImDrawList` objects outside the standard context, use **`ImGui::GetDrawListSharedData()`** to access the shared font atlas and rendering data required for initialization.

### Window-Scoped Drawing

When you need shapes to move with a specific window and respect its boundaries, call `ImGui::GetWindowDrawList()` after `ImGui::Begin()`. Primitives drawn here use screen-space coordinates, so always offset positions using `ImGui::GetCursorScreenPos()` to align with the window's content region.

### Global Background and Foreground Layers

The background and foreground draw lists exist outside the window hierarchy. According to the source code in [`imgui.cpp`](https://github.com/ocornut/imgui/blob/main/imgui.cpp), these are persistent lists maintained by the `ImGuiContext` that bypass individual window clipping, making them ideal for global effects.

## Rendering Primitive Shapes

The `ImDrawList` class exposes a family of `Add*` functions defined in [`imgui_draw.cpp`](https://github.com/ocornut/imgui/blob/main/imgui_draw.cpp) for emitting geometry. Each function accepts packed 32-bit colors (`ImU32`) created with the `IM_COL32(r, g, b, a)` macro, and optional parameters for thickness or rounding. Key methods include:

- **`AddLine()`** – Defined at line 1478 in [`imgui_draw.cpp`](https://github.com/ocornut/imgui/blob/main/imgui_draw.cpp).
- **`AddRect()`** and **`AddRectFilled()`** – Outlined and filled rectangles.
- **`AddCircle()`** and **`AddCircleFilled()`** – Parametric circles with specified segment counts.
- **`AddTriangle()`** and **`AddTriangleFilled()`** – With `AddTriangleFilled` implemented at line 1593 in [`imgui_draw.cpp`](https://github.com/ocornut/imgui/blob/main/imgui_draw.cpp).
- **`AddText()`** and **`AddImage()`** – For custom font rendering and texture sampling.

The following example demonstrates drawing inside a window using screen-space coordinates:

```cpp
ImGui::Begin("Custom Shapes");

// Get the draw list for the current window
ImDrawList* draw = ImGui::GetWindowDrawList();

// Absolute screen position of the top-left corner of the window's content area
ImVec2 p = ImGui::GetCursorScreenPos();
float   radius = 40.0f;

// Draw a filled circle
draw->AddCircleFilled(p + ImVec2(60, 60), radius, IM_COL32(255, 0, 0, 255));

// Draw a thick line
draw->AddLine(p + ImVec2(120, 20), p + ImVec2(200, 80), IM_COL32(0, 255, 0, 255), 4.0f);

// Draw a triangle (outline)
draw->AddTriangle(p + ImVec2(250, 20), p + ImVec2(300, 80), p + ImVec2(200, 80),
                  IM_COL32(0, 0, 255, 255), 3.0f);

ImGui::End();

```

**Key points:** `GetWindowDrawList()` guarantees that primitives respect the window's scrolling and clipping. The `IM_COL32` macro packs RGBA components into the `ImU32` format expected by the vertex buffer.

## Managing Clipping Regions

To restrict drawing to a specific region, use **`PushClipRect()`** and **`PopClipRect()`**, implemented around line 592 in [`imgui_draw.cpp`](https://github.com/ocornut/imgui/blob/main/imgui_draw.cpp). These functions modify the clipping state stored per draw command, which the backend renderer evaluates during GPU submission.

```cpp
ImDrawList* fg = ImGui::GetForegroundDrawList();   // rendered last
ImVec2 a(300, 300), b(500, 500);

// Define a clipping region (a square)
fg->PushClipRect(a, b, true);                     // intersect = true
fg->AddRectFilled(a, b, IM_COL32(255, 255, 0, 128)); // semi-transparent yellow
fg->PopClipRect();                                // restore previous clip

```

Only geometry falling within the axis-aligned bounding box defined by `a` and `b` will be rasterized. Setting the `intersectWithCurrentClipRect` parameter to `true` performs an intersection with the existing clip region, ensuring nested clipping behaves correctly.

## Drawing Behind or In Front of All UI

For effects that must appear behind or above the entire Dear ImGui interface, use the global draw lists. The background list is processed first by the backend, while the foreground list is processed last.

```cpp
// This code can be placed anywhere after ImGui::NewFrame()
ImDrawList* bg = ImGui::GetBackgroundDrawList();

// Full-screen gradient background
ImVec2 screen_sz = ImGui::GetIO().DisplaySize;
bg->AddRectFilled(ImVec2(0, 0), screen_sz, IM_COL32(20, 20, 30, 255));
bg->AddRectFilled(ImVec2(0, 0), ImVec2(screen_sz.x, screen_sz.y * 0.5f),
                  IM_COL32(30, 30, 50, 255));

```

This renders a two-tone background that remains behind all windows and widgets, as the `ImDrawData` structure processes background lists before window lists during the `ImGui::Render()` phase.

## Advanced Configuration and Callbacks

You can temporarily modify rendering behavior by adjusting **`drawList->Flags`**, defined in [`imgui.h`](https://github.com/ocornut/imgui/blob/main/imgui.h) under the `ImDrawListFlags_` enumeration. For example, enabling `ImDrawListFlags_AntiAliasedLines` affects subsequent line primitives.

For advanced effects requiring custom GPU state changes or shader switches, use **`AddCallback(callback, user_data)`**. This pushes a special command into `CmdBuffer` that instructs the backend renderer (such as [`imgui_impl_opengl3.cpp`](https://github.com/ocornut/imgui/blob/main/imgui_impl_opengl3.cpp)) to execute your callback function, receiving the parent `ImDrawList*` and current `ImDrawCmd*` as parameters.

## Implementation Details in the Source Code

Understanding the internal structure helps debug custom rendering issues. The relevant files in the ocornut/imgui repository include:

- **[`imgui.h`](https://github.com/ocornut/imgui/blob/main/imgui.h)** – Public API declarations for `ImDrawList`, helper getters, color macros, and flag enumerations.
- **[`imgui_draw.cpp`](https://github.com/ocornut/imgui/blob/main/imgui_draw.cpp)** – Contains implementations of all `Add*` primitives, clipping logic, and callback handling at the line numbers referenced above.
- **[`imgui_internal.h`](https://github.com/ocornut/imgui/blob/main/imgui_internal.h)** – Defines internal structures such as `ImDrawListSharedData` and `ImDrawListSplitter` used for managing vertex buffers and command lists.
- **`backends/imgui_impl_*.cpp`** (e.g., [`imgui_impl_opengl3.cpp`](https://github.com/ocornut/imgui/blob/main/imgui_impl_opengl3.cpp)) – Backend renderers that consume the `ImDrawData` structure and convert command buffers into GPU draw calls.

When you call an `Add*` method, the implementation writes vertex data (position, UV coordinates, and color) into **`VtxBuffer`**, then appends an `ImDrawCmd` to **`CmdBuffer`** containing the draw range and texture ID. At frame end, the `ImGui_Impl*` backend iterates these buffers to submit batches to the GPU.

## Summary

- **Obtain a context** using `ImGui::GetWindowDrawList()`, `GetBackgroundDrawList()`, or `GetForegroundDrawList()` depending on whether shapes should respect window bounds or render globally.
- **Emit geometry** via `Add*` methods (implemented in [`imgui_draw.cpp`](https://github.com/ocornut/imgui/blob/main/imgui_draw.cpp)) such as `AddLine` (line 1478) and `AddTriangleFilled` (line 1593), using `IM_COL32` for color packing.
- **Control visibility** with `PushClipRect()` and `PopClipRect()` (around line 592 in [`imgui_draw.cpp`](https://github.com/ocornut/imgui/blob/main/imgui_draw.cpp)) to restrict rendering to specific screen regions.
- **Respect the coordinate system** by using `ImGui::GetCursorScreenPos()` for absolute positioning within windows.
- **Advanced users** can inject custom rendering logic using `AddCallback()` or modify anti-aliasing flags via `drawList->Flags`.

## Frequently Asked Questions

### How do I convert RGB float values to the ImU32 format required by ImDrawList?

Use the **`IM_COL32(r, g, b, a)`** macro defined in [`imgui.h`](https://github.com/ocornut/imgui/blob/main/imgui.h). This macro packs four 8-bit unsigned integer channels into a single 32-bit value (0xRRGGBBAA). For example, opaque red is `IM_COL32(255, 0, 0, 255)`. You can also use `ImGui::ColorConvertFloat4ToU32()` to convert from `ImVec4` float components.

### What is the difference between GetWindowDrawList and GetForegroundDrawList?

**`GetWindowDrawList()`** returns the list associated with the current window, applying that window's scroll offset and clipping rectangle automatically. Shapes drawn here move with the window and respect its boundaries. **`GetForegroundDrawList()`** returns a global list rendered on top of all ImGui content regardless of window order, useful for system-wide overlays or drag-and-drop visuals that must persist across multiple windows.

### Can I use ImDrawList for rendering outside of the standard ImGui frame loop?

Yes, but you must manually manage the `ImDrawList` lifecycle and shared data. Create a standalone `ImDrawList` instance and initialize it using **`ImGui::GetDrawListSharedData()`** to access the font atlas and curve tessellation data required for text and anti-aliased primitives. You will also need to handle the `ImDrawData` assembly and backend submission yourself, mimicking the process in `ImGui::Render()`.

### Why are my custom shapes being clipped incorrectly or not appearing at all?

First, verify you are using **screen-space coordinates** (absolute pixels relative to the display top-left) rather than window-local coordinates. Use `ImGui::GetCursorScreenPos()` to obtain the correct offset. Second, check active **clipping rectangles** set by previous `PushClipRect` calls or parent widgets; call `PopClipRect()` to restore previous states. Finally, ensure you call draw list methods between `ImGui::NewFrame()` and `ImGui::Render()`, as the backend only processes buffers during the render phase.