1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
mod decoder;
#[doc(inline)]
pub use decoder::*;
mod encoder;
#[doc(inline)]
pub use encoder::*;
mod shared;
#[doc(inline)]
pub use shared::*;
#[cfg(test)]
mod tests {
use std::ops::Deref;
use image::*;
use crate::*;
fn hsv_to_rgb(h: f64, s: f64, v: f64) -> [u8; 3] {
let h = (h - h.floor()) * 6.0;
let f = h - h.floor();
let u = (v * 255.0) as u8;
let p = (v * (1.0 - s) * 255.0) as u8;
let q = (v * (1.0 - s * f) * 255.0) as u8;
let t = (v * (1.0 - s * (1.0 - f)) * 255.0) as u8;
match h as u8 {
0 => [u, t, p],
1 => [q, u, p],
2 => [p, u, t],
3 => [p, q, u],
4 => [t, p, u],
5 => [u, p, q],
_ => unreachable!("h must be between 0.0 and 1.0"),
}
}
fn generate_color_wheel(width: u32, height: u32, background_alpha: bool) -> DynamicImage {
let f = |x, y| {
let width = width as f64;
let height = height as f64;
let x = x as f64 - width / 2.0;
let y = y as f64 - height / 2.0;
let theta = y.atan2(x);
let tau = 2.0 * std::f64::consts::PI;
let h = (theta + std::f64::consts::PI) / tau;
let s = (4.0 * (x * x + y * y) / width / height).sqrt();
let v = 1.0;
if s > 1.0 {
Rgba([0, 0, 0, if background_alpha { 0 } else { 255 }])
} else {
let [r, g, b] = hsv_to_rgb(h, s, v);
Rgba([r, g, b, 255])
}
};
DynamicImage::ImageRgba8(ImageBuffer::from_fn(width, height, f))
}
const SIZE: u32 = 96;
#[test]
fn encode_decode() {
let test_image_no_alpha = generate_color_wheel(SIZE, SIZE, false);
let encoded = Encoder::from_image(&test_image_no_alpha).unwrap().encode_lossless();
let decoded = Decoder::new(encoded.deref()).decode().unwrap().to_image().to_rgb8();
assert_eq!(test_image_no_alpha.to_rgb8().deref(), decoded.deref());
let test_image_alpha = generate_color_wheel(SIZE, SIZE, true);
let encoded = Encoder::from_image(&test_image_alpha).unwrap().encode_lossless();
let decoded = Decoder::new(encoded.deref()).decode().unwrap().to_image().to_rgba8();
fn compare(p1: &Rgba<u8>, p2: &Rgba<u8>) -> bool {
if p1.channels()[3] == 0 && p2.channels()[3] == 0 {
true
} else {
p1 == p2
}
}
for (p1, p2) in test_image_alpha.to_rgba8().pixels().zip(decoded.pixels()) {
assert!(compare(p1, p2))
}
}
#[test]
fn get_info() {
let test_image_no_alpha = generate_color_wheel(SIZE, SIZE, false);
let encoded = Encoder::from_image(&test_image_no_alpha).unwrap().encode_lossless();
let features = BitstreamFeatures::new(encoded.deref()).unwrap();
assert_eq!(features.width(), SIZE);
assert_eq!(features.height(), SIZE);
assert!(!features.has_alpha());
assert!(!features.has_animation());
let test_image_alpha = generate_color_wheel(SIZE, SIZE, true);
let encoded = Encoder::from_image(&test_image_alpha).unwrap().encode_lossless();
let features = BitstreamFeatures::new(encoded.deref()).unwrap();
assert_eq!(features.width(), SIZE);
assert_eq!(features.height(), SIZE);
assert!(features.has_alpha());
assert!(!features.has_animation());
}
}