Native images
NativeImage decodes and manipulates images through OpenTUIās native library. Every decoded image uses top-left, straight-alpha, sRGB RGBA8 pixels.
Decode WebP to RGBA
import { NativeImage } from "@opentui/core"
const image = await NativeImage.load("./image.webp")
try {
const { data, width, height, stride } = image.raw("rgba8")
console.log({ data, width, height, stride })
} finally {
image.dispose()
}
data is a Uint8Array in row-major RGBA order. Use raw("bgra8") for BGRA. The returned RawImage also contains width, height, stride, format, colorSpace: "srgb", and alpha: "straight".
Inputs and formats
| API | Input |
|---|---|
NativeImage.load(source) |
Path, file:/HTTP(S)/blob:/data: URL, URL, Blob, Response, Uint8Array, or ArrayBuffer |
NativeImage.decode(data) |
Encoded Uint8Array or ArrayBuffer |
NativeImage.fromRgba(...) |
Straight-alpha sRGB RGBA8 pixels, dimensions, and optional row stride |
imageInfo(data) |
Encoded Uint8Array or ArrayBuffer; returns metadata without retaining an image handle |
Format detection uses encoded bytes, not names, URL suffixes, response headers, or file extensions.
imageInfo() validates encoded metadata and decodes when pixel inspection is required. decode() and fromRgba() copy their inputs and do not retain caller buffers.
| Format | Behavior |
|---|---|
| PNG | Supports alpha, sRGB and embedded ICC color metadata, and EXIF orientation |
| JPEG | Decodes opaque RGBA8; applies EXIF orientation |
| WebP | Decodes lossy, lossless, and alpha images; animated WebP is rejected |
| GIF | Decodes the first displayed frame on the logical canvas |
load() accepts { signal, fetch }. signal cancels source acquisition; fetch replaces globalThis.fetch for fetched URLs. Paths, blobs, and response bodies are buffered before native decoding.
Metadata and pixels
image.info() returns:
| Field | Description |
|---|---|
width, height |
Decoded, orientation-corrected dimensions |
sourceWidth, sourceHeight |
Original input dimensions; before orientation for encoded inputs and preserved by derived images |
format |
png, jpeg, webp, gif, or raw-rgba |
colorStatus |
explicit-srgb or assumed-srgb |
orientation |
1 after decode; imageInfo() reports encoded orientation |
hasAlpha |
Whether decoded pixels contain transparency |
| Method | Result |
|---|---|
raw(format?) |
Allocates and returns RGBA8 or BGRA8 pixels and image metadata |
takeRaw() |
Transfers ownership of the native RGBA8 pixels without copying |
copyTo(destination, options?) |
Copies pixels into an existing Uint8Array |
width, height |
Decoded dimensions |
ptr |
Opaque native ImageHandle, valid until disposal or takeRaw() |
copyTo() defaults to RGBA8 with stride width * 4; BGRA8 and custom strides are supported. A custom stride must fit one row, and the destination must fit every row.
takeRaw() consumes an exclusively owned NativeImage; it throws while a native render buffer retains the image. Later accessors and operations throw, while dispose() remains a safe no-op. The returned OwnedRawImage.data directly views native memory. Dispose it only after every consumer has finished, because explicit disposal frees the native allocation and invalidates the view:
const image = await NativeImage.load(new Blob([encodedImage]))
const raw = image.takeRaw()
try {
consumeRgba(raw.data, raw.width, raw.height, raw.stride)
} finally {
raw.dispose()
}
OwnedRawImage.dispose() is idempotent and required. Keep the owner alive for as long as any consumer uses data.
Operations
Operations are immutable: each returns a new NativeImage; the source remains unchanged.
| Method | Description |
|---|---|
clone() |
Copy the image |
resize({ width?, height?, kernel? }) |
Resize; one omitted dimension preserves aspect ratio |
extract({ left, top, width, height }) |
Crop a rectangle |
extend(options?) |
Add RGBA padding |
rotate(90 | 180 | 270) |
Rotate clockwise |
flip() / flop() |
Flip vertically / horizontally |
composite(overlay, options?) |
Composite in linear light |
Resize kernels are area (the default), default, triangle, cubic-bspline, catmull-rom, mitchell, and nearest. Blend modes are source-over (the default), source, and destination-over; opacity is 0..1.
extend() defaults omitted sides to zero and its background to transparent RGBA. composite() defaults to offset (0, 0), source-over, and opacity 1; negative offsets are clipped to the base image.
Dispose every image you own, including operation results:
const source = await NativeImage.load("photo.jpg")
const thumbnail = source.resize({ width: 320 })
try {
console.log(thumbnail.raw().data)
} finally {
thumbnail.dispose()
source.dispose()
}
dispose() is idempotent. Other methods throw after disposal.
Errors and limits
ImageLoadError exposes code, source, and optional HTTP status; codes are file-read, network, http-status, and unsupported-url-scheme. Aborts rethrow AbortSignal.reason.
ImageError exposes numeric status and code: invalid-handle, unsupported-format, unsupported-color-space, malformed-data, dimension-limit, memory-limit, invalid-argument, out-of-memory, output-too-small, internal-error, or unsupported-feature. JavaScript argument validation uses TypeError and RangeError.
Encoded input is limited to 64 MiB. Images and operation outputs are limited to 16,384 pixels per axis, 25 million pixels, and 100 MiB of RGBA storage per image. Decompressed ICC profiles are limited to 8,000,000 bytes.
Supported sRGB cICP takes precedence over other PNG color chunks. Otherwise RGB or grayscale ICC v2/v4 monitor profiles are converted to sRGB with Little CMS. iCCP takes precedence over sRGB, gAMA, and cHRM; unsupported cICP falls through to lower-priority metadata.
OpenTUI keeps the eight most recently used validated ICC profiles and sRGB transforms. Profiles are matched by complete decompressed ICC bytes and RGB/grayscale mode, then cleared when the native library is unloaded.
Opaque, orientation-free PNGs retain their encoded bytes without decoding pixels immediately. Unchanged Kitty rendering sends the original PNG, including its ICC profile, as f=100; the terminal performs PNG decoding and color management. Pixel decoding and ICC conversion occur once on demand for raw(), pixel copying, image operations, Sixel, blocks, or a Kitty crop, resize, or opacity change. Consequently, corrupt IDAT data can be accepted for direct Kitty passthrough and fail only if a decoded-pixel path is later requested.
To load and display an encoded source, see Image. To draw an existing or transformed NativeImage, use OptimizedBuffer.drawImage().