aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorAkshay <[email protected]>2021-04-18 10:50:13 +0100
committerAkshay <[email protected]>2021-04-18 10:50:13 +0100
commit869fe69fdf947230ca4129d3d1f9749360035e64 (patch)
treed6bdbd525fa16b1b5260ab1d47aef4eda85a6931 /src
parent7d2924015e691fa229c6b6fd96240ef47b3a9e5f (diff)
add RLE compression functions
Diffstat (limited to 'src')
-rw-r--r--src/decode.rs69
-rw-r--r--src/encode.rs44
-rw-r--r--src/lib.rs16
-rw-r--r--src/rle.rs89
4 files changed, 191 insertions, 27 deletions
diff --git a/src/decode.rs b/src/decode.rs
index a9abd37..9c6a065 100644
--- a/src/decode.rs
+++ b/src/decode.rs
@@ -4,7 +4,7 @@ use bitvec::prelude::*;
4use byteorder::{LittleEndian, ReadBytesExt}; 4use byteorder::{LittleEndian, ReadBytesExt};
5 5
6use crate::error::{OBIError, OBIResult}; 6use crate::error::{OBIError, OBIResult};
7use crate::{FileHeader, Image, ImageInfoHeader}; 7use crate::{CompressionType, FileHeader, Image, ImageInfoHeader};
8 8
9pub fn decode_image(obi_data: &mut Cursor<Vec<u8>>) -> OBIResult<Image> { 9pub fn decode_image(obi_data: &mut Cursor<Vec<u8>>) -> OBIResult<Image> {
10 // file header 10 // file header
@@ -43,22 +43,59 @@ pub fn decode_image(obi_data: &mut Cursor<Vec<u8>>) -> OBIResult<Image> {
43 post_compression_size, 43 post_compression_size,
44 }; 44 };
45 45
46 // pixmap data 46 let data: Vec<bool> = match CompressionType::from_u32(compression_type) {
47 let mut data_bytes = vec![]; 47 CompressionType::RLE => {
48 obi_data 48 let mut rest = vec![];
49 .read_to_end(&mut data_bytes) 49 let mut lengths = vec![];
50 .map_err(|_| OBIError::Decode)?; 50 loop {
51 let data = data_bytes 51 let l = obi_data
52 .iter() 52 .read_u32::<LittleEndian>()
53 .map(|&b| { 53 .map_err(|_| OBIError::Encode)?;
54 BitVec::<Lsb0, u8>::from_element(b) 54 if l == 0 {
55 .into_iter() 55 break;
56 .map(|e| e as bool) 56 }
57 .collect::<Vec<bool>>() 57 lengths.push(l);
58 }) 58 }
59 .flatten() 59 obi_data
60 .collect::<Vec<_>>(); 60 .read_to_end(&mut rest)
61 .map_err(|_| OBIError::Decode)?;
62 let data_points = rest
63 .iter()
64 .map(|&b| {
65 BitVec::<Lsb0, u8>::from_element(b)
66 .into_iter()
67 .map(|e| e as bool)
68 .collect::<Vec<bool>>()
69 })
70 .flatten()
71 .collect::<Vec<bool>>();
61 72
73 let data = data_points
74 .into_iter()
75 .zip(lengths)
76 .map(|(d, l)| vec![d; l as usize])
77 .flatten()
78 .collect::<Vec<bool>>();
79 data
80 }
81 _ => {
82 let mut rest = vec![];
83 obi_data
84 .read_to_end(&mut rest)
85 .map_err(|_| OBIError::Decode)?;
86 let data_points = rest
87 .iter()
88 .map(|&b| {
89 BitVec::<Lsb0, u8>::from_element(b)
90 .into_iter()
91 .map(|e| e as bool)
92 .collect::<Vec<bool>>()
93 })
94 .flatten()
95 .collect::<Vec<_>>();
96 data_points
97 }
98 };
62 return Ok(Image { 99 return Ok(Image {
63 file_header, 100 file_header,
64 image_info_header, 101 image_info_header,
diff --git a/src/encode.rs b/src/encode.rs
index a8c58e0..30edc49 100644
--- a/src/encode.rs
+++ b/src/encode.rs
@@ -3,8 +3,11 @@ use std::borrow::Borrow;
3use bitvec::{prelude::*, vec::BitVec}; 3use bitvec::{prelude::*, vec::BitVec};
4use byteorder::{LittleEndian, WriteBytesExt}; 4use byteorder::{LittleEndian, WriteBytesExt};
5 5
6use crate::error::{OBIError, OBIResult}; 6use crate::{
7use crate::Image; 7 error::{OBIError, OBIResult},
8 rle::RLE,
9 CompressionType, Image,
10};
8 11
9pub fn encode_image<I>(obi_image: I) -> OBIResult<Vec<u8>> 12pub fn encode_image<I>(obi_image: I) -> OBIResult<Vec<u8>>
10where 13where
@@ -40,17 +43,38 @@ where
40 .write_u32::<LittleEndian>(image_info_header.post_compression_size) 43 .write_u32::<LittleEndian>(image_info_header.post_compression_size)
41 .map_err(|_| OBIError::Encode)?; 44 .map_err(|_| OBIError::Encode)?;
42 45
46 let write_pixel_data = |pixels: &Vec<bool>, obi_data: &mut Vec<u8>| -> OBIResult<()> {
47 for byte in pixels.chunks(8) {
48 let mut bv = BitVec::<Lsb0, u8>::new();
49 for &b in byte {
50 bv.push(b)
51 }
52 obi_data
53 .write_u8(bv.load::<u8>())
54 .map_err(|_| OBIError::Encode)?;
55 }
56 Ok(())
57 };
58
43 // pixmap data 59 // pixmap data
44 let pixmap = &obi_image.data; 60 let pixmap = &obi_image.data;
45 for byte in pixmap.chunks(8) { 61 match CompressionType::from_u32(obi_image.image_info_header.compression_type) {
46 let mut bv = BitVec::<Lsb0, u8>::new(); 62 CompressionType::RLE => {
47 for &b in byte { 63 let (data_points, lengths): (Vec<_>, Vec<_>) = pixmap.compress().into_iter().unzip();
48 bv.push(b); 64 for l in lengths {
65 obi_data
66 .write_u32::<LittleEndian>(l as u32)
67 .map_err(|_| OBIError::Encode)?;
68 }
69 // end length sequence with zero
70 obi_data
71 .write_u32::<LittleEndian>(0)
72 .map_err(|_| OBIError::Encode)?;
73 // begin data point sequence
74 write_pixel_data(&data_points, &mut obi_data)?;
49 } 75 }
50 obi_data 76 _ => write_pixel_data(pixmap, &mut obi_data)?,
51 .write_u8(bv.load::<u8>()) 77 };
52 .map_err(|_| OBIError::Encode)?;
53 }
54 78
55 return Ok(obi_data); 79 return Ok(obi_data);
56} 80}
diff --git a/src/lib.rs b/src/lib.rs
index f949d06..3995c16 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -7,8 +7,10 @@ use std::io;
7mod decode; 7mod decode;
8mod encode; 8mod encode;
9pub mod error; 9pub mod error;
10mod rle;
10 11
11use crate::error::{OBIError, OBIResult}; 12use error::{OBIError, OBIResult};
13use rle::RLE;
12 14
13#[non_exhaustive] 15#[non_exhaustive]
14#[derive(Copy, Clone, Debug, PartialEq)] 16#[derive(Copy, Clone, Debug, PartialEq)]
@@ -83,6 +85,14 @@ impl CompressionType {
83 _ => panic!("Invalid compression algorithm"), 85 _ => panic!("Invalid compression algorithm"),
84 } 86 }
85 } 87 }
88 pub fn to_u32(self) -> u32 {
89 match self {
90 CompressionType::None => 0,
91 CompressionType::RLE => 1,
92 CompressionType::Kosinki => 10,
93 _ => panic!("Invalid compression algorithm"),
94 }
95 }
86} 96}
87 97
88#[derive(Debug)] 98#[derive(Debug)]
@@ -111,6 +121,10 @@ impl Image {
111 } 121 }
112 } 122 }
113 123
124 pub fn use_compression(&mut self, comp: CompressionType) {
125 self.image_info_header.compression_type = comp.to_u32();
126 }
127
114 pub fn width(&self) -> u32 { 128 pub fn width(&self) -> u32 {
115 self.image_info_header.width 129 self.image_info_header.width
116 } 130 }
diff --git a/src/rle.rs b/src/rle.rs
new file mode 100644
index 0000000..f6d1e05
--- /dev/null
+++ b/src/rle.rs
@@ -0,0 +1,89 @@
1pub trait RLE {
2 type Data;
3 fn compress(&self) -> Vec<Run<Self::Data>>;
4 fn decompress(runs: Vec<Run<Self::Data>>) -> Vec<Self::Data>;
5}
6
7pub type Run<T> = (T, usize);
8
9impl<T> RLE for Vec<T>
10where
11 T: Clone + PartialEq,
12{
13 type Data = T;
14 fn compress(&self) -> Vec<Run<Self::Data>> {
15 let mut runs = vec![];
16 if self.is_empty() {
17 return runs;
18 }
19 let mut idx = 0;
20 loop {
21 let first = &self[idx];
22 let run_length = self[idx..].iter().take_while(|&item| item == first).count();
23
24 runs.push((first.clone(), run_length));
25
26 idx += run_length;
27 if idx > self.len() - 1 {
28 break;
29 }
30 }
31 runs
32 }
33 fn decompress(runs: Vec<Run<Self::Data>>) -> Vec<Self::Data> {
34 runs.into_iter()
35 .map(|(item, size)| vec![item; size])
36 .flatten()
37 .collect()
38 }
39}
40
41#[cfg(test)]
42mod tests {
43 use super::*;
44 #[test]
45 fn singleton() {
46 let data = "a".chars().collect::<Vec<_>>();
47 assert_eq!(Vec::<char>::compress(&data), vec![('a', 1),]);
48 }
49 #[test]
50 fn identity() {
51 let data = "aabbccddaabbaa".chars().collect::<Vec<_>>();
52 assert_eq!(Vec::<char>::decompress(data.compress()), data);
53 }
54 #[test]
55 fn repeated_singleton() {
56 let data = "aaaaaaaaaaaaa".chars().collect::<Vec<_>>();
57 assert_eq!(Vec::<char>::compress(&data), vec![('a', 13),]);
58 }
59 #[test]
60 fn empty_runs() {
61 let data = "".chars().collect::<Vec<_>>();
62 assert!(data.compress().is_empty());
63 }
64 #[test]
65 fn empty_decompress() {
66 assert!(Vec::<char>::decompress(vec![]).is_empty());
67 }
68 #[test]
69 fn check_runs1() {
70 let data = "aaaabbbbcccc".chars().collect::<Vec<_>>();
71 assert_eq!(data.compress(), vec![('a', 4), ('b', 4), ('c', 4)]);
72 }
73 #[test]
74 fn check_runs2() {
75 let data = "aabbccddaabbaa".chars().collect::<Vec<_>>();
76 assert_eq!(
77 data.compress(),
78 vec![
79 ('a', 2),
80 ('b', 2),
81 ('c', 2),
82 ('d', 2),
83 ('a', 2),
84 ('b', 2),
85 ('a', 2)
86 ]
87 );
88 }
89}