8.19.54. Texture references

Dear ImGui 1.92 reworked texture handling: the raw ImTextureID you used to hand to image() is now an ImTextureRef. This tutorial shows the full path for displaying your own image — decode a PNG, upload it to a GL texture, and pass that texture through an ImTextureRef:

var private g_tex : uint = 0u          // GL texture name
let PICTURE_PATH = "{get_das_root()}/modules/dasImgui/doc/source/_static/icons/cube.png"

// 1. decode a PNG to RGBA pixels (dasStbImage)
var img : Image
let (loaded, err) = img->load(PICTURE_PATH, 4)   // 4 channels => RGBA
// 2. upload to a GL texture
glGenTextures(1, safe_addr(g_tex))
glBindTexture(GL_TEXTURE_2D, g_tex)
glTexImage2D(GL_TEXTURE_2D, 0, int(GL_RGBA), img.width, img.height, 0,
             GL_RGBA, GL_UNSIGNED_BYTE, unsafe(addr(img.bytes[0])))
// 3. wrap the GL texture name in an ImTextureRef
unsafe {
    var ref : ImTextureRef
    ref._TexID = uint64(g_tex)
    // 4. draw it
    image(TR_PIC, (user_texture_id = ref,
                   size = float2(float(img.width), float(img.height))))
}

_TexID is the user slot of ImTextureRef (an ImTextureID == the GL texture name here). With _TexData left null, the backend treats it as a user-managed texture and binds the GL id directly — that is how you pass your own texture in 1.92.

The demo loads one of the repo’s icon PNGs as a stand-in for an application texture; point PICTURE_PATH at any image of your own.

Source: modules/dasImgui/examples/tutorial/texture_ref.das.

