diff --git a/src/plugins/binaryview.rs b/src/plugins/binaryview.rs index ef39536bad..4ed47d8bc0 100644 --- a/src/plugins/binaryview.rs +++ b/src/plugins/binaryview.rs @@ -1,6 +1,6 @@ use crossterm::{cursor, terminal, Attribute, RawScreen}; use indexmap::IndexMap; -use nu::{serve_plugin, Args, CommandConfig, Plugin, ShellError, Spanned, Value}; +use nu::{serve_plugin, Args, CommandConfig, NamedType, Plugin, ShellError, Spanned, Value}; use pretty_hex::*; struct BinaryView; @@ -13,24 +13,26 @@ impl BinaryView { impl Plugin for BinaryView { fn config(&mut self) -> Result { + let mut named = IndexMap::new(); + named.insert("hires".to_string(), NamedType::Switch); Ok(CommandConfig { name: "binaryview".to_string(), positional: vec![], is_filter: false, is_sink: true, - named: IndexMap::new(), + named, rest_positional: true, }) } - fn sink(&mut self, _args: Args, input: Vec>) { + fn sink(&mut self, args: Args, input: Vec>) { for v in input { match v { Spanned { item: Value::Binary(b), .. } => { - let _ = view_binary(&b); + let _ = view_binary(&b, args.has("hires")); } _ => {} } @@ -38,58 +40,93 @@ impl Plugin for BinaryView { } } -fn view_binary(b: &[u8]) -> Result<(), Box> { +fn view_binary(b: &[u8], hires_mode: bool) -> Result<(), Box> { if b.len() > 3 { match (b[0], b[1], b[2]) { (0x4e, 0x45, 0x53) => { - view_contents_interactive(b)?; + view_contents_interactive(b, hires_mode)?; return Ok(()); } _ => {} } } - view_contents(b)?; + view_contents(b, hires_mode)?; Ok(()) } -pub struct Context { +pub struct RenderContext { pub width: usize, pub height: usize, pub frame_buffer: Vec<(u8, u8, u8)>, - pub prev_frame_buffer: Option>, pub since_last_button: Vec, + pub hires_mode: bool, } -impl Context { - pub fn blank() -> Context { - Context { +impl RenderContext { + pub fn blank(hires_mode: bool) -> RenderContext { + RenderContext { width: 0, height: 0, frame_buffer: vec![], - prev_frame_buffer: None, since_last_button: vec![0; 8], + hires_mode, } } pub fn clear(&mut self) { self.frame_buffer = vec![(0, 0, 0); self.width * self.height as usize]; } - pub fn flush(&mut self) -> Result<(), Box> { + + fn render_to_screen_lores(&mut self) -> Result<(), Box> { + let mut prev_color: Option<(u8, u8, u8)> = None; + let mut prev_count = 1; + let cursor = cursor(); cursor.goto(0, 0)?; - // check if we should redraw - if let Some(ref prev) = self.prev_frame_buffer { - if self.frame_buffer == *prev { - return Ok(()); + for pixel in &self.frame_buffer { + match prev_color { + Some(c) if c == *pixel => { + prev_count += 1; + } + Some(c) => { + print!( + "{}", + ansi_term::Colour::RGB(c.0, c.1, c.2) + .paint((0..prev_count).map(|_| "█").collect::()) + ); + prev_color = Some(*pixel); + prev_count = 1; + } + _ => { + prev_color = Some(*pixel); + prev_count = 1; + } } } + if prev_count > 0 { + if let Some(color) = prev_color { + print!( + "{}", + ansi_term::Colour::RGB(color.0, color.1, color.2) + .paint((0..prev_count).map(|_| "█").collect::()) + ); + } + } + println!("{}", Attribute::Reset); + Ok(()) + } + fn render_to_screen_hires(&mut self) -> Result<(), Box> { let mut prev_fg: Option<(u8, u8, u8)> = None; let mut prev_bg: Option<(u8, u8, u8)> = None; let mut prev_count = 1; let mut pos = 0; let fb_len = self.frame_buffer.len(); + + let cursor = cursor(); + cursor.goto(0, 0)?; + while pos < (fb_len - self.width) { let top_pixel = self.frame_buffer[pos]; let bottom_pixel = self.frame_buffer[pos + self.width]; @@ -133,44 +170,16 @@ impl Context { _ => {} } } - - self.prev_frame_buffer = Some(self.frame_buffer.clone()); - /* - for pixel in &self.frame_buffer { - match prev_color { - Some(c) if c == pixel.1 => { - prev_count += 1; - } - Some(c) => { - print!( - "{}", - ansi_term::Colour::RGB(c.0, c.1, c.2) - .paint((0..prev_count).map(|_| pixel.0).collect::()) - ); - prev_color = Some(pixel.1); - prev_count = 1; - } - _ => { - prev_color = Some(pixel.1); - prev_count = 1; - } - } - } - - if prev_count > 0 { - if let Some(color) = prev_color { - print!( - "{}", - ansi_term::Colour::RGB(color.0, color.1, color.2) - .paint((0..prev_count).map(|_| "▄").collect::()) - ); - } - } - */ println!("{}", Attribute::Reset); - Ok(()) } + pub fn flush(&mut self) -> Result<(), Box> { + if self.hires_mode { + self.render_to_screen_hires() + } else { + self.render_to_screen_lores() + } + } pub fn update(&mut self) -> Result<(), Box> { let terminal = terminal(); let terminal_size = terminal.terminal_size(); @@ -180,7 +189,11 @@ impl Context { cursor.hide()?; self.width = terminal_size.0 as usize + 1; - self.height = terminal_size.1 as usize * 2; + self.height = if self.hires_mode { + terminal_size.1 as usize * 2 + } else { + terminal_size.1 as usize + }; } Ok(()) @@ -234,7 +247,7 @@ fn load_from_jpg_buffer(buffer: &[u8]) -> Option<(RawImageBuffer)> { }) } -pub fn view_contents(buffer: &[u8]) -> Result<(), Box> { +pub fn view_contents(buffer: &[u8], hires_mode: bool) -> Result<(), Box> { let mut raw_image_buffer = load_from_png_buffer(buffer); if raw_image_buffer.is_none() { @@ -248,9 +261,9 @@ pub fn view_contents(buffer: &[u8]) -> Result<(), Box> { } let raw_image_buffer = raw_image_buffer.unwrap(); - let mut context: Context = Context::blank(); - let _ = context.update(); - context.clear(); + let mut render_context: RenderContext = RenderContext::blank(hires_mode); + let _ = render_context.update(); + render_context.clear(); match raw_image_buffer.colortype { image::ColorType::RGBA(8) => { @@ -263,8 +276,8 @@ pub fn view_contents(buffer: &[u8]) -> Result<(), Box> { let resized_img = image::imageops::resize( &img, - context.width as u32, - context.height as u32, + render_context.width as u32, + render_context.height as u32, image::FilterType::Lanczos3, ); @@ -272,8 +285,7 @@ pub fn view_contents(buffer: &[u8]) -> Result<(), Box> { for pixel in resized_img.pixels() { use image::Pixel; let rgb = pixel.to_rgb(); - //print!("{}", rgb[0]); - context.frame_buffer[count] = (rgb[0], rgb[1], rgb[2]); + render_context.frame_buffer[count] = (rgb[0], rgb[1], rgb[2]); count += 1; } } @@ -287,8 +299,8 @@ pub fn view_contents(buffer: &[u8]) -> Result<(), Box> { let resized_img = image::imageops::resize( &img, - context.width as u32, - context.height as u32, + render_context.width as u32, + render_context.height as u32, image::FilterType::Lanczos3, ); @@ -297,7 +309,7 @@ pub fn view_contents(buffer: &[u8]) -> Result<(), Box> { use image::Pixel; let rgb = pixel.to_rgb(); //print!("{}", rgb[0]); - context.frame_buffer[count] = (rgb[0], rgb[1], rgb[2]); + render_context.frame_buffer[count] = (rgb[0], rgb[1], rgb[2]); count += 1; } } @@ -308,7 +320,7 @@ pub fn view_contents(buffer: &[u8]) -> Result<(), Box> { } } - context.flush()?; + render_context.flush()?; let cursor = cursor(); let _ = cursor.show(); @@ -319,7 +331,10 @@ pub fn view_contents(buffer: &[u8]) -> Result<(), Box> { Ok(()) } -pub fn view_contents_interactive(buffer: &[u8]) -> Result<(), Box> { +pub fn view_contents_interactive( + buffer: &[u8], + hires_mode: bool, +) -> Result<(), Box> { use rawkey::{KeyCode, RawKey}; let mut nes = neso::Nes::new(48000.0); @@ -329,7 +344,7 @@ pub fn view_contents_interactive(buffer: &[u8]) -> Result<(), Box Result<(), Box Result<(), Box, &[u8]>::from_raw(256, 240, slice).unwrap(); let resized_img = image::imageops::resize( &img, - context.width as u32, - context.height as u32, + render_context.width as u32, + render_context.height as u32, image::FilterType::Lanczos3, ); - context.clear(); + render_context.clear(); let mut count = 0; for pixel in resized_img.pixels() { use image::Pixel; let rgb = pixel.to_rgb(); - context.frame_buffer[count] = (rgb[0], rgb[1], rgb[2]); + render_context.frame_buffer[count] = (rgb[0], rgb[1], rgb[2]); count += 1; } - context.flush()?; + render_context.flush()?; if rawkey.is_pressed(rawkey::KeyCode::Escape) { break 'gameloop;