8.10.2. STBIMAGE-02 — Saving and Encoding Images

This tutorial covers saving images to files, encoding to in-memory byte arrays, loading from memory, and round-trip verification.

The examples assume img holds a loaded image — see STBIMAGE-01 — Loading and Inspecting Images.

8.10.2.1. Saving to File

Image.save(path, quality) saves to file. The format is determined by the file extension (.png, .bmp, .tga, .jpg/.jpeg, .hdr). The quality parameter only matters for JPEG (1–100, default 90):

let (ok, error) = img.save("output.png")
let (ok2, _) = img.save("output.jpg", 75)  // JPEG quality 75

8.10.2.2. JPEG Quality

Lower quality values produce smaller files with more compression artifacts:

for (q in fixed_array(10, 50, 90)) {
    var buf : array<uint8>
    img.encode("jpg", buf, q)
    print("Quality {q}: {length(buf)} bytes\n")
}

8.10.2.3. Encoding to Memory

Image.encode(format, buf, quality) writes compressed image data into a byte array. The format is specified without a dot: "png", "bmp", "tga", "jpg":

var buf : array<uint8>
let (ok, error) = img.encode("png", buf)
print("Encoded {length(buf)} bytes\n")

8.10.2.4. Loading from Memory

Image.load_from_memory(buf) decodes an image from a byte array. Also available: load_hdr_from_memory(), load_16_from_memory():

var inscope img2 : Image
let (ok, error) = img2.load_from_memory(buf)

Format detection works from memory too:

print("HDR: {is_hdr_from_memory(buf)}\n")
print("16-bit: {is_16_bit_from_memory(buf)}\n")

8.10.2.5. Round-trip Verification

Lossless formats (PNG, BMP, TGA) preserve pixel values exactly through an encode/decode cycle:

var buf : array<uint8>
img.encode("png", buf)
var inscope decoded : Image
decoded.load_from_memory(buf)
// decoded.bytes == img.bytes