8.19.54.1. Walkthrough

  1options gen2
  2options _comment_hygiene = true
  3options gc
  4
  5require imgui
  6require imgui_app
  7require opengl/opengl_boost
  8require live/glfw_live
  9require live/live_api
 10require live/live_commands
 11require live/live_vars
 12require live_host
 13require imgui/imgui_live
 14require imgui/imgui_boost_runtime
 15require imgui/imgui_boost_v2
 16require imgui/imgui_widgets_builtin
 17require imgui/imgui_containers_builtin
 18require imgui/imgui_visual_aids
 19require stbimage/stbimage_boost
 20require daslib/safe_addr
 21
 22// =============================================================================
 23// TUTORIAL: texture_ref — display YOUR OWN texture via the imgui 1.92 ImTextureRef.
 24//
 25// 1.92 replaced the raw `ImTextureID` handle with `ImTextureRef`. To show a real
 26// image (not the font atlas), the steps are:
 27//
 28//   1. decode a PNG to RGBA pixels      — dasStbImage `Image::load(path, 4)`
 29//   2. upload to a GL texture           — glGenTextures + glTexImage2D
 30//   3. wrap the GL handle               — var ref : ImTextureRef; ref._TexID = handle
 31//   4. draw it                          — image(IDENT, (user_texture_id = ref, ...))
 32//
 33// `_TexID` is the user slot of ImTextureRef (an ImTextureID == the GL texture name
 34// here). With `_TexData` left null, the backend treats it as a user-managed texture
 35// and binds the GL id directly — that is how you pass your own texture in 1.92.
 36//
 37// STANDALONE: daslang.exe modules/dasImgui/examples/tutorial/texture_ref.das
 38// LIVE:       daslang-live modules/dasImgui/examples/tutorial/texture_ref.das
 39// =============================================================================
 40
 41// One of the repo's icon PNGs stands in for an application texture.
 42let PICTURE_PATH = "{get_das_root()}/modules/dasImgui/doc/source/_static/icons/cube.png"
 43
 44var private g_tex : uint = 0u            // GL texture name
 45var private g_tex_w = 0
 46var private g_tex_h = 0
 47var private g_load_err = ""
 48
 49def private upload_picture() {
 50    var img : Image
 51    let (ok, err) = img->load(PICTURE_PATH, 4)   // request 4 channels => RGBA
 52    if (!ok) {
 53        g_load_err = "load failed: {err}"
 54        return
 55    }
 56    g_tex_w = img.width
 57    g_tex_h = img.height
 58    glGenTextures(1, safe_addr(g_tex))
 59    glBindTexture(GL_TEXTURE_2D, g_tex)
 60    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)
 61    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR)
 62    glTexImage2D(GL_TEXTURE_2D, 0, int(GL_RGBA), img.width, img.height, 0,
 63                 GL_RGBA, GL_UNSIGNED_BYTE, unsafe(addr(img.bytes[0])))
 64}
 65
 66// Build the 1.92 image() handle from the GL texture name. _TexID is the user
 67// slot (ImTextureID == ImU64 == uint64 here); _TexData stays null, so the
 68// backend binds the GL id directly as a user-managed texture.
 69def private texture_ref() : ImTextureRef {
 70    unsafe {
 71        var ref : ImTextureRef
 72        ref._TexID = uint64(g_tex)
 73        return ref
 74    }
 75}
 76
 77[export]
 78def init() {
 79    live_create_window("dasImgui texture_ref tutorial", 820, 520)
 80    live_imgui_init(live_window)
 81    let io & = unsafe(GetIO())
 82    GetStyle().FontScaleMain = 1.3
 83    // GL context is live now — decode + upload the picture once.
 84    upload_picture()
 85}
 86
 87[export]
 88def update() {
 89    if (!live_begin_frame()) return
 90    begin_frame()
 91
 92    ImGui_ImplGlfw_NewFrame()
 93    apply_synth_io_override()
 94    NewFrame()
 95
 96    SetNextWindowPos(ImVec2(40.0f, 40.0f), ImGuiCond.Always)
 97    SetNextWindowSize(ImVec2(740.0f, 440.0f), ImGuiCond.Always)
 98    window(TR_WIN, (text = "texture_ref", closable = false,
 99                    flags = ImGuiWindowFlags.None)) {
100        if (g_tex == 0u) {
101            text(TR_ERR, (text = "no texture: {g_load_err}"))
102        } else {
103            text("A real PNG, decoded + uploaded to a GL texture, shown through")
104            text("an ImTextureRef whose _TexID is the GL texture name.")
105            separator()
106
107            // Full image, then a tinted + bordered copy — same texture reused.
108            // the named-tuple argument mints an ImTextureRef temp — a handled-type
109            // local, unsafe-scoped like any minted ImTextureRef
110            unsafe {
111                image(TR_PIC, (user_texture_id = texture_ref(),
112                               size = float2(float(g_tex_w), float(g_tex_h)),
113                               uv0 = float2(0.0f, 0.0f),
114                               uv1 = float2(1.0f, 1.0f),
115                               tint_col = float4(1.0f, 1.0f, 1.0f, 1.0f),
116                               border_col = float4(0.6f, 0.6f, 0.6f, 1.0f)))
117            }
118            same_line(TR_GAP)
119            unsafe {
120                image(TR_TINT, (user_texture_id = texture_ref(),
121                                size = float2(float(g_tex_w), float(g_tex_h)),
122                                uv0 = float2(0.0f, 0.0f),
123                                uv1 = float2(1.0f, 1.0f),
124                                tint_col = float4(0.4f, 0.8f, 1.0f, 1.0f),
125                                border_col = float4(1.0f, 1.0f, 1.0f, 1.0f)))
126            }
127            separator()
128            text("{g_tex_w}x{g_tex_h}. Swap PICTURE_PATH / _TexID for your own texture.")
129        }
130    }
131
132    end_of_frame()
133    Render()
134    var w, h : int
135    live_get_framebuffer_size(w, h)
136    glViewport(0, 0, w, h)
137    glClearColor(0.10f, 0.10f, 0.12f, 1.0f)
138    glClear(GL_COLOR_BUFFER_BIT)
139    live_imgui_render()
140
141    live_end_frame()
142}
143
144[export]
145def shutdown() {
146    if (g_tex != 0u) {
147        glDeleteTextures(1, safe_addr(g_tex))
148    }
149    live_imgui_shutdown()
150    live_destroy_window()
151}
152
153[export]
154def main() {
155    init()
156    while (!exit_requested()) {
157        update()
158        maybe_collect_gc()
159    }
160    shutdown()
161}

8.19.54.1.1. Requires

stbimage/stbimage_boost for the PNG decode (the Image type and Image::load), opengl/opengl_boost for the GL upload (already pulled in by the live backend), and daslib/safe_addr for the texture-name address.

8.19.54.1.2. Behaviour

The picture is decoded + uploaded once in init (the GL context is live after live_imgui_init), and the texture name is kept in a module global. Each frame image() draws it twice through a freshly built ImTextureRef — once plain, once tinted — to show the same texture reused while tint_col varies per call. (border_col is echoed into ImageState for snapshot assertions only; ImGui’s Image() takes no per-call border, it comes from the ImGuiCol_Border style.) The texture is freed with glDeleteTextures on shutdown.

8.19.54.1.3. Migration note

Pre-1.92 code passed an ImTextureID straight to image(). In 1.92 the argument is an ImTextureRef; put your texture handle in ref._TexID (the user slot) and leave ref._TexData null. The font atlas moved the same way: io.Fonts.TexIDio.Fonts.TexRef (an ImTextureRef), gated on io.Fonts.TexData != null.

See also

Full source: modules/dasImgui/examples/tutorial/texture_ref.das

Related: tree_node_ex + imageimage() against the font atlas, alongside tree_node_ex.