widgetry/tools/
screenshot.rs

1use abstutil::Timer;
2
3use crate::runner::State;
4use crate::{Prerender, ScreenDims, SharedAppState};
5
6/// Take a screenshot of the entire canvas, tiling it based on the window's width and height.
7pub(crate) fn screenshot_everything<A: 'static + SharedAppState>(
8    state: &mut State<A>,
9    dir_path: &str,
10    prerender: &Prerender,
11    zoom: f64,
12    dims: ScreenDims,
13) -> anyhow::Result<()> {
14    if dims.width > state.canvas.window_width || dims.height > state.canvas.window_height {
15        bail!(
16            "Can't take screenshots of dims {:?} when the window is only {:?}",
17            dims,
18            state.canvas.get_window_dims()
19        );
20    }
21
22    let mut timer = Timer::new("capturing screen");
23    let num_tiles_x = (state.canvas.map_dims.0 * zoom / dims.width).ceil() as usize;
24    let num_tiles_y = (state.canvas.map_dims.1 * zoom / dims.height).ceil() as usize;
25    let orig_zoom = state.canvas.cam_zoom;
26    let orig_x = state.canvas.cam_x;
27    let orig_y = state.canvas.cam_y;
28
29    timer.start_iter("capturing images", num_tiles_x * num_tiles_y);
30    state.canvas.cam_zoom = zoom;
31    fs_err::create_dir_all(dir_path)?;
32
33    // See https://github.com/a-b-street/abstreet/issues/671 for context. Some maps are so large
34    // and detailed that they drain all memory on some video cards. As a workaround, just free up
35    // some of the objects in the middle of this test.
36    let free_memory_frequency = 10;
37    let mut cnt = 0;
38
39    for tile_y in 0..num_tiles_y {
40        for tile_x in 0..num_tiles_x {
41            timer.next();
42            state.canvas.cam_x = (tile_x as f64) * dims.width;
43            state.canvas.cam_y = (tile_y as f64) * dims.height;
44
45            cnt += 1;
46            if cnt % free_memory_frequency == 0 {
47                state.free_memory();
48            }
49
50            let suffix = state.draw(prerender, true).unwrap_or_else(String::new);
51
52            // Sometimes the very first image captured is of the debug mode used to launch this. Not
53            // sure why, and it's not so reproducible. But double-drawing seems to help.
54            if tile_x == 0 && tile_y == 0 {
55                state.draw(prerender, true);
56            }
57
58            let filename = format!(
59                "{}/{:02}x{:02}{}.png",
60                dir_path,
61                tile_x + 1,
62                tile_y + 1,
63                suffix
64            );
65            prerender.inner.screencap(dims, filename.clone())?;
66        }
67    }
68
69    state.canvas.cam_zoom = orig_zoom;
70    state.canvas.cam_x = orig_x;
71    state.canvas.cam_y = orig_y;
72    Ok(())
73}