diff --git a/Cargo.toml b/Cargo.toml index fad25c4..7915f27 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,7 +22,7 @@ path = "src/lib.rs" clap = { version = "4.4.6", features = ["derive"] } env_logger = "0.10.0" log = "0.4.20" -sdl2 = { version = "0.36.0", default-features = false } +sdl2 = { version = "0.36.0", default-features = false, features = ["ttf"] } cairo-rs = { version = "0.18.2", optional = true } librsvg = { version = "2.57.0", optional = true } resvg = { version = "0.36.0", optional = true } @@ -44,7 +44,7 @@ rgb = "0.8.37" rstest = "0.18.2" [package.metadata.vcpkg] -dependencies = ["sdl2"] +dependencies = ["sdl2", "sdl2-ttf"] git = "https://github.com/microsoft/vcpkg" rev = "a42af01" # release 2023.11.20 diff --git a/build.rs b/build.rs new file mode 100644 index 0000000..a04d3a8 --- /dev/null +++ b/build.rs @@ -0,0 +1,7 @@ +fn main() { + // fix for static linking issue (undefined references to 'deflate') + // caused by wrong order of -lz -png (defining group with correct order + // fixes that) + #[cfg(target_os="linux")] + println!("cargo:rustc-link-arg=-Wl,--start-group,-lpng,-lz,--end-group"); +} diff --git a/resources/DejaVuSansMono.ttf b/resources/DejaVuSansMono.ttf new file mode 100644 index 0000000..46ea2b7 Binary files /dev/null and b/resources/DejaVuSansMono.ttf differ diff --git a/src/lib.rs b/src/lib.rs index c9ac40b..959d37e 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -9,6 +9,7 @@ use sdl2::rect::Rect; use sdl2::render::Canvas; use sdl2::render::Texture; use sdl2::render::TextureCreator; +use sdl2::rwops::RWops; use sdl2::video::Window; use sdl2::video::WindowContext; use sdl2::VideoSubsystem; @@ -456,6 +457,136 @@ impl<'a> CanvasEntity for CheckerBoard<'a> { } } +mod digits_display_module { + use sdl2::pixels::Color; + use sdl2::rect::Point; + use sdl2::rect::Rect; + use sdl2::render::Texture; + use sdl2::render::TextureCreator; + use sdl2::render::TextureQuery; + use sdl2::ttf::Font; + use sdl2::video::WindowContext; + + use super::CanvasEntity; + + fn to_digits(digits: &mut Vec, value: i32) { + digits.clear(); + let mut v = value.abs(); + loop { + let d: u8 = (v % 10) as u8; + v = v / 10; + digits.push(d); + if v == 0 { + break; + }; + } + } + + pub struct DigitsDisplay<'a> { + texture: Texture<'a>, + glyph_width: u32, + glyph_height: u32, + position: Point, + sign: bool, + digits: Vec, + } + + impl<'a> DigitsDisplay<'a> { + pub fn new( + font: &Font, + texture_creator: &'a TextureCreator, + ) -> Result, String> { + let font_surface = font + .render("0123456789-") + .blended(Color::RGBA(0, 0, 0, 255)) + .map_err(|e| e.to_string())?; + + let texture = texture_creator + .create_texture_from_surface(&font_surface) + .map_err(|e| e.to_string())?; + + let TextureQuery { width, height, .. } = texture.query(); + let glyph_width = width / 11; + let glyph_height = height; + + Ok(DigitsDisplay { + texture, + glyph_width, + glyph_height, + position: Point::new(0, 0), + sign: false, + digits: Vec::with_capacity(64), + }) + } + + pub fn update(&mut self, value: i32) { + self.sign = value < 0; + to_digits(&mut self.digits, value); + } + } + + impl<'a> CanvasEntity for DigitsDisplay<'a> { + fn draw(&self, renderer: &mut sdl2::render::WindowCanvas) -> Result<(), String> { + let glyph_width = self.glyph_width; + let glyph_height = self.glyph_height; + + let mut m = 0u32; + + let mut render = |index: u32, position: u32| -> Result<(), String> { + renderer.copy( + &self.texture, + Rect::new((glyph_width * index) as i32, 0, glyph_width, glyph_height), + Rect::new( + self.position.x + (glyph_width * position) as i32, + self.position.y, + glyph_width, + glyph_height, + ), + ) + }; + + if self.sign { + render(10, m)?; + m += 1; + } + + for &digit in self.digits.iter().rev() { + render(digit as u32, m)?; + m += 1; + } + Ok(()) + } + + fn size(&self) -> (u32, u32) { + let TextureQuery { width, height, .. } = self.texture.query(); + (width, height) + } + + fn reposition(&mut self, position: Point) { + self.position = position; + } + } + + #[cfg(test)] + mod test { + use super::*; + + #[test] + fn test_to_digits() { + let mut vec = Vec::::with_capacity(64); + to_digits(&mut vec, 123); + assert_eq!(vec, vec![3, 2, 1]); + } + + #[test] + fn test_to_digits_negative() { + let mut vec = Vec::::with_capacity(64); + to_digits(&mut vec, -123); + assert_eq!(vec, vec![3, 2, 1]); + } + } +} + fn get_texture_builder<'a, P: AsRef>( path: P, backend: SvgBackend, @@ -615,6 +746,19 @@ pub fn app>( let sdl_context = sdl2::init()?; let video_subsystem = sdl_context.video()?; + let ttf_context = sdl2::ttf::init().map_err(|e| e.to_string())?; + + let mut db = fontdb::Database::new(); + db.load_system_fonts(); + //println!() + for f in db.faces() { + println!("{:?}", f); + } + + // Load a font TODO: not sure if needed backup, perhaps will load with fontdb only + let font = include_bytes!("../resources/DejaVuSansMono.ttf"); + let font = &ttf_context + .load_font_from_rwops(RWops::from_bytes(font)?, 16)?; let min_size: (u32, u32) = (800, 600); let max_size: (u32, u32) = get_max_window_size(&video_subsystem)?; @@ -671,13 +815,16 @@ pub fn app>( )); } + // canvas elements: let left = left_svg.rasterize(&texture_creator, scale)?; let right = right_svg.rasterize(&texture_creator, scale)?; - - let mut drag = drag_module::Drag::new(); let mut diff = Diff::new(left, right); let mut workarea = CheckerBoard::new(&texture_creator, diff.size())?; + let mut mouse_x_cord = digits_display_module::DigitsDisplay::new(&font, &texture_creator)?; + let mut mouse_y_cord = digits_display_module::DigitsDisplay::new(&font, &texture_creator)?; + // app logic handling: + let mut drag = drag_module::Drag::new(); let mut event_pump = sdl_context.event_pump()?; 'running: loop { @@ -686,7 +833,8 @@ pub fn app>( canvas.set_draw_color(Color::RGBA(255, 255, 255, 255)); canvas.clear(); - let mut center = canvas.viewport().center(); + let viewport = canvas.viewport(); + let mut center = viewport.center(); for event in event_pump.poll_iter() { match event { @@ -746,10 +894,23 @@ pub fn app>( diff.update(&mouse_state); diff.draw(&mut canvas)?; + // TODO: should display coordinates inside working area and not inside window + mouse_x_cord.update(mouse_state.x()); + mouse_x_cord + .reposition(viewport.bottom_left() - Point::new(0, mouse_x_cord.size().1 as i32)); + mouse_x_cord.draw(&mut canvas)?; + + mouse_y_cord.update(mouse_state.y()); + mouse_y_cord.reposition( + viewport.bottom_left() + Point::new(100, 0) // TODO: better calculation for position + - Point::new(0, mouse_x_cord.size().1 as i32), + ); + mouse_y_cord.draw(&mut canvas)?; + canvas.present(); let frame_duration = frame_start.elapsed().as_micros() as u64; - trace!("Frame duration: {}us", frame_duration); + trace!("Frame duration average: {}us", frame_duration); match testing { Some(val) => {