Skip to content

Commit

Permalink
Add Crop Option
Browse files Browse the repository at this point in the history
  • Loading branch information
zicklag committed Jan 1, 2021
1 parent 4f76eeb commit a25b4d1
Show file tree
Hide file tree
Showing 4 changed files with 81 additions and 13 deletions.
6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,13 +47,17 @@ Run `cast2gif --help` to get the usage instructions:
Renders Asciinema .cast files as gif, svg, or animated png.

USAGE:
cast2gif [FLAGS] <cast_file> <out_file>
cast2gif [FLAGS] [OPTIONS] <cast_file> <out_file>

FLAGS:
-f, --force Overwrite existing output file
-h, --help Prints help information
-V, --version Prints version information

OPTIONS:
-c, --crop <crop> crop the recording while rendering. Specify crop in terminal cells as
`top=[int],left=[int],width=[int],height=[int]`.

ARGS:
<cast_file> The asciinema .cast file to render
<out_file> The file to render to
Expand Down
56 changes: 56 additions & 0 deletions src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,13 +37,22 @@ pub fn run() {
.expect("Panic while handling panic");
}

#[derive(Debug)]
enum OutputFormat {
Gif,
// TODO: Other image formats
// Png,
// Svg,
}

#[derive(Debug, Clone, Copy)]
pub struct CropSettings {
pub top: u16,
pub left: u16,
pub width: u16,
pub height: u16,
}

