use std::io::Read;
use image::error::{DecodingError, ImageFormatHint};
use image::metadata::Orientation;
use image::{ColorType, ImageDecoder, ImageError, ImageResult};
use crate::{Image, Metadata, Options};
const EXTENSIONS: [&str; 3] = ["heic", "heif", "hif"];
const BRANDS: [&[u8; 4]; 4] = [b"heic", b"heix", b"heim", b"heis"];
pub fn register_image_decoder_hooks() -> bool {
let mut all = true;
for ext in EXTENSIONS {
all &= image::hooks::register_decoding_hook(
ext.into(),
Box::new(|reader| Ok(Box::new(HeifDecoder::new(reader)?))),
);
}
const MASK: &[u8] = b"\x00\x00\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff";
static SIGNATURES: [[u8; 12]; 4] = {
let mut s = [[0u8; 12]; 4];
let mut i = 0;
while i < 4 {
let mut j = 0;
while j < 4 {
s[i][4 + j] = b"ftyp"[j];
s[i][8 + j] = BRANDS[i][j];
j += 1;
}
i += 1;
}
s
};
for signature in &SIGNATURES {
image::hooks::register_format_detection_hook("heic".into(), signature, Some(MASK));
}
all
}
pub struct HeifDecoder {
image: Image,
metadata: Metadata,
}
impl HeifDecoder {
pub fn new(mut reader: impl Read) -> ImageResult<Self> {
let mut bytes = Vec::new();
reader
.read_to_end(&mut bytes)
.map_err(ImageError::IoError)?;
Self::from_bytes(&bytes, &Options::default())
}
pub fn from_bytes(bytes: &[u8], options: &Options) -> ImageResult<Self> {
let image = crate::decode_with_options(bytes, options).map_err(decoding_error)?;
let metadata = crate::read_metadata(bytes).map_err(decoding_error)?;
Ok(Self { image, metadata })
}
}
fn decoding_error(e: crate::Error) -> ImageError {
ImageError::Decoding(DecodingError::new(ImageFormatHint::Name("HEIF".into()), e))
}
impl ImageDecoder for HeifDecoder {
fn dimensions(&self) -> (u32, u32) {
(self.image.width, self.image.height)
}
fn color_type(&self) -> ColorType {
match (self.image.bit_depth > 8, self.image.has_alpha) {
(false, false) => ColorType::Rgb8,
(false, true) => ColorType::Rgba8,
(true, false) => ColorType::Rgb16,
(true, true) => ColorType::Rgba16,
}
}
fn icc_profile(&mut self) -> ImageResult<Option<Vec<u8>>> {
Ok(self.metadata.icc_profile.clone())
}
fn exif_metadata(&mut self) -> ImageResult<Option<Vec<u8>>> {
Ok(self.metadata.exif.clone())
}
fn xmp_metadata(&mut self) -> ImageResult<Option<Vec<u8>>> {
Ok(self.metadata.xmp.clone())
}
fn orientation(&mut self) -> ImageResult<Orientation> {
Ok(Orientation::NoTransforms)
}
fn set_limits(&mut self, limits: image::Limits) -> ImageResult<()> {
limits.check_support(&image::LimitSupport::default())?;
limits.check_dimensions(self.image.width, self.image.height)?;
Ok(())
}
fn read_image(self, buf: &mut [u8]) -> ImageResult<()> {
let channels = if self.image.has_alpha { 4 } else { 3 };
let max = u32::from(self.image.max_value());
let pixels = self.image.data.chunks_exact(4).map(|p| &p[..channels]);
if self.image.bit_depth > 8 {
for (out, v) in buf.chunks_exact_mut(2).zip(pixels.flatten()) {
let v16 = ((u32::from(*v) * 65535 + max / 2) / max) as u16;
out.copy_from_slice(&v16.to_ne_bytes());
}
} else {
for (out, v) in buf.iter_mut().zip(pixels.flatten()) {
*out = *v as u8;
}
}
Ok(())
}
fn read_image_boxed(self: Box<Self>, buf: &mut [u8]) -> ImageResult<()> {
(*self).read_image(buf)
}
}