LibHeifSharp
| Language | C# (.NET Standard 2.0 and later) |
|---|---|
| License | LGPL-3.0 |
| Repository | github.com/0xC0000054/libheif-sharp |
| Documentation | README, sample programs |
| Depends on | native libheif 1.9 or later (with libde265 to decode and x265 to encode), which you provide |
| Read / write | yes / yes, HEIC and AVIF, alpha, high bit depth, thumbnails, depth images, Exif/XMP |
LibHeifSharp exposes the libheif C API to .NET through P/Invoke: contexts, image handles, decoding and encoding options, metadata, thumbnails, depth and auxiliary images, and encoder parameters. It has no image class of its own; you copy the decoded planes into whatever image type your application uses (ImageSharp, System.Drawing.Bitmap, a WPF WriteableBitmap). The samples repository has complete decoder and encoder programs that convert to and from ImageSharp images.
The NuGet package contains only the managed wrapper. You must ship a native libheif library (named libheif.dll, libheif.so or libheif.dylib) built with the codecs you need, e.g. from vcpkg on Windows or the system package on Linux.
Installation#
dotnet add package LibHeifSharp
# native library, Windows:
vcpkg install libheif:x64-windows
# Linux:
sudo apt install libheif1 libheif-plugin-libde265 libheif-plugin-x265
Basic usage: decode the primary image#
using LibHeifSharp;
using System;
using System.Runtime.InteropServices;
using var context = new HeifContext("in.heic");
using var handle = context.GetPrimaryImageHandle();
Console.WriteLine($"{handle.Width} x {handle.Height}, {handle.BitDepth}-bit, alpha {handle.HasAlphaChannel}");
using var image = handle.Decode(HeifColorspace.Rgb, HeifChroma.InterleavedRgb24);
var plane = image.GetPlane(HeifChannel.Interleaved);
// copy into a tightly packed RGB buffer, row by row (rows may be padded)
int rowBytes = plane.Width * 3;
byte[] rgb = new byte[rowBytes * plane.Height];
for (int y = 0; y < plane.Height; y++)
Marshal.Copy(plane.Scan0 + y * plane.Stride, rgb, y * rowBytes, rowBytes);
Grid tiles, cropping and rotation are applied by libheif. Use HeifChroma.InterleavedRgba32 when HasAlphaChannel is true, and InterleavedRgb48LE / InterleavedRgba64LE for 10 and 12-bit images.
How to encode an image to HEIC#
using LibHeifSharp;
using System.Runtime.InteropServices;
static void SaveHeic(string path, byte[] rgb, int width, int height, int quality)
{
using var image = new HeifImage(width, height, HeifColorspace.Rgb, HeifChroma.InterleavedRgb24);
image.AddPlane(HeifChannel.Interleaved, width, height, 8);
var plane = image.GetPlane(HeifChannel.Interleaved);
for (int y = 0; y < height; y++)
Marshal.Copy(rgb, y * width * 3, plane.Scan0 + y * plane.Stride, width * 3);
using var context = new HeifContext();
using var encoder = context.GetEncoder(HeifCompressionFormat.Hevc); // throws if no HEVC encoder
encoder.SetLossyQuality(quality); // 0-100
context.EncodeImage(image, encoder);
context.WriteToFile(path);
}
Check LibHeifInfo.HaveEncoder(HeifCompressionFormat.Hevc) first to find out whether the native libheif includes x265 or kvazaar.
How to read Exif and XMP#
using var context = new HeifContext("in.heic");
using var handle = context.GetPrimaryImageHandle();
byte[] exif = handle.GetExifMetadata(); // null if there is none
byte[] xmp = handle.GetXmpMetadata();
How to get thumbnails and depth images#
foreach (var id in handle.GetThumbnailImageIds())
{
using var thumb = handle.GetThumbnailImage(id);
Console.WriteLine($"thumbnail {thumb.Width} x {thumb.Height}");
}
if (handle.HasDepthImage)
{
foreach (var id in handle.GetDepthImageIds())
{
using var depth = handle.GetDepthImage(id);
using var depthImage = depth.Decode(HeifColorspace.Monochrome, HeifChroma.Monochrome);
}
}
How to list all images in a file#
using var context = new HeifContext("burst.heic");
foreach (var id in context.GetTopLevelImageIds())
{
using var h = context.GetImageHandle(id);
Console.WriteLine($"{h.Width} x {h.Height}{(h.IsPrimaryImage ? " primary" : "")}");
}
Tips#
LibHeifInfo.Versionreports the native libheif version; some features (decoder selection, chroma upsampling options) need newer versions, checked withLibHeifInfo.HaveVersion.HeifContextalso opens aStreamorbyte[].- To load the native library from a custom path, register a resolver with
NativeLibrary.SetDllImportResolver, as the samples do.