fn execute_cli() -> anyhow::Result<()> {
use clap::{crate_authors, crate_version, App, AppSettings, Arg};

Expand All @@ -60,6 +69,13 @@ fn execute_cli() -> anyhow::Result<()> {
.arg(Arg::with_name("out_file")
.help("The file to render to")
.required(true))
.arg(Arg::with_name("crop")
.long("crop")
.short("c")
.help("crop the recording while rendering. \
Specify crop in terminal cells as \
`top=[int],left=[int],width=[int],height=[int]`.")
.takes_value(true))
// TODO: Implement other file formats
// .arg(Arg::with_name("format")
// .long("format")
Expand Down Expand Up @@ -137,6 +153,45 @@ fn execute_cli() -> anyhow::Result<()> {
// Some("png") => OutputFormat::Png,
// Some(other) => panic!("Invalid option to --format: {}", other),
// };
let crop = {
let mut top = None;
let mut left = None;
let mut width = None;
let mut height = None;

if let Some(crop_str) = args.value_of("crop") {
for pair in crop_str.split(",") {
let split: Vec<_> = pair.split("=").collect();
let key = split.get(0);
let value = split.get(1);

if let Some(value) = value {
let value: u16 = value.parse().context("Could not parse crop value as int")?;

if let Some(&key) = key {
match key {
"top" => top = Some(value),
"left" => left = Some(value),
"width" => width = Some(value),
"height" => height = Some(value),
_ => continue,
}
}
}
}
};

if top.is_none() || left.is_none() || width.is_none() || height.is_none() {
None
} else {
Some(CropSettings {
top: top.unwrap(),
left: left.unwrap(),
width: width.unwrap(),
height: height.unwrap(),
})
}
};

// Create the progress bars
let multi = MultiProgress::new();
Expand All @@ -158,6 +213,7 @@ fn execute_cli() -> anyhow::Result<()> {
cast_file,
&out_file,
progress_handler,
crop
)
.expect("TODO");
});
Expand Down
21 changes: 13 additions & 8 deletions src/frame_renderer/fontkit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ use std::iter::FromIterator;
use std::sync::Arc;

use super::parse_color;
use crate::types::*;
use crate::{cli::CropSettings, types::*};

lazy_static! {
static ref FONT_DATA: Arc<Vec<u8>> = Arc::new(Vec::from_iter(
Expand All @@ -34,7 +34,7 @@ thread_local! {
static FONT: Font = Font::from_bytes(FONT_DATA.clone(), 0).expect("Could not load font");
}

pub(crate) fn render_frame_to_png(frame: TerminalFrame) -> RgbaFrame {
pub(crate) fn render_frame_to_png(frame: TerminalFrame, crop: Option<CropSettings>) -> RgbaFrame {
flame!(guard "Render Frame To PNG");

flame!(start "Init Values");
Expand All @@ -43,6 +43,11 @@ pub(crate) fn render_frame_to_png(frame: TerminalFrame) -> RgbaFrame {
// TODO: Configurable background color
const DEFAULT_BG_COLOR: RGBA8 = RGBA::new(0, 0, 0, 255);

let crop_rows = crop.map(|x| x.height).unwrap_or(rows);
let crop_cols = crop.map(|x| x.width).unwrap_or(cols);
let crop_top = crop.map(|x| x.top).unwrap_or(0);
let crop_left = crop.map(|x| x.left).unwrap_or(0);

// Glyph rendering config
lazy_static! {
// static ref TRANS: Transform2F = Transform2F::default();
Expand Down Expand Up @@ -73,8 +78,8 @@ pub(crate) fn render_frame_to_png(frame: TerminalFrame) -> RgbaFrame {
let font_transform =
Transform2F::from_translation(Vector2F::new(0., -font_height_offset as f32));

let height = (rows as i32 * font_height) as usize;
let width = (cols as i32 * font_width) as usize;
let height = (crop_rows as i32 * font_height) as usize;
let width = (crop_cols as i32 * font_width) as usize;

// Image to render to
let pixel_count = width * height;
Expand All @@ -89,11 +94,11 @@ pub(crate) fn render_frame_to_png(frame: TerminalFrame) -> RgbaFrame {
flame!(end "Init Values");

flame!(start "Render Cells");
for row in 0..rows {
for col in 0..cols {
for (row_i, row) in (crop_top..(crop_top + crop_rows)).enumerate() {
for (col_i, col) in (crop_left..(crop_left + crop_cols)).enumerate() {
let cell = frame.screen.cell(row, col).expect("Error indexing cell");
let ypos = row as i32 * font_height;
let xpos = col as i32 * font_width;
let ypos = row_i as i32 * font_height;
let xpos = col_i as i32 * font_width;
let mut subimg = image.sub_image_mut(
xpos as usize,
ypos as usize,
Expand Down
11 changes: 7 additions & 4 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
use cli::CropSettings;
use lazy_static::lazy_static;
use thiserror::Error;

Expand Down Expand Up @@ -76,6 +77,7 @@ fn png_raster_thread<Fi>(
frames: Fi,
progress_sender: flume::Sender<ProgressCmd>,
frame_sender: flume::Sender<RgbaFrame>,
crop: Option<CropSettings>,
) where
Fi: IntoIterator<Item = Result<TerminalFrame, AsciinemaError>>,
{
Expand All @@ -93,7 +95,7 @@ fn png_raster_thread<Fi>(
let fs = frame_sender.clone();
let ps = progress_sender.clone();
rayon::spawn(move || {
let frame = frame_renderer::render_frame_to_png(frame);
let frame = frame_renderer::render_frame_to_png(frame, crop);
fs.send(frame).expect("TODO");
ps.send(ProgressCmd::IncrementRasterProgress).expect("TODO");
});
Expand Down Expand Up @@ -125,6 +127,7 @@ pub fn convert_to_gif_with_progress<R, W, C>(
reader: R,
writer: W,
update_progress: C,
crop: Option<CropSettings>,
) -> Result<(), Error>
where
R: Read + Send + 'static,
Expand All @@ -146,7 +149,7 @@ where

// Spawn the png rasterizer thread
let ps = progress_sender.clone();
rayon::spawn(move || png_raster_thread(term_frames, ps, raster_sender));
rayon::spawn(move || png_raster_thread(term_frames, ps, raster_sender, crop));

// Create gifski gif encoder
let (collector, gif_writer) = gifski::new(gifski::Settings {
Expand Down Expand Up @@ -197,10 +200,10 @@ impl gifski::progress::ProgressReporter for GifWriterProgressHandler {
fn done(&mut self, _msg: &str) {}
}

pub fn convert_to_gif<R, W>(reader: R, writer: W) -> Result<(), Error>
pub fn convert_to_gif<R, W>(reader: R, writer: W, crop: Option<CropSettings>) -> Result<(), Error>
where
R: Read + Send + 'static,
W: Write + Send,
{
convert_to_gif_with_progress(reader, writer, NullProgressHandler)
convert_to_gif_with_progress(reader, writer, NullProgressHandler, crop)
}

0 comments on commit a25b4d1

Please sign in to comment.