Skip to content

Lua: Image Rendering

Tin Švagelj edited this page Apr 20, 2026 · 2 revisions

Image Rendering

Conky can display images through two libraries:

  • Imlib2 — for raster images (PNG, JPEG, BMP, etc.)
  • RSVG — for SVG vector graphics

Both are optional and may not be available depending on how Conky was built.

See Using Lua Scripts for general setup.

Warning

Like Cairo, image library bindings must be loaded with require() and any handles or images must be freed after use to avoid resource leaks. Since draw hooks run every update, a missing cleanup call will cause Conky to consume more and more memory over time. This will be especially noticable with images as they're much larger than most other resources.

Imlib2

Imlib2 is an image loading and rendering library. Conky exposes its API through Lua bindings that are nearly identical to the C API.

require("imlib2")

Loading and Displaying an Image

The basic workflow is: load an image, set it as the current context image, render it onto the Conky drawable, then free it.

Important

Imlib2 renders directly to X11 drawables — it requires conky_window fields that are X11-specific and does not work on Wayland. For cross-platform image rendering, use cairo_place_image() from the Cairo integration helper instead.

require("imlib2")

function conky_main()
    if conky_window == nil then return end

    -- Set up Imlib2 to draw on the Conky window (X11 only)
    imlib_context_set_display(conky_window.display)
    imlib_context_set_visual(conky_window.visual)
    imlib_context_set_colormap(conky_window.colormap)
    imlib_context_set_drawable(conky_window.drawable)

    -- Load and render an image
    local image = imlib_load_image("/path/to/image.png")
    if image then
        imlib_context_set_image(image)
        imlib_render_image_on_drawable(10, 10)
        imlib_free_image()
    end
end

Scaling Images

To render at a different size, use imlib_render_image_on_drawable_at_size():

local image = imlib_load_image("/path/to/icon.png")
if image then
    imlib_context_set_image(image)
    imlib_render_image_on_drawable_at_size(10, 10, 64, 64)
    imlib_free_image()
end

Cropping and Scaling

To render a portion of an image, create a cropped and scaled copy:

local image = imlib_load_image("/path/to/image.png")
if image then
    imlib_context_set_image(image)
    -- Crop a 100x100 region from (50, 50) and scale it to 200x200
    local cropped = imlib_create_cropped_scaled_image(50, 50, 100, 100, 200, 200)
    imlib_free_image()  -- free the original

    imlib_context_set_image(cropped)
    imlib_render_image_on_drawable(10, 10)
    imlib_free_image()  -- free the cropped copy
end

Cairo Integration Helper

Conky provides a convenience function that combines Imlib2 image loading with Cairo rendering:

require("cairo_imlib2_helper")

-- Draw an image onto a Cairo surface with optional scaling
cairo_place_image("/path/to/image.png", cr, 10, 10, 64, 64, 1.0)
-- Arguments: filename, cairo context, x, y, width, height, alpha

This is simpler than using the Imlib2 API directly when you're already working with Cairo.

Querying Image Dimensions

local image = imlib_load_image("/path/to/image.png")
if image then
    imlib_context_set_image(image)
    local w = imlib_image_get_width()
    local h = imlib_image_get_height()
    -- use w, h for layout calculations
    imlib_free_image()
end

Tip

If you're loading the same image every update cycle, consider caching the loaded image in a global variable and only reloading when necessary (e.g. on a timer). imlib_load_image() does use an internal cache, but skipping the call entirely is still faster.

RSVG (SVG Rendering)

RSVG renders SVG vector graphics onto Cairo surfaces. Because SVGs are resolution-independent, they scale cleanly to any size.

require("rsvg")

Rendering an SVG

The workflow is: create a handle from a file, set up a viewport rectangle that defines where and how large the SVG should be drawn, render it onto a Cairo context, then clean up.

require("cairo")
require("rsvg")

function conky_main()
    local surface = conky_surface()
    if not surface then return end
    local cr = cairo_create(surface)

    -- Load the SVG
    local handle = rsvg_create_handle_from_file("/path/to/image.svg")
    if handle then
        -- Define the viewport (position and size)
        local viewport = RsvgRectangle:create()
        viewport:set(10, 10, 200, 200)

        -- Render onto the Cairo context
        rsvg_handle_render_document(handle, cr, viewport)

        -- Cleanup
        viewport:destroy()
        rsvg_destroy_handle(handle)
    end

    cairo_destroy(cr)
end

Positioning with Cairo Transforms

Use cairo_translate() to position the SVG independently of the viewport origin:

cairo_save(cr)
cairo_translate(cr, 50, 100)  -- offset the SVG

local viewport = RsvgRectangle:create()
viewport:set(0, 0, 200, 200)  -- viewport at translated origin
rsvg_handle_render_document(handle, cr, viewport)
viewport:destroy()

cairo_restore(cr)

Querying SVG Dimensions

To get the intrinsic size of an SVG (the size defined in the file):

local handle = rsvg_create_handle_from_file("/path/to/image.svg")
if handle then
    local has_size, w, h = rsvg_handle_get_intrinsic_size_in_pixels(handle)
    if has_size then
        -- use w, h for layout
    end
    rsvg_destroy_handle(handle)
end

Rendering Specific Layers

SVG files can contain named elements (via id attributes). To render only a specific element:

if rsvg_handle_has_sub(handle, "#layer1") then
    local viewport = RsvgRectangle:create()
    viewport:set(0, 0, 200, 200)
    rsvg_handle_render_layer(handle, cr, "#layer1", viewport)
    viewport:destroy()
end

Further Reading

Clone this wiki locally