#![deny(missing_docs)]
use core::ops::Index;
use nom::{
bytes::complete::tag,
multi::{length_count, many_m_n},
number::complete::u8,
sequence::tuple,
IResult,
};
use std::convert::TryInto;
pub mod path;
pub mod shape;
pub mod style;
use path::Path;
use shape::Shape;
use style::Style;
#[cfg(feature = "cairo-rs")]
mod render;
#[cfg(feature = "cairo-rs")]
pub use render::render;
#[derive(Debug, Clone, PartialEq)]
pub struct Coord(f32);
impl Coord {
pub fn as_f32(&self) -> f32 {
self.0
}
pub fn to_f64(&self) -> f64 {
self.0.into()
}
fn parse(i: &[u8]) -> IResult<&[u8], Coord> {
let (i, a) = u8(i)?;
if a < 128 {
Ok((i, Coord((a as f32) - 32.)))
} else {
let (i, b) = u8(i)?;
let c = ((a & 0x7f) as u16) << 8 | (b as u16);
Ok((i, Coord((c as f32) / 102. - 128.)))
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct Point {
pub x: Coord,
pub y: Coord,
}
impl Point {
fn parse(i: &[u8]) -> IResult<&[u8], Point> {
let (i, (x, y)) = tuple((Coord::parse, Coord::parse))(i)?;
Ok((i, Point { x, y }))
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct Float24(f32);
impl Float24 {
const ZERO: Float24 = Float24(0.);
const ONE: Float24 = Float24(1.);
pub fn as_f32(&self) -> f32 {
self.0
}
pub fn to_f64(&self) -> f64 {
self.0 as f64
}
fn parse(i: &[u8]) -> IResult<&[u8], Float24> {
let (i, (a, b, c)) = tuple((u8, u8, u8))(i)?;
let value = (a as u32) << 16 | (b as u32) << 8 | (c as u32);
if value == 0 {
Ok((i, Float24(0.)))
} else {
let sign = !!(a & 0x80);
let exp: i16 = ((a & 0x7e) >> 1) as i16 - 32;
let mantissa = (value & 0x01ffff) << 6;
let int = ((sign as u32) << 31) | (((exp + 127) as u32) << 23) | mantissa;
let float = unsafe { std::mem::transmute(int) };
Ok((i, Float24(float)))
}
}
}
impl From<Coord> for Float24 {
fn from(coord: Coord) -> Float24 {
Float24(coord.0)
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct Transform([Float24; 6]);
impl Index<usize> for Transform {
type Output = Float24;
fn index(&self, idx: usize) -> &Float24 {
&self.0[idx]
}
}
impl Transform {
#[cfg(test)]
const IDENTITY: Transform = Transform([
Float24::ONE,
Float24::ZERO,
Float24::ZERO,
Float24::ONE,
Float24::ZERO,
Float24::ZERO,
]);
fn parse(i: &[u8]) -> IResult<&[u8], Transform> {
let (i, vec) = many_m_n(6, 6, Float24::parse)(i)?;
let array = vec
.try_into()
.unwrap_or_else(|vec| panic!("Transform didn’t contain six float24: {:?}", vec));
Ok((i, Transform(array)))
}
}
fn check_header(i: &[u8]) -> IResult<&[u8], ()> {
let (i, _) = tag(b"ncif")(i)?;
Ok((i, ()))
}
#[derive(Debug, Clone, PartialEq)]
pub struct Hvif {
pub styles: Vec<Style>,
pub paths: Vec<Path>,
pub shapes: Vec<Shape>,
}
impl Hvif {
pub fn parse(i: &[u8]) -> IResult<&[u8], Hvif> {
let (i, _) = check_header(i)?;
let (i, styles) = length_count(u8, Style::parse)(i)?;
let (i, paths) = length_count(u8, Path::parse)(i)?;
let (i, shapes) = length_count(u8, Shape::parse)(i)?;
Ok((
i,
Hvif {
styles,
paths,
shapes,
},
))
}
}
#[cfg(test)]
mod tests {
use super::*;
macro_rules! assert_size (
($t:ty, $sz:expr) => (
assert_eq!(::std::mem::size_of::<$t>(), $sz);
);
);
#[test]
fn sizes() {
assert_size!(Point, 8);
assert_size!(Hvif, 72);
}
#[test]
fn empty() {
let empty = b"ncif\0\0\0";
let (i, hvif) = Hvif::parse(empty).unwrap();
assert!(i.is_empty());
assert_eq!(hvif.styles.len(), 0);
assert_eq!(hvif.paths.len(), 0);
assert_eq!(hvif.shapes.len(), 0);
}
}