use crate::Transform;
use bitflags::bitflags;
use nom::{multi::many_m_n, number::complete::u8, sequence::tuple, IResult};
#[derive(Debug, Clone, PartialEq)]
pub enum Colour {
Rgba(u8, u8, u8, u8),
Rgb(u8, u8, u8),
Grey(u8, u8),
GreyNoAlpha(u8),
}
impl Colour {
fn parse_rgba(i: &[u8]) -> IResult<&[u8], Colour> {
let (i, (r, g, b, a)) = tuple((u8, u8, u8, u8))(i)?;
Ok((i, Colour::Rgba(r, g, b, a)))
}
fn parse_rgb(i: &[u8]) -> IResult<&[u8], Colour> {
let (i, (r, g, b)) = tuple((u8, u8, u8))(i)?;
Ok((i, Colour::Rgb(r, g, b)))
}
fn parse_grey(i: &[u8]) -> IResult<&[u8], Colour> {
let (i, (grey, alpha)) = tuple((u8, u8))(i)?;
Ok((i, Colour::Grey(grey, alpha)))
}
fn parse_grey_no_alpha(i: &[u8]) -> IResult<&[u8], Colour> {
let (i, grey) = u8(i)?;
Ok((i, Colour::GreyNoAlpha(grey)))
}
pub fn is_opaque(&self) -> bool {
match *self {
Colour::Rgba(_, _, _, a) | Colour::Grey(_, a) => a == 255,
Colour::Rgb(_, _, _) | Colour::GreyNoAlpha(_) => true,
}
}
pub fn to_rgb(&self) -> (u8, u8, u8) {
match *self {
Colour::Rgba(r, g, b, a) if a == 255 => (r, g, b),
Colour::Rgb(r, g, b) => (r, g, b),
Colour::Grey(grey, alpha) if alpha == 255 => (grey, grey, grey),
Colour::GreyNoAlpha(grey) => (grey, grey, grey),
_ => panic!("This function must only be called on opaque colours."),
}
}
pub fn to_rgba(&self) -> (u8, u8, u8, u8) {
match *self {
Colour::Rgba(r, g, b, a) => (r, g, b, a),
Colour::Rgb(r, g, b) => (r, g, b, 255),
Colour::Grey(grey, alpha) => (grey, grey, grey, alpha),
Colour::GreyNoAlpha(grey) => (grey, grey, grey, 255),
}
}
}
enum StyleType {
SolidColour = 1,
Gradient = 2,
SolidColourNoAlpha = 3,
SolidGrey = 4,
SolidGreyNoAlpha = 5,
}
impl StyleType {
fn parse(i: &[u8]) -> IResult<&[u8], StyleType> {
let (i, type_) = u8(i)?;
use StyleType::*;
Ok((
i,
match type_ {
1 => SolidColour,
2 => Gradient,
3 => SolidColourNoAlpha,
4 => SolidGrey,
5 => SolidGreyNoAlpha,
_ => {
unreachable!("Unknown style type {}", type_);
}
},
))
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum GradientType {
Linear = 0,
Circular = 1,
Diamond = 2,
Conic = 3,
Xy = 4,
SqrtXy = 5,
}
impl GradientType {
fn parse(i: &[u8]) -> IResult<&[u8], GradientType> {
let (i, type_) = u8(i)?;
use GradientType::*;
Ok((
i,
match type_ {
0 => Linear,
1 => Circular,
2 => Diamond,
3 => Conic,
4 => Xy,
5 => SqrtXy,
_ => unreachable!("Unknown gradient type {}", type_),
},
))
}
}
bitflags! {
pub struct GradientFlags: u8 {
const TRANSFORM = 0b00000010;
const NO_ALPHA = 0b00000100;
const GREYS = 0b00010000;
}
}
impl GradientFlags {
fn parse(i: &[u8]) -> IResult<&[u8], GradientFlags> {
let (i, flags) = u8(i)?;
Ok((i, GradientFlags::from_bits_truncate(flags)))
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct Stop {
pub offset: u8,
pub colour: Colour,
}
impl Stop {
fn parse_rgba(i: &[u8]) -> IResult<&[u8], Stop> {
let (i, (offset, colour)) = tuple((u8, Colour::parse_rgba))(i)?;
Ok((i, Stop { offset, colour }))
}
fn parse_rgb(i: &[u8]) -> IResult<&[u8], Stop> {
let (i, (offset, colour)) = tuple((u8, Colour::parse_rgb))(i)?;
Ok((i, Stop { offset, colour }))
}
fn parse_grey(i: &[u8]) -> IResult<&[u8], Stop> {
let (i, (offset, colour)) = tuple((u8, Colour::parse_grey))(i)?;
Ok((i, Stop { offset, colour }))
}
fn parse_grey_no_alpha(i: &[u8]) -> IResult<&[u8], Stop> {
let (i, (offset, colour)) = tuple((u8, Colour::parse_grey_no_alpha))(i)?;
Ok((i, Stop { offset, colour }))
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct Gradient {
pub type_: GradientType,
pub flags: GradientFlags,
pub stops: Vec<Stop>,
pub transform: Option<Transform>,
}
impl Gradient {
fn parse(i: &[u8]) -> IResult<&[u8], Gradient> {
let (i, (type_, flags)) = tuple((GradientType::parse, GradientFlags::parse))(i)?;
let (i, num_stops) = u8(i)?;
let num_stops = num_stops as usize;
let (i, transform) = if flags.contains(GradientFlags::TRANSFORM) {
let (i, transform) = Transform::parse(i)?;
(i, Some(transform))
} else {
(i, None)
};
let (i, stops) = if flags.contains(GradientFlags::NO_ALPHA) {
if flags.contains(GradientFlags::GREYS) {
many_m_n(num_stops, num_stops, Stop::parse_grey_no_alpha)(i)?
} else {
many_m_n(num_stops, num_stops, Stop::parse_rgb)(i)?
}
} else if flags.contains(GradientFlags::GREYS) {
many_m_n(num_stops, num_stops, Stop::parse_grey)(i)?
} else {
many_m_n(num_stops, num_stops, Stop::parse_rgba)(i)?
};
Ok((
i,
Gradient {
type_,
flags,
stops,
transform,
},
))
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum Style {
SolidColour(Colour),
Gradient(Gradient),
}
impl Style {
pub fn parse(i: &[u8]) -> IResult<&[u8], Style> {
let (i, type_) = StyleType::parse(i)?;
match type_ {
StyleType::SolidColour => {
let (i, colour) = Colour::parse_rgba(i)?;
Ok((i, Style::SolidColour(colour)))
}
StyleType::Gradient => {
let (i, gradient) = Gradient::parse(i)?;
Ok((i, Style::Gradient(gradient)))
}
StyleType::SolidColourNoAlpha => {
let (i, colour) = Colour::parse_rgb(i)?;
Ok((i, Style::SolidColour(colour)))
}
StyleType::SolidGrey => {
let (i, colour) = Colour::parse_grey(i)?;
Ok((i, Style::SolidColour(colour)))
}
StyleType::SolidGreyNoAlpha => {
let (i, colour) = Colour::parse_grey_no_alpha(i)?;
Ok((i, Style::SolidColour(colour)))
}
}
}
}
#[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!(Colour, 5);
assert_size!(GradientType, 1);
assert_size!(GradientFlags, 1);
assert_size!(Stop, 6);
assert_size!(Gradient, 56);
assert_size!(Style, 64);
}
#[test]
fn solid_grey() {
let grey = b"\x05\x80";
let (i, style) = Style::parse(grey).unwrap();
assert!(i.is_empty());
assert_eq!(style, Style::SolidColour(Colour::GreyNoAlpha(128)));
}
#[test]
fn solid_colour() {
let red = b"\x01\xff\x00\x00\x80";
let (i, style) = Style::parse(red).unwrap();
assert!(i.is_empty());
assert_eq!(style, Style::SolidColour(Colour::Rgba(255, 0, 0, 128)));
}
#[test]
fn gradient() {
let data = b"\x02\x00\x04\x02\x00\x20\x40\x60\xff\x60\x40\x20";
let (i, style) = Style::parse(data).unwrap();
println!("{:?}", i);
assert!(i.is_empty());
if let Style::Gradient(gradient) = style {
assert_eq!(gradient.type_, GradientType::Linear);
assert_eq!(gradient.flags, GradientFlags::NO_ALPHA);
assert_eq!(
gradient.stops,
[
Stop {
offset: 0,
colour: Colour::Rgb(32, 64, 96)
},
Stop {
offset: 255,
colour: Colour::Rgb(96, 64, 32)
},
]
);
assert_eq!(gradient.transform, None);
} else {
panic!("Parsed style isn’t a gradient.");
}
}
#[test]
fn gradient_matrix() {
let data = b"\x02\x00\x02\x00\x40\x00\x00\x00\x00\x00\x00\x00\x00\x40\x00\x00\x00\x00\x00\x00\x00\x00";
let (i, style) = Style::parse(data).unwrap();
println!("{:?}", i);
assert!(i.is_empty());
if let Style::Gradient(gradient) = style {
assert_eq!(gradient.type_, GradientType::Linear);
assert_eq!(gradient.flags, GradientFlags::TRANSFORM);
assert_eq!(gradient.stops, []);
assert_eq!(gradient.transform.unwrap(), Transform::IDENTITY);
} else {
panic!("Parsed style isn’t a gradient.");
}
}
}