diff --git a/src/main.rs b/src/main.rs index f72600a..dc45a7e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -42,7 +42,7 @@ fn run(config: &Config) -> Result<()> { let ttf_context = ttf::init().map_err(|e| anyhow::anyhow!("TTF init failed: {}", e))?; let font: Option = ttf_context - .load_font("/System/Library/Fonts/Supplemental/Arial.ttf", 20) + .load_font("/System/Library/Fonts/Supplemental/Arial.ttf", 18) .ok(); let window = video_subsystem @@ -121,6 +121,8 @@ fn run(config: &Config) -> Result<()> { .event_pump() .map_err(|e| anyhow::anyhow!("Event pump failed: {}", e))?; + let info_height: i32 = 50; + info!("Main loop started - waiting for events..."); let mut running = true; @@ -150,21 +152,11 @@ fn run(config: &Config) -> Result<()> { } sdl2::keyboard::Keycode::S => { player_state.show_subtitle = !player_state.show_subtitle; - info!( - "Subtitle: {}", - if player_state.show_subtitle { - "ON" - } else { - "OFF" - } - ); + info!("Subtitle: {}", if player_state.show_subtitle { "ON" } else { "OFF" }); } sdl2::keyboard::Keycode::Y => { player_state.show_yolo = !player_state.show_yolo; - info!( - "YOLO: {}", - if player_state.show_yolo { "ON" } else { "OFF" } - ); + info!("YOLO: {}", if player_state.show_yolo { "ON" } else { "OFF" }); } sdl2::keyboard::Keycode::C => { player_state.show_chunks = !player_state.show_chunks; @@ -172,61 +164,43 @@ fn run(config: &Config) -> Result<()> { sdl2::keyboard::Keycode::M => { player_state.muted = !player_state.muted; } - sdl2::keyboard::Keycode::F => {} sdl2::keyboard::Keycode::Left => { - if shift { - if let Some(ref mut dec) = decoder { - let current = - player_state.current_frame.saturating_sub(60); - dec.seek( - ((current as f64 / player_state.fps) * 1000.0) as u64, - )?; - player_state.current_frame = current; - } - } else { - if let Some(ref mut dec) = decoder { - let current = - player_state.current_frame.saturating_sub(1); - dec.seek( - ((current as f64 / player_state.fps) * 1000.0) as u64, - )?; - player_state.current_frame = current; - } + let step = if shift { 60 } else { 1 }; + if let Some(ref mut dec) = decoder { + let current = player_state.current_frame.saturating_sub(step); + dec.seek(((current as f64 / player_state.fps) * 1000.0) as u64)?; + player_state.current_frame = current; } } sdl2::keyboard::Keycode::Right => { - if shift { - if let Some(ref mut dec) = decoder { - let current = player_state.current_frame + 60; - dec.seek( - ((current as f64 / player_state.fps) * 1000.0) as u64, - )?; - player_state.current_frame = current; - } - } else { - if let Some(ref mut dec) = decoder { - let current = player_state.current_frame + 1; - dec.seek( - ((current as f64 / player_state.fps) * 1000.0) as u64, - )?; - player_state.current_frame = current; - } + let step = if shift { 60 } else { 1 }; + if let Some(ref mut dec) = decoder { + let current = player_state.current_frame + step; + dec.seek(((current as f64 / player_state.fps) * 1000.0) as u64)?; + player_state.current_frame = current; } } - sdl2::keyboard::Keycode::Equals - | sdl2::keyboard::Keycode::KpPlus => { - player_state.zoom = (player_state.zoom * 1.2).min(5.0); + sdl2::keyboard::Keycode::Up => { + if player_state.zoom > 1.0 { + player_state.pan_y = (player_state.pan_y - 50.0).max(-500.0); + } } - sdl2::keyboard::Keycode::Minus - | sdl2::keyboard::Keycode::KpMinus => { + sdl2::keyboard::Keycode::Down => { + if player_state.zoom > 1.0 { + player_state.pan_y = (player_state.pan_y + 50.0).min(500.0); + } + } + sdl2::keyboard::Keycode::Equals | sdl2::keyboard::Keycode::KpPlus => { + player_state.zoom = (player_state.zoom * 1.2).min(10.0); + } + sdl2::keyboard::Keycode::Minus | sdl2::keyboard::Keycode::KpMinus => { player_state.zoom = (player_state.zoom / 1.2).max(0.5); + if player_state.zoom == 1.0 { + player_state.pan_x = 0.0; + player_state.pan_y = 0.0; + } } - sdl2::keyboard::Keycode::Backquote => { - player_state.zoom = 1.0; - player_state.pan_x = 0.0; - player_state.pan_y = 0.0; - } - sdl2::keyboard::Keycode::R => { + sdl2::keyboard::Keycode::Backquote | sdl2::keyboard::Keycode::R => { player_state.zoom = 1.0; player_state.pan_x = 0.0; player_state.pan_y = 0.0; @@ -235,6 +209,17 @@ fn run(config: &Config) -> Result<()> { } } } + sdl2::event::Event::MouseWheel { y, .. } => { + if y > 0 { + player_state.zoom = (player_state.zoom * 1.1).min(10.0); + } else if y < 0 { + player_state.zoom = (player_state.zoom / 1.1).max(0.5); + if player_state.zoom == 1.0 { + player_state.pan_x = 0.0; + player_state.pan_y = 0.0; + } + } + } _ => {} } } @@ -269,55 +254,57 @@ fn run(config: &Config) -> Result<()> { } } - if let Some(ref mut tex) = texture { - let dst = if player_state.zoom != 1.0 { - let info = video_info.as_ref().unwrap(); - let w = (info.width as f32 * player_state.zoom) as u32; - let h = (info.height as f32 * player_state.zoom) as u32; - let x = ((config.width as i32 - w as i32) / 2) as i32 - + player_state.pan_x as i32; - let y = ((config.height as i32 - h as i32) / 2) as i32 - + player_state.pan_y as i32; - Rect::new(x, y, w, h) - } else { - Rect::new(0, 0, 0, 0) - }; + let (vid_width, vid_height) = video_info + .as_ref() + .map(|i| (i.width, i.height)) + .unwrap_or((config.width, config.height)); - if player_state.zoom == 1.0 { - canvas.copy(tex, None, None).ok(); - } else { - canvas.copy(tex, None, Some(dst)).ok(); - } + let scale: f64 = if player_state.zoom != 1.0 { + player_state.zoom as f64 + } else { + let scale_x = config.width as f64 / vid_width as f64; + let scale_y = (config.height as i32 - info_height) as f64 / vid_height as f64; + scale_x.min(scale_y).min(1.0) + }; + + let scaled_w = (vid_width as f64 * scale) as u32; + let scaled_h = (vid_height as f64 * scale) as u32; + let offset_x = + ((config.width as i32 - scaled_w as i32) / 2) as i32 + player_state.pan_x as i32; + let offset_y = ((config.height as i32 - info_height - scaled_h as i32) / 2) as i32 + + player_state.pan_y as i32; + + if let Some(ref mut tex) = texture { + let dst = Rect::new(offset_x, offset_y, scaled_w, scaled_h); + canvas.copy(tex, None, Some(dst)).ok(); } if player_state.show_yolo { if let Some(ref mut yolo_loader) = yolo { let detections = yolo_loader.get_detections(player_state.current_frame); for det in detections { - let x1 = (det.x1 as f32 * player_state.zoom) as i32 - + player_state.pan_x as i32 - + ((config.width as i32 - video_info.as_ref().map(|i| i.width as i32).unwrap_or(0)) / 2); - let y1 = (det.y1 as f32 * player_state.zoom) as i32 - + player_state.pan_y as i32 - + ((config.height as i32 - video_info.as_ref().map(|i| i.height as i32).unwrap_or(0)) / 2); - let w = ((det.x2 - det.x1) as f32 * player_state.zoom) as u32; - let h = ((det.y2 - det.y1) as f32 * player_state.zoom) as u32; + let x1 = (det.x1 * scale) as i32 + offset_x; + let y1 = (det.y1 * scale) as i32 + offset_y; + let w = ((det.x2 - det.x1) * scale) as u32; + let h = ((det.y2 - det.y1) * scale) as u32; canvas.set_draw_color(sdl2::pixels::Color::RGB(0, 255, 0)); let _ = canvas.draw_rect(Rect::new(x1, y1, w, h)); - if let Some(ref f) = font { - let label = format!("{} {:.0}%", det.class_name, det.confidence * 100.0); - if let Ok(surface) = f - .render(&label) - .solid(sdl2::pixels::Color::RGB(0, 255, 0)) - { - let tex_label = texture_creator - .create_texture_from_surface(&surface) - .ok(); - if let Some(tex_label) = tex_label { - let label_rect = Rect::new(x1, y1 - 24, w.min(150), 24); - canvas.copy(&tex_label, None, Some(label_rect)).ok(); + if w > 30 && h > 10 { + if let Some(ref f) = font { + let label = + format!("{} {:.0}%", det.class_name, det.confidence * 100.0); + if let Ok(surface) = + f.render(&label).solid(sdl2::pixels::Color::RGB(0, 255, 0)) + { + if let Ok(tex_label) = + texture_creator.create_texture_from_surface(&surface) + { + let lw = surface.width().min(w as u32); + let label_rect = Rect::new(x1, y1.saturating_sub(20), lw, 18); + canvas.copy(&tex_label, None, Some(label_rect)).ok(); + } } } } @@ -333,21 +320,13 @@ fn run(config: &Config) -> Result<()> { .render(&text) .blended(sdl2::pixels::Color::RGBA(255, 255, 255, 255)) { - let tex_label = texture_creator - .create_texture_from_surface(&surface) - .ok(); - if let Some(tex_label) = tex_label { + if let Ok(tex_label) = texture_creator.create_texture_from_surface(&surface) { let query = tex_label.query(); let x = (config.width - query.width) / 2; - let y = config.height - query.height - 40; + let y = config.height - query.height - 20; let rect = Rect::new(x as i32, y as i32, query.width, query.height); - canvas.set_draw_color(sdl2::pixels::Color::RGBA(0, 0, 0, 180)); - let _ = canvas.fill_rect(Rect::new( - rect.x() - 10, - rect.y() - 5, - rect.width() + 20, - rect.height() + 10, - )); + canvas.set_draw_color(sdl2::pixels::Color::RGBA(0, 0, 0, 200)); + let _ = canvas.fill_rect(Rect::new(rect.x() - 8, rect.y() - 4, rect.width() + 16, rect.height() + 8)); canvas.copy(&tex_label, None, Some(rect)).ok(); } } @@ -358,51 +337,25 @@ fn run(config: &Config) -> Result<()> { if let Some(ref f) = font { let time_str = format_time(player_state.current_time_ms); - let frame_str = format!( - "Frame: {}/{} ({:.1}fps)", - player_state.current_frame, player_state.total_frames, player_state.fps + let line1 = format!("Time: {} Frame: {}/{} FPS: {:.1}", + time_str, player_state.current_frame, player_state.total_frames, player_state.fps); + let line2 = format!("[S]ubtitle:{} [Y]OLO:{} [C]hunks [M]ute [+/-]Zoom{:.1}x [`]Reset", + if player_state.show_subtitle { " ON " } else { " OFF" }, + if player_state.show_yolo { " ON " } else { " OFF" }, + player_state.zoom ); - let status_parts = vec![ - format!("Time: {}", time_str), - frame_str, - if player_state.show_subtitle { - "Subtitle: ON".to_string() - } else { - String::new() - }, - if player_state.show_yolo { - "YOLO: ON".to_string() - } else { - String::new() - }, - if player_state.zoom != 1.0 { - format!("Zoom: {:.1}x", player_state.zoom) - } else { - String::new() - }, - ]; - let y_offset = 10; - for (i, part) in status_parts.iter().enumerate() { - if !part.is_empty() { - if let Ok(surface) = f - .render(part) - .solid(sdl2::pixels::Color::RGB(200, 200, 200)) - { - let tex_label = texture_creator - .create_texture_from_surface(&surface) - .ok(); - if let Some(tex_label) = tex_label { - let rect = Rect::new(10, y_offset + (i as i32 * 22), surface.width(), surface.height()); - canvas.copy(&tex_label, None, Some(rect)).ok(); - } + for (i, line) in [line1, line2].iter().enumerate() { + if let Ok(surface) = f.render(line).solid(sdl2::pixels::Color::RGB(180, 180, 180)) { + if let Ok(tex_label) = texture_creator.create_texture_from_surface(&surface) { + let rect = Rect::new(10, 8 + (i as i32 * 20), surface.width(), surface.height()); + canvas.copy(&tex_label, None, Some(rect)).ok(); } } } } canvas.present(); - std::thread::sleep(std::time::Duration::from_millis(16)); }