Modules

Image

moduleSource
macOSLinuxWindowswasm

since 0.28.0

#import "Extensions/Image";

Decode, encode and transform raster images, in plain Jai (natively, in the playground and in -os wasm programs). A jaic extension, not official Jai. See docs/extensions/image.md.

ExampleOpen in playground ↗
#import "Extensions/Image";
#import "File";

file, found := read_entire_file("photo.png");
image, ok, error := decode_image_as(cast([] u8) file, .RGBA8);
if !ok { print("%\n", error); return; }
defer deinit(*image);
small := resize(image, 64, 64, .BOX);
png := encode_png(small);

This one is only checked to compile, not run.

Show moreShow less

Every procedure that returns an Image or a []u8 allocates it with context.allocator; the caller owns it. Errors are static strings.

Procedures#

General#

make_imagesince 0.28.0
(width: s32, height: s32, format: Pixel_Format) -> Image

A zeroed image of the given size; empty when the size or format is invalid.

decode_imagesince 0.28.0
(data: [] u8, max_pixels: s64 = DEFAULT_MAX_PIXELS) -> (image: Image, ok: bool, error: string)

Decodes a PNG, JPEG, QOI, BMP or PNM file in the format it is stored in (the nearest Pixel_Format to it).

decode_image_assince 0.28.0
(data: [] u8, format: Pixel_Format, max_pixels: s64 = DEFAULT_MAX_PIXELS) -> (image: Image, ok: bool, error: string)

decode_image, then convert to format if the file holds something else.

Convert#

convert_lumasince 0.28.0
(r: s32, g: s32, b: s32) -> u8

Rec. 709 luma in 16-bit fixed point; the weights sum to 65536 so white stays white.

convertsince 0.28.0
(image: Image, format: Pixel_Format) -> Image

The same pixels in another format. Gray is made from colour with Rec. 709 luma, colour from gray by repeating it, alpha is added as 255 and dropped without compositing. Always returns a new image (a copy when the format is already right); an invalid image gives an empty one.

Transform#

resizesince 0.28.0
(image: Image, width: s32, height: s32, filter := Resize_Filter.BILINEAR) -> Image

A new image of width x height. NEAREST picks the closest source pixel. BILINEAR blends the four pixels around each sample point (it does not widen its footprint when shrinking, so use BOX to shrink by much). BOX averages the exact source area under each output pixel. Channels are filtered independently in 14-bit fixed point, so alpha is not premultiplied: colours of transparent pixels bleed into their neighbours. An invalid image or size gives an empty image.

cropsince 0.28.0
(image: Image, x: s32, y: s32, width: s32, height: s32) -> (Image, ok: bool)

The width x height rectangle with its top left corner at (x, y). Not ok when any part of it is outside the image or it is empty.

rotate_90since 0.28.0
(image: Image, clockwise := true) -> Image

A new image turned a quarter turn, clockwise or counter-clockwise; its width is the old height.

premultiply_alphasince 0.28.0
(image: *Image)

Multiplies the colour channels by alpha (rounded), in place. A no-op for formats without alpha.

PNG#

decode_pngsince 0.28.0
(data: [] u8, max_pixels: s64 = DEFAULT_MAX_PIXELS) -> (image: Image, ok: bool, error: string)

Decodes a PNG to the nearest pixel format: GRAY8, GRAY_ALPHA8, RGB8 or RGBA8. Palettes are expanded to RGB8 (RGBA8 with a tRNS chunk), a tRNS colour key on gray or RGB adds alpha, and 1, 2 and 4-bit gray is scaled to 8 bits. 16-bit samples keep their high byte (truncation). Adam7 interlacing is supported. Gamma, colour profiles and other ancillary chunks are ignored, and of an animated PNG only the default image is decoded. Chunk CRCs are checked for the critical chunks and tRNS.

encode_pngsince 0.28.0
(image: Image, level: s32 = 6) -> [] u8

Encodes as an 8-bit PNG: gray, gray with alpha, RGB or RGBA (BGRA is written as RGBA). Each row gets the filter whose output has the smallest sum of absolute values (read as signed bytes): estimated from a sample of the row for levels 1 to 8, over the whole row at level 9, which is slower. level is the deflate level, 0 to 9; 0 stores the rows unfiltered and uncompressed.

JPEG#

decode_jpegsince 0.28.0
(data: [] u8, max_pixels: s64) -> (image: Image, ok: bool, error: string)
jpeg_read_segmentssince 0.28.0
(d: *Jpeg_Decoder, max_pixels: s64) -> string

Walks the marker segments after SOI. Returns an empty string on success.

JPEG huffman#

jpeg_extendsince 0.28.0
(value: s32, size: s32) -> s32

Sign-extends a size-bit magnitude read from the stream.

jpeg_huffman_longsince 0.28.0
(h: *Jpeg_Huffman, bits: u64) -> (symbol: s32, length: s32)

Decodes a code longer than 9 bits (or reports a bad code as symbol 0). bits is the left-aligned bit buffer.

