ImageLib — Image Control Library

Display bitmap images in Plan9Basic applications with full control over scaling, positioning, and interactivity. Load images from local files or directly from the web via HTTP/HTTPS. Supports PNG (with transparency), JPEG, BMP, and GIF formats. Features include six wrap/scaling modes, rotation, opacity, mouse wheel zoom, and a complete mouse event system. 78 functions.

CategoryCountDescription
Error Handling4image_error, errormsg$, strerror$, clearerror
Creation & Destruction4image# (3 overloads), image_free
Loading & Saving4image_load (2 variants), image_save (2 variants)
Bitmap Properties5bitmapwidth, bitmapheight, isempty, clear# (2 overloads)
Wrap Mode2wrapmode (get/set)
Position & Size14x, y, width, height (get/set), bounds#, size#, move#
Alignment & Margins12align (get/set), margin#, margins#, marginleft/top/right/bottom (get/set)
Visibility & State8visible, enabled, opacity, hittest (get/set)
Tag & Rotation4tag (get/set), rotation (get/set)
Parent & Z-Order4parent# (get/set), bringtofront#, sendtoback#, invalidate#
Events178 event types × set/get + clearcallbacks#

Cross-Platform Support

PlatformStatusNotes
Windows✅ Full SupportWin32/Win64
Linux✅ Full SupportGTK-based
Android✅ Full SupportMobile-optimized

Supported Image Formats

FormatExtensionNotes
PNG.pngRecommended for transparency support
JPEG.jpg / .jpegBest for photographs
BMP.bmpUncompressed bitmap
GIF.gifSimple animations not supported (shows first frame)

Error Handling

FunctionSignatureDescription
image_error()image_error@Last error code (0 = no error)
image_errormsg$()image_errormsg$@Last error message as string
image_strerror$(code)image_strerror$@nDescription for a given error code
image_clearerror()image_clearerror@Clear the error state

Numeric Values Reference

Wrap Mode image_wrapmode#

ValueModeDescription
0OriginalDisplay at original size, top-left aligned
1FitScale to fit while keeping aspect ratio (default)
2StretchStretch to fill control (may distort)
3TileRepeat image to fill control
4CenterCenter without resizing
5PlaceFit if larger, center if smaller

Control Alignment image_align#

ValueDescription
0None (absolute positioning)
1Top
2Left
3Right
4Bottom
9Client (fill parent)
11Center

Creation & Destruction

