From 71c320d87cba448f284cacec39717d7b607e90be Mon Sep 17 00:00:00 2001 From: Nathan Vegdahl Date: Tue, 6 Jun 2017 21:11:35 -0700 Subject: [PATCH] Added command-line argument to render an image cropped. --- src/main.rs | 30 +++++++++++++++++++++++++++++ src/renderer.rs | 51 +++++++++++++++++++++++++++++++++++-------------- 2 files changed, 67 insertions(+), 14 deletions(-) diff --git a/src/main.rs b/src/main.rs index 1411b4f..0f525b8 100644 --- a/src/main.rs +++ b/src/main.rs @@ -114,6 +114,21 @@ fn main() { } ) ) + .arg( + Arg::with_name("crop") + .long("crop") + .value_name("X1 Y1 X2 Y2") + .help("Only render the image between pixel coordinates (X1, Y1) and (X2, Y2). Coordinates are zero-indexed and inclusive.") + .takes_value(true) + .number_of_values(4) + .validator( + |s| { + usize::from_str(&s) + .and(Ok(())) + .or(Err("must be four integers".to_string())) + } + ) + ) .arg( Arg::with_name("threads") .short("t") @@ -170,6 +185,20 @@ fn main() { return; } + let crop = args.values_of("crop") + .map( + |mut vals| { + let coords = (u32::from_str(vals.next().unwrap()).unwrap(), u32::from_str(vals.next().unwrap()).unwrap(), u32::from_str(vals.next().unwrap()).unwrap(), u32::from_str(vals.next().unwrap()).unwrap()); + if coords.0 > coords.2 { + panic!("Argument '--crop': X1 must be less than or equal to X2"); + } + if coords.1 > coords.3 { + panic!("Argument '--crop': Y1 must be less than or equal to Y2"); + } + coords + } + ); + // Parse data tree of scene file println!( "Parsing scene file...", @@ -254,6 +283,7 @@ fn main() { println!("Rendering scene with {} threads...", thread_count); let (mut image, rstats) = r.render( max_samples_per_bucket, + crop, thread_count, args.is_present("blender_output"), ); diff --git a/src/renderer.rs b/src/renderer.rs index b04d50f..0e55d29 100644 --- a/src/renderer.rs +++ b/src/renderer.rs @@ -68,7 +68,7 @@ impl RenderStats { } impl<'a> Renderer<'a> { - pub fn render(&self, max_samples_per_bucket: u32, thread_count: u32, do_blender_output: bool) -> (Image, RenderStats) { + pub fn render(&self, max_samples_per_bucket: u32, crop: Option<(u32, u32, u32, u32)>, thread_count: u32, do_blender_output: bool) -> (Image, RenderStats) { let mut tpool = Pool::new(thread_count); let image = Image::new(self.resolution.0, self.resolution.1); @@ -84,6 +84,18 @@ impl<'a> Renderer<'a> { // For printing render progress let pixels_rendered = Mutex::new(Cell::new(0)); + // Calculate dimensions and coordinates of what we're rendering. This + // accounts for cropping. + let (width, height, start_x, start_y) = if let Some((x1, y1, x2, y2)) = crop { + let x1 = min(x1 as usize, img_width - 1); + let y1 = min(y1 as usize, img_height - 1); + let x2 = min(x2 as usize, img_width - 1); + let y2 = min(y2 as usize, img_height - 1); + (x2 - x1 + 1, y2 - y1 + 1, x1, y1) + } else { + (img_width, img_height, 0, 0) + }; + // Render tpool.scoped( |scope| { @@ -92,9 +104,21 @@ impl<'a> Renderer<'a> { let jq = &job_queue; let ajq = &all_jobs_queued; let img = ℑ - let cstats = &collective_stats; let pixrenref = &pixels_rendered; - scope.execute(move || self.render_job(jq, ajq, img, cstats, pixrenref, do_blender_output)); + let cstats = &collective_stats; + scope.execute( + move || { + self.render_job( + jq, + ajq, + img, + width * height, + pixrenref, + cstats, + do_blender_output, + ) + } + ); } // Print initial 0.00% progress @@ -116,8 +140,8 @@ impl<'a> Renderer<'a> { // Populate job queue let bucket_n = { - let bucket_count_x = ((img_width / bucket_w) + 1) as u32; - let bucket_count_y = ((img_height / bucket_h) + 1) as u32; + let bucket_count_x = ((width / bucket_w) + 1) as u32; + let bucket_count_y = ((height / bucket_h) + 1) as u32; let larger = cmp::max(bucket_count_x, bucket_count_y); let pow2 = upper_power_of_two(larger); pow2 * pow2 @@ -127,21 +151,21 @@ impl<'a> Renderer<'a> { let x = bx as usize * bucket_w; let y = by as usize * bucket_h; - let w = if img_width >= x { - min(bucket_w, img_width - x) + let w = if width >= x { + min(bucket_w, width - x) } else { bucket_w }; - let h = if img_height >= y { - min(bucket_h, img_height - y) + let h = if height >= y { + min(bucket_h, height - y) } else { bucket_h }; - if x < img_width && y < img_height && w > 0 && h > 0 { + if x < width && y < height && w > 0 && h > 0 { job_queue.push( BucketJob { - x: x as u32, - y: y as u32, + x: (start_x + x) as u32, + y: (start_y + y) as u32, w: w as u32, h: h as u32, } @@ -164,7 +188,7 @@ impl<'a> Renderer<'a> { } /// Waits for buckets in the job queue to render and renders them when available. - fn render_job(&self, job_queue: &MsQueue, all_jobs_queued: &RwLock, image: &Image, collected_stats: &RwLock, pixels_rendered: &Mutex>, do_blender_output: bool) { + fn render_job(&self, job_queue: &MsQueue, all_jobs_queued: &RwLock, image: &Image, total_pixels: usize, pixels_rendered: &Mutex>, collected_stats: &RwLock, do_blender_output: bool) { let mut stats = RenderStats::new(); let mut timer = Timer::new(); let mut total_timer = Timer::new(); @@ -183,7 +207,6 @@ impl<'a> Renderer<'a> { let max_y = self.resolution.1 as f32 / self.resolution.0 as f32; let x_extent = max_x - min_x; let y_extent = max_y - min_y; - let total_pixels = self.resolution.0 * self.resolution.1; // Render 'render_loop: loop {