libvips Parity Roadmap

The core is done. libviprs already implements the full core image-processing surface of libvips — arithmetic, conversion, morphology, convolution, colour, resample, histogram, create, draw, mosaicing, frequency filtering, band and matrix operations — as ~168 CLI commands / 247 API operations, every one pinned differential- or golden-tested against a libvips 8.18.4 oracle. What remains on the road to full libvips parity is not core processing: it is missing file formats, a short tail of niche operations, and deeper numeric pixel carriers (signed, 32-bit, double, and complex band formats). This page tracks exactly that remainder, phased by dependency and demand.

~62% of libvips 8.18.4's ~262 distinct callable operations shipped (~163); ~67% crediting shipped format I/O
~87% of non-codec operations covered (188 of 215 addressable: 163 direct commands + 25 subsumed via colourspace/conv folds)
8 / ~22 format families with real codecs (PNG, JPEG, TIFF, PDF, CSV, matrix, PPM, .v) — ~55–60% weighted by real-world demand
3 / 10 band-format depths carried natively (u8, u16, f32) — the numeric axis closed by issues #516#519

A small set of libvips operations is excluded by design and never planned: system (external command execution), sequential / tilecache / linecache (libvips demand-cache plumbing, replaced by the viprs planner and streaming engine), and the thumbnail_buffer / thumbnail_source streaming variants. libvips load-vs-save asymmetries also pin the target honestly: SVG, OpenSlide, camera RAW, and OpenEXR are load-only in libvips itself, so libviprs owes no encoder there.

Phase 1 — Near-term

