diff options
author | Akshay <[email protected]> | 2021-04-18 10:50:13 +0100 |
---|---|---|
committer | Akshay <[email protected]> | 2021-04-18 10:50:13 +0100 |
commit | 869fe69fdf947230ca4129d3d1f9749360035e64 (patch) | |
tree | d6bdbd525fa16b1b5260ab1d47aef4eda85a6931 /src | |
parent | 7d2924015e691fa229c6b6fd96240ef47b3a9e5f (diff) |
add RLE compression functions
Diffstat (limited to 'src')
-rw-r--r-- | src/decode.rs | 69 | ||||
-rw-r--r-- | src/encode.rs | 44 | ||||
-rw-r--r-- | src/lib.rs | 16 | ||||
-rw-r--r-- | src/rle.rs | 89 |
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::*; | |||
4 | use byteorder::{LittleEndian, ReadBytesExt}; | 4 | use byteorder::{LittleEndian, ReadBytesExt}; |
5 | 5 | ||
6 | use crate::error::{OBIError, OBIResult}; | 6 | use crate::error::{OBIError, OBIResult}; |
7 | use crate::{FileHeader, Image, ImageInfoHeader}; | 7 | use crate::{CompressionType, FileHeader, Image, ImageInfoHeader}; |
8 | 8 | ||
9 | pub fn decode_image(obi_data: &mut Cursor<Vec<u8>>) -> OBIResult<Image> { | 9 | pub 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; | |||
3 | use bitvec::{prelude::*, vec::BitVec}; | 3 | use bitvec::{prelude::*, vec::BitVec}; |
4 | use byteorder::{LittleEndian, WriteBytesExt}; | 4 | use byteorder::{LittleEndian, WriteBytesExt}; |
5 | 5 | ||
6 | use crate::error::{OBIError, OBIResult}; | 6 | use crate::{ |
7 | use crate::Image; | 7 | error::{OBIError, OBIResult}, |
8 | rle::RLE, | ||
9 | CompressionType, Image, | ||
10 | }; | ||
8 | 11 | ||
9 | pub fn encode_image<I>(obi_image: I) -> OBIResult<Vec<u8>> | 12 | pub fn encode_image<I>(obi_image: I) -> OBIResult<Vec<u8>> |
10 | where | 13 | where |
@@ -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 | } |
@@ -7,8 +7,10 @@ use std::io; | |||
7 | mod decode; | 7 | mod decode; |
8 | mod encode; | 8 | mod encode; |
9 | pub mod error; | 9 | pub mod error; |
10 | mod rle; | ||
10 | 11 | ||
11 | use crate::error::{OBIError, OBIResult}; | 12 | use error::{OBIError, OBIResult}; |
13 | use 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 @@ | |||
1 | pub 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 | |||
7 | pub type Run<T> = (T, usize); | ||
8 | |||
9 | impl<T> RLE for Vec<T> | ||
10 | where | ||
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)] | ||
42 | mod 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 | } | ||