I’m very happy to announce that, after lots of testing and improvement, vips-ffm is now version 1.0 πŸŽ‰!

In case you missed the last post, vips-ffm is a set of bindings, designed for the JVM, that lets you use the popular libvips1 library for image manipulation - think image thumbnails, cropping, resizing, and such. JNI-based alternatives like JVips exist, but have not been updated for a couple of years, and Java now offers safer, faster native library access via the Foreign Function and Memory (FFM) API2.

Improvements since the last post include (around a hundred commits):

  • Production users giving really helpful feedback to test and shape the API - thank you!
  • Many more samples testing all sorts of functionality on macOS, Windows, and Linux (including arm64/amd64 architectures)
  • Comprehensive Javadocs for all libvips operations and enums, also hosted on a website
  • Rewritten using the libvips operations API to make the bindings even safer
  • 100% operation and enum coverage for libvips, thanks to automation, with no custom JNI code
  • Preliminary benchmarks showing speed improvements of 10-35% compared to JVips, and 50-70% compared to AWT

I really wanted to make this library the best way to use libvips with JVM systems, including Kotlin, and I’m super happy with how it’s shaped up. I hope it’s useful for others! Go check the repo out, and give it a star to lend me some dopamine 🌟!

Here’s what a Kotlin sample (using the Java bindings) looks like in the 1.0 release:

import app.photofox.vipsffm.Vips
import app.photofox.vipsffm.VImage
import app.photofox.vipsffm.VipsOption
import app.photofox.vipsffm.enums.VipsAccess

// ...

// Call once to initialise libvips when your program starts, from any thread
Vips.init()

// Use `Vips.run` to wrap your usage of the API, and get an arena with an appropriate lifetime to use
// Usage of the API, arena, and resulting V-Objects must be done from the thread that called `Vips.run`
Vips.run { arena ->
    val sourceImage = VImage.newFromFile(
      arena,
      "sample/src/main/resources/sample_images/rabbit.jpg",
      VipsOption.Enum("access", VipsAccess.ACCESS_SEQUENTIAL)
    )
    val sourceWidth = sourceImage.width
    val sourceHeight = sourceImage.height
    logger.info("source image size: $sourceWidth x $sourceHeight")

    val outputPath = workingDirectory.resolve("rabbit_copy.jpg")
    sourceImage.writeToFile(outputPath.absolutePathString())

    val thumbnail = sourceImage.thumbnail(
      "sample/src/main/resources/sample_images/rabbit.jpg",
      400
    )
    val thumbnailWidth = thumbnail.width
    val thumbnailHeight = thumbnail.height
    logger.info("thumbnail image size: $thumbnailWidth x $thumbnailHeight")
}

// Optionally call at the end of your program, for memory leak detection, from any thread
Vips.shutdown()

Releasing version 1.0 is an indication that I feel it’s good enough for production usage. I’ll continue to make improvements and fix bugs as more people start using it, and give feedback.


  1. libvips - https://www.libvips.org/Β 

  2. JEP 454 - Foreign Function & Memory API - https://openjdk.org/jeps/454Β 

« Announcing vips-ffm - libvips Bindings Using JDK 22's FFM API πŸŽ‰