FunctionSignatureDescription
image#(parent#)image#@#Create image control with default size
image#(parent#, w, h)image#@#nnCreate with size (positioned at 0, 0)
image#(parent#, x, y, w, h)image#@#nnnnCreate with position and size
image_free(img#)image_free@#Destroy image control and release resources
╯ plan9basic
let img# = image#(frm#, 50, 50, 400, 300)

' Or just specify size
let img# = image#(frm#, 400, 300)

' Cleanup
image_free(img#)

Loading & Saving

FunctionSignatureDescription
image_load(img#, path$)image_load@#$Load from file or URL; returns 1 on success, 0 on failure
image_load#(img#, path$)image_load#@#$Load from file or URL; returns image pointer
image_save(img#, filepath$)image_save@#$Save to file; returns 1 on success, 0 on failure
image_save#(img#, filepath$)image_save#@#$Save to file; returns image pointer
╯ plan9basic
' Load from local file
image_load#(img#, "photo.png")

' Load from web URL
image_load#(img#, "https://picsum.photos/400/300")

' Check if load succeeded
if image_load(img#, "photo.png") = 1 then
    println "Loaded OK"
else
    println "Load failed: " + image_errormsg$()
endif

' Save to file
image_save#(img#, "output.png")
ⓘ Note: Image loading supports both local file paths and full HTTP/HTTPS URLs. Web images are downloaded automatically. Use image_load (returns 1/0) when you need to check success, or image_load# (returns pointer) for chaining.

Bitmap Properties

FunctionSignatureDescription
image_bitmapwidth(img#)image_bitmapwidth@#Get the actual loaded image width in pixels
image_bitmapheight(img#)image_bitmapheight@#Get the actual loaded image height in pixels
image_isempty(img#)image_isempty@#Returns 1 if no image is loaded, 0 if loaded
image_clear#(img#)image_clear#@#Clear the image (transparent)
image_clear#(img#, color$)image_clear#@#$Clear with a fill color
╯ plan9basic
' Check before accessing
if image_isempty(img#) = 0 then
    println "Image: " + str$(image_bitmapwidth(img#)) + "x" + str$(image_bitmapheight(img#))
endif

' Clear to transparent
image_clear#(img#)

' Clear to a color
image_clear#(img#, "#336699")
⚠ Warning: image_bitmapwidth / image_bitmapheight return the actual pixel size of the loaded image, which is different from image_width / image_height which return the control size on the form.

Wrap Mode

FunctionSignatureDescription
image_wrapmode(img#)image_wrapmode@#Get current wrap mode (0–5)
image_wrapmode#(img#, mode)image_wrapmode#@#nSet wrap mode
╯ plan9basic
' Fit preserves aspect ratio (best for photos)
image_wrapmode#(img#, 1)

' Stretch fills the control (may distort)
image_wrapmode#(img#, 2)

' Tile repeats the image (patterns)
image_wrapmode#(img#, 3)

' Place: fit-if-larger, center-if-smaller
image_wrapmode#(img#, 5)
ⓘ Note: Use Fit (1) for photos, Stretch (2) for backgrounds, Tile (3) for patterns, and Place (5) when you want small images centered but large ones scaled down.

Position & Size

FunctionSignatureDescription
image_x(img#)image_x@#Get X position
image_x#(img#, x)image_x#@#nSet X position
image_y(img#)image_y@#Get Y position
image_y#(img#, y)image_y#@#nSet Y position
image_width(img#)image_width@#Get control width
image_width#(img#, w)image_width#@#nSet control width
image_height(img#)image_height@#Get control height
image_height#(img#, h)image_height#@#nSet control height
image_bounds#(img#, x, y, w, h)image_bounds#@#nnnnSet position and size in one call
image_size#(img#, w, h)image_size#@#nnSet size only
image_move#(img#, x, y)image_move#@#nnSet position only

Alignment & Margins

FunctionSignatureDescription
image_align(img#)image_align@#Get control alignment mode
image_align#(img#, n)image_align#@#nSet alignment (0=none, 1=top, 2=left, 3=right, 4=bottom, 9=client, 11=center)
image_margin#(img#, n)image_margin#@#nSet uniform margin on all four sides
image_margins#(img#, l, t, r, b)image_margins#@#nnnnSet individual margins (left, top, right, bottom)
image_marginleft(img#)image_marginleft@#Get left margin
image_marginleft#(img#, n)image_marginleft#@#nSet left margin
image_margintop(img#)image_margintop@#Get top margin
image_margintop#(img#, n)image_margintop#@#nSet top margin
image_marginright(img#)image_marginright@#Get right margin
image_marginright#(img#, n)image_marginright#@#nSet right margin
image_marginbottom(img#)image_marginbottom@#Get bottom margin
image_marginbottom#(img#, n)image_marginbottom#@#nSet bottom margin
╯ plan9basic
' Full-screen background image
image_align#(img#, 9)    ' Client = fill
image_margin#(img#, 0)

Visibility & State

FunctionSignatureDescription
image_visible(img#)image_visible@#Get visibility (0/1)
image_visible#(img#, n)image_visible#@#nSet visibility
image_enabled(img#)image_enabled@#Get enabled state (0/1)
image_enabled#(img#, n)image_enabled#@#nSet enabled state
image_opacity(img#)image_opacity@#Get opacity (0.0–1.0)
image_opacity#(img#, value)image_opacity#@#nSet opacity (0.0=transparent, 1.0=opaque)
image_hittest(img#)image_hittest@#Get hit-test state (0/1)
image_hittest#(img#, n)image_hittest#@#nEnable/disable mouse hit testing
╯ plan9basic
' Semi-transparent overlay
image_opacity#(imgOverlay#, 0.5)

' Disable mouse interaction (click-through)
image_hittest#(imgBackground#, 0)
ⓘ Note: Set image_hittest#(img#, 0) to make an image “click-through” — mouse events will pass through to controls behind it. Useful for decorative overlays.

Tag & Rotation

FunctionSignatureDescription
image_tag(img#)image_tag@#Get user-defined integer tag
image_tag#(img#, n)image_tag#@#nSet user-defined integer tag
image_rotation(img#)image_rotation@#Get rotation angle in degrees
image_rotation#(img#, angle)image_rotation#@#nSet rotation angle (0–360 degrees)
╯ plan9basic
' Rotate 45 degrees
image_rotation#(img#, 45)

' Use tags to identify thumbnails
image_tag#(img1#, 1)
image_tag#(img2#, 2)
image_tag#(img3#, 3)

Parent & Z-Order

FunctionSignatureDescription
image_parent#(img#)image_parent#@#Get parent control pointer
image_parent#(img#, parent#)image_parent#@##Move image to a different parent
image_bringtofront#(img#)image_bringtofront#@#Bring to front of Z-order
image_sendtoback#(img#)image_sendtoback#@#Send to back of Z-order
image_invalidate#(img#)image_invalidate#@#Force the image to redraw
╯ plan9basic
' Layer images: background behind foreground
image_sendtoback#(imgBackground#)
image_bringtofront#(imgForeground#)

' Force redraw after external changes
image_invalidate#(img#)

Events

Each event has a setter (image_onXXX#(img#, func$)) and a getter (image_onXXX$(img#)). Use image_clearcallbacks#(img#) to disconnect all callbacks at once.

Click Events

Event SetterGetterCallback Signature
image_onclick#(img#, func$)image_onclick$(img#)function name(sender#)
image_ondblclick#(img#, func$)image_ondblclick$(img#)function name(sender#)

Mouse Events

Event SetterGetterCallback Signature
image_onmousedown#(img#, func$)image_onmousedown$(img#)function name(sender#, button, x, y, shift$)
image_onmouseup#(img#, func$)image_onmouseup$(img#)function name(sender#, button, x, y, shift$)
image_onmousemove#(img#, func$)image_onmousemove$(img#)function name(sender#, x, y, shift$)
image_onmouseenter#(img#, func$)image_onmouseenter$(img#)function name(sender#)
image_onmouseleave#(img#, func$)image_onmouseleave$(img#)function name(sender#)
image_onmousewheel#(img#, func$)image_onmousewheel$(img#)function name(sender#, delta, shift$)
ⓘ Note: OnMouseWheel provides a delta value: positive for scroll up, negative for scroll down. Perfect for implementing zoom functionality.

Other Events

Event SetterGetterCallback Signature
image_onresize#(img#, func$)image_onresize$(img#)function name(sender#)
image_clearcallbacks#(img#)Disconnect all event callbacks

Free Web Image Sources

Lorem Picsum (https://picsum.photos) provides free stock photos for testing:

URL PatternDescription
https://picsum.photos/400/300Random image 400×300
https://picsum.photos/id/10/400/300Specific image by ID
https://picsum.photos/seed/hello/400/300Consistent image from seed text
https://picsum.photos/400/300?grayscaleGrayscale image
https://picsum.photos/400/300?blur=5Blurred image (1–10)
╯ plan9basic
' Random image each time
image_load#(img#, "https://picsum.photos/400/300")

' Same image every time (use seed)
image_load#(img#, "https://picsum.photos/seed/myapp/400/300")

' Specific image by ID
image_load#(img#, "https://picsum.photos/id/42/400/300")
ⓘ Note: Use the seed parameter to get the same image consistently across runs. Without it, each request returns a random image.

Complete Examples

Web Image Gallery

╯ gallery.bas
function OnThumbClick(sender#) local tag, url$
    tag = image_tag(sender#)
    if tag = 1 then url$ = "https://picsum.photos/seed/nature/660/290"
    elseif tag = 2 then url$ = "https://picsum.photos/seed/city/660/290"
    elseif tag = 3 then url$ = "https://picsum.photos/seed/ocean/660/290"
    endif
    image_load#(imgMain#, url$)
endfunction

let frm# = form#("Gallery", 700, 450)
form_position#(frm#, 4)

' Thumbnails
let img1# = image#(frm#, 20, 20, 150, 100)
let img2# = image#(frm#, 190, 20, 150, 100)
let img3# = image#(frm#, 360, 20, 150, 100)
image_wrapmode#(img1#, 1)
image_wrapmode#(img2#, 1)
image_wrapmode#(img3#, 1)
image_load#(img1#, "https://picsum.photos/seed/nature/150/100")
image_load#(img2#, "https://picsum.photos/seed/city/150/100")
image_load#(img3#, "https://picsum.photos/seed/ocean/150/100")

' Main display
let imgMain# = image#(frm#, 20, 140, 660, 290)
image_wrapmode#(imgMain#, 1)

image_tag#(img1#, 1)
image_tag#(img2#, 2)
image_tag#(img3#, 3)
image_onclick#(img1#, "OnThumbClick")
image_onclick#(img2#, "OnThumbClick")
image_onclick#(img3#, "OnThumbClick")

form_show(frm#)

while form_visible(frm#) = 1
    processmessages()
end while

Wrap Mode Selector

╯ wrapmodes.bas
function SetOriginal(sender#)
    image_wrapmode#(img#, 0)
    label_text#(lblMode#, "Mode: Original")
endfunction

function SetFit(sender#)
    image_wrapmode#(img#, 1)
    label_text#(lblMode#, "Mode: Fit")
endfunction

function SetStretch(sender#)
    image_wrapmode#(img#, 2)
    label_text#(lblMode#, "Mode: Stretch")
endfunction

function SetTile(sender#)
    image_wrapmode#(img#, 3)
    label_text#(lblMode#, "Mode: Tile")
endfunction

function SetCenter(sender#)
    image_wrapmode#(img#, 4)
    label_text#(lblMode#, "Mode: Center")
endfunction

let frm# = form#("Wrap Modes", 600, 500)
form_position#(frm#, 4)

let img# = image#(frm#, 10, 50, 580, 400)
image_load#(img#, "https://picsum.photos/seed/demo/400/300")
image_wrapmode#(img#, 1)

let lblMode# = label#(frm#, "Mode: Fit", 10, 460)

let btnOriginal# = button#(frm#, "Original", 10, 10, 80, 30)
let btnFit# = button#(frm#, "Fit", 100, 10, 80, 30)
let btnStretch# = button#(frm#, "Stretch", 190, 10, 80, 30)
let btnTile# = button#(frm#, "Tile", 280, 10, 80, 30)
let btnCenter# = button#(frm#, "Center", 370, 10, 80, 30)

button_onclick#(btnOriginal#, "SetOriginal")
button_onclick#(btnFit#, "SetFit")
button_onclick#(btnStretch#, "SetStretch")
button_onclick#(btnTile#, "SetTile")
button_onclick#(btnCenter#, "SetCenter")

form_show(frm#)

while form_visible(frm#) = 1
    processmessages()
end while

Zoom with Mouse Wheel

╯ zoom.bas
let zoomLevel = 100

function OnZoom(sender#, delta, shift$) local w, h, origW, origH
    origW = image_bitmapwidth(sender#)
    origH = image_bitmapheight(sender#)

    if delta > 0 then
        zoomLevel = zoomLevel + 10
        if zoomLevel > 400 then zoomLevel = 400
    else
        zoomLevel = zoomLevel - 10
        if zoomLevel < 10 then zoomLevel = 10
    endif

    w = origW * zoomLevel / 100
    h = origH * zoomLevel / 100
    image_size#(sender#, w, h)
    label_text#(lblZoom#, "Zoom: " + str$(zoomLevel) + "%")
endfunction

let frm# = form#("Zoom", 800, 600)
form_position#(frm#, 4)

let img# = image#(frm#, 0, 40, 800, 560)
image_wrapmode#(img#, 0)
image_load#(img#, "https://picsum.photos/seed/zoom/600/400")

let lblZoom# = label#(frm#, "Zoom: 100%", 10, 10)
image_onmousewheel#(img#, "OnZoom")

form_show(frm#)

while form_visible(frm#) = 1
    processmessages()
end while

Image Rotation

╯ rotate.bas
function RotateLeft(sender#) local angle
    angle = image_rotation(img#) - 15
    if angle < 0 then angle = angle + 360
    image_rotation#(img#, angle)
    label_text#(lblAngle#, "Angle: " + str$(angle))
endfunction

function RotateRight(sender#) local angle
    angle = image_rotation(img#) + 15
    if angle >= 360 then angle = angle - 360
    image_rotation#(img#, angle)
    label_text#(lblAngle#, "Angle: " + str$(angle))
endfunction

let frm# = form#("Rotate", 500, 500)
form_position#(frm#, 4)

let img# = image#(frm#, 100, 50, 300, 300)
image_load#(img#, "https://picsum.photos/seed/rotate/300/300")
image_wrapmode#(img#, 1)

let lblAngle# = label#(frm#, "Angle: 0", 200, 370)

let btnLeft# = button#(frm#, "< Left", 100, 410, 120, 40)
let btnRight# = button#(frm#, "Right >", 280, 410, 120, 40)
button_onclick#(btnLeft#, "RotateLeft")
button_onclick#(btnRight#, "RotateRight")

form_show(frm#)

while form_visible(frm#) = 1
    processmessages()
end while

Opacity Layering

╯ opacity.bas
let opacity = 1.0

function ToggleFade(sender#) local pct
    if opacity > 0.5 then
        opacity = 0.0
    else
        opacity = 1.0
    endif
    image_opacity#(imgFront#, opacity)
    pct = opacity * 100
    label_text#(lblOpacity#, "Opacity: " + str$(pct) + "%")
endfunction

let frm# = form#("Opacity", 600, 500)
form_position#(frm#, 4)

let imgBack# = image#(frm#, 50, 50, 500, 350)
image_load#(imgBack#, "https://picsum.photos/seed/back/500/350")
image_wrapmode#(imgBack#, 2)

let imgFront# = image#(frm#, 50, 50, 500, 350)
image_load#(imgFront#, "https://picsum.photos/seed/front/500/350")
image_wrapmode#(imgFront#, 2)

let lblOpacity# = label#(frm#, "Opacity: 100%", 250, 420)
let btnFade# = button#(frm#, "Toggle", 250, 450, 100, 35)
button_onclick#(btnFade#, "ToggleFade")

form_show(frm#)

while form_visible(frm#) = 1
    processmessages()
end while

Best Practices

PracticeWhy
Use Fit mode (1) for photosPreserves aspect ratio without distortion
Use Stretch mode (2) for backgroundsFills the entire control area
Use Tile mode (3) for patternsRepeats the image seamlessly
Check image_isempty(img#) after loadingDetects load failures before accessing bitmap properties
Use PNG for transparencyPNG images correctly display transparent areas
Distinguish control vs bitmap sizeimage_width = control size; image_bitmapwidth = actual image pixels
Use seed for consistent web imageshttps://picsum.photos/seed/myapp/400/300 always returns the same image
Match requested size to display sizeRequest web images at the size you’ll display them to avoid unnecessary bandwidth
Set image_hittest#(img#, 0) for decorationsMakes the image click-through so mouse events reach controls behind it

Quick Reference

FunctionSignatureDescription
image_error / errormsg$ / strerror$ / clearerrorvariousError handling (4)
image#(parent#[, w, h] | [, x, y, w, h])variousCreate (3 overloads)
image_free(img#)image_free@#Destroy
image_load / image_load# / image_save / image_save#variousLoading & saving (4)
image_bitmapwidth / bitmapheight / isempty / clear#variousBitmap properties (5)
image_wrapmode (get/set)variousWrap mode (2)
image_x/y/width/height (get/set) / bounds# / size# / move#variousPosition & size (14)
image_align / margin# / margins# / margin[left/top/right/bottom]variousAlignment & margins (12)
image_visible / enabled / opacity / hittestvariousVisibility & state (8)
image_tag / rotationvariousTag & rotation (4)
image_parent# / bringtofront# / sendtoback# / invalidate#variousParent & Z-order (4)
image_onclick/ondblclick/onmousedown/up/move/enter/leave/onmousewheel/onresizevariousEvents set+get (17)
image_clearcallbacks#image_clearcallbacks#@#Disconnect all events

78 functions. Part of the Plan9Basic GUI library system.