Constraint: pure Rust only. No new C dependencies, no new pixel carriers required (the #517 uint carrier kicks off in parallel). Phase 1 closes the bulk of remaining everyday format demand — lifting format-family coverage to 12/22 — and 10 missing op nicknames, taking non-codec op coverage to ~92%.

Formats

Gapvips reference / C liblibviprs todayEffortPriorityTracking
WebP — still decode + lossless encode webpload / webpsave (libwebp) Typed-Unsupported stub. Pure-Rust path: image-webp via the existing image 0.25 dep. Lossy encode has no mature pure-Rust encoder (libwebp FFI later, or ship lossless-first); animated WebP moves to Phase 2 behind the multi-page model. M P0 #497
GIF — still load + save gifload / gifsave (libnsgif / cgif in vips) Stub. Pure-Rust gif crate covers still decode/encode incl. palette quantisation; animated GIF moves to Phase 2 behind the multi-page model. M P0 #499
SVG — load (rasterise) svgload (librsvg in vips); load-only — vips has no svgsave Absent. Pure-Rust resvg reaches parity without C; no encoder owed. M P0 #502
Radiance HDR (.hdr/.pic) load + save radload / radsave (native vips code, RGBE coding); includes the rad2float / float2rad coding pair Stub. Pure-Rust reachable; f32 carrier already exists. S P1 #506

Operations

Gapvips reference / C liblibviprs todayEffortPriorityTracking
sobel / scharr / prewitt named edge operators VipsSobel / VipsScharr / VipsPrewitt (native vips VipsEdge subclasses; fixed separable kernel pairs + magnitude combine) Missing — but conv / convsep / compass (9 base convolution ops) exist to build on; uchar path can be EXACT-oracle. Three ops share one kernel/magnitude core — do first. M P1 #512
canny edge detector canny (native vips: gaussian blur → gradient → non-max suppression) Missing; builds directly on the #512 kernel/magnitude core. M P1 #511
matrixmultiply matrixmultiply (native vips) Missing; matrix container (.mat text) and matrix ops already shipped. S P2 #513
join + 2-image remainder join / remainder (native vips) Missing as generic 2-image forms; constant-remainder and insert/embed primitives exist. S P2 #514
Oklab / Oklch — residual only colourspace Oklab/Oklch routes (native vips, Ottosson matrices) ~80% already done: viprs colourspace … oklab and oklch verified bit-exact against vips (max-abs-diff 0.000000). Only the packed LabQ coding route fails; issue to be re-scoped to that residual (full LabQ exactness lands with the Phase 3 carriers). S P2 #515

Pixel carriers — kickoff

Gapvips reference / C liblibviprs todayEffortPriorityTracking
uint (u32) carrier — starts alongside Phase 1 VipsBandFormat uint (native vips band format + promotion rules) Absent; cast rejects the target. Pure-Rust enum/codec/op work; first of the four foundational carriers (see Phase 3) and biggest oracle-class payoff together with #516. L P0 #517

Phase 2 — Mid-term

Theme: the multi-page/animation raster model, plus the first optional FFI codecs. The libvips n-pages "toilet-roll" model is shared infrastructure, not a codec — it gates animated GIF, animated WebP, and HEIF sequences all at once. New C bindings (libheif, libwebp-lossy) stay behind off-by-default Cargo features, preserving the pure-Rust default build.

Formats

Gapvips reference / C liblibviprs todayEffortPriorityTracking
Multi-page / animation raster model (shared infrastructure) vips n-pages "toilet-roll" representation (page-height metadata over a tall raster) Absent; prerequisite for animated GIF/WebP and HEIF sequences. Not a codec — lands once, unblocks three formats. L P0 #497 / #499
Animated GIF + animated WebP (completes #497/#499) gifload/gifsave, webpload/webpsave with n/page args Still halves land in Phase 1; animation follows the multi-page model. M P1 #497 / #499
HEIF / AVIF load + save heifload / heifsave (libheif + libde265/x265 or libaom) Stub. No mature pure-Rust HEVC path — libheif FFI behind an off-by-default feature; AVIF decode has partial pure-Rust options. HEIF sequences need the multi-page model. L P1 #498
JPEG-XL load + save jxlload / jxlsave (libjxl) Stub. Pure-Rust decode reachable (jxl-oxide); encode needs libjxl FFI or lossless-only first. L P2 #500
OpenEXR load openexrload (OpenEXR C++ lib); load-only in vips — no save op exists Stub. Pure-Rust exr crate reaches parity; f32 carrier exists. M P2 #504

Operations — unfiled gaps (to be filed as issues)

Nine further op workstreams were found in the 8.18.4 diff with no tracking issue yet. They are recorded here so nothing silently drops; each will get its own issue before work starts.

Gapvips referenceNotesTracking
invert (photographic negative)invertExplicitly documented as absent in OP_MAP.md; simple on existing carriers.unfiled
case (image-array form)caseScalar/two-image forms shipped; N-image-array selector missing.unfiled
fill_nearestfill_nearestMorphology-adjacent; existing distance-transform infra to build on.unfiled
conva / convasepconva / convasepApproximate convolution variants; exact conv/convsep already shipped.unfiled
quadraticquadraticPolynomial resample transform.unfiled
transpose3dtranspose3dToilet-roll page transpose; pairs naturally with the Phase 2 multi-page model.unfiled
match / mosaic1match / mosaic1First-order mosaicing extras; core mosaic/merge shipped.unfiled
profile_loadprofile_loadNamed ICC profile loader.unfiled
complex2 and remosaiccomplex2 / remosaicComplete only after Phase 3: complex2 needs the native complex carrier (#519); remosaic needs the join-tree metadata channel.unfiled

Phase 3 — Foundational carriers & long tail

The pixel carriers are foundations, not afterthoughts. They sit in Phase 3 only because nothing in Phases 1–2 hard-blocks on them — libviprs deliberately ships subset surfaces (cast rejects unsupported targets with a typed error rather than deferring ops). But they are what upgrades ~43 OP_MAP.md rows from EXACT-AFTER-CAST / FOURIER / GOLDEN-ONLY oracle classes to fully EXACT, and they gate the scientific formats below. Recommended landing order: #516 and #517 first (independent, biggest payoff; #517 starts back in Phase 1), then #518, then #519. All four are pure-Rust; no C bindings needed.

Pixel carriers

Gapvips reference / C liblibviprs todayEffortPriorityTracking
Signed integer carriers (char / short / int) VipsBandFormat char/short/int + full promotion lattice Absent — source of most of the 29 EXACT-AFTER-CAST oracle rows. Blast radius: pixel.rs exhaustive matches, stride math, .v codec, round-half-even save-cast rules, every promotion table, test-harness oracle classes. L P0 #516
uint (u32) carrier — completion VipsBandFormat uint Kickoff in Phase 1; full-surface promotion/oracle work completes here (e.g. the hist_match uchar-vs-uint LUT GOLDEN-ONLY row). L P0 #517
double (f64) carrier VipsBandFormat double Absent; prerequisite for the dpcomplex half of #519 and for FITS/scientific formats. L P1 #518
Native complex carriers (complex / dpcomplex) VipsBandFormat complex/dpcomplex (vips FFT side uses fftw3; viprs uses rustfft) Worked around today as float-pair Fourier bands in the .v container — the cause of all 12 FOURIER oracle rows. dpcomplex half hard-depends on #518. XL P1 #519

Formats — niche & scientific

Gapvips reference / C liblibviprs todayEffortPriorityTracking
JPEG 2000 load + save jp2kload / jp2ksave (OpenJPEG) Stub; pure-Rust JP2K options are immature — likely optional OpenJPEG FFI. L P2 #501
OpenSlide load (whole-slide images) openslideload (libopenslide); load-only in vips Absent; C FFI behind an optional feature. No encoder owed. L P2 #503
FITS load + save (astronomy) fitsload / fitssave (cfitsio) Stub; gated on the signed (#516) and double (#518) carriers. M P2 #505
Camera RAW load rawload-style dcraw path (libraw/dcraw); load-only in vips Absent; no encoder owed. Pure-Rust rawloader-class crates partially reach parity. L P2 #507
Ultra HDR (gain-map JPEG) load + save uhdrload-family (libultrahdr); includes the uhdr2scRGB coding op Absent; builds on the shipped JPEG codec plus gain-map metadata. M P2 #508
Scientific volumes — MAT / NIfTI / Analyze load matload / niftiload / analyzeload (matio / niftiio) Absent; gated on the #516/#518 carriers. Caveat: NIfTI is not compiled into the local vips reference build, so that half has no local differential oracle (golden-only). M P2 #510
ImageMagick fallback loader magickload / magicksave (ImageMagick/GraphicsMagick) Deliberate strategic fork: ImageMagick FFI contradicts the pure-Rust / untrusted-input posture. Recommendation on the issue: formally scope out, or gate behind an off-by-default FFI feature. scope decision #509

How this roadmap was grounded

Tracking: all open issues · formats #497#510 · operations #511#515 · pixel carriers #516#519.