diff --git a/.github/workflows/release_tag.yml b/.github/workflows/release_tag.yml index a486a9d..5742b62 100644 --- a/.github/workflows/release_tag.yml +++ b/.github/workflows/release_tag.yml @@ -23,6 +23,11 @@ jobs: - name: Checkout uses: actions/checkout@v2 + - name: Test + uses: actions-rs/cargo@v1 + with: + command: test + - name: Install rust toolchain uses: actions-rs/toolchain@v1 with: diff --git a/Cargo.lock b/Cargo.lock index ebe17fa..9969739 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -100,7 +100,7 @@ checksum = "ae44d1a3d5a19df61dd0c8beb138458ac2a53a7ac09eba97d55592540004306b" [[package]] name = "c2g" -version = "0.1.1" +version = "0.3.0" dependencies = [ "clap", "env_logger", diff --git a/Cargo.toml b/Cargo.toml index e35b56f..fe1fc7a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "c2g" -version = "0.2.1" +version = "0.3.0" authors = ["Tomas Farias "] edition = "2018" diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..711b288 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,13 @@ +FROM rust:1.50 as builder + +WORKDIR /build + +COPY . . + +RUN cargo build --release + +FROM scratch + +COPY --from=builder /build/target/release/c2g /c2g + +CMD ["/c2g"] diff --git a/src/cli.rs b/src/cli.rs index 94f8540..1080606 100644 --- a/src/cli.rs +++ b/src/cli.rs @@ -41,6 +41,12 @@ impl<'a> Chess2Gif<'a> { .default_value("chess.gif") .help("Write GIF to file"), ) + .arg( + Arg::with_name("flip") + .long("flip") + .takes_value(false) + .help("By default, white appears at the bottom, use this flag to flip the board"), + ) .arg( Arg::with_name("size") .short("s") @@ -117,9 +123,11 @@ impl<'a> Chess2Gif<'a> { .try_into() .expect("Invalid light color"); + let flip = matches.is_present("flip"); + Ok(Chess2Gif { pgn: pgn, - giffer: PGNGiffer::new(pieces_path, font_path, size, output, 100, dark, light)?, + giffer: PGNGiffer::new(pieces_path, font_path, flip, size, output, 100, dark, light)?, }) } diff --git a/src/drawer.rs b/src/drawer.rs index fc259c6..cac63c5 100644 --- a/src/drawer.rs +++ b/src/drawer.rs @@ -156,6 +156,7 @@ pub enum DrawerError { pub struct BoardDrawer { svgs: SVGForest, size: u32, + flip: bool, dark: Rgba, light: Rgba, } @@ -163,6 +164,7 @@ pub struct BoardDrawer { impl BoardDrawer { pub fn new( piece_path: &str, + flip: bool, font: &str, size: u32, dark: [u8; 4], @@ -172,6 +174,7 @@ impl BoardDrawer { Ok(BoardDrawer { svgs: svgs, size: size, + flip: flip, dark: image::Rgba(dark), light: image::Rgba(light), }) @@ -241,8 +244,14 @@ impl BoardDrawer { log::debug!("Initializing {:?} in {:?}", piece, square); self.draw_piece(&square, &piece.role, piece.color, false, &mut board)?; } + self.draw_ranks(2, 6, &mut board)?; + if self.flip == true { + imageops::flip_horizontal_in_place(&mut board); + imageops::flip_vertical_in_place(&mut board); + } + Ok(board) } @@ -253,7 +262,7 @@ impl BoardDrawer { img: &mut RgbaImage, ) -> Result<(), DrawerError> { for n in from..to { - let square = Square::new(n * 8); + let square = Square::new((n * 8) + (self.flip as u32 * 7)); self.draw_square(&square, img)?; } @@ -306,13 +315,18 @@ impl BoardDrawer { } }; + if self.flip == true { + imageops::flip_horizontal_in_place(img); + imageops::flip_vertical_in_place(img); + } + Ok(()) } pub fn draw_square(&mut self, square: &Square, img: &mut RgbaImage) -> Result<(), DrawerError> { log::debug!("Drawing square: {}", square); let pixmap = self.square_pixmap(self.square_size(), self.square_size(), square)?; - let square_img = ImageBuffer::from_raw(pixmap.width(), pixmap.height(), pixmap.take()) + let mut square_img = ImageBuffer::from_raw(pixmap.width(), pixmap.height(), pixmap.take()) .ok_or(DrawerError::ImageTooBig { image: format!("{}x{} square", self.square_size(), self.square_size()), })?; @@ -320,6 +334,11 @@ impl BoardDrawer { let x = self.square_size() * u32::from(square.file()); let y = self.size - self.square_size() * (u32::from(square.rank()) + 1); + if self.flip == true { + imageops::flip_vertical_in_place(&mut square_img); + imageops::flip_horizontal_in_place(&mut square_img); + } + imageops::overlay(img, &square_img, x, y); Ok(()) @@ -343,7 +362,13 @@ impl BoardDrawer { log::debug!("Piece coordinates: ({}, {})", x, y); let height = self.square_size(); - let resized_piece = self.piece_image(color, square, role, height, height)?; + let mut resized_piece = self.piece_image(color, square, role, height, height)?; + + if self.flip == true { + imageops::flip_vertical_in_place(&mut resized_piece); + imageops::flip_horizontal_in_place(&mut resized_piece); + } + imageops::replace(img, &resized_piece, x, y); Ok(()) @@ -421,8 +446,10 @@ impl BoardDrawer { true => pixmap.fill(self.dark_color()), false => pixmap.fill(self.light_color()), }; - if utils::has_coordinate(square) { - if square.rank() == Rank::First { + if utils::has_coordinate(square, self.flip) { + if (square.rank() == Rank::First && self.flip == false) + || (square.rank() == Rank::Eighth && self.flip == true) + { let file_pixmap = self.coordinate_pixmap( square.file().char(), square, @@ -443,7 +470,9 @@ impl BoardDrawer { ); } - if square.file() == File::A { + if (square.file() == File::A && self.flip == false) + || (square.file() == File::H && self.flip == true) + { let rank_pixmap = self.coordinate_pixmap( square.rank().char(), square, @@ -470,7 +499,8 @@ mod tests { fn test_square_image() { let dark: [u8; 4] = [249, 100, 100, 1]; let light: [u8; 4] = [255, 253, 253, 1]; - let mut drawer = BoardDrawer::new("some/path/".to_string(), None, 80, dark, light).unwrap(); + let mut drawer = + BoardDrawer::new("some/path/", false, "roboto.ttf", 80, dark, light).unwrap(); let square = Square::new(0); // A1 is dark let expected = ImageBuffer::from_pixel(10, 10, image::Rgba(dark)); @@ -485,7 +515,7 @@ mod tests { fn test_sizes() { let dark: [u8; 4] = [249, 100, 100, 1]; let light: [u8; 4] = [255, 253, 253, 1]; - let drawer = BoardDrawer::new("some/path/".to_string(), None, 80, dark, light).unwrap(); + let drawer = BoardDrawer::new("some/path/", false, "roboto.ttf", 80, dark, light).unwrap(); assert_eq!(drawer.size(), 80); assert_eq!(drawer.square_size(), 10); @@ -495,7 +525,8 @@ mod tests { fn test_square_pixmap() { let dark: [u8; 4] = [249, 100, 100, 1]; let light: [u8; 4] = [255, 253, 253, 1]; - let mut drawer = BoardDrawer::new("some/path/".to_string(), None, 80, dark, light).unwrap(); + let mut drawer = + BoardDrawer::new("some/path/", false, "roboto.ttf", 80, dark, light).unwrap(); let mut pixmap = Pixmap::new(10, 10).unwrap(); let square = Square::new(9); // B2 is dark diff --git a/src/giffer.rs b/src/giffer.rs index cc58856..0de82d1 100644 --- a/src/giffer.rs +++ b/src/giffer.rs @@ -39,6 +39,7 @@ impl<'a> PGNGiffer<'a> { pub fn new( pieces_path: &str, font_path: &str, + flip: bool, board_size: u32, output_path: &str, ms_delay: u16, @@ -49,7 +50,7 @@ impl<'a> PGNGiffer<'a> { fs::File::create(output_path).map_err(|source| GifferError::CreateOutput { source })?; let buffer = BufWriter::with_capacity(1000, file); - let drawer = BoardDrawer::new(pieces_path, font_path, board_size as u32, dark, light) + let drawer = BoardDrawer::new(pieces_path, flip, font_path, board_size as u32, dark, light) .map_err(|source| GifferError::DrawerError { source: source })?; let mut encoder = Encoder::new(buffer, drawer.size() as u16, drawer.size() as u16, &[]) diff --git a/src/utils.rs b/src/utils.rs index 4b852f3..006a824 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -1,7 +1,9 @@ use shakmaty::{File, Rank, Square}; -pub fn has_coordinate(s: &Square) -> bool { - if s.rank() == Rank::First || s.file() == File::A { +pub fn has_coordinate(s: &Square, flip: bool) -> bool { + if (s.rank() == Rank::First || s.file() == File::A) && flip == false { + true + } else if (s.rank() == Rank::Eighth || s.file() == File::H) && flip == true { true } else { false @@ -15,12 +17,24 @@ mod tests { #[test] fn test_has_coordinate() { let square = Square::new(0); // A1 - assert!(has_coordinate(&square)); + assert!(has_coordinate(&square, false)); let square = Square::new(9); // B2 - assert!(!has_coordinate(&square)); + assert!(!has_coordinate(&square, false)); let square = Square::new(56); // A8 - assert!(has_coordinate(&square)); + assert!(has_coordinate(&square, false)); + } + + #[test] + fn test_has_coordinate_flip() { + let square = Square::new(0); // A1 + assert!(!has_coordinate(&square, true)); + + let square = Square::new(7); // H1 + assert!(has_coordinate(&square, true)); + + let square = Square::new(63); // H8 + assert!(has_coordinate(&square, true)); } }