NURLNURL registrynurl-lang.org →

← image

image 0.4.0 API

jpeg_enc.nu

image/jpeg_enc.nu — baseline JPEG (JFIF) encode.

Writes the interoperable core of the format: baseline sequential DCT, 8-bit, Huffman-coded with the ITU T.81 Annex K standard tables, Annex K quantisation tables scaled by a libjpeg-style quality factor (1–100). Greyscale images (1/2 channels) encode as one component; colour (3/4) as YCbCr with 4:4:4, 4:2:2 or 4:2:0 subsampling — chroma is box-averaged before encoding. Alpha channels are dropped.

( jpeg_encode im 90 ) → ( Vec u ) 4:2:0 below quality 90, 4:4:4 from 90 up ( jpeg_encode_sub im 85 2 ) → ( Vec u ) subsampling forced: 0 = 4:4:4, 1 = 4:2:2, 2 = 4:2:0

A decode→encode→decode round trip stays within normal JPEG loss for the chosen quality; the output decodes in libjpeg/Pillow and in this package's own jpeg_decode.

API

: JEnc

: JEnc {
    ( Vec u ) out
    i bbuf  i bcnt
    ( Vec i ) qy  ( Vec i ) qc            // quant tables, zigzag order
    ( Vec i ) ydc_co ( Vec i ) ydc_si
    ( Vec i ) yac_co ( Vec i ) yac_si
    ( Vec i ) cdc_co ( Vec i ) cdc_si
    ( Vec i ) cac_co ( Vec i ) cac_si
    ( Vec f ) A  ( Vec i ) zz
}

@ jpeg_encode_sub Image im i quality i subsamp → ( Vec u )

subsamp: 0 = 4:4:4, 1 = 4:2:2, 2 = 4:2:0 (ignored for greyscale).

@ jpeg_encode Image im i quality → ( Vec u )

Quality-driven default: 4:2:0 below 90, 4:4:4 from 90 up (mirrors what mainstream encoders pick at high quality).


jpeg.nu

image/jpeg.nu — JPEG decode: baseline AND progressive (Huffman).

Supports the JPEGs real encoders emit: 8-bit precision, Huffman coding, 1 component (greyscale) or 3 (YCbCr → RGB), any 4:4:4 / 4:2:2 / 4:2:0 / 4:1:1 subsampling, restart intervals (DRI/RSTn), sequential (SOF0/SOF1) and progressive (SOF2) frames — spectral selection and successive approximation, DC and AC first/refinement scans, EOB runs. Chroma planes use libjpeg's "fancy" (triangular) upsampling for 2x1/2x2 expansion — bit-matching libjpeg's default path — and a separable float IDCT, so output matches a reference decoder (libjpeg/Pillow) to within IDCT rounding — JPEG is lossy and the spec permits IDCT variation, so this is not bit-exact.

Not supported (clean None, never an out-of-bounds read): arithmetic coding, 12-bit, lossless/hierarchical, and 4-component (CMYK/YCCK).

API

: Jpeg

: Jpeg {
    ( Vec u ) buf i len
    i width i height i ncomp
    ( Vec i ) cid ( Vec i ) chf ( Vec i ) cvf ( Vec i ) ctq ( Vec i ) ctd ( Vec i ) cta ( Vec i ) cpred
    i hmax i vmax i restart
    ( Vec i ) qt
    ( Vec i ) hmin ( Vec i ) hmaxc ( Vec i ) hptr ( Vec i ) hvals
    ( Vec f ) idct_a ( Vec i ) zz
    i bpos i bbuf i bcnt i marker
    b ok
    // progressive state: scan parameters, per-component coefficient grids
    b prog i ss i se i ah i al i eobrun
    i nscomp ( Vec i ) scomp
    ( Vec ( Vec i ) ) coefs
    ( Vec i ) cbw ( Vec i ) cbh  // padded block-grid dims per comp
}

@ jpeg_decode ( Vec u ) buf → ?Image


image.nu

image — pure-NURL image codecs. One include for the whole package: the Image raster, PPM (P5/P6), PNG decode + encode, and file load/save with format sniffing. (Baseline JPEG decode is planned as a later milestone.)

