1extern crate wasm_bindgen;
2use wasm_bindgen::prelude::*;
3
4use std::collections::HashMap;
5use std::f64;
6
7pub fn get_normalizer(name: String) -> (fn(f64) -> f64) {
8 let f: fn(f64) -> f64 = match name.to_lowercase().as_ref() {
9 "log" => |f| f.log10(),
10 "linear" => |f| f,
11 _ => |f| f,
12 };
13 f
14}
15
16#[wasm_bindgen]
17#[derive(Clone, Debug)]
18pub struct RGBAValue {
19 red: u8,
20 green: u8,
21 blue: u8,
22 alpha: u8,
23}
24
25#[wasm_bindgen]
26#[derive(Clone, Debug)]
27pub struct Colormap {
28 table: Vec<RGBAValue>,
29}
30
31#[wasm_bindgen]
32#[derive(Clone, Debug)]
33pub struct ColormapCollection {
34 color_maps: HashMap<String, Colormap>,
35}
36
37impl RGBAValue {
38 pub fn new(red: u8, green: u8, blue: u8, alpha: u8) -> RGBAValue {
39 RGBAValue { red, green, blue, alpha }
40 }
41}
42
43#[wasm_bindgen]
44impl Colormap {
45 #[wasm_bindgen(constructor)]
46 pub fn new(
47 rgba: Vec<u8>,
48 ) -> Colormap {
49 if rgba.len() % 4 != 0 {
50 panic!("Needs RGBA flattened.");
51 }
52 let mut table: Vec<RGBAValue> = Vec::new();
53 for i in 0..rgba.len()/4 {
54 table.push( RGBAValue::new(
55 rgba[i * 4 + 0],
56 rgba[i * 4 + 1],
57 rgba[i * 4 + 2],
58 rgba[i * 4 + 3]
59 ));
60 }
61 Colormap {
62 table: table,
63 }
64 }
65}
66
67#[wasm_bindgen]
68impl ColormapCollection {
69 #[wasm_bindgen(constructor)]
70 pub fn new() -> ColormapCollection {
71 let mut color_maps = HashMap::new();
72 let mut default_cmap: Vec<RGBAValue> = Vec::new();
73 for i in 0..256 {
74 default_cmap.push(RGBAValue::new(i as u8, i as u8, i as u8, 255));
75 }
76 color_maps.insert(String::from("default"),
77 Colormap {
78 table: default_cmap,
79 }
80 );
81 ColormapCollection { color_maps }
82 }
83
84 pub fn add_colormap(&mut self, name: String, table: Vec<u8>) {
85 self.color_maps.insert(name.clone(), Colormap::new(table));
86 }
87
88 pub fn normalize(
89 &mut self,
90 name: String,
91 buffer: Vec<f64>,
92 image: &mut [u8],
93 min_val: Option<f64>,
94 max_val: Option<f64>,
95 take_log: bool,
96 ) {
97 let f = match take_log {
98 true => get_normalizer("log".to_string()),
99 false => get_normalizer("linear".to_string()),
100 };
101 let mut cmin_val: f64 = 0.0;
102 let mut cmax_val: f64 = 0.0;
103 if min_val == None || max_val == None {
104 cmin_val = f64::MAX;
105 cmax_val = f64::MIN;
106 for v in &buffer {
107 cmin_val = cmin_val.min(*v);
108 cmax_val = cmax_val.max(*v);
109 }
110 }
111 cmin_val = match min_val {
112 Some(v) => v,
113 None => cmin_val,
114 };
115 cmax_val = match max_val {
116 Some(v) => v,
117 None => cmax_val,
118 };
119 let cmap = match self.color_maps.get(&name) {
120 Some(cmap) => cmap,
121 None => panic!("Colormap {:?} does not exist.", name),
122 };
123 cmin_val = f(cmin_val);
124 cmax_val = f(cmax_val);
125 let tsize = cmap.table.len();
126 for (i, &x) in buffer.iter().enumerate() {
127 let scaled = ((f(x) - cmin_val) / (cmax_val - cmin_val))
128 .min(1.0)
129 .max(0.0);
130 let bin_id = ((scaled * (tsize as f64)) as usize)
131 .max(0).min(tsize - 1);
132 image[i * 4 + 0] = cmap.table[bin_id].red;
133 image[i * 4 + 1] = cmap.table[bin_id].green;
134 image[i * 4 + 2] = cmap.table[bin_id].blue;
135 image[i * 4 + 3] = cmap.table[bin_id].alpha;
136 }
137 }
138}
139
140#[cfg(test)]
141mod tests {
142
143 use super::*;
144
145 fn linear_ramp_cmap() -> Vec<u8> {
146 let mut rgba_map: Vec<u8> = Vec::new();
147 for i in 0..256 {
148 rgba_map.push(i as u8);
149 rgba_map.push(0);
150 rgba_map.push(0);
151 rgba_map.push(255);
152 }
153 rgba_map
154 }
155
156 #[test]
157 fn create_colormap() {
158 let rgba_map = linear_ramp_cmap();
161 let _cm = Colormap::new(rgba_map);
162 for (i, rgba) in _cm.table.iter().enumerate() {
164 assert_eq!(i as u8, rgba.red);
165 assert_eq!(0, rgba.green);
166 assert_eq!(0, rgba.blue);
167 assert_eq!(255, rgba.alpha);
168 }
169 }
170
171 #[test]
172 #[should_panic]
173 fn create_bad_colormap() {
174 let mut rgba_map = linear_ramp_cmap().clone();
175 rgba_map.pop();
176 let _cm = Colormap::new(rgba_map);
177 }
178
179 #[test]
180 fn create_colormap_collection() {
181 let mut cmap_collection = ColormapCollection::new();
182 cmap_collection.add_colormap("simple".to_string(), linear_ramp_cmap());
183
184 let mut ibuf: Vec<f64> = Vec::new();
186 for i in 0..256 {
187 ibuf.push( (i as f64) / 256.0);
188 }
189
190 let mut obuf: Vec<u8> = Vec::new();
192 obuf.resize(256 * 4, 0);
193
194 cmap_collection.normalize("default".to_string(), ibuf.clone(), obuf.as_mut_slice(), None, None, false);
195
196 for (i, rgba) in obuf.chunks_exact(4).enumerate() {
197 assert_eq!(rgba[0], i as u8);
198 assert_eq!(rgba[1], i as u8);
199 assert_eq!(rgba[2], i as u8);
200 assert_eq!(rgba[3], 255);
201 }
202
203 cmap_collection.normalize("simple".to_string(), ibuf.clone(), obuf.as_mut_slice(), None, None, false);
204
205 for (i, rgba) in obuf.chunks_exact(4).enumerate() {
206 assert_eq!(rgba[0], i as u8);
207 assert_eq!(rgba[1], 0);
208 assert_eq!(rgba[2], 0);
209 assert_eq!(rgba[3], 255);
210 }
211
212 ibuf.resize(0, 0.0);
214
215 for i in 0..256 {
216 ibuf.push( 10_f64.powf((i as f64) / 256.0));
217 }
218
219 cmap_collection.normalize("simple".to_string(), ibuf.clone(), obuf.as_mut_slice(), None, None, true);
220 for (i, rgba) in obuf.chunks_exact(4).enumerate() {
221 assert_eq!(rgba[0], i as u8);
222 assert_eq!(rgba[1], 0);
223 assert_eq!(rgba[2], 0);
224 assert_eq!(rgba[3], 255);
225 }
226 }
227}