1use crate::Transform;
10use bitflags::bitflags;
11use nom::{multi::many_m_n, number::complete::u8, sequence::tuple, IResult};
12
13#[derive(Debug, Clone, PartialEq)]
16pub enum Colour {
17 Rgba(u8, u8, u8, u8),
19
20 Rgb(u8, u8, u8),
22
23 Grey(u8, u8),
25
26 GreyNoAlpha(u8),
28}
29
30impl Colour {
31 fn parse_rgba(i: &[u8]) -> IResult<&[u8], Colour> {
32 let (i, (r, g, b, a)) = tuple((u8, u8, u8, u8))(i)?;
33 Ok((i, Colour::Rgba(r, g, b, a)))
34 }
35
36 fn parse_rgb(i: &[u8]) -> IResult<&[u8], Colour> {
37 let (i, (r, g, b)) = tuple((u8, u8, u8))(i)?;
38 Ok((i, Colour::Rgb(r, g, b)))
39 }
40
41 fn parse_grey(i: &[u8]) -> IResult<&[u8], Colour> {
42 let (i, (grey, alpha)) = tuple((u8, u8))(i)?;
43 Ok((i, Colour::Grey(grey, alpha)))
44 }
45
46 fn parse_grey_no_alpha(i: &[u8]) -> IResult<&[u8], Colour> {
47 let (i, grey) = u8(i)?;
48 Ok((i, Colour::GreyNoAlpha(grey)))
49 }
50
51 pub fn is_opaque(&self) -> bool {
53 match *self {
54 Colour::Rgba(_, _, _, a) | Colour::Grey(_, a) => a == 255,
55 Colour::Rgb(_, _, _) | Colour::GreyNoAlpha(_) => true,
56 }
57 }
58
59 pub fn to_rgb(&self) -> (u8, u8, u8) {
63 match *self {
64 Colour::Rgba(r, g, b, a) if a == 255 => (r, g, b),
65 Colour::Rgb(r, g, b) => (r, g, b),
66 Colour::Grey(grey, alpha) if alpha == 255 => (grey, grey, grey),
67 Colour::GreyNoAlpha(grey) => (grey, grey, grey),
68 _ => panic!("This function must only be called on opaque colours."),
69 }
70 }
71
72 pub fn to_rgba(&self) -> (u8, u8, u8, u8) {
74 match *self {
75 Colour::Rgba(r, g, b, a) => (r, g, b, a),
76 Colour::Rgb(r, g, b) => (r, g, b, 255),
77 Colour::Grey(grey, alpha) => (grey, grey, grey, alpha),
78 Colour::GreyNoAlpha(grey) => (grey, grey, grey, 255),
79 }
80 }
81}
82
83enum StyleType {
84 SolidColour = 1,
85 Gradient = 2,
86 SolidColourNoAlpha = 3,
87 SolidGrey = 4,
88 SolidGreyNoAlpha = 5,
89}
90
91impl StyleType {
92 fn parse(i: &[u8]) -> IResult<&[u8], StyleType> {
93 let (i, type_) = u8(i)?;
94 use StyleType::*;
95 Ok((
96 i,
97 match type_ {
98 1 => SolidColour,
99 2 => Gradient,
100 3 => SolidColourNoAlpha,
101 4 => SolidGrey,
102 5 => SolidGreyNoAlpha,
103 _ => {
104 unreachable!("Unknown style type {}", type_);
105 }
106 },
107 ))
108 }
109}
110
111#[derive(Debug, Clone, Copy, PartialEq)]
113pub enum GradientType {
114 Linear = 0,
116
117 Circular = 1,
119
120 Diamond = 2,
122
123 Conic = 3,
125
126 Xy = 4,
128
129 SqrtXy = 5,
131}
132
133impl GradientType {
134 fn parse(i: &[u8]) -> IResult<&[u8], GradientType> {
135 let (i, type_) = u8(i)?;
136 use GradientType::*;
137 Ok((
138 i,
139 match type_ {
140 0 => Linear,
141 1 => Circular,
142 2 => Diamond,
143 3 => Conic,
144 4 => Xy,
145 5 => SqrtXy,
146 _ => unreachable!("Unknown gradient type {}", type_),
147 },
148 ))
149 }
150}
151
152bitflags! {
153 pub struct GradientFlags: u8 {
155 const TRANSFORM = 0b00000010;
157
158 const NO_ALPHA = 0b00000100;
160
161 const GREYS = 0b00010000;
166 }
167}
168
169impl GradientFlags {
170 fn parse(i: &[u8]) -> IResult<&[u8], GradientFlags> {
171 let (i, flags) = u8(i)?;
172 Ok((i, GradientFlags::from_bits_truncate(flags)))
173 }
174}
175
176#[derive(Debug, Clone, PartialEq)]
178pub struct Stop {
179 pub offset: u8,
181
182 pub colour: Colour,
184}
185
186impl Stop {
187 fn parse_rgba(i: &[u8]) -> IResult<&[u8], Stop> {
188 let (i, (offset, colour)) = tuple((u8, Colour::parse_rgba))(i)?;
189 Ok((i, Stop { offset, colour }))
190 }
191
192 fn parse_rgb(i: &[u8]) -> IResult<&[u8], Stop> {
193 let (i, (offset, colour)) = tuple((u8, Colour::parse_rgb))(i)?;
194 Ok((i, Stop { offset, colour }))
195 }
196
197 fn parse_grey(i: &[u8]) -> IResult<&[u8], Stop> {
198 let (i, (offset, colour)) = tuple((u8, Colour::parse_grey))(i)?;
199 Ok((i, Stop { offset, colour }))
200 }
201
202 fn parse_grey_no_alpha(i: &[u8]) -> IResult<&[u8], Stop> {
203 let (i, (offset, colour)) = tuple((u8, Colour::parse_grey_no_alpha))(i)?;
204 Ok((i, Stop { offset, colour }))
205 }
206}
207
208#[derive(Debug, Clone, PartialEq)]
210pub struct Gradient {
211 pub type_: GradientType,
213
214 pub flags: GradientFlags,
216
217 pub stops: Vec<Stop>,
219
220 pub transform: Option<Transform>,
222}
223
224impl Gradient {
225 fn parse(i: &[u8]) -> IResult<&[u8], Gradient> {
226 let (i, (type_, flags)) = tuple((GradientType::parse, GradientFlags::parse))(i)?;
227 let (i, num_stops) = u8(i)?;
229 let num_stops = num_stops as usize;
230 let (i, transform) = if flags.contains(GradientFlags::TRANSFORM) {
231 let (i, transform) = Transform::parse(i)?;
232 (i, Some(transform))
233 } else {
234 (i, None)
235 };
236 let (i, stops) = if flags.contains(GradientFlags::NO_ALPHA) {
237 if flags.contains(GradientFlags::GREYS) {
238 many_m_n(num_stops, num_stops, Stop::parse_grey_no_alpha)(i)?
239 } else {
240 many_m_n(num_stops, num_stops, Stop::parse_rgb)(i)?
241 }
242 } else if flags.contains(GradientFlags::GREYS) {
243 many_m_n(num_stops, num_stops, Stop::parse_grey)(i)?
244 } else {
245 many_m_n(num_stops, num_stops, Stop::parse_rgba)(i)?
246 };
247 Ok((
248 i,
249 Gradient {
250 type_,
251 flags,
252 stops,
253 transform,
254 },
255 ))
256 }
257}
258
259#[derive(Debug, Clone, PartialEq)]
261pub enum Style {
262 SolidColour(Colour),
264
265 Gradient(Gradient),
267}
268
269impl Style {
270 pub fn parse(i: &[u8]) -> IResult<&[u8], Style> {
272 let (i, type_) = StyleType::parse(i)?;
273 match type_ {
274 StyleType::SolidColour => {
275 let (i, colour) = Colour::parse_rgba(i)?;
276 Ok((i, Style::SolidColour(colour)))
277 }
278 StyleType::Gradient => {
279 let (i, gradient) = Gradient::parse(i)?;
280 Ok((i, Style::Gradient(gradient)))
281 }
282 StyleType::SolidColourNoAlpha => {
283 let (i, colour) = Colour::parse_rgb(i)?;
284 Ok((i, Style::SolidColour(colour)))
285 }
286 StyleType::SolidGrey => {
287 let (i, colour) = Colour::parse_grey(i)?;
288 Ok((i, Style::SolidColour(colour)))
289 }
290 StyleType::SolidGreyNoAlpha => {
291 let (i, colour) = Colour::parse_grey_no_alpha(i)?;
292 Ok((i, Style::SolidColour(colour)))
293 }
294 }
295 }
296}
297
298#[cfg(test)]
299mod tests {
300 use super::*;
301
302 macro_rules! assert_size (
303 ($t:ty, $sz:expr) => (
304 assert_eq!(::std::mem::size_of::<$t>(), $sz);
305 );
306 );
307
308 #[test]
309 fn sizes() {
310 assert_size!(Colour, 5);
311 assert_size!(GradientType, 1);
312 assert_size!(GradientFlags, 1);
313 assert_size!(Stop, 6);
314 assert_size!(Gradient, 56);
315 assert_size!(Style, 64);
316 }
317
318 #[test]
319 fn solid_grey() {
320 let grey = b"\x05\x80";
321 let (i, style) = Style::parse(grey).unwrap();
322 assert!(i.is_empty());
323 assert_eq!(style, Style::SolidColour(Colour::GreyNoAlpha(128)));
324 }
325
326 #[test]
327 fn solid_colour() {
328 let red = b"\x01\xff\x00\x00\x80";
329 let (i, style) = Style::parse(red).unwrap();
330 assert!(i.is_empty());
331 assert_eq!(style, Style::SolidColour(Colour::Rgba(255, 0, 0, 128)));
332 }
333
334 #[test]
335 fn gradient() {
336 let data = b"\x02\x00\x04\x02\x00\x20\x40\x60\xff\x60\x40\x20";
337 let (i, style) = Style::parse(data).unwrap();
338 println!("{:?}", i);
339 assert!(i.is_empty());
340 if let Style::Gradient(gradient) = style {
341 assert_eq!(gradient.type_, GradientType::Linear);
342 assert_eq!(gradient.flags, GradientFlags::NO_ALPHA);
343 assert_eq!(
344 gradient.stops,
345 [
346 Stop {
347 offset: 0,
348 colour: Colour::Rgb(32, 64, 96)
349 },
350 Stop {
351 offset: 255,
352 colour: Colour::Rgb(96, 64, 32)
353 },
354 ]
355 );
356 assert_eq!(gradient.transform, None);
357 } else {
358 panic!("Parsed style isn’t a gradient.");
359 }
360 }
361
362 #[test]
363 fn gradient_matrix() {
364 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";
365 let (i, style) = Style::parse(data).unwrap();
366 println!("{:?}", i);
367 assert!(i.is_empty());
368 if let Style::Gradient(gradient) = style {
369 assert_eq!(gradient.type_, GradientType::Linear);
370 assert_eq!(gradient.flags, GradientFlags::TRANSFORM);
371 assert_eq!(gradient.stops, []);
372 assert_eq!(gradient.transform.unwrap(), Transform::IDENTITY);
373 } else {
374 panic!("Parsed style isn’t a gradient.");
375 }
376 }
377}