Rand Stats

Vips::Native

zef:apogee

Actions Status

NAME

Vips::Native - Raku FFI wrapper for libvips with bundled prebuilts

SYNOPSIS

use Vips::Native;

# 1. Smart-crop + resize to fixed output dimensions, saved as PNG.
smart-resize("input.png", "thumb.png", 128, 128);

# 2. Letterbox-resize to a fixed square + return raw 8-bit RGB pixels.
#    Aspect ratio preserved; gap filled with the chosen background.
#    Output is suitable to feed to ML inference engines.
my Buf[uint8] $bytes = letterbox-to-buffer(
    'photo.png'.IO,
    :size(448),
    :bg(255, 255, 255),
);
say $bytes.bytes;   # 448 × 448 × 3 = 602112

DESCRIPTION

Vips::Native is a Raku NativeCall wrapper around libvips, shipped with prebuilt platform bundles so the common case (install, import, run) doesn't require a working libvips toolchain on the host.

The module exposes two high-level helpers:

The full (variadic) libvips C API is available via direct is native bindings under Vips::Native::FFI — see the module's SYNOPSIS for the bindings exported. Most of those go through a tiny non-variadic C shim (src/vips_native_shim.c) for ABI safety on Apple arm64 / Linux aarch64, where Raku's NativeCall can't marshal C variadics correctly.

INSTALLATION

Vips::Native tries three lookup paths at install + load time, in order:

Supported platforms

Every supported platform ships a working bundle with the varargs shim included.

Build-time environment

EXTERNAL API

smart-resize

sub smart-resize(
    Str $in-path,
    Str $out-path,
    Int $out-width,
    Int $out-height,
    --> Bool
) is export

Smart-crop the source image to match the output aspect ratio (using libvips's VIPS_INTERESTING_ATTENTION saliency model — picks the visually-important rectangle, not just the centre), then Lanczos3- resize the cropped image to $out-width × $out-height and save as PNG to $out-path.

Returns True on success; fails with the underlying error if libvips errors out.

A vips_smart_resize shell binary is also installed:

vips_smart_resize --in=/path/to/image.png --out=/path/to/save.png \
                  --width=180 --height=180

letterbox-to-buffer

sub letterbox-to-buffer(
    IO::Path() $path,
    UInt :$size = 448,
    :@bg = (255, 255, 255),
    --> Buf[uint8]
) is export

Resize $path to fit inside a $size × $size square while preserving aspect ratio, pad the unfilled region with the @bg colour, composite alpha over that colour for RGBA inputs, and return the raw 8-bit RGB pixel buffer in NHWC byte order. Length is always exactly $size × $size × 3.

The preprocess pipeline image classifiers want: deterministic gap fill, fixed input dimensions, no GC entanglement with libvips's heap. Pair with ONNX::Native or your inference engine of choice to feed RGB pixels directly.

The buffer is owned by the caller — internally we copy from the vips_image_write_to_memory result into a Raku-owned buf8 and release the source via vips_shim_free, so there's no lifetime trap.

Inputs may be 3-band RGB or 4-band RGBA (UCHAR pixels only — 16-bit PNG / HDR / float images are out of scope for v1). Greyscale and CMYK fail loudly with details.

Vips::Native::FFI

The complete (variadic + non-variadic) libvips C bindings are exported from Vips::Native::FFI:

vips_flatten and vips_embed require the libvips_shim (their options take a VipsArrayDouble * that's awkward to construct directly from Raku). Every prebuilt bundle includes it; if you're running against system libvips, the shim is compiled at install time via the platform's C toolchain and falls back gracefully if no toolchain is available.

AUTHOR

COPYRIGHT AND LICENSE

Copyright 2025-2026 Matt Doughty.

This library is free software; you can redistribute it and/or modify it under the Artistic License 2.0.