// SPDX-FileCopyrightText: 2026 Stan Grams // // SPDX-License-Identifier: GPL-2.0-or-later //! Encode a test card, decode it back, write both as PNGs to a directory. //! //! `cargo run -p trx-sstv --example round_trip_png -- /tmp/out` use trx_sstv::encode::{encode, Frame}; use trx_sstv::mode::mode_for_vis; use trx_sstv::{ImageCanvas, SstvConfig, SstvDecoder, SstvEvent}; fn main() { let dir = std::env::args().nth(1).unwrap_or_else(|| ".".into()); for vis in [44u8, 60, 12, 8, 95] { let mode = mode_for_vis(vis).expect("mode in table"); let (w, h) = (usize::from(mode.width), usize::from(mode.height)); let mut rgb = vec![0u8; w * h * 3]; for y in 0..h { for x in 0..w { let at = (y * w + x) * 3; let (r, g, b) = if y < h / 3 { [ (255u8, 255u8, 255u8), (255, 255, 0), (0, 255, 255), (0, 255, 0), (255, 0, 255), (255, 0, 0), (0, 0, 255), (0, 0, 0), ][x * 8 / w] } else if y < 2 * h / 3 { let t = (x * 255 / w) as u8; (t, 255 - t, ((y * 255) / h) as u8) } else { // Diagonal stripes: a line-timing error shows up as a kink. if ((x + y) / 16) % 2 == 0 { (240, 240, 40) } else { (20, 20, 90) } }; rgb[at] = r; rgb[at + 1] = g; rgb[at + 2] = b; } } let name = mode.name.replace(' ', "-"); let mut sent = ImageCanvas::new(w, h); for y in 0..h { sent.put_row(y, &rgb[y * w * 3..(y + 1) * w * 3]); } std::fs::write( format!("{dir}/{name}-sent.png"), sent.to_png().expect("png"), ) .expect("write"); let frame = Frame { width: w, height: h, rgb: &rgb, }; let mut audio = encode(mode, &frame, 48_000); audio.extend(std::iter::repeat_n(0.0, 4800)); let mut decoder = SstvDecoder::new(48_000, SstvConfig::default()); let mut got = None; for block in audio.chunks(1024) { for event in decoder.process_samples(block) { if let SstvEvent::Complete(image) = event { got = Some(image); } } } let image = got.expect("no image decoded"); let mut canvas = ImageCanvas::new(w, h); for y in 0..h { canvas.put_row(y, &image.rgb[y * w * 3..(y + 1) * w * 3]); } std::fs::write( format!("{dir}/{name}-decoded.png"), canvas.to_png().expect("png"), ) .expect("write"); println!( "{}: {} lines, complete={}", mode.name, image.lines, image.complete ); } }