1use ecolor::Color32;
2use egui::{CornerRadius, DragValue, Pos2, Rect, Sense, Stroke};
3use serde::{Deserialize, Serialize};
4use surfer_translation_types::VariableValue;
5
6use crate::translation::ycbcr_to_rgb;
7use crate::wave_container::{ScopeRef, ScopeRefExt, VariableRef, VariableRefExt, WaveContainer};
8use crate::{Message, system_state::SystemState};
9
10#[derive(Serialize, Deserialize, Debug, Clone)]
11#[serde(default)]
12pub(crate) struct FrameBufferSettings {
13 pub pixels_per_row: usize,
14 pub square_pixels: bool,
15 #[serde(flatten)]
16 pub color_settings: PixelColorSettings,
17}
18
19#[derive(Serialize, Deserialize, Debug, Clone, Copy, PartialEq, Eq, Default)]
20pub enum FrameBufferColorMode {
21 #[default]
22 Grayscale,
23 Rgb,
24 YCbCr,
25}
26
27#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
28#[serde(default)]
29pub(crate) struct PixelColorSettings {
30 #[serde(default)]
31 pub color_mode: FrameBufferColorMode,
32 pub grayscale_bits: u8,
33 pub r_bits: u8,
34 pub g_bits: u8,
35 pub b_bits: u8,
36 #[serde(default = "default_y_bits")]
37 pub y_bits: u8,
38 #[serde(default = "default_cb_bits")]
39 pub cb_bits: u8,
40 #[serde(default = "default_cr_bits")]
41 pub cr_bits: u8,
42}
43
44fn default_y_bits() -> u8 {
45 8
46}
47
48fn default_cb_bits() -> u8 {
49 8
50}
51
52fn default_cr_bits() -> u8 {
53 8
54}
55
56impl Default for PixelColorSettings {
57 fn default() -> Self {
58 Self {
59 color_mode: FrameBufferColorMode::Grayscale,
60 grayscale_bits: 1,
61 r_bits: 3,
62 g_bits: 3,
63 b_bits: 2,
64 y_bits: default_y_bits(),
65 cb_bits: default_cb_bits(),
66 cr_bits: default_cr_bits(),
67 }
68 }
69}
70
71impl Default for FrameBufferSettings {
72 fn default() -> Self {
73 Self {
74 pixels_per_row: 16,
75 square_pixels: true,
76 color_settings: PixelColorSettings::default(),
77 }
78 }
79}
80
81#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
82pub(crate) struct ArrayLevel {
83 pub min_index: i64,
84 pub max_index: i64,
85 pub first_index: i64,
86 pub last_index: i64,
87}
88
89#[derive(Debug, Clone, PartialEq, Eq)]
90pub(crate) struct FrameBufferContentCacheKey {
91 pub content: FrameBufferContent,
92 pub cursor_position: num::BigUint,
93}
94
95#[derive(Debug, Clone)]
96pub(crate) struct FrameBufferArrayCache {
97 pub key: FrameBufferContentCacheKey,
98 pub cached_value: Option<std::sync::Arc<[bool]>>,
99}
100
101#[derive(Debug, Clone, PartialEq, Eq)]
102pub(crate) struct FrameBufferPixelCacheKey {
103 pub array_key: FrameBufferContentCacheKey,
104 pub settings: PixelColorSettings,
105}
106
107#[derive(Debug, Clone)]
108pub(crate) struct FrameBufferPixelCache {
109 pub key: FrameBufferPixelCacheKey,
110 pub pixel_colors: std::sync::Arc<[Color32]>,
111}
112
113#[derive(Debug, Clone, PartialEq, Eq)]
114pub(crate) enum FrameBufferContent {
115 Array {
116 scope_ref: ScopeRef,
117 levels: Vec<ArrayLevel>,
120 },
121 Variable(VariableRef),
122}
123
124impl SystemState {
125 pub fn draw_frame_buffer_window(&mut self, ctx: &egui::Context, msgs: &mut Vec<Message>) {
126 let mut open = true;
127 egui::Window::new("Frame Buffer")
128 .open(&mut open)
129 .resizable(true)
130 .show(ctx, |ui| {
131 let frame_buffer_value = self.selected_variable_for_frame_buffer();
132 let Some((bits, array_cache_key, variable_name)) = frame_buffer_value.as_ref()
133 else {
134 ui.label("Place the cursor.");
135 return;
136 };
137
138 let color_settings_key = {
139 let settings = &mut self.user.frame_buffer;
140 let color_settings = &mut settings.color_settings;
141
142 ui.checkbox(&mut settings.square_pixels, "Square pixels");
143
144 ui.horizontal(|ui| {
145 ui.label("Color mode");
146 egui::ComboBox::from_id_salt("frame_buffer_color_mode")
147 .selected_text(match color_settings.color_mode {
148 FrameBufferColorMode::Grayscale => "Grayscale",
149 FrameBufferColorMode::Rgb => "RGB",
150 FrameBufferColorMode::YCbCr => "YCbCr",
151 })
152 .show_ui(ui, |ui| {
153 ui.selectable_value(
154 &mut color_settings.color_mode,
155 FrameBufferColorMode::Grayscale,
156 "Grayscale",
157 );
158 ui.selectable_value(
159 &mut color_settings.color_mode,
160 FrameBufferColorMode::Rgb,
161 "RGB",
162 );
163 ui.selectable_value(
164 &mut color_settings.color_mode,
165 FrameBufferColorMode::YCbCr,
166 "YCbCr",
167 );
168 });
169 });
170
171 match color_settings.color_mode {
172 FrameBufferColorMode::Grayscale => {
173 ui.horizontal(|ui| {
174 ui.label("Grayscale bits");
175 ui.add(
176 DragValue::new(&mut color_settings.grayscale_bits).range(1..=8),
177 );
178 });
179 }
180 FrameBufferColorMode::Rgb => {
181 ui.horizontal(|ui| {
182 ui.label("R bits");
183 ui.add(DragValue::new(&mut color_settings.r_bits).range(0..=8));
184 ui.label("G bits");
185 ui.add(DragValue::new(&mut color_settings.g_bits).range(0..=8));
186 ui.label("B bits");
187 ui.add(DragValue::new(&mut color_settings.b_bits).range(0..=8));
188 });
189 }
190 FrameBufferColorMode::YCbCr => {
191 ui.horizontal(|ui| {
192 ui.label("Y bits");
193 ui.add(DragValue::new(&mut color_settings.y_bits).range(0..=8));
194 ui.label("Cb bits");
195 ui.add(DragValue::new(&mut color_settings.cb_bits).range(0..=8));
196 ui.label("Cr bits");
197 ui.add(DragValue::new(&mut color_settings.cr_bits).range(0..=8));
198 });
199 }
200 }
201
202 color_settings.clone()
203 };
204
205 ui.separator();
206
207 if bits.is_empty() {
208 ui.label("No bits available");
209 return;
210 }
211
212 let pixel_cache_key = FrameBufferPixelCacheKey {
213 array_key: array_cache_key.clone(),
214 settings: color_settings_key.clone(),
215 };
216
217 let pixel_colors = if let Some(cache) = self
218 .frame_buffer_pixel_cache
219 .as_ref()
220 .filter(|cache| cache.key == pixel_cache_key)
221 {
222 cache.pixel_colors.clone()
223 } else {
224 let decoded = match color_settings_key.color_mode {
225 FrameBufferColorMode::Rgb => {
226 let r_bits = color_settings_key.r_bits as usize;
227 let g_bits = color_settings_key.g_bits as usize;
228 let b_bits = color_settings_key.b_bits as usize;
229 let bits_per_pixel = r_bits + g_bits + b_bits;
230 if bits_per_pixel == 0 {
231 ui.label("Set at least one RGB channel bit count above zero.");
232 return;
233 }
234 decode_rgb_pixels(bits, r_bits, g_bits, b_bits)
235 }
236 FrameBufferColorMode::YCbCr => {
237 let y_bits = color_settings_key.y_bits as usize;
238 let cb_bits = color_settings_key.cb_bits as usize;
239 let cr_bits = color_settings_key.cr_bits as usize;
240 let bits_per_pixel = y_bits + cb_bits + cr_bits;
241 if bits_per_pixel == 0 {
242 ui.label("Set at least one YCbCr channel bit count above zero.");
243 return;
244 }
245 decode_ycbcr_pixels(bits, y_bits, cb_bits, cr_bits)
246 }
247 FrameBufferColorMode::Grayscale => {
248 let gray_bits = color_settings_key.grayscale_bits as usize;
249 decode_grayscale_pixels(bits, gray_bits)
250 }
251 };
252
253 let decoded: std::sync::Arc<[Color32]> = decoded.into();
254
255 self.frame_buffer_pixel_cache = Some(FrameBufferPixelCache {
256 key: pixel_cache_key,
257 pixel_colors: decoded.clone(),
258 });
259 decoded
260 };
261
262 if pixel_colors.is_empty() {
263 ui.label("No pixels to draw with current bit settings.");
264 return;
265 }
266
267 let settings = &mut self.user.frame_buffer;
268 let columns = settings.pixels_per_row.min(pixel_colors.len()).max(1);
269 let rows = pixel_colors.len().div_ceil(columns);
270 ui.horizontal(|ui| {
271 ui.label(format!("Var: {variable_name} | {columns}×{rows}"));
272
273 if ui.button("Copy image").clicked() {
274 let total = columns * rows;
275 let mut padded = pixel_colors.to_vec();
276 padded.resize(total, Color32::BLACK);
277 ui.ctx().copy_image(egui::ColorImage {
278 size: [columns, rows],
279 pixels: padded,
280 source_size: egui::vec2(columns as f32, rows as f32),
281 });
282 }
283 });
284 self.draw_array_index_range(ui);
285
286 let settings = &mut self.user.frame_buffer;
287 let max_columns = pixel_colors.len().max(1);
288 settings.pixels_per_row = settings.pixels_per_row.clamp(1, max_columns);
289
290 ui.horizontal(|ui| {
291 ui.label("Pixels in x-direction");
292 ui.add(
293 egui::Slider::new(&mut settings.pixels_per_row, 1..=max_columns).integer(),
294 );
295 });
296
297 ui.separator();
298
299 let available = ui.available_size_before_wrap();
300
301 if available.x <= 0.0 || available.y <= 0.0 {
302 return;
303 }
304
305 let (pixel_width, pixel_height) = if settings.square_pixels {
306 let side = (available.x / columns as f32).min(available.y / rows as f32);
307 (side, side)
308 } else {
309 (available.x / columns as f32, available.y / rows as f32)
310 };
311
312 let image_size =
313 egui::vec2(pixel_width * columns as f32, pixel_height * rows as f32);
314 let (rect, _) = ui.allocate_exact_size(image_size, Sense::hover());
315 let painter = ui.painter_at(rect);
316
317 for (index, color) in pixel_colors.iter().copied().enumerate() {
318 let x = index % columns;
319 let y = index / columns;
320
321 let min = Pos2 {
322 x: rect.min.x + x as f32 * pixel_width,
323 y: rect.min.y + y as f32 * pixel_height,
324 };
325 let max = Pos2 {
326 x: min.x + pixel_width,
327 y: min.y + pixel_height,
328 };
329
330 painter.rect_filled(Rect { min, max }, CornerRadius::ZERO, color);
331 }
332
333 painter.rect_stroke(
334 rect,
335 CornerRadius::ZERO,
336 Stroke::new(1.0, ui.visuals().weak_text_color()),
337 egui::StrokeKind::Inside,
338 );
339 });
340
341 if !open {
342 msgs.push(Message::SetFrameBufferVisibleVariable(None));
343 }
344 }
345
346 fn draw_array_index_range(&mut self, ui: &mut egui::Ui) {
347 let Some(FrameBufferContent::Array {
348 scope_ref: _,
349 levels,
350 }) = self.frame_buffer_content.as_mut()
351 else {
352 return;
353 };
354
355 if levels.is_empty() {
356 return;
357 }
358
359 let total_levels = levels.len();
360
361 for (i, level) in levels.iter_mut().enumerate() {
362 let (min, max) = (level.min_index, level.max_index);
363 level.first_index = level.first_index.clamp(min, max);
364 level.last_index = level.last_index.clamp(min, max);
365 if level.first_index > level.last_index {
366 level.last_index = level.first_index;
367 }
368 ui.horizontal(|ui| {
369 if total_levels == 1 {
370 ui.label("First array index");
371 } else {
372 ui.label(format!("Level {} first index", i + 1));
373 }
374 ui.add(DragValue::new(&mut level.first_index).range(min..=max));
375 if total_levels == 1 {
376 ui.label("Last array index");
377 } else {
378 ui.label(format!("Level {} last index", i + 1));
379 }
380 ui.add(DragValue::new(&mut level.last_index).range(min..=max));
381 });
382 if level.first_index > level.last_index {
383 level.first_index = level.last_index;
384 }
385 }
386 }
387
388 fn selected_variable_for_frame_buffer(
389 &mut self,
390 ) -> Option<(std::sync::Arc<[bool]>, FrameBufferContentCacheKey, String)> {
391 let waves = self.user.waves.as_ref()?;
392 let cursor = waves.cursor.as_ref()?.to_biguint()?;
393 let wave_container = waves.inner.as_waves()?;
394 let content = self.frame_buffer_content.clone()?;
395 let cache_key = FrameBufferContentCacheKey {
396 content: content.clone(),
397 cursor_position: cursor.clone(),
398 };
399 let cached = self
400 .frame_buffer_array_cache
401 .as_ref()
402 .filter(|cache| cache.key == cache_key)
403 .cloned();
404
405 let cached = if let Some(cached) = cached {
406 cached
407 } else {
408 let cached = match &content {
409 FrameBufferContent::Variable(variable_ref) => build_variable_frame_buffer_cache(
410 wave_container,
411 variable_ref,
412 &cursor,
413 cache_key,
414 )?,
415 FrameBufferContent::Array { scope_ref, levels } => {
416 if levels.is_empty() {
417 return None;
418 }
419
420 let sorted_variables =
421 resolve_leaf_scopes_and_variables(wave_container, scope_ref, levels)?;
422 let cached_value =
423 build_cached_variable_value(wave_container, &sorted_variables, &cursor);
424 FrameBufferArrayCache {
425 key: cache_key,
426 cached_value,
427 }
428 }
429 };
430 self.frame_buffer_array_cache = Some(cached.clone());
431 cached
432 };
433
434 let bits = cached.cached_value.as_ref()?.clone();
435
436 let variable_name = match &content {
437 FrameBufferContent::Variable(variable_ref) => variable_ref.full_path_string_no_index(),
438 FrameBufferContent::Array { scope_ref, .. } => scope_ref.full_name(),
439 };
440
441 Some((bits, cached.key.clone(), variable_name))
442 }
443}
444
445fn build_cached_variable_value(
446 wave_container: &WaveContainer,
447 sorted_variables: &[VariableRef],
448 cursor: &num::BigUint,
449) -> Option<std::sync::Arc<[bool]>> {
450 let capacity: usize = sorted_variables
452 .iter()
453 .filter_map(|v| wave_container.variable_meta(v).ok()?.num_bits)
454 .map(|b| b as usize)
455 .sum();
456
457 if capacity == 0 {
458 return None;
459 }
460
461 let mut concat_bits: Vec<bool> = Vec::with_capacity(capacity);
462
463 for var_ref in sorted_variables {
464 let Ok(meta) = wave_container.variable_meta(var_ref) else {
465 continue;
466 };
467 let Some(bits) = meta.num_bits else {
468 continue;
469 };
470 let bits = bits as usize;
471
472 let value = wave_container
474 .query_variable(var_ref, cursor)
475 .ok()
476 .flatten()
477 .and_then(|q| q.current)
478 .map(|(_, v)| v);
479
480 match value {
481 Some(VariableValue::BigUint(v)) => {
482 append_biguint_lower_bits_with_left_zero_pad(&v, bits, &mut concat_bits);
483 }
484 Some(VariableValue::String(s)) => {
485 append_str_lower_bits_with_left_zero_pad(&s, bits, &mut concat_bits);
486 }
487 None => {
488 concat_bits.extend(std::iter::repeat_n(false, bits));
489 }
490 }
491 }
492
493 if concat_bits.is_empty() {
494 None
495 } else {
496 Some(concat_bits.into())
497 }
498}
499
500fn build_variable_frame_buffer_cache(
501 wave_container: &WaveContainer,
502 variable_ref: &VariableRef,
503 cursor: &num::BigUint,
504 key: FrameBufferContentCacheKey,
505) -> Option<FrameBufferArrayCache> {
506 let meta = wave_container.variable_meta(variable_ref).ok()?;
507 let word_length = meta.num_bits? as usize;
508 let query_result = wave_container
509 .query_variable(variable_ref, cursor)
510 .ok()
511 .flatten()?;
512 let (_, value) = query_result.current?;
513 let padded: std::sync::Arc<[bool]> = frame_buffer_bits(&value, word_length).into();
514
515 Some(FrameBufferArrayCache {
516 key,
517 cached_value: Some(padded),
518 })
519}
520
521fn resolve_leaf_scopes_and_variables(
522 wave_container: &WaveContainer,
523 scope_ref: &ScopeRef,
524 levels: &[ArrayLevel],
525) -> Option<Vec<VariableRef>> {
526 let (scope_levels, var_level) = levels.split_at(levels.len() - 1);
527 let var_level = &var_level[0];
528
529 let mut current_scopes = vec![scope_ref.clone()];
530 for level in scope_levels {
531 let clamped_first = level.first_index.clamp(level.min_index, level.max_index);
532 let clamped_last = level.last_index.clamp(level.min_index, level.max_index);
533 let mut next_scopes = Vec::new();
534 for scope in ¤t_scopes {
535 let mut selected: Vec<ScopeRef> = wave_container
536 .child_scopes(scope)
537 .unwrap_or_default()
538 .into_iter()
539 .filter(|s| {
540 let idx = scope_array_index(s);
541 idx >= clamped_first && idx <= clamped_last
542 })
543 .collect();
544 selected.sort_by_key(scope_array_index);
545 next_scopes.extend(selected);
546 }
547 current_scopes = next_scopes;
548 }
549
550 if current_scopes.is_empty() {
551 return None;
552 }
553
554 let clamped_first = var_level
555 .first_index
556 .clamp(var_level.min_index, var_level.max_index);
557 let clamped_last = var_level
558 .last_index
559 .clamp(var_level.min_index, var_level.max_index);
560 if clamped_first > clamped_last {
561 return None;
562 }
563
564 let mut sorted_variables = Vec::new();
565 for leaf_scope in ¤t_scopes {
566 let mut variables = wave_container.variables_in_scope(leaf_scope);
567 variables.sort_by_key(variable_array_index);
568 sorted_variables.extend(variables.into_iter().filter(|var_ref| {
569 let idx = variable_array_index(var_ref);
570 idx >= clamped_first && idx <= clamped_last
571 }));
572 }
573
574 Some(sorted_variables)
575}
576
577pub(crate) fn build_frame_buffer_content(
583 wave_container: &WaveContainer,
584 scope_ref: &ScopeRef,
585) -> Option<(Vec<ArrayLevel>, Vec<VariableRef>)> {
586 let mut levels: Vec<ArrayLevel> = Vec::new();
589 let mut probe = scope_ref.clone();
590 loop {
591 let children = wave_container.child_scopes(&probe).unwrap_or_default();
592 if children.is_empty() {
593 break;
594 }
595 let indices: Vec<i64> = children.iter().map(scope_array_index).collect();
596 let min_idx = *indices.iter().min().unwrap_or(&0);
597 let max_idx = *indices.iter().max().unwrap_or(&0);
598 levels.push(ArrayLevel {
599 min_index: min_idx,
600 max_index: max_idx,
601 first_index: min_idx,
602 last_index: max_idx,
603 });
604 probe = children.into_iter().min_by_key(scope_array_index).unwrap();
605 }
606
607 let leaf_vars = wave_container.variables_in_scope(&probe);
609 let var_indices: Vec<i64> = leaf_vars
610 .iter()
611 .map(variable_array_index)
612 .filter(|&i| i != i64::MAX)
613 .collect();
614 let (var_min, var_max) = if var_indices.is_empty() {
615 (0, 0)
616 } else {
617 (
618 *var_indices.iter().min().unwrap(),
619 *var_indices.iter().max().unwrap(),
620 )
621 };
622 levels.push(ArrayLevel {
623 min_index: var_min,
624 max_index: var_max,
625 first_index: var_min,
626 last_index: var_max,
627 });
628
629 let depth = levels.len().saturating_sub(1);
631 let mut leaf_scopes = vec![scope_ref.clone()];
632 for _ in 0..depth {
633 leaf_scopes = leaf_scopes
634 .iter()
635 .flat_map(|s| wave_container.child_scopes(s).unwrap_or_default())
636 .collect();
637 }
638 let all_leaf_vars: Vec<VariableRef> = leaf_scopes
639 .iter()
640 .flat_map(|s| wave_container.variables_in_scope(s))
641 .collect();
642
643 Some((levels, all_leaf_vars))
644}
645
646fn scope_array_index(scope_ref: &ScopeRef) -> i64 {
647 let name = scope_ref.name();
648 name.parse::<i64>()
649 .ok()
650 .or_else(|| {
651 name.strip_prefix('[')
652 .and_then(|s| s.strip_suffix(']'))
653 .and_then(|s| s.parse::<i64>().ok())
654 })
655 .unwrap_or(i64::MAX)
656}
657
658fn variable_array_index(var_ref: &VariableRef) -> i64 {
659 fn parse_index_name(name: &str) -> Option<i64> {
660 name.parse::<i64>().ok().or_else(|| {
661 name.strip_prefix('[')
662 .and_then(|s| s.strip_suffix(']'))
663 .and_then(|s| s.parse::<i64>().ok())
664 })
665 }
666
667 var_ref
668 .index
669 .or_else(|| parse_index_name(&var_ref.name))
670 .unwrap_or(i64::MAX)
671}
672
673fn frame_buffer_bits(value: &VariableValue, word_length: usize) -> Vec<bool> {
674 match value {
675 VariableValue::BigUint(v) => {
676 let mut out = Vec::with_capacity(word_length);
677 append_biguint_lower_bits_with_left_zero_pad(v, word_length, &mut out);
678 out
679 }
680 VariableValue::String(v) => bits_with_left_zero_pad(v, word_length),
681 }
682}
683
684fn append_str_lower_bits_with_left_zero_pad(src: &str, width: usize, out: &mut Vec<bool>) {
685 if width == 0 {
686 return;
687 }
688
689 let start = src.len().saturating_sub(width);
690 let suffix = &src.as_bytes()[start..];
691
692 for _ in suffix.len()..width {
693 out.push(false);
694 }
695 out.extend(suffix.iter().map(|b| *b == b'1'));
696}
697
698fn append_biguint_lower_bits_with_left_zero_pad(
699 value: &num::BigUint,
700 width: usize,
701 out: &mut Vec<bool>,
702) {
703 if width == 0 {
704 return;
705 }
706
707 let value_bits = value.bits() as usize;
708 if value_bits >= width {
709 for bit_idx in (0..width).rev() {
710 out.push(value.bit(bit_idx as u64));
711 }
712 } else {
713 for _ in 0..(width - value_bits) {
714 out.push(false);
715 }
716 for bit_idx in (0..value_bits).rev() {
717 out.push(value.bit(bit_idx as u64));
718 }
719 }
720}
721
722fn bits_with_left_zero_pad(src: &str, width: usize) -> Vec<bool> {
723 let mut out = Vec::with_capacity(width);
724 append_str_lower_bits_with_left_zero_pad(src, width, &mut out);
725 out
726}
727
728fn decode_grayscale_pixels(bits: &[bool], grayscale_bits: usize) -> Vec<Color32> {
729 let step = grayscale_bits.max(1);
730 let full = bits.len() / step;
731 let has_tail = !bits.len().is_multiple_of(step);
732 let mut out = Vec::with_capacity(full + usize::from(has_tail));
733 for start in (0..full * step).step_by(step) {
735 let gray = scale_to_u8(bits_to_u16(&bits[start..start + step]), step);
736 out.push(Color32::from_rgb(gray, gray, gray));
737 }
738 if has_tail {
740 let start = full * step;
741 let gray = scale_to_u8(bits_to_u16_padded(bits, start, step), step);
742 out.push(Color32::from_rgb(gray, gray, gray));
743 }
744 out
745}
746
747fn decode_rgb_pixels(bits: &[bool], r_bits: usize, g_bits: usize, b_bits: usize) -> Vec<Color32> {
748 let bits_per_pixel = r_bits + g_bits + b_bits;
749 let step = bits_per_pixel.max(1);
750 let full = bits.len() / step;
751 let has_tail = !bits.len().is_multiple_of(step);
752 let mut out = Vec::with_capacity(full + usize::from(has_tail));
753 for start in (0..full * step).step_by(step) {
755 let red = scale_to_u8(bits_to_u16(&bits[start..start + r_bits]), r_bits);
756 let green = scale_to_u8(
757 bits_to_u16(&bits[start + r_bits..start + r_bits + g_bits]),
758 g_bits,
759 );
760 let blue = scale_to_u8(
761 bits_to_u16(&bits[start + r_bits + g_bits..start + step]),
762 b_bits,
763 );
764 out.push(Color32::from_rgb(red, green, blue));
765 }
766 if has_tail {
768 let start = full * step;
769 let red = scale_to_u8(bits_to_u16_padded(bits, start, r_bits), r_bits);
770 let green = scale_to_u8(bits_to_u16_padded(bits, start + r_bits, g_bits), g_bits);
771 let blue = scale_to_u8(
772 bits_to_u16_padded(bits, start + r_bits + g_bits, b_bits),
773 b_bits,
774 );
775 out.push(Color32::from_rgb(red, green, blue));
776 }
777 out
778}
779
780fn decode_ycbcr_pixels(
781 bits: &[bool],
782 y_bits: usize,
783 cb_bits: usize,
784 cr_bits: usize,
785) -> Vec<Color32> {
786 let bits_per_pixel = y_bits + cb_bits + cr_bits;
787 let step = bits_per_pixel.max(1);
788 let full = bits.len() / step;
789 let has_tail = !bits.len().is_multiple_of(step);
790 let mut out = Vec::with_capacity(full + usize::from(has_tail));
791
792 for start in (0..full * step).step_by(step) {
794 let y = scale_to_u8(bits_to_u16(&bits[start..start + y_bits]), y_bits);
795 let cb = scale_to_u8(
796 bits_to_u16(&bits[start + y_bits..start + y_bits + cb_bits]),
797 cb_bits,
798 );
799 let cr = scale_to_u8(
800 bits_to_u16(&bits[start + y_bits + cb_bits..start + step]),
801 cr_bits,
802 );
803 let (red, green, blue) = ycbcr_to_rgb(y, cb, cr);
804 out.push(Color32::from_rgb(red, green, blue));
805 }
806
807 if has_tail {
809 let start = full * step;
810 let y = scale_to_u8(bits_to_u16_padded(bits, start, y_bits), y_bits);
811 let cb = scale_to_u8(bits_to_u16_padded(bits, start + y_bits, cb_bits), cb_bits);
812 let cr = scale_to_u8(
813 bits_to_u16_padded(bits, start + y_bits + cb_bits, cr_bits),
814 cr_bits,
815 );
816 let (red, green, blue) = ycbcr_to_rgb(y, cb, cr);
817 out.push(Color32::from_rgb(red, green, blue));
818 }
819
820 out
821}
822
823fn bits_to_u16_padded(bits: &[bool], start: usize, len: usize) -> u16 {
825 let mut value = 0u16;
826 for offset in 0..len {
827 value = (value << 1) | u16::from(bits.get(start + offset).copied().unwrap_or(false));
828 }
829 value
830}
831
832fn bits_to_u16(bits: &[bool]) -> u16 {
834 let mut value = 0u16;
835 for &b in bits {
836 value = (value << 1) | u16::from(b);
837 }
838 value
839}
840
841fn scale_to_u8(value: u16, bits: usize) -> u8 {
842 if bits == 0 {
843 return 0;
844 }
845 let max_in = (1u16 << bits) - 1;
846 ((u32::from(value) * 255) / u32::from(max_in)) as u8
847}
848
849#[cfg(test)]
850mod tests {
851 use super::*;
852 use num::BigUint;
853
854 #[test]
855 fn frame_buffer_bits_pads_to_word_length() {
856 let bits = frame_buffer_bits(&VariableValue::BigUint(BigUint::from(0b101u8)), 5);
857 assert_eq!(bits, vec![false, false, true, false, true]);
858 }
859
860 #[test]
861 fn frame_buffer_bits_truncates_to_word_length() {
862 let bits = frame_buffer_bits(&VariableValue::String("101101".to_string()), 4);
863 assert_eq!(bits, vec![true, true, false, true]);
864 }
865
866 #[test]
867 fn bits_to_u16_padded_reads_and_zero_pads() {
868 let bits = vec![true, false, true];
869 assert_eq!(bits_to_u16_padded(&bits, 0, 3), 0b101);
870 assert_eq!(bits_to_u16_padded(&bits, 1, 4), 0b0100);
871 }
872
873 #[test]
874 fn scale_to_u8_scales_full_range() {
875 assert_eq!(scale_to_u8(0, 1), 0);
876 assert_eq!(scale_to_u8(1, 1), 255);
877 assert_eq!(scale_to_u8(7, 3), 255);
878 assert_eq!(scale_to_u8(4, 3), 145);
879 }
880
881 #[test]
882 fn decode_grayscale_pixels_uses_bit_groups() {
883 let bits = vec![false, false, true, true];
884 let pixels = decode_grayscale_pixels(&bits, 2);
885 assert_eq!(pixels.len(), 2);
886 assert_eq!(pixels[0], Color32::from_rgb(0, 0, 0));
887 assert_eq!(pixels[1], Color32::from_rgb(255, 255, 255));
888 }
889
890 #[test]
891 fn decode_rgb_pixels_supports_different_channel_widths() {
892 let bits = vec![
893 true, false, false, true, true, false, ];
895 let pixels = decode_rgb_pixels(&bits, 2, 2, 2);
896 assert_eq!(pixels.len(), 1);
897 assert_eq!(pixels[0], Color32::from_rgb(170, 85, 170));
898 }
899
900 #[test]
901 fn decode_ycbcr_pixels_supports_8bit_channels() {
902 let bits = vec![
903 true, false, false, false, false, false, false, false, true, false, false, false, false, false, false, false, true, false, false, false, false, false, false, false, ];
907 let pixels = decode_ycbcr_pixels(&bits, 8, 8, 8);
908 assert_eq!(pixels.len(), 1);
909 assert_eq!(pixels[0], Color32::from_rgb(128, 128, 128));
910 }
911
912 #[test]
913 fn variable_array_index_parses_bracketed_name() {
914 let var_ref = VariableRef::new(ScopeRef::empty(), "[2]".to_string());
915 assert_eq!(variable_array_index(&var_ref), 2);
916 }
917
918 #[test]
919 fn variable_array_index_parses_plain_numeric_name() {
920 let var_ref = VariableRef::new(ScopeRef::empty(), "7".to_string());
921 assert_eq!(variable_array_index(&var_ref), 7);
922 }
923
924 #[test]
925 fn variable_array_index_prefers_explicit_index() {
926 let var_ref = VariableRef::new_with_id_and_index(
927 ScopeRef::empty(),
928 "[2]".to_string(),
929 Default::default(),
930 Some(9),
931 );
932 assert_eq!(variable_array_index(&var_ref), 9);
933 }
934
935 #[test]
936 fn variable_array_index_falls_back_to_max_for_non_numeric_names() {
937 let var_ref = VariableRef::new(ScopeRef::empty(), "data".to_string());
938 assert_eq!(variable_array_index(&var_ref), i64::MAX);
939 }
940}