control_code/dec/
sixel.rs

1//            DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
2//                    Version 2, December 2004
3//
4// Copyleft (ↄ) meh. <meh@schizofreni.co> | http://meh.schizofreni.co
5//
6// Everyone is permitted to copy and distribute verbatim or modified
7// copies of this license document, and changing it is allowed as long
8// as the name is changed.
9//
10//            DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
11//   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
12//
13//  0. You just DO WHAT THE FUCK YOU WANT TO.
14
15use std::io::{self, Write};
16use nom::{self, IResult, Needed, digit};
17use {Format, CSI};
18use util::number;
19
20#[derive(Eq, PartialEq, Copy, Clone, Debug, Default)]
21pub struct Map(pub u8);
22
23impl Map {
24	#[inline]
25	pub fn is_empty(&self) -> bool {
26		self.0 == 0
27	}
28
29	#[inline]
30	pub fn get(&self, index: u8) -> bool {
31		self.0 >> index & 1 == 1
32	}
33
34	#[inline]
35	pub fn set(&mut self, index: u8, value: bool) {
36		if value {
37			self.0 |= 1 << index;
38		}
39		else {
40			self.0 &= !(1 << index);
41		}
42	}
43}
44
45impl Format for Map {
46	#[inline]
47	fn fmt<W: Write>(&self, mut f: W) -> io::Result<()> {
48		f.write_all(&[self.0 + 0x3F])
49	}
50}
51
52#[derive(Eq, PartialEq, Copy, Clone, Debug)]
53pub struct Header {
54	pub aspect:     (u32, u32),
55	pub background: bool,
56	pub grid:       Option<u32>,
57}
58
59named!(pub header<Header>,
60	do_parse!(
61		args: call!(CSI::parameters) >>
62		char!('q') >>
63
64		(Header {
65			aspect: match arg!(args[0] => 0) {
66				1         => (2, 1),
67				2         => (5, 1),
68				3 | 4     => (3, 1),
69				5 | 6     => (2, 1),
70				7 | 8 | 9 => (1, 1),
71				_         => (2, 1),
72			},
73
74			background: match arg!(args[1] => 1) {
75				1     => false,
76				2 | _ => true,
77			},
78
79			grid: arg!(args[2])
80		})));
81
82impl Format for Header {
83	fn fmt<W: Write>(&self, mut f: W) -> io::Result<()> {
84		if self.aspect != (2, 1) {
85			try!(f.write_all(&[match self.aspect {
86				(5, 1) => b'2',
87				(3, 1) => b'3',
88				(1, 1) => b'9',
89				_      => b'0',
90			}]));
91		}
92
93		if !self.background {
94			try!(f.write_all(b";1"));
95		}
96
97		if let Some(grid) = self.grid {
98			if self.background {
99				try!(f.write_all(b";"));
100			}
101
102			try!(write!(f, "{}", grid));
103		}
104
105		f.write_all(b"q")
106	}
107}
108
109#[derive(Eq, PartialEq, Clone, Debug)]
110pub enum Sixel {
111	Value(Map),
112	Repeat(u32, Map),
113
114	Raster {
115		aspect: (u32, u32),
116		size:   (u32, u32),
117	},
118
119	Enable(u32),
120	Define(u32, Color),
121	CarriageReturn,
122	LineFeed,
123}
124
125#[derive(Eq, PartialEq, Copy, Clone, Debug)]
126pub enum Color {
127	Hsl(u16, u8, u8),
128	Rgb(u8, u8, u8),
129	Rgba(u8, u8, u8, u8),
130}
131
132impl Format for Sixel {
133	fn fmt<W: Write>(&self, mut f: W) -> io::Result<()> {
134		match *self {
135			Sixel::Value(value) => {
136				try!(value.fmt(f.by_ref()));
137			}
138
139			Sixel::Repeat(times, value) => {
140				try!(write!(f, "!{}", times));
141				try!(value.fmt(f.by_ref()));
142			}
143
144			Sixel::Raster { aspect, size } => {
145				try!(write!(f, "\"{};{};{};{}", aspect.0, aspect.1, size.0, size.1));
146			}
147
148			Sixel::Enable(id) => {
149				try!(write!(f, "#{}", id));
150			}
151
152			Sixel::Define(id, color) => {
153				try!(write!(f, "#{};", id));
154
155				match color {
156					Color::Hsl(h, s, l) => {
157						try!(write!(f, "1;{};{};{}", h, l, s));
158					}
159
160					Color::Rgb(r, g, b) => {
161						try!(write!(f, "2;{};{};{}",
162							(r as f32 / 255.0 * 100.0) as u8,
163							(g as f32 / 255.0 * 100.0) as u8,
164							(b as f32 / 255.0 * 100.0) as u8));
165					}
166
167					Color::Rgba(r, g, b, a) => {
168						try!(write!(f, "3;{};{};{};{}", r, g, b, a));
169					}
170				}
171			}
172
173			Sixel::CarriageReturn => {
174				try!(f.write_all(b"$"));
175			}
176
177			Sixel::LineFeed => {
178				try!(f.write_all(b"-"));
179			}
180		}
181
182		Ok(())
183	}
184}
185
186pub fn parse(i: &[u8]) -> IResult<&[u8], Sixel> {
187	if let IResult::Done(rest, value) = value(i) {
188		IResult::Done(rest, Sixel::Value(value))
189	}
190	else {
191		inner(i)
192	}
193}
194
195named!(inner<Sixel>,
196	alt!(repeat | color | cr | lf | raster));
197
198fn value(i: &[u8]) -> IResult<&[u8], Map> {
199	const TABLE: [u8; 256] = [
200		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
201		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
202		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
203		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
204		1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
205		1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
206		1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
207		1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0,
208		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
209		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
210		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
211		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
212		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
213		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
214		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
215		0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
216	];
217
218	if i.is_empty() {
219		return IResult::Incomplete(Needed::Size(1));
220	}
221
222	if TABLE[i[0] as usize] == 1 {
223		IResult::Done(&i[1..], Map(i[0] - 0x3F))
224	}
225	else {
226		IResult::Error(nom::ErrorKind::Custom(0))
227	}
228}
229
230named!(repeat<Sixel>,
231	do_parse!(
232		char!('!') >>
233		count: digit >>
234		value: value >>
235
236		(Sixel::Repeat(number(count), value))));
237
238named!(raster<Sixel>,
239	do_parse!(
240		char!('"') >>
241		args: call!(CSI::parameters) >>
242
243		(Sixel::Raster {
244			aspect: (arg!(args[0] => 0), arg!(args[1] => 0)),
245			size:   (arg!(args[2] => 0), arg!(args[3] => 0)),
246		})));
247
248named!(color<Sixel>,
249	do_parse!(
250		char!('#') >>
251		id: digit >>
252
253		color: opt!(switch!(take!(3),
254			b";1;" => do_parse!(
255				h: digit >>
256				char!(';') >>
257				l: digit >>
258				char!(';') >>
259				s: digit >>
260
261				(Color::Hsl(number(h) as u16, number(s) as u8, number(l) as u8))) |
262
263			b";2;" => do_parse!(
264				r: digit >>
265				char!(';') >>
266				g: digit >>
267				char!(';') >>
268				b: digit >>
269
270				(Color::Rgb(
271					(number(r) as f32 / 100.0 * 255.0) as u8,
272					(number(g) as f32 / 100.0 * 255.0) as u8,
273					(number(b) as f32 / 100.0 * 255.0) as u8))) |
274
275			b";3;" => do_parse!(
276				r: digit >>
277				char!(';') >>
278				g: digit >>
279				char!(';') >>
280				b: digit >>
281				char!(';') >>
282				a: digit >>
283
284				(Color::Rgba(number(r) as u8, number(g) as u8, number(b) as u8, number(a) as u8))))) >>
285
286		(if let Some(color) = color {
287			Sixel::Define(number(id), color)
288		}
289		else {
290			Sixel::Enable(number(id))
291		})));
292	
293named!(cr<Sixel>,
294	value!(Sixel::CarriageReturn, char!('$')));
295
296named!(lf<Sixel>,
297	value!(Sixel::LineFeed, char!('-')));
298
299pub mod shim {
300	pub use super::Sixel as T;
301	pub use super::Sixel::*;
302	pub use super::{Header, Map, Color};
303	pub use super::{parse, header};
304}
305
306#[cfg(test)]
307mod test {
308	mod parse {
309		use DEC::SIXEL::{self, parse, header};
310
311		macro_rules! test {
312			($string:expr => $item:expr) => (
313				assert_eq!($item,
314					parse($string).unwrap().1);
315			);
316
317			($ident:ident $string:expr => $item:expr) => (
318				assert_eq!($item,
319					$ident($string).unwrap().1);
320			);
321		}
322
323		#[test]
324		fn start() {
325			test!(header b"q" =>
326				SIXEL::Header { aspect: (2, 1), background: false, grid: None });
327
328			test!(header b"0q" =>
329				SIXEL::Header { aspect: (2, 1), background: false, grid: None });
330
331			test!(header b"1q" =>
332				SIXEL::Header { aspect: (2, 1), background: false, grid: None });
333
334			test!(header b"2q" =>
335				SIXEL::Header { aspect: (5, 1), background: false, grid: None });
336
337			test!(header b"3q" =>
338				SIXEL::Header { aspect: (3, 1), background: false, grid: None });
339
340			test!(header b"4q" =>
341				SIXEL::Header { aspect: (3, 1), background: false, grid: None });
342
343			test!(header b"5q" =>
344				SIXEL::Header { aspect: (2, 1), background: false, grid: None });
345
346			test!(header b"6q" =>
347				SIXEL::Header { aspect: (2, 1), background: false, grid: None });
348
349			test!(header b"7q" =>
350				SIXEL::Header { aspect: (1, 1), background: false, grid: None });
351
352			test!(header b"8q" =>
353				SIXEL::Header { aspect: (1, 1), background: false, grid: None });
354
355			test!(header b"9q" =>
356				SIXEL::Header { aspect: (1, 1), background: false, grid: None });
357
358			test!(header b";2q" =>
359				SIXEL::Header { aspect: (2, 1), background: true, grid: None });
360
361			test!(header b";;100q" =>
362				SIXEL::Header { aspect: (2, 1), background: false, grid: Some(100) });
363		}
364
365		#[test]
366		fn value() {
367			test!(b"?" =>
368				SIXEL::Value(SIXEL::Map(0b000000)));
369
370			test!(b"~" =>
371				SIXEL::Value(SIXEL::Map(0b111111)));
372		}
373
374		#[test]
375		fn map() {
376			let mut map = SIXEL::Map::default();
377
378			map.set(0, true);
379			assert_eq!(SIXEL::Map(1), map);
380
381			map.set(0, false);
382			assert_eq!(SIXEL::Map(0), map);
383
384			map.set(1, true);
385			assert_eq!(SIXEL::Map(2), map);
386		}
387	}
388
389	mod format {
390		use DEC::SIXEL::{self, parse};
391		use format;
392
393		macro_rules! test {
394			($code:expr) => (
395				let item = $code;
396				assert_eq!(item, parse(&format(&item)).unwrap().1);
397			);
398		}
399
400		#[test]
401		fn repeat() {
402			test!(SIXEL::Repeat(4, SIXEL::Map::default()));
403		}
404	}
405}