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