# Introduction

`ol.vips` wraps libvips’s image graph in
[Closeable](https://docs.oracle.com/en/java/javase/25/docs/api/java.base/java/io/Closeable.html)
Clojure values. Loader functions such as `v/from-file`, `v/from-buffer`,
and `v/from-stream` return image handles, and operations such as
`ops/resize`, `ops/colourspace`, and `v/call` return new handles rather
than mutating the original image.

This model is immutable and pipeline-oriented. In the common case,
libvips reads only enough of the input to discover image metadata and
build an operation graph. Calling loaders and transforms usually does
not decode the whole image up front.

libvips is demand-driven, so the pixel work happens when something
downstream asks for image data. In `ol.vips`, that usually means a sink
such as `v/write-to-file`, `v/write-to-buffer`, or `v/write-to-stream`.
At that point libvips pulls pixels through the pipeline, processing
small regions with a horizontally threaded execution model instead of
materializing every intermediate image in memory.

Some operations that need actual pixel values can trigger evaluation
earlier, but metadata queries such as `v/width`, `v/height`, and
`v/metadata` can often be satisfied from loader metadata alone. Because
the image graph is immutable and operations are generally side-effect
free, libvips can also reuse and cache operation results internally.

`ol.vips` adds a small amount of Clojure-specific structure on top of
that model. The `ol.vips.operations` namespace is generated from libvips
introspection data, `ol.vips.enums` exposes the enum keywords used by
many options, and the top-level `ol.vips` namespace provides the file,
buffer, stream, metadata, and convenience helpers you use most often.

## Why libvips?

The typical libvips workflow is exactly what many applications need:
load a large source image, resize or transform it, and write a smaller
web-friendly JPEG, PNG, WebP, AVIF, or TIFF result. `ol.vips` gives
Clojure access to that model without shelling out to external image
tools or spawning child processes.

libvips is
https://github.com/libvips/libvips/wiki/Why-is-libvips-quick[blazing
fast] and
https://github.com/libvips/libvips/wiki/Speed-and-memory-use[memory
efficient] because it usually works on small regions of uncompressed
image data at a time, takes advantage of multiple CPU cores, and avoids
materializing every intermediate result in memory. That execution model
is a strong fit for image pipelines, thumbnails, upload processing,
preprocessing for vision workloads, and other server-side image tasks.

It also brings broad format and operation support:
[300 operations](https://www.libvips.org/API/current/func-list.html)
covering arithmetic, histograms, convolution, morphology, colour,
resampling, statistics, and more, with correct handling of colour
spaces, embedded ICC profiles, and alpha channels.

[libvips](https://github.com/libvips/libvips) was originally created at
Birkbeck College and is currently maintained by
[John Cupitt](https://github.com/jcupitt) and other contributors.

### Bindings Generation

libvips itself is very amenable to automatic bindings generation. The
core of `ol.vips` is small introspection layer that loads the native
library then introspects it to generate `ol.vips.operations` and
`ol.vips.enums`, and then a small runtime for loading/reading/writing.

As a convenience for Clojure users there are separate jars available
with the native libraries per-platform, but `ol.vips` it self does not
depend on them directly. You can depend on one (or more) of those native
deps, or provide your own libvips build with `-Dol.vips.native.preload`.
You’ll need to choose one.

This means that you can even use a custom libvips build or a newer
version of libvips without having to wait for `ol.vips` to catch up
(though I expect to keep it up to date). Any new or updated operations
in libvips can be used with this library using the lower level
`ol.vips/call` function. You can even generate the bindings yourself if
you need to, simply use `ol.vips.codegen`.

## Going to Production

See [Going to Production](https://docs.outskirtslabs.com/ol.vips/next/going-to-production)
for production guidance on pipeline shape, sequential access, untrusted
inputs, sanity checks, allocator tuning, and cache settings in `ol.vips`.

See [Examples](examples.adoc)
for runnable snippets that cover file, buffer, stream, metadata,
thumbnailing, transforms, composition, and animated-image workflows.

## Loading the native library

`ol.vips` uses this procedure to load the native components:

1. Load any explicit libraries listed in `-Dol.vips.native.preload`.
2. Load the extracted libraries from the platform native jar on the
classpath.
3. If that fails, fall back to the system library loader, which can
resolve `libvips` from `LD_LIBRARY_PATH`.

### 1. Preload

`-Dol.vips.native.preload` is for exact native library file paths. Use
it when you want to point `ol.vips` at a specific custom build in a
non-standard location, or when you need to preload dependency libraries
before libvips itself. On Nix-like systems that can include things like
`libstdc{plus}{plus}.so.6` as well as your `libvips` library. The value
is a single string containing one or more full library file paths
separated by the OS path separator, which is `:` on Linux and macOS and
`;` on Windows.

If `ol.vips.native.preload` is set, those entries are always loaded
first. If any preload entry fails to load, the packaged native-jar path
is abandoned for that initialization attempt and `ol.vips` falls back to
the system library loader instead of continuing with the native jar.

### 2. Classpath native bundle

`ol.vips` detects the current platform, builds a resource path like
`ol/vips/native/<os>-<arch>` on macOS and Windows or
`ol/vips/native/<os>-<arch>-<libc>` on Linux, and then looks
for `manifest.edn` under that path.

For example, Linux x86-64 glibc resolves to
`ol/vips/native/linux-x86-64-gnu/manifest.edn`. If that resource is
present, `ol.vips` reads the manifest, extracts the bundled native
libraries into the local cache, and loads those extracted files.

You can override parts of that platform detection with
`-Dol.vips.native.platform-id`, `-Dol.vips.native.os`,
`-Dol.vips.native.arch`, and `-Dol.vips.native.libc`.

In practice, this means the companion jar for the current runtime
platform needs to be on the classpath, meaning the current OS, CPU
architecture, and Linux libc when applicable. This path is attempted
after any explicit `ol.vips.native.preload` entries.

In the successful packaged case, `v/init!` will report
`:native-load-source :packaged` and expose the extracted primary library
path in `:primary-library-path`, which is useful when debugging exactly
what got loaded.

### 3. System fallback

The system-library fallback loads by library name rather than full file
path. Use this when you want the OS or JVM loader to resolve libvips
from `LD_LIBRARY_PATH`, standard system locations, or other
platform-specific loader configuration. A platform native jar is not
required for this path; if the packaged load fails, including because no
matching native jar is present on the classpath, `ol.vips` can still
fall back to the system loader.

By default the system fallback tries `vips-cpp` and then `vips`. Most
users should not need to change this. `-Dol.vips.native.system-libs`
exists for the uncommon case where your environment exposes libvips
under different system library names. Like `ol.vips.native.preload`, it
accepts multiple entries separated by the OS path separator.

The fallback only applies to native library loading. If the packaged
libraries load successfully but `vips_init` fails afterwards,
`ol.vips` does not automatically retry via the system loader. For
debugging, `v/init!` exposes runtime state including whether
initialization came from the packaged path or the system fallback.
