use crate::{ app::AppState, bitmap::{abs_difference, MapPoint}, consts::FONT_PATH, lisp::{ error::{EvalError, LispError, ParseError}, eval::Evaluator, lex::Lexer, parse::Parser, }, message::Message, }; use std::{ fs::OpenOptions, io::{self, Cursor, Read}, path::Path, }; use log::info; use obi::Image; use sdl2::{ keyboard::{Keycode, Mod}, pixels::Color, render::Canvas, ttf::Sdl2TtfContext, video::Window, }; #[macro_export] macro_rules! rect( ($x:expr, $y:expr, $w:expr, $h:expr) => ( ::sdl2::rect::Rect::new($x as i32, $y as i32, $w as u32, $h as u32) ) ); pub fn draw_text>( canvas: &mut Canvas, ttf_context: &Sdl2TtfContext, text: S, color: Color, (x, y): (i32, i32), ) -> u32 { let text = text.as_ref(); let texture_creator = canvas.texture_creator(); let mut font = ttf_context.load_font(FONT_PATH, 17).unwrap(); font.set_style(sdl2::ttf::FontStyle::NORMAL); font.set_hinting(sdl2::ttf::Hinting::Mono); let surface = font .render(if text.is_empty() { " " } else { text }) .blended(color) .unwrap(); let texture = texture_creator .create_texture_from_surface(&surface) .unwrap(); let (width, height) = font.size_of(&text).unwrap(); let area = rect!(x, y, width, height); canvas.copy(&texture, None, area).unwrap(); width } pub fn is_copy_event(keycode: Option, keymod: Mod) -> bool { keycode == Some(Keycode::C) && (keymod == Mod::LCTRLMOD || keymod == Mod::RCTRLMOD) } pub fn is_paste_event(keycode: Option, keymod: Mod) -> bool { keycode == Some(Keycode::V) && (keymod == Mod::LCTRLMOD || keymod == Mod::RCTRLMOD) } pub fn handle_error(err: ParseError, src: &str, file_name: &str) -> Message { let mut message = Message::new(); message.set_error(err.display(&src, file_name)); message } pub fn load_script>(path: P, app: &mut AppState) -> Result<(), LispError> { let mut script = OpenOptions::new() .read(true) .write(false) .create(false) .open(path.as_ref()) .map_err(EvalError::ScriptLoadError)?; let mut buf = String::new(); script .read_to_string(&mut buf) .map_err(EvalError::ScriptLoadError)?; let mut parser = Parser::new(Lexer::new(&buf)); let mut evaluator = Evaluator { app, context: Vec::new(), }; for expr in parser.parse_exprs().map_err(|err| { LispError::Stringified(err.display(&buf, path.as_ref().to_str().unwrap_or(""))) })? { evaluator.eval(&expr)?; } Ok(()) } pub fn load_file>(path: P) -> Result { info!("loading existing file `{:?}`", path.as_ref()); let mut image = OpenOptions::new() .read(true) .write(false) .create(false) .open(&path)?; let mut buf = Vec::new(); image.read_to_end(&mut buf)?; Ok(Image::decode(&mut (Cursor::new(buf))).unwrap()) // TODO: obi error } pub fn compress(scanline: &[T]) -> Vec<(T, usize)> { let mut runs = vec![]; if scanline.is_empty() { return runs; } let mut idx = 0; loop { let first = &scanline[idx]; let run_length = scanline[idx..] .iter() .take_while(|&item| item == first) .count(); runs.push((first.clone(), run_length)); idx += run_length; if idx > scanline.len() - 1 { break; } } runs } pub fn rect_coords(s: MapPoint, e: MapPoint) -> (MapPoint, MapPoint) { let (width, height) = (abs_difference(s.x, e.x), abs_difference(s.y, e.y)); let start_loc = if e.x >= s.x && e.y >= s.y { (s.x, s.y) } else if e.x >= s.x && e.y < s.y { (s.x, e.y) } else if e.x < s.x && e.y >= s.y { (e.x, s.y) } else { (e.x, e.y) } .into(); let end_loc = start_loc + (width, height).into(); (start_loc, end_loc) } #[cfg(test)] mod tests { use super::compress; #[test] fn compression() { let sl = [1, 1, 1, 2, 2, 2]; assert_eq!(compress(&sl), vec![(1, 3), (2, 3)]); } }