Skip to content

v1.4.0: HEIF / HEIC decoding

Latest

Choose a tag to compare

@sdcb sdcb released this 04 Nov 05:02
· 90 commits to main since this release

Sdcb.SimdCodecs 1.4.0 adds HEIF / HEIC decoding — the format of modern Android and iPhone photos — as a new, separately installed package, Sdcb.SimdCodecs.Heif. It is pure managed (HEVC intra decoding included), SIMD-accelerated, and faster than libheif.

dotnet add package Sdcb.SimdCodecs.Heif

Patent notice. HEVC (H.265) is patent-encumbered. Sdcb.SimdCodecs.Heif is MIT and conveys no patent licence; evaluate your HEVC patent obligations before deploying it.

New: Sdcb.SimdCodecs.Heif

  • Opt-in, not part of the facade package. Sdcb.SimdCodecs does not reference it. Install it and register it once at start-up; from then on every facade entry point handles HEIF like a built-in format:

    SimdCodecs.Register(HeifImageDecoder.Registration);

    Before registration SimdCodecs.DetectFormat still reports ImageFormat.Heif, so an application can tell users what it cannot open, while IsDecodable and the Try* methods return false.

  • What it decodes: HEIC still images, including the tiled grid images every phone camera writes; HEVC Main, Main 10 and Main Still Picture (4:2:0, 8 and 10-bit); WPP, sign data hiding, transform skip, deblocking, SAO.

  • Display-oriented output: clap (clean aperture), irot and imir are applied; the ICC profile is passed through; nclx / VUI colour signalling selects the matrix and range.

  • Every facade layout, written straight into your buffer: TryDecodeInto writes Rgba32, Bgra32, Rgb24, Bgr24 and Gray8 at any stride, one tile at a time, with no full-size intermediate frame. Gray8 is the stored luma.

  • Codec API: HeifDecoder.ReadInfo (container only), Decode (RGBA8), DecodeTo (your buffer).

  • Byte-exact: the decoded Y / Cb / Cr planes equal ffmpeg 8.1.2's on every tile of the test images; the Y′CbCr → RGB conversion is defined in-repo and frozen.

  • Two LINQPad samples ship in the package: decode and facade.

Performance

Ryzen 7 5800X, .NET 10, competitors in the same process on the same bytes (full record).

Sdcb.SimdCodecs.Heif vs
HEVC decoding, one core 0.68–0.87× the time libde265 1.1.1
To RGBA, 8 cores (12.6 MP phone photo: 50 ms vs 196 ms) 0.20–0.32× the time libheif 1.23.1
To RGBA, 8 cores 0.09–0.14× the time Magick.NET 14.13.1
Peak memory, 12.6 MP photo (48 MB of RGBA) 75 MB libheif 80 MB, Magick 220 MB
Peak memory, 50 MP photo (192 MB of RGBA) 230 MB libheif 373 MB, Magick 940 MB

AVX2 / Vector128 (SSE, AdvSimd) / scalar kernels for the inverse transform, intra prediction, SAO and colour conversion; grid tiles decode in parallel, and an image without tiles decodes its CTB rows as a WPP wavefront with parallel filter passes.

Verification

  • Every vector kernel has a scalar twin; all tiers (Vector256, Vector128, scalar; x64 and arm64) produce identical output, and CI runs the HEIF tests on each.
  • Fuzz soak: 300 000 mutated files on the default tier plus 30 000 on each forced lower tier, with no failure; malformed data fails with InvalidDataException (or false from Try*).
  • NativeAOT and Mono AOT (browser-wasm) output matches the JIT.

Facade

  • SimdCodecs.Register(ImageDecoderRegistration) and the public ImageDecoderRegistration type (in Sdcb.SimdCodecs.Abstractions) let an application enable a codec the facade does not reference. Registration is idempotent and thread-safe; registered codecs are sniffed after the built-in ones.

Known gaps

  • 10-bit sources are output as 8-bit samples in 1.x (16-bit output is planned for 2.0).
  • Not decoded: alpha auxiliary images (the colour image decodes, opaque), HDR gain maps, depth maps; Exif in HEIF is not read.
  • Not supported: image sequences (.heics) and the HEVC range-extension profiles (Main 10 Intra, Main 12, 4:4:4) — rejected cleanly.
  • AVIF is not supported.