Library / SDK
kleisauke/net-vips avatar
kleisauke/net-vips

NetVips: a .NET binding for libvips that streams instead of buffering

.NET binding for libvips.

535 stars39 forksC#MIT

At a glance

What is it?
NetVips is a .NET binding for the libvips image processing library, and its selling point is that a program using it does not manipulate images directly but builds a pipeline of operations that executes in one pass, streaming the image in parallel from source to destination. The readme reports it around 26 times faster than Magick.NET and 4 times faster than ImageSharp on its own benchmark.
Who is it for?
Adopt NetVips if you process images in .NET at a volume where the difference between streaming and buffering shows up in memory or latency, because that is the problem libvips was built to solve and the binding exposes it directly. Do not adopt it if you need a library with no native dependency at all, because the shared library has to be present on the machine.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 25 days ago.
What is it written in?
Mainly C#, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

The model: pipelines rather than operations

The readme's explanation of how it works is short and it is the part to understand before anything else.

Programs that use NetVips don't manipulate images directly, instead they create pipelines of image processing operations building on a source image. When the end of the pipe is connected to a destination, the whole pipeline executes at once, streaming the image in parallel from source to destination a section at a time.

That is a different mental model from the usual image library, where you call resize on an image and get a new image back. Here the intermediate results do not exist as objects you hold. The library works out the order of operations, fuses them into one pass, and only the final destination is written out. A shrink followed by a sharpen is one traversal of the data rather than two.

The two benefits follow directly and the readme states them as a pair. Because NetVips is parallel, it's quick, and because it doesn't need to keep entire images in memory, it's light.

The benchmark the readme points at is the NetVips.Benchmarks project, and the test is described: loads a large image, shrinks by 10 percent, sharpens, and saves again. On that test NetVips is around 26 times faster than Magick.NET and 4 times faster than ImageSharp.

The readme also links the libvips documentation and specifically its chapter on how libvips opens files, which it says gives some more background. That pointer is a good sign of where the real complexity lives: not in the .NET side, which is a binding, but in the way the native library decides how to open a file, which is where a lot of the streaming behaviour comes from.

The claim of correctness is worth noting too. The readme says the binding passes the vips test suite cleanly with no leaks on Windows, macOS and Linux, and that is a much stronger statement than a passing unit test suite of your own. The upstream suite is the same one the native library is developed against, so passing it cleanly is a meaningful compatibility signal rather than a marketing phrase.

Supported platforms and the native packages

The supported platforms list is short. .NET Framework 4.6.2 and higher, and .NET 6.0 and higher.

Installation needs the libvips shared library on your library search path, version 8.2 or later, and the readme points at a separate repository for the packaging details. But the useful part is that you usually do not have to arrange the shared library yourself, because there are separate NuGet packages containing the pre-compiled libvips binaries for the most common platforms.

The table covers ten combinations: Windows 64-bit, Windows 32-bit, Windows ARM64, Linux x64 with glibc, Linux x64 with musl, Linux ARM64v8 with glibc, Linux ARM64v8 with musl, Linux ARMv7, macOS x64 and macOS ARM64.

The footnotes explain the Linux split, which is the distinction that catches people out. The glibc entries use glibc as the standard C library and cover Ubuntu, Debian and similar. The musl entries use musl and cover Alpine and Gentoo Linux and similar. A container image based on Alpine needs the musl build, and installing the glibc one into it will fail in a way that is not obvious.

There is also a note that the version number of these native NuGet packages is in sync with the libvips version number, which is a helpful convention. It means the version you see on the native package tells you which libvips you are getting, so you can reason about features rather than guessing.

The native binaries provide support for the JPEG, PNG, Ultra HDR, WebP, AVIF, TIFF, GIF and SVG input image formats.

So the practical install is the NetVips package plus the one native package that matches your platform and your libc, and the library search path question largely goes away.

Verifying the install, and the two failures you will hit

The readme gives a small test program for checking that the binding found libvips, and it is worth knowing because the failure modes are specific.

csharp
if (ModuleInitializer.VipsInitialized)
{
    Console.WriteLine($"Inited libvips {NetVips.Version(0)}.{NetVips.Version(1)}.{NetVips.Version(2)}");
}
else
{
    Console.WriteLine(ModuleInitializer.Exception.Message);
}
Console.ReadLine();

If NetVips was able to find the libvips shared library, you should see a line reporting the libvips version. If you see something else, NetVips was unable to initialize libvips.

The readme is clear that this can happen for a variety of reasons, even though most of the time it is because NetVips was not able to find libvips or due to x86 and x64 architecture problems. And then it gives the two exceptions that cover most cases, in a table with the inner exception, the HRESULT and the solution.

A DllNotFoundException with HRESULT 0x8007007E means the library was not found, and the solution is to make sure to add the bin folder of the libvips Windows build to your PATH environment variable, if you wish to not use the separate NuGet packages. That is the case where you are relying on a manual libvips install rather than the native package.

A BadImageFormatException with HRESULT 0x8007000B means the library was found but the architecture is wrong. The solution is to make sure that when you target the AnyCPU platform the Prefer 32-bit option is unchecked, or to try targeting x64 instead.

Those two are the classic .NET native interop failures, and having them tabulated with the exact HRESULT values saves a lot of time, because the exception type alone does not tell you which of the two problems you have.

The install itself, when the native package is in place, is the usual NuGet command:

powershell
    Install-Package NetVips

