use crate::{Float24, Point, Transform};
use bitflags::bitflags;
use nom::{
bytes::complete::tag, multi::length_count, number::complete::u8, sequence::tuple, IResult,
};
bitflags! {
pub struct ShapeFlags: u8 {
const TRANSFORM = 0b00000010;
const HINTING = 0b00000100;
const LOD_SCALE = 0b00001000;
const HAS_TRANSFORMERS = 0b00010000;
const TRANSLATION = 0b00100000;
}
}
impl ShapeFlags {
fn parse(i: &[u8]) -> IResult<&[u8], ShapeFlags> {
let (i, flags) = u8(i)?;
Ok((i, ShapeFlags::from_bits_truncate(flags)))
}
}
fn parse_translation(i: &[u8]) -> IResult<&[u8], Transform> {
let (i, Point { x, y }) = Point::parse(i)?;
Ok((
i,
Transform([
Float24::ONE,
Float24::ZERO,
Float24::ZERO,
Float24::ONE,
x.into(),
y.into(),
]),
))
}
enum TransformerType {
Affine = 20,
Contour = 21,
Perspective = 22,
Stroke = 23,
}
impl TransformerType {
fn parse(i: &[u8]) -> IResult<&[u8], TransformerType> {
let (i, type_) = u8(i)?;
use TransformerType::*;
Ok((
i,
match type_ {
20 => Affine,
21 => Contour,
22 => Perspective,
23 => Stroke,
_ => {
unreachable!("Unknown transformer type {}", type_);
}
},
))
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum LineJoin {
MiterJoin,
MiterJoinRevert,
RoundJoin,
BevelJoin,
MiterJoinRound,
}
impl From<u8> for LineJoin {
fn from(bits: u8) -> LineJoin {
use LineJoin::*;
match bits {
0b0000 => MiterJoin,
0b0001 => MiterJoinRevert,
0b0010 => RoundJoin,
0b0011 => BevelJoin,
0b0100 => MiterJoinRound,
_ => unreachable!("Unknown LineJoin."),
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum LineCap {
Butt,
Square,
Round,
}
impl From<u8> for LineCap {
fn from(bits: u8) -> LineCap {
use LineCap::*;
match bits {
0b0000 => Butt,
0b0001 => Square,
0b0010 => Round,
_ => unreachable!("Unknown LineCap."),
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum Transformer {
Affine(Transform),
Contour {
width: i8,
line_join: LineJoin,
miter_limit: u8,
},
Stroke {
width: i8,
line_join: LineJoin,
line_cap: LineCap,
miter_limit: u8,
},
}
impl Transformer {
fn parse(i: &[u8]) -> IResult<&[u8], Transformer> {
let (i, type_) = TransformerType::parse(i)?;
use Transformer::*;
match type_ {
TransformerType::Affine => {
let (i, matrix) = Transform::parse(i)?;
Ok((i, Affine(matrix)))
}
TransformerType::Contour => {
let (i, (width, line_options, miter_limit)) = tuple((u8, u8, u8))(i)?;
let width = ((width as i16) - 128) as i8;
let line_join = LineJoin::from(line_options);
Ok((
i,
Contour {
width,
line_join,
miter_limit,
},
))
}
TransformerType::Perspective => {
todo!("Unsupported in HVIF.")
}
TransformerType::Stroke => {
let (i, (width, line_options, miter_limit)) = tuple((u8, u8, u8))(i)?;
let width = ((width as i16) - 128) as i8;
let line_join = LineJoin::from(line_options & 0xf);
let line_cap = LineCap::from(line_options >> 4);
Ok((
i,
Stroke {
width,
line_join,
line_cap,
miter_limit,
},
))
}
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct Shape {
pub style: u8,
pub paths: Vec<u8>,
pub flags: ShapeFlags,
pub transform: Option<Transform>,
pub lod_scale: Option<(u8, u8)>,
pub transformers: Vec<Transformer>,
}
impl Shape {
pub fn parse(i: &[u8]) -> IResult<&[u8], Shape> {
let (i, _) = tag(b"\x0a")(i)?;
let (i, style) = u8(i)?;
let (i, paths) = length_count(u8, u8)(i)?;
let (i, flags) = ShapeFlags::parse(i)?;
let (i, transform) = if flags.contains(ShapeFlags::TRANSFORM) {
let (i, matrix) = Transform::parse(i)?;
(i, Some(matrix))
} else if flags.contains(ShapeFlags::TRANSLATION) {
let (i, matrix) = parse_translation(i)?;
(i, Some(matrix))
} else {
(i, None)
};
let (i, lod_scale) = if flags.contains(ShapeFlags::LOD_SCALE) {
let (i, lod_scale) = tuple((u8, u8))(i)?;
(i, Some(lod_scale))
} else {
(i, None)
};
let (i, transformers) = if flags.contains(ShapeFlags::HAS_TRANSFORMERS) {
length_count(u8, Transformer::parse)(i)?
} else {
(i, Vec::new())
};
Ok((
i,
Shape {
style,
paths,
flags,
transform,
lod_scale,
transformers,
},
))
}
}
#[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!(ShapeFlags, 1);
assert_size!(LineJoin, 1);
assert_size!(LineCap, 1);
assert_size!(Transformer, 28);
assert_size!(Shape, 88);
}
#[test]
fn empty() {
let data = b"\x0a\x00\x00\x00";
let (i, shape) = Shape::parse(data).unwrap();
assert!(i.is_empty());
assert_eq!(shape.style, 0);
assert_eq!(shape.paths, []);
assert_eq!(shape.flags, ShapeFlags::empty());
assert_eq!(shape.transform, None);
assert_eq!(shape.lod_scale, None);
assert_eq!(shape.transformers, []);
}
#[test]
fn transform() {
let data = b"\x0a\x00\x00\x02\x40\x00\x00\x00\x00\x00\x00\x00\x00\x40\x00\x00\x00\x00\x00\x00\x00\x00";
let (i, shape) = Shape::parse(data).unwrap();
assert!(i.is_empty());
assert_eq!(shape.style, 0);
assert_eq!(shape.paths, []);
assert_eq!(shape.flags, ShapeFlags::TRANSFORM);
assert_eq!(shape.transform.unwrap(), Transform::IDENTITY);
assert_eq!(shape.lod_scale, None);
assert_eq!(shape.transformers, []);
}
#[test]
fn transformer() {
let data = b"\x0a\x00\x00\x10\x01\x17\x84\x00\x04";
let (i, shape) = Shape::parse(data).unwrap();
assert!(i.is_empty());
assert_eq!(shape.style, 0);
assert_eq!(shape.paths, []);
assert_eq!(shape.flags, ShapeFlags::HAS_TRANSFORMERS);
assert_eq!(shape.transform, None);
assert_eq!(shape.lod_scale, None);
assert_eq!(
shape.transformers,
[Transformer::Stroke {
width: 4,
line_join: LineJoin::MiterJoin,
line_cap: LineCap::Butt,
miter_limit: 4
}]
);
}
}