#heif #heic #libheif

libheif-rs

Safe wrapper around the libheif-sys crate for parsing heif/heic files

33 releases (4 stable)

new 1.1.0 Jan 23, 2025
1.0.2 May 16, 2024
1.0.1 Apr 14, 2024
1.0.0 Nov 28, 2023
0.6.0 Jul 17, 2019

#23 in Images

Download history 1421/week @ 2024-10-09 1028/week @ 2024-10-16 664/week @ 2024-10-23 1054/week @ 2024-10-30 1014/week @ 2024-11-06 783/week @ 2024-11-13 782/week @ 2024-11-20 527/week @ 2024-11-27 635/week @ 2024-12-04 927/week @ 2024-12-11 758/week @ 2024-12-18 544/week @ 2024-12-25 1166/week @ 2025-01-01 1161/week @ 2025-01-08 1045/week @ 2025-01-15 1201/week @ 2025-01-22

4,630 downloads per month
Used in 12 crates (9 directly)

MIT license

2MB
3K SLoC

libheif-rs

Safe wrapper around the libheif-sys crate for parsing heif/heic files.

CHANGELOG

System dependencies

  • libheif-dev >= 1.18.0

Linux

Crate libheif-sys uses pkg-confing to find installed libheif.

Windows

Crate libheif-sys uses vcpkg crate to find libheif installed with help of vcpkg.

You can use cargo-vcpkg to install libheif with help of cargo command:

cargo vcpkg -v build

cargo-vcpkg can fetch and build a vcpkg installation of required packages from scratch. It merges package requirements specified in the Cargo.toml of crates in the dependency tree.

PS: I tried build libheif for Windows, but libheif-rs tests filed because libheif doesn't have any encoder plugins available.

Examples

Read HEIF file

use libheif_rs::{
    Channel, RgbChroma, ColorSpace, HeifContext, Result,
    ItemId, LibHeif
};

fn main() -> Result<()> {
    let lib_heif = LibHeif::new();
    let ctx = HeifContext::read_from_file("./data/test.heif")?;
    let handle = ctx.primary_image_handle()?;
    assert_eq!(handle.width(), 1652);
    assert_eq!(handle.height(), 1791);

    // Get Exif
    let mut meta_ids: Vec<ItemId> = vec![0; 1];
    let count = handle.metadata_block_ids(&mut meta_ids, b"Exif");
    assert_eq!(count, 1);
    let exif: Vec<u8> = handle.metadata(meta_ids[0])?;

    // Decode the image
    let image = lib_heif.decode(
        &handle,
        ColorSpace::Rgb(RgbChroma::Rgb),
        None,
    )?;
    assert_eq!(
        image.color_space(), 
        Some(ColorSpace::Rgb(RgbChroma::Rgb)),
    );
    assert_eq!(image.width(), 1652);
    assert_eq!(image.height(), 1791);

    // Scale the image
    let small_img = image.scale(1024, 800, None)?;
    assert_eq!(small_img.width(), 1024);
    assert_eq!(small_img.height(), 800);

    // Get "pixels"
    let planes = small_img.planes();
    let interleaved_plane = planes.interleaved.unwrap();
    assert_eq!(interleaved_plane.width, 1024);
    assert_eq!(interleaved_plane.height, 800);
    assert!(!interleaved_plane.data.is_empty());
    assert!(interleaved_plane.stride > 0);

    Ok(())
}

Write HEIF file

use tempfile::NamedTempFile;
use libheif_rs::{
    Channel, RgbChroma, ColorSpace, CompressionFormat,
    EncoderQuality, HeifContext, Image, Result, LibHeif
};

fn main() -> Result<()> {
    let width = 640;
    let height = 480;

    let mut image = Image::new(
        width,
        height,
        ColorSpace::Rgb(RgbChroma::C444)
    )?;

    image.create_plane(Channel::R, width, height, 8)?;
    image.create_plane(Channel::G, width, height, 8)?;
    image.create_plane(Channel::B, width, height, 8)?;

    let planes = image.planes_mut();
    let plane_r = planes.r.unwrap();
    let stride = plane_r.stride;

    let data_r = plane_r.data;
    let data_g = planes.g.unwrap().data;
    let data_b = planes.b.unwrap().data;

    // Fill data of planes by some "pixels"
    for y in 0..height {
        let mut pixel_index = stride * y as usize;
        for x in 0..width {
            let color = ((x * y) as u32).to_le_bytes();
            data_r[pixel_index] = color[0];
            data_g[pixel_index] = color[1];
            data_b[pixel_index] = color[2];
            pixel_index += 1;
        }
    }

    // Encode image and save it into file.
    let lib_heif = LibHeif::new();
    let mut context = HeifContext::new()?;
    let mut encoder = lib_heif.encoder_for_format(
        CompressionFormat::Av1,
    )?;
    encoder.set_quality(EncoderQuality::LossLess)?;
    context.encode_image(&image, &mut encoder, None)?;

    let tmp_file = NamedTempFile::new().unwrap();
    context.write_to_file(tmp_file.path().to_str().unwrap())?;

    Ok(())
}

Dependencies