$ deps/image/src/image.nu ?? ( image_load photo.png ) { T im → { // … use im (width/height/channels/data) … ( image_save_png out.png im ) ( image_free im ) } F → { ( nurleprintln decode failed ) } }

API

@ image_decode ( Vec u ) buf → ?Image

Decode by sniffing the magic bytes: PNG signature → png_decode, JPEG SOI (FF D8) → jpeg_decode, P → PPM.

@ image_load s path → ?Image

Load and decode a file. None on an I/O error or an unrecognised/malformed format.

@ image_save_png s path Image im → b

@ image_save_jpeg s path Image im i quality → b

Write a baseline JPEG (quality 1–100; 4:2:0 below 90, 4:4:4 from 90 up).

@ image_save_ppm s path Image im → b


core.nu

image/core.nu — the Image type, byte helpers, and PPM (P5/P6).

An Image is a tightly-packed, row-major, 8-bit-per-channel raster: channels 1 = grayscale, 3 = RGB, 4 = RGBA. data holds widthheightchannels bytes. Every codec in the package decodes into, and encodes from, this one representation.

API

: Image

: Image {
    i width
    i height
    i channels
    ( Vec u ) data
}

: ~ s __img_err ``

@ image_error → s

@ image_new i w i h i ch → Image

A zero-filled image of the given geometry.

@ image_of i w i h i ch ( Vec u ) data → Image

Adopt an existing byte buffer (moved in; not copied).

@ image_free Image im → v

@ image_width Image im → i

@ image_height Image im → i

@ image_channels Image im → i

@ image_get Image im i x i y i c → i

@ image_set Image im i x i y i c i v → v

@ ppm_decode ( Vec u ) buf → ?Image

@ ppm_encode Image im → ( Vec u )

Encode as binary PPM: 3/4-channel → P6 (alpha dropped), 1-channel → P5.


png.nu

image/png.nu — PNG decode + encode over the stdlib DEFLATE codec.

Decode: the full still-image feature matrix — every legal bit depth (1 / 2 / 4 / 8 / 16, deeper samples scaled to the 8-bit raster), every colour type (grey, RGB, palette → RGB, grey+alpha, RGBA), Adam7 interlacing, and tRNS transparency (palette alpha and grey/RGB colour keys, which add an alpha channel to the output). All five scanline filters are reconstructed at any pixel width. Encode: 8-bit, non-interlaced, filter 0, colour type chosen from the image's channel count. Lossless — a decode→encode→decode round-trip reproduces the pixels exactly.

png_decode returns None on malformed input; the codec never reads out of bounds.

API

@ png_decode ( Vec u ) buf → ?Image

@ png_encode Image im → ( Vec u )

Encode an image as a non-interlaced, filter-0, 8-bit PNG.


ops.nu

image/ops.nu — raster operations on the Image type.

Everything a downstream package used to hand-roll: resize (nearest / bilinear / box-average), crop, flips, quarter-turn rotations, channel conversion, fill, rectangle / line drawing, and blit with alpha.

Colours are one packed integer, 0xRRGGBBAA (e.g. opaque red 0xFF0000FF), mapped onto whatever channel count the target image has: greyscale targets take the BT.601 luma, images without an alpha channel drop A. Reads from a greyscale image replicate the grey value into R, G and B; a missing alpha channel reads as 255.

Transform functions (image_resize*, image_crop, flips, rotations, image_convert, image_clone) return a NEW image — free both. Drawing functions (image_fill*, image_draw_*, image_blit) mutate in place, clip to the raster, and overwrite (no blending) — except image_blit, which source-over-blends when the source has an alpha channel.

API

@ image_get_rgba Image im i x i y → i

Read pixel (x,y) as packed 0xRRGGBBAA regardless of the image's channel count (grey replicates into R/G/B, missing alpha reads 255). Out of bounds every stored channel reads 0.

@ image_set_rgba Image im i x i y i rgba → v

Write packed 0xRRGGBBAA into pixel (x,y), mapping onto the image's channels (grey targets take the luma; a missing alpha channel drops A). Out-of-bounds writes are ignored.

@ image_clone Image im → Image

@ image_convert Image im i ch → Image

Convert to ch channels (1 grey, 2 grey+alpha, 3 RGB, 4 RGBA). Grey ↔ RGB uses BT.601 luma; added alpha is 255; dropped alpha is dropped.

@ image_crop Image im i x i y i w i h → Image

Copy the w×h window whose top-left corner is (x,y). The window is clipped to the source; a fully-outside window yields a 0×0 image.

@ image_flip_h Image im → Image

@ image_flip_v Image im → Image

@ image_rot90 Image im → Image

Clockwise quarter turns.

@ image_rot180 Image im → Image

@ image_rot270 Image im → Image

@ image_resize_nearest Image im i nw i nh → Image

Nearest neighbour: out(x,y) = in( xsw/nw, ysh/nh ). Exact, blocky.

@ image_resize Image im i nw i nh → Image

Bilinear, edge-clamped, pixel-centre aligned. The right default for upscaling and mild downscaling; for thumbnails (large shrink) prefer image_resize_area, which averages instead of skipping.

@ image_resize_area Image im i nw i nh → Image

Box average: each output pixel is the mean of its source rectangle. Exact anti-aliased downscale for integer factors (a 2× shrink averages each 2×2 block); the right choice for thumbnails.

@ image_fill Image im i rgba → v

@ image_fill_rect Image im i x0 i y0 i x1 i y1 i rgba → v

Fill the rectangle with corners (x0,y0)–(x1,y1), inclusive.

@ image_draw_rect Image im i x0 i y0 i x1 i y1 i t i rgba → v

Rectangle outline with corners (x0,y0)–(x1,y1) inclusive, t pixels thick, growing inward.

@ image_draw_line Image im i x0 i y0 i x1 i y1 i rgba → v

Bresenham line from (x0,y0) to (x1,y1), clipped per pixel.

@ image_blit Image dst Image src i dx i dy → v

Copy src onto dst with its top-left corner at (dx,dy), clipped to the destination. Channel counts may differ (pixels are mapped like image_set_rgba). If the source has an alpha channel the copy is source-over blended; otherwise it overwrites.


main.nu

img — the image package's command-line tool.

img info <file> format-sniffed decode + geometry img convert <in> <out> [-q N] transcode; format from <out>'s extension (.png .jpg .jpeg .ppm .pgm) img resize <in> <out> <W>x<H> [-q N] resize + transcode. 800x600 exact, 800x / x600 keep aspect. Shrinks use box averaging (clean thumbnails), growth uses bilinear.

-q N JPEG quality 1–100 (default 90)

Exit codes: 0 ok · 1 processing error · 2 usage.

API

@ main → i