1use ecolor::Color32;
2use eyre::Result;
3use num::{BigUint, One, ToPrimitive};
4use surfer_translation_types::{
5 TranslationPreference, VariableValue, kind_for_binary_representation,
6};
7
8use crate::translation::{BasicTranslator, ValueKind};
9use crate::wave_container::{ScopeId, VarId, VariableMeta};
10
11pub struct RGBTranslator {}
12
13impl BasicTranslator<VarId, ScopeId> for RGBTranslator {
14 fn name(&self) -> String {
15 String::from("RGB")
16 }
17
18 fn basic_translate(&self, num_bits: u32, value: &VariableValue) -> (String, ValueKind) {
19 match value {
20 VariableValue::BigUint(v) => {
21 let nibble_length = num_bits.div_ceil(3);
22 let b = v % (BigUint::one() << nibble_length);
23 let g = (v >> nibble_length) % (BigUint::one() << nibble_length);
24 let r = (v >> (2 * nibble_length)) % (BigUint::one() << nibble_length);
25 let (r_u8, g_u8, b_u8) = if nibble_length >= 8 {
26 let scale = nibble_length - 8;
27 (
28 (r >> scale).to_u8().unwrap_or(255),
29 (g >> scale).to_u8().unwrap_or(255),
30 (b >> scale).to_u8().unwrap_or(255),
31 )
32 } else {
33 let scale = 8 - nibble_length;
34 (
35 (r << scale).to_u8().unwrap_or(255),
36 (g << scale).to_u8().unwrap_or(255),
37 (b << scale).to_u8().unwrap_or(255),
38 )
39 };
40 let s = format!("#{r_u8:02x}{g_u8:02x}{b_u8:02x}");
41 (s, ValueKind::Custom(Color32::from_rgb(r_u8, g_u8, b_u8)))
42 }
43 VariableValue::String(s) => (s.clone(), kind_for_binary_representation(s)),
44 }
45 }
46
47 fn translates(&self, variable: &VariableMeta) -> Result<TranslationPreference> {
48 if let Some(num_bits) = variable.num_bits {
49 if num_bits.is_multiple_of(3u32) {
50 Ok(TranslationPreference::Yes)
51 } else {
52 Ok(TranslationPreference::No)
53 }
54 } else {
55 Ok(TranslationPreference::No)
56 }
57 }
58}
59
60pub struct YCbCrTranslator {}
61
62impl BasicTranslator<VarId, ScopeId> for YCbCrTranslator {
63 fn name(&self) -> String {
64 String::from("YCbCr")
65 }
66
67 fn basic_translate(&self, num_bits: u32, value: &VariableValue) -> (String, ValueKind) {
68 match value {
69 VariableValue::BigUint(v) => {
70 let nibble_length = num_bits.div_ceil(3);
71 let cr = v % (BigUint::one() << nibble_length);
72 let cb = (v >> nibble_length) % (BigUint::one() << nibble_length);
73 let y = (v >> (2 * nibble_length)) % (BigUint::one() << nibble_length);
74 let (y_u8, cb_u8, cr_u8) = if nibble_length >= 8 {
75 let scale = nibble_length - 8;
76 (
77 (y >> scale).to_u8().unwrap_or(255),
78 (cb >> scale).to_u8().unwrap_or(255),
79 (cr >> scale).to_u8().unwrap_or(255),
80 )
81 } else {
82 let scale = 8 - nibble_length;
83 (
84 (y << scale).to_u8().unwrap_or(255),
85 (cb << scale).to_u8().unwrap_or(255),
86 (cr << scale).to_u8().unwrap_or(255),
87 )
88 };
89 let s = format!("#{y_u8:02x}{cb_u8:02x}{cr_u8:02x}");
90 let (r_u8, g_u8, b_u8) = ycbcr_to_rgb(y_u8, cb_u8, cr_u8);
91 (s, ValueKind::Custom(Color32::from_rgb(r_u8, g_u8, b_u8)))
92 }
93 VariableValue::String(s) => (s.clone(), kind_for_binary_representation(s)),
94 }
95 }
96
97 fn translates(&self, variable: &VariableMeta) -> Result<TranslationPreference> {
98 if let Some(num_bits) = variable.num_bits {
99 if num_bits.is_multiple_of(3u32) {
100 Ok(TranslationPreference::Yes)
101 } else {
102 Ok(TranslationPreference::No)
103 }
104 } else {
105 Ok(TranslationPreference::No)
106 }
107 }
108}
109
110pub struct GrayScaleTranslator {}
111
112impl BasicTranslator<VarId, ScopeId> for GrayScaleTranslator {
113 fn name(&self) -> String {
114 String::from("Grayscale")
115 }
116
117 fn basic_translate(&self, num_bits: u32, value: &VariableValue) -> (String, ValueKind) {
118 match value {
119 VariableValue::BigUint(v) => {
120 let g = if num_bits >= 8 {
121 let scale = num_bits - 8;
122 (v >> scale).to_u8().unwrap_or(255)
123 } else {
124 let scale = 8 - num_bits;
125 (v << scale).to_u8().unwrap_or(255)
126 };
127 let s = format!("#{g:02x}");
128 (s, ValueKind::Custom(Color32::from_gray(g)))
129 }
130 VariableValue::String(s) => (s.clone(), kind_for_binary_representation(s)),
131 }
132 }
133}
134
135fn ycbcr_to_rgb(y: u8, cb: u8, cr: u8) -> (u8, u8, u8) {
138 let y_f = f32::from(y);
139 let cb_i = (i32::from(cb) - 128) as f32;
140 let cr_i = (i32::from(cr) - 128) as f32;
141
142 let r = (y_f + 1.402_f32 * cr_i).round() as i32;
143 let g = (y_f - 0.344136_f32 * cb_i - 0.714136_f32 * cr_i).round() as i32;
144 let b = (y_f + 1.772_f32 * cb_i).round() as i32;
145
146 fn clamp_u8(x: i32) -> u8 {
147 x.clamp(0, 255) as u8
148 }
149
150 (clamp_u8(r), clamp_u8(g), clamp_u8(b))
151}
152
153#[cfg(test)]
154mod test {
155 use super::*;
156 use num::BigUint;
157
158 fn translate_rgb(num_bits: u32, value: u32) -> (String, ValueKind) {
159 let translator = RGBTranslator {};
160 let biguint_value = VariableValue::BigUint(BigUint::from(value));
161 translator.basic_translate(num_bits, &biguint_value)
162 }
163
164 fn assert_color_value(result: &ValueKind, expected_r: u8, expected_g: u8, expected_b: u8) {
165 if let ValueKind::Custom(color) = result {
166 let actual_color = Color32::from_rgb(expected_r, expected_g, expected_b);
167 assert_eq!(color, &actual_color, "Color mismatch");
168 } else {
169 panic!("Expected Custom color value, got {result:?}");
170 }
171 }
172
173 #[test]
174 fn rgb_translator_name() {
175 let translator = RGBTranslator {};
176 assert_eq!(translator.name(), "RGB");
177 }
178
179 #[test]
181 fn rgb_translator_24bit_pure_red() {
182 let (hex, kind) = translate_rgb(24, 0xFF0000);
183 assert_eq!(hex, "#ff0000");
184 assert_color_value(&kind, 255, 0, 0);
185 }
186
187 #[test]
188 fn rgb_translator_24bit_pure_green() {
189 let (hex, kind) = translate_rgb(24, 0x00FF00);
190 assert_eq!(hex, "#00ff00");
191 assert_color_value(&kind, 0, 255, 0);
192 }
193
194 #[test]
195 fn rgb_translator_24bit_pure_blue() {
196 let (hex, kind) = translate_rgb(24, 0x0000FF);
197 assert_eq!(hex, "#0000ff");
198 assert_color_value(&kind, 0, 0, 255);
199 }
200
201 #[test]
202 fn rgb_translator_24bit_white() {
203 let (hex, kind) = translate_rgb(24, 0xFFFFFF);
204 assert_eq!(hex, "#ffffff");
205 assert_color_value(&kind, 255, 255, 255);
206 }
207
208 #[test]
209 fn rgb_translator_24bit_black() {
210 let (hex, kind) = translate_rgb(24, 0x000000);
211 assert_eq!(hex, "#000000");
212 assert_color_value(&kind, 0, 0, 0);
213 }
214
215 #[test]
216 fn rgb_translator_24bit_mixed_colors() {
217 let (hex, kind) = translate_rgb(24, 0xABCDEF);
219 assert_eq!(hex, "#abcdef");
220 assert_color_value(&kind, 0xAB, 0xCD, 0xEF);
221 }
222
223 #[test]
224 fn rgb_translator_24bit_low_values() {
225 let (hex, kind) = translate_rgb(24, 0x010203);
227 assert_eq!(hex, "#010203");
228 assert_color_value(&kind, 0x01, 0x02, 0x03);
229 }
230
231 #[test]
233 fn rgb_translator_3bit_max_value() {
234 let (hex, kind) = translate_rgb(3, 0x7);
237 assert_eq!(hex, "#808080");
239 assert_color_value(&kind, 128, 128, 128);
240 }
241
242 #[test]
243 fn rgb_translator_6bit_all_ones() {
244 let (hex, kind) = translate_rgb(6, 0x3F);
247 assert_eq!(hex, "#c0c0c0");
248 assert_color_value(&kind, 192, 192, 192);
249 }
250
251 #[test]
252 fn rgb_translator_9bit_full_range() {
253 let (hex, kind) = translate_rgb(9, 0x1FF);
256 assert_eq!(hex, "#e0e0e0");
257 assert_color_value(&kind, 224, 224, 224);
258 }
259
260 #[test]
261 fn rgb_translator_12bit_mixed() {
262 let (hex, kind) = translate_rgb(12, 0xFA5);
266 assert_eq!(hex, "#f0a050");
267 assert_color_value(&kind, 240, 160, 80);
268 }
269
270 #[test]
271 fn rgb_translator_15bit_downscaling() {
272 let (hex, kind) = translate_rgb(15, 0x7FFF);
275 assert_eq!(hex, "#f8f8f8");
276 assert_color_value(&kind, 248, 248, 248);
277 }
278
279 #[test]
280 fn rgb_translator_18bit_downscaling() {
281 let (hex, kind) = translate_rgb(18, 0x3FFFF);
284 assert_eq!(hex, "#fcfcfc");
285 assert_color_value(&kind, 252, 252, 252);
286 }
287
288 #[test]
289 fn rgb_translator_21bit_downscaling() {
290 let (hex, kind) = translate_rgb(21, 0x1FFFFF);
293 assert_eq!(hex, "#fefefe");
294 assert_color_value(&kind, 254, 254, 254);
295 }
296
297 #[test]
299 fn rgb_translator_24bit_high_red_low_others() {
300 let (hex, kind) = translate_rgb(24, 0xFF0000);
301 assert_eq!(hex, "#ff0000");
302 assert_color_value(&kind, 255, 0, 0);
303 }
304
305 #[test]
306 fn rgb_translator_24bit_medium_values() {
307 let (hex, kind) = translate_rgb(24, 0x808080);
309 assert_eq!(hex, "#808080");
310 assert_color_value(&kind, 128, 128, 128);
311 }
312
313 #[test]
314 fn rgb_translator_12bit_asymmetric() {
315 let (hex, kind) = translate_rgb(12, 0xF81);
319 assert_eq!(hex, "#f08010");
320 assert_color_value(&kind, 240, 128, 16);
321 }
322
323 #[test]
325 fn rgb_translator_string_value() {
326 let translator = RGBTranslator {};
327 let value = VariableValue::String("test_string".to_string());
328 let (result, _kind) = translator.basic_translate(24, &value);
329 assert_eq!(result, "test_string");
330 }
331
332 fn translate_gray(num_bits: u32, value: u32) -> (String, ValueKind) {
334 let translator = GrayScaleTranslator {};
335 let biguint_value = VariableValue::BigUint(BigUint::from(value));
336 translator.basic_translate(num_bits, &biguint_value)
337 }
338
339 fn assert_gray_value(result: &ValueKind, g: u8) {
340 if let ValueKind::Custom(color) = result {
341 let expected = Color32::from_gray(g);
342 assert_eq!(color, &expected, "Gray color mismatch");
343 } else {
344 panic!("Expected Custom color value, got {result:?}");
345 }
346 }
347
348 #[test]
349 fn grayscale_translator_name() {
350 let translator = GrayScaleTranslator {};
351 assert_eq!(translator.name(), "Grayscale");
352 }
353
354 #[test]
356 fn grayscale_8bit_black() {
357 let (hex, kind) = translate_gray(8, 0x00);
358 assert_eq!(hex, "#00");
359 assert_gray_value(&kind, 0);
360 }
361
362 #[test]
363 fn grayscale_8bit_mid_gray() {
364 let (hex, kind) = translate_gray(8, 0x80);
365 assert_eq!(hex, "#80");
366 assert_gray_value(&kind, 128);
367 }
368
369 #[test]
370 fn grayscale_8bit_white() {
371 let (hex, kind) = translate_gray(8, 0xFF);
372 assert_eq!(hex, "#ff");
373 assert_gray_value(&kind, 255);
374 }
375
376 #[test]
378 fn grayscale_1bit_max() {
379 let (hex, kind) = translate_gray(1, 0x1);
380 assert_eq!(hex, "#80");
381 assert_gray_value(&kind, 128);
382 }
383
384 #[test]
385 fn grayscale_4bit_max() {
386 let (hex, kind) = translate_gray(4, 0xF);
387 assert_eq!(hex, "#f0");
388 assert_gray_value(&kind, 240);
389 }
390
391 #[test]
392 fn grayscale_7bit_max() {
393 let (hex, kind) = translate_gray(7, 0x7F);
394 assert_eq!(hex, "#fe");
395 assert_gray_value(&kind, 254);
396 }
397
398 #[test]
400 fn grayscale_12bit_high_nibble() {
401 let (hex, kind) = translate_gray(12, 0xF00);
402 assert_eq!(hex, "#f0");
403 assert_gray_value(&kind, 0xF0);
404 }
405
406 #[test]
407 fn grayscale_12bit_mid_nibble() {
408 let (hex, kind) = translate_gray(12, 0x0F0);
409 assert_eq!(hex, "#0f");
410 assert_gray_value(&kind, 0x0F);
411 }
412
413 #[test]
414 fn grayscale_12bit_low_nibble() {
415 let (hex, kind) = translate_gray(12, 0x00F);
416 assert_eq!(hex, "#00");
417 assert_gray_value(&kind, 0x00);
418 }
419
420 #[test]
421 fn grayscale_16bit_high_byte() {
422 let (hex, kind) = translate_gray(16, 0xFF00);
423 assert_eq!(hex, "#ff");
424 assert_gray_value(&kind, 0xFF);
425 }
426
427 #[test]
428 fn grayscale_16bit_mid_value() {
429 let (hex, kind) = translate_gray(16, 0x7F00);
430 assert_eq!(hex, "#7f");
431 assert_gray_value(&kind, 0x7F);
432 }
433
434 #[test]
435 fn grayscale_16bit_low_byte() {
436 let (hex, kind) = translate_gray(16, 0x00FF);
437 assert_eq!(hex, "#00");
438 assert_gray_value(&kind, 0x00);
439 }
440
441 #[test]
443 fn grayscale_string_value() {
444 let translator = GrayScaleTranslator {};
445 let value = VariableValue::String("gray_string".to_string());
446 let (result, _kind) = translator.basic_translate(8, &value);
447 assert_eq!(result, "gray_string");
448 }
449
450 fn assert_rgb_approx(actual: (u8, u8, u8), expected: (u8, u8, u8), tol: u8) {
452 let (ar, ag, ab) = actual;
453 let (er, eg, eb) = expected;
454 let dr = ar.abs_diff(er);
455 let dg = ag.abs_diff(eg);
456 let db = ab.abs_diff(eb);
457 assert!(
458 dr <= tol && dg <= tol && db <= tol,
459 "actual={actual:?} expected={expected:?} tol={tol} diffs=({dr}, {dg}, {db})"
460 );
461 }
462
463 #[test]
464 fn ycbcr_gray_identity_low_mid_high() {
465 assert_eq!(ycbcr_to_rgb(0, 128, 128), (0, 0, 0));
467 assert_eq!(ycbcr_to_rgb(128, 128, 128), (128, 128, 128));
468 assert_eq!(ycbcr_to_rgb(255, 128, 128), (255, 255, 255));
469 }
470
471 #[test]
472 fn ycbcr_pure_red_sample_bt601() {
473 let rgb = ycbcr_to_rgb(76, 85, 255);
475 assert_rgb_approx(rgb, (255, 0, 0), 2);
477 }
478
479 #[test]
480 fn ycbcr_pure_green_sample_bt601() {
481 let rgb = ycbcr_to_rgb(150, 44, 21);
483 assert_rgb_approx(rgb, (0, 255, 0), 2);
484 }
485
486 #[test]
487 fn ycbcr_pure_blue_sample_bt601() {
488 let rgb = ycbcr_to_rgb(29, 255, 107);
490 assert_rgb_approx(rgb, (0, 0, 255), 2);
491 }
492
493 fn translate_ycbcr(num_bits: u32, y: u8, cb: u8, cr: u8) -> (String, ValueKind) {
495 let translator = YCbCrTranslator {};
496 let nibble_length = num_bits.div_ceil(3) as u32;
497 let packed: u32 = (u32::from(y) << (2 * nibble_length))
498 | (u32::from(cb) << nibble_length)
499 | u32::from(cr);
500 let value = VariableValue::BigUint(BigUint::from(packed));
501 translator.basic_translate(num_bits, &value)
502 }
503
504 #[test]
505 fn ycbcr_translator_name() {
506 let translator = YCbCrTranslator {};
507 assert_eq!(translator.name(), "YCbCr");
508 }
509
510 #[test]
511 fn ycbcr_24bit_gray_identity_low_mid_high() {
512 let (hex0, kind0) = translate_ycbcr(24, 0, 128, 128);
513 assert_eq!(hex0, "#008080");
514 assert_color_value(&kind0, 0, 0, 0);
515
516 let (hexm, kindm) = translate_ycbcr(24, 128, 128, 128);
517 assert_eq!(hexm, "#808080");
518 assert_color_value(&kindm, 128, 128, 128);
519
520 let (hexw, kindw) = translate_ycbcr(24, 255, 128, 128);
521 assert_eq!(hexw, "#ff8080");
522 assert_color_value(&kindw, 255, 255, 255);
523 }
524
525 #[test]
526 fn ycbcr_24bit_pure_red_sample_bt601() {
527 let (hex, kind) = translate_ycbcr(24, 76, 85, 255);
529 assert_eq!(hex, "#4c55ff");
530 let (er, eg, eb) = ycbcr_to_rgb(76, 85, 255);
531 assert_color_value(&kind, er, eg, eb);
532 }
533
534 #[test]
535 fn ycbcr_24bit_pure_green_sample_bt601() {
536 let (hex, kind) = translate_ycbcr(24, 150, 44, 21);
538 assert_eq!(hex, "#962c15");
539 let (er, eg, eb) = ycbcr_to_rgb(150, 44, 21);
540 assert_color_value(&kind, er, eg, eb);
541 }
542
543 #[test]
544 fn ycbcr_24bit_pure_blue_sample_bt601() {
545 let (hex, kind) = translate_ycbcr(24, 29, 255, 107);
547 assert_eq!(hex, "#1dff6b");
548 let (er, eg, eb) = ycbcr_to_rgb(29, 255, 107);
549 assert_color_value(&kind, er, eg, eb);
550 }
551
552 #[test]
553 fn ycbcr_12bit_scaled_values() {
554 let (hex, kind) = translate_ycbcr(12, 0xF, 0x8, 0x1);
557 assert_eq!(hex, "#f08010");
558 let (er, eg, eb) = ycbcr_to_rgb(240, 128, 16);
559 assert_color_value(&kind, er, eg, eb);
560 }
561
562 #[test]
563 fn ycbcr_3bit_scaled_values() {
564 let (hex, kind) = translate_ycbcr(3, 1, 1, 1);
567 assert_eq!(hex, "#808080");
568 let (er, eg, eb) = ycbcr_to_rgb(128, 128, 128);
569 assert_color_value(&kind, er, eg, eb);
570 }
571}