| 1 | //! HEIC/HEIF through the system libheif. libheif applies the file's rotation and mirroring. |
| 2 | |
| 3 | use std::path::Path; |
| 4 | |
| 5 | use anyhow::anyhow; |
| 6 | use libheif_rs::{ColorSpace, HeifContext, LibHeif, RgbChroma}; |
| 7 | |
| 8 | use super::color::ToSrgb; |
| 9 | use super::{DecodedImage, Options}; |
| 10 | |
| 11 | pub fn decode(path: &Path, opts: &Options) -> anyhow::Result<DecodedImage> { |
| 12 | let bytes = std::fs::read(path)?; |
| 13 | let ctx = HeifContext::read_from_bytes(&bytes)?; |
| 14 | let handle = ctx.primary_image_handle()?; |
| 15 | let lib = LibHeif::new(); |
| 16 | let image = lib.decode(&handle, ColorSpace::Rgb(RgbChroma::Rgba), None)?; |
| 17 | let planes = image.planes(); |
| 18 | let plane = planes |
| 19 | .interleaved |
| 20 | .ok_or_else(|| anyhow!("no interleaved plane"))?; |
| 21 | let (w, h) = (plane.width, plane.height); |
| 22 | let row = w as usize * 4; |
| 23 | let mut pixels = Vec::with_capacity(row * h as usize); |
| 24 | for y in 0..h as usize { |
| 25 | pixels.extend_from_slice(&plane.data[y * plane.stride..][..row]); |
| 26 | } |
| 27 | if opts.color { |
| 28 | let transform = match handle.color_profile_raw().filter(|p| !p.data.is_empty()) { |
| 29 | Some(icc) => ToSrgb::from_icc(&icc.data), |
| 30 | None => handle.color_profile_nclx().and_then(|n| { |
| 31 | ToSrgb::from_cicp( |
| 32 | n.color_primaries() as u8, |
| 33 | n.transfer_characteristics() as u8, |
| 34 | ) |
| 35 | }), |
| 36 | }; |
| 37 | if let Some(t) = transform { |
| 38 | t.apply(&mut pixels); |
| 39 | } |
| 40 | } |
| 41 | Ok(DecodedImage::still([w, h], pixels, "HEIF")) |
| 42 | } |