oxihuman_viewer/
error_diffusion_view.rs1#![allow(dead_code)]
4
5pub struct ErrorDiffusionView {
6 pub color_levels: u32,
7 pub serpentine_scan: bool,
8 pub strength: f32,
9}
10
11pub fn new_error_diffusion_view() -> ErrorDiffusionView {
12 ErrorDiffusionView {
13 color_levels: 256,
14 serpentine_scan: true,
15 strength: 1.0,
16 }
17}
18
19pub fn ed_set_color_levels(v: &mut ErrorDiffusionView, n: u32) {
20 v.color_levels = n.clamp(2, 256);
21}
22
23pub fn ed_quantize(v: &ErrorDiffusionView, value: f32) -> (f32, f32) {
25 let levels = v.color_levels as f32;
26 let quantized = (value * (levels - 1.0)).round() / (levels - 1.0);
27 let error = (value - quantized) * v.strength;
28 (quantized.clamp(0.0, 1.0), error)
29}
30
31pub fn ed_floyd_steinberg_weights() -> (f32, f32, f32, f32) {
33 (7.0 / 16.0, 3.0 / 16.0, 5.0 / 16.0, 1.0 / 16.0)
34}
35
36pub fn ed_is_high_fidelity(v: &ErrorDiffusionView) -> bool {
37 v.color_levels >= 128
38}
39
40pub fn ed_blend(a: &ErrorDiffusionView, b: &ErrorDiffusionView, t: f32) -> ErrorDiffusionView {
41 let t = t.clamp(0.0, 1.0);
42 let cl = (a.color_levels as f32 + (b.color_levels as f32 - a.color_levels as f32) * t).round()
43 as u32;
44 ErrorDiffusionView {
45 color_levels: cl.clamp(2, 256),
46 serpentine_scan: if t < 0.5 {
47 a.serpentine_scan
48 } else {
49 b.serpentine_scan
50 },
51 strength: a.strength + (b.strength - a.strength) * t,
52 }
53}
54
55#[cfg(test)]
56mod tests {
57 use super::*;
58
59 #[test]
60 fn test_new() {
61 let v = new_error_diffusion_view();
63 assert_eq!(v.color_levels, 256);
64 }
65
66 #[test]
67 fn test_quantize_zero() {
68 let v = new_error_diffusion_view();
70 let (q, _e) = ed_quantize(&v, 0.0);
71 assert!(q.abs() < 1e-6);
72 }
73
74 #[test]
75 fn test_floyd_steinberg_weights_sum() {
76 let (w0, w1, w2, w3) = ed_floyd_steinberg_weights();
78 let sum = w0 + w1 + w2 + w3;
79 assert!((sum - 1.0).abs() < 1e-6);
80 }
81
82 #[test]
83 fn test_high_fidelity_by_default() {
84 let v = new_error_diffusion_view();
86 assert!(ed_is_high_fidelity(&v));
87 }
88
89 #[test]
90 fn test_blend() {
91 let a = ErrorDiffusionView {
93 color_levels: 16,
94 serpentine_scan: false,
95 strength: 0.0,
96 };
97 let b = ErrorDiffusionView {
98 color_levels: 16,
99 serpentine_scan: false,
100 strength: 2.0,
101 };
102 let c = ed_blend(&a, &b, 0.5);
103 assert!((c.strength - 1.0).abs() < 1e-5);
104 }
105}