The readme notes that the module initializer is what performs the initialization, which is why the test program can ask about VipsInitialized and inspect ModuleInitializer.Exception without any explicit setup call in your own code.

The example, and the shape of the API

The readme's example section is truncated, but what is visible shows the API shape clearly.

csharp
using NetVips;

using var im = Image.NewFromFile("image.jpg");

The first thing to notice is that the source image is opened through a static factory on Image, and that the result is held with using. Since the pipeline model means intermediate results are never written out in full, the lifetime question still applies to the source and to anything you do write out yourself, and using is the way to express that.

The comment that follows describes the kind of operation: putting an image at a position in a larger canvas, making the rest transparent. That is a compositing operation, and in a pipeline model it becomes another link rather than a new image.

The naming is worth noting for anyone coming from ImageSharp or Magick.NET. Image.NewFromFile is a plain static factory, and the operations read as methods on an image object that return something you can pipe onward. The readme's own phrasing for the whole library is that programs create pipelines of image processing operations building on a source image, so the fluent style is the intended shape rather than an accident of the binding.

The samples directory in the repository holds a NetVips.Samples project and its own README, which is where the fuller set of operations would be found. The samples and the documentation site at kleisauke.github.io/net-vips/ are the two places to look for the operations list, since the binding covers the libvips API surface rather than curating it.

The repository and how the build is organised

The top level of the repository shows a project with more going on than a binding normally would.

There is a NetVips.slnx solution, using the newer XML based solution format, a global.json pinning the .NET SDK, and a .nuke/ directory. Nuke is a build automation system written in C#, and its presence here means the build is a compiled project rather than a set of shell scripts, which is how you get a build with proper dependency modelling when you are producing packages for many target frameworks.

There is also build.sh and build.ps1 for the container and Windows paths, a docker.sh, a Dockerfile, and an appveyor.yml. The CI configuration is split across the badge row in the readme, with one workflow covering Linux, macOS and Windows and a separate AppVeyor project for NetVips nightly packaging. The nightly packaging job existing separately from the main CI is a hint about how the native packages get refreshed, since those are built on a different cadence from the managed ones.

The docs/ directory holds the documentation, and the readme says the docs are formatted and published online. The samples/ directory holds NetVips.Samples and its README.

There are two changelogs, CHANGELOG.md and CHANGELOG.native.md, and the second one is the more interesting artefact. It tracks the native side separately, which makes sense given the note that the native NuGet package versions are in sync with the libvips version numbers. A consumer can then read two histories: what changed in the binding, and what changed in the libvips version it is pinned to.

The Dockerfile is minimal and clearly a development environment rather than a runtime image. It is built from the .NET 10 SDK on Alpine, labels the maintainer, adds bash and font-noto, sets a working directory of /app, and runs the build script. The presence of font-noto is the detail worth noting, since libvips can render text and needs fonts available to do it.

The project is C#, MIT licensed, with the recent releases v3.2.0 from 2026-01-01, a v3.2.0-rc1 release candidate from 2025-12-16 and v3.1.0 from 2025-06-07, and the last push on 2026-09-28.

Editorial conclusion

Adopt NetVips if you process images in .NET at a volume where the difference between streaming and buffering shows up in memory or latency, because that is the problem libvips was built to solve and the binding exposes it directly. Do not adopt it if you need a library with no native dependency at all, because the shared library has to be present on the machine. Before you commit, add the matching NetVips.Native package for your platform rather than relying on a system install, and check the two failure modes the readme documents, since a missing DLL and a wrong architecture produce different exceptions.

Frequently asked questions

What is NetVips?

It is a .NET binding for the libvips image processing library, published as a NuGet package. It supports .NET Framework 4.6.2 and higher and .NET 6.0 and higher, and the readme states that the binding passes the vips test suite cleanly with no leaks on Windows, macOS and Linux.

How does the NetVips pipeline model work?

Programs using NetVips do not manipulate images directly but create pipelines of operations on a source image. When the pipeline is connected to a destination, the whole thing executes at once, streaming the image in parallel from source to destination a section at a time, so intermediate results are never written out in full and whole images are not held in memory.

How do I install NetVips and the native libvips library?

You need libvips 8.2 or later on your library search path, but in practice you install the matching NetVips.Native NuGet package instead, which contains the pre-compiled binaries. The readme lists ten of them, covering Windows x64, x86 and ARM64, Linux x64, ARM64 and ARMv7 in both glibc and musl variants, and macOS x64 and ARM64.

What happens if NetVips cannot find libvips?

The initialization fails and ModuleInitializer.Exception carries the reason. A DllNotFoundException with HRESULT 0x8007007E means the library was not found, solved by adding the bin folder of the libvips Windows build to your PATH if you are not using the separate NuGet packages. A BadImageFormatException with HRESULT 0x8007000B means the architecture is wrong, solved by unchecking Prefer 32-bit when targeting AnyCPU or targeting x64.

How does NetVips compare with Magick.NET and ImageSharp?

On the NetVips.Benchmarks test, which loads a large image, shrinks it by 10 percent, sharpens and saves again, the readme reports NetVips as around 26 times faster than Magick.NET and 4 times faster than ImageSharp. The readme attributes this to the library being parallel and not needing to keep entire images in memory.

Official sources

  1. kleisauke/net-vips on GitHub
  2. License: MIT
  3. Project website
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/kleisauke-net-vips.svg)](https://hysenlabs.com/projects/kleisauke-net-vips)

Was this useful?