Skip to main content

kallo_cli/commands/
render.rs

1//! `kalo render` — apply the model to a whole image.
2
3use kallo_api::{RenderOptions, render};
4use kallo_io::resize_to_width;
5
6use crate::cli::RenderArgs;
7use crate::commands::common::{Rendering, print_report, read_input, resolve};
8use crate::error::CliError;
9
10/// Run the subcommand.
11pub fn run(args: RenderArgs) -> Result<(), CliError> {
12    let selection = resolve(&args.stock)?;
13    // A film can arrive as a file. It is loaded before the image is, because
14    // the film is an input to the model rather than a pass over its output.
15    let film = match &args.stock.film_file {
16        Some(path) => Some(crate::filmfile::read(path).map_err(|error| CliError::Film {
17            path: path.clone(),
18            message: error.to_string(),
19        })?),
20        None => None,
21    };
22
23    let mut image = read_input(&args.input, args.input_encoding.into())?;
24    // Downsampled before anything else, because everything after this reads the
25    // frame at the size it is rendered at: the meter averages it, the grain is
26    // scaled by it, and the emulsions blur across it.
27    if let Some(size) = args.size {
28        image = resize_to_width(&image, size);
29    }
30
31    let options = RenderOptions {
32        ev: args.ev.into(),
33        print_ev: args.print_ev,
34        balance_from: args.balance_from,
35        frame_mm: args.frame_mm,
36        emulsion: !args.no_emulsion,
37        halation: args.halation,
38        couplers: args.couplers,
39        interimage: args.interimage,
40        grain: args.grain,
41        grain_um: args.grain_um,
42        grain_seed: args.grain_seed,
43        scanner: args.scanner,
44        scan_optics_um: args.scan_optics_um,
45        gpu: args.gpu,
46    };
47    let outcome = render(&mut image, film, &selection, &options, &args.output)?;
48    for warning in &outcome.warnings {
49        eprintln!("warning: {warning}");
50    }
51
52    print_report(
53        &outcome.params,
54        &outcome.prepared,
55        outcome.width,
56        outcome.height,
57        &Rendering {
58            grain: outcome.finishing.grain.as_ref(),
59            emulsion: outcome.finishing.emulsion.as_ref(),
60            halation: outcome.finishing.halation.as_ref(),
61            couplers: outcome.finishing.couplers.as_ref(),
62            interimage: outcome.finishing.interimage.as_ref(),
63            scanner: outcome.scanner.as_ref(),
64            metering: outcome.metering,
65            backend: outcome.backend.to_string(),
66        },
67    );
68    println!();
69    println!(
70        "  wrote     {} ({}x{})",
71        args.output.display(),
72        outcome.width,
73        outcome.height
74    );
75    Ok(())
76}