jpeg_get_bitssince 0.28.0
(n: s32) -> s32

Reads n bits, 1 <= n <= 16. At least n bits must be buffered.

jpeg_refillsince 0.28.0
()

Tops the buffer up to at least 32 bits (one code plus its extra bits is at most 31).

jpeg_sync_restartsince 0.28.0
(d: *Jpeg_Decoder, expected: *s32, skipped: *s32) -> bool

Called between restart intervals: drops the buffered bits and finds the next RSTn marker. expected is the number (0..7) the next restart marker should carry; skipped returns how many whole intervals were lost when the marker found is further ahead. False when the entropy data has no further restart marker (the scan is over).

JPEG scan#

JPEG output#

jpeg_idct_blocksince 0.28.0
(block: *s32, last: s32, out: *u8, stride: s64)

The 8x8 inverse DCT is a separable integer transform with 13-bit constants: a column pass that keeps two extra fractional bits, then a row pass that rounds, adds the level shift of 128 and clamps. Columns and rows that hold only a DC term take a shortcut. last is the highest zigzag position that may be non-zero; a block with only a DC term becomes a flat tile.

jpeg_idct_1dsince 0.28.0
(i0: s32, i1: s32, i2: s32, i3: s32, i4: s32, i5: s32, i6: s32, i7: s32, o: *[8] s32)

One 8-point transform scaled by 2^13, a rotation-based factorisation: an even half over inputs 0, 2, 4, 6 and an odd half over 1, 3, 5, 7.

jpeg_row_rgbsince 0.28.0
(r: *u8, g: *u8, b: *u8, out: *u8, n: s64)
jpeg_row_cmyksince 0.28.0
(c: *u8, m: *u8, y: *u8, k: *u8, out: *u8, n: s64)

Four-component rows. Adobe files store inverted ink values, so the colour is the product of the stored value and the stored black.

jpeg_component_rowsince 0.28.0
(d: *Jpeg_Decoder, rows: *Jpeg_Rows, c: s32, y: s32) -> *u8

A full-resolution row of component c: the plane itself, or the upsampled neighbours. Chroma that is halved horizontally (and vertically) gets the triangle filter, other ratios repeat samples.

QOI#

decode_qoisince 0.28.0
(data: [] u8, max_pixels: s64 = DEFAULT_MAX_PIXELS) -> (image: Image, ok: bool, error: string)

Decodes a QOI file to RGB8 (3 channels) or RGBA8 (4 channels).

encode_qoisince 0.28.0
(image: Image) -> [] u8

Encodes as QOI with 3 channels, or 4 when the image has alpha. Gray is written as colour.

BMP#

decode_bmpsince 0.28.0
(data: [] u8, max_pixels: s64 = DEFAULT_MAX_PIXELS) -> (image: Image, ok: bool, error: string)

Decodes BMP files of 1, 4, 8 (palette, optionally RLE), 16, 24 and 32 bits per pixel, with channel masks for 16 and 32. Palettised and 24-bit images become RGB8. 16 and 32-bit images become RGBA8 when they have an alpha mask (or, for 32-bit without masks, any nonzero alpha byte) and RGB8 otherwise.

encode_bmpsince 0.28.0
(image: Image) -> [] u8

Encodes as an uncompressed bottom-up BMP: 24-bit, or 32-bit with an alpha mask (a V4 header) when the image has alpha. Gray is written as colour.

PNM#

decode_pnmsince 0.28.0
(data: [] u8, max_pixels: s64 = DEFAULT_MAX_PIXELS) -> (image: Image, ok: bool, error: string)

Decodes P2/P5 to GRAY8 and P3/P6 to RGB8. Samples are scaled from maxval (1 to 65535) to 8 bits with rounding; binary samples are 16-bit big-endian when maxval exceeds 255.

encode_ppmsince 0.28.0
(image: Image) -> [] u8

Encodes as binary PPM (P6), or PGM (P5) for gray; alpha is dropped.

Types#

Pixel_Formatenumsince 0.28.0
:: enum

How the bytes of one pixel are laid out. Channels are 8 bits; decoders reduce deeper samples.

Imagestructsince 0.28.0
:: struct

Rows are packed top to bottom with no padding: pixel (x, y) starts at (y * width + x) * bytes_per_pixel(format).

Resize_Filterenumsince 0.28.0
:: enum

How resize samples the source; see resize.

Constants#

DEFAULT_MAX_PIXELSsince 0.28.0
:: 1 << 26

Decoders refuse images with more pixels than this, before allocating them.

JPEG_ZIGZAGsince 0.28.0
:: u8.[ 0, 1, 8, 16, 9, 2, 3, 10, 17, 24, 32, 25, 18, 11, 4, 5, 12, 19, 26, 33, 40, 48, 41, 34, 27, 20, 13, 6, 7, 14, 21, 28, 35, 42, 49, 56, 57, 50, 43, 36, 29, 2 ...