1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
use abstutil::Timer;

use crate::runner::State;
use crate::{Prerender, ScreenDims, SharedAppState};

/// Take a screenshot of the entire canvas, tiling it based on the window's width and height.
pub(crate) fn screenshot_everything<A: 'static + SharedAppState>(
    state: &mut State<A>,
    dir_path: &str,
    prerender: &Prerender,
    zoom: f64,
    dims: ScreenDims,
) -> anyhow::Result<()> {
    if dims.width > state.canvas.window_width || dims.height > state.canvas.window_height {
        bail!(
            "Can't take screenshots of dims {:?} when the window is only {:?}",
            dims,
            state.canvas.get_window_dims()
        );
    }

    let mut timer = Timer::new("capturing screen");
    let num_tiles_x = (state.canvas.map_dims.0 * zoom / dims.width).ceil() as usize;
    let num_tiles_y = (state.canvas.map_dims.1 * zoom / dims.height).ceil() as usize;
    let orig_zoom = state.canvas.cam_zoom;
    let orig_x = state.canvas.cam_x;
    let orig_y = state.canvas.cam_y;

    timer.start_iter("capturing images", num_tiles_x * num_tiles_y);
    state.canvas.cam_zoom = zoom;
    fs_err::create_dir_all(dir_path)?;

    // See https://github.com/a-b-street/abstreet/issues/671 for context. Some maps are so large
    // and detailed that they drain all memory on some video cards. As a workaround, just free up
    // some of the objects in the middle of this test.
    let free_memory_frequency = 10;
    let mut cnt = 0;

    for tile_y in 0..num_tiles_y {
        for tile_x in 0..num_tiles_x {
            timer.next();
            state.canvas.cam_x = (tile_x as f64) * dims.width;
            state.canvas.cam_y = (tile_y as f64) * dims.height;

            cnt += 1;
            if cnt % free_memory_frequency == 0 {
                state.free_memory();
            }

            let suffix = state.draw(prerender, true).unwrap_or_else(String::new);

            // Sometimes the very first image captured is of the debug mode used to launch this. Not
            // sure why, and it's not so reproducible. But double-drawing seems to help.
            if tile_x == 0 && tile_y == 0 {
                state.draw(prerender, true);
            }

            let filename = format!(
                "{}/{:02}x{:02}{}.png",
                dir_path,
                tile_x + 1,
                tile_y + 1,
                suffix
            );
            prerender.inner.screencap(dims, filename.clone())?;
        }
    }

    state.canvas.cam_zoom = orig_zoom;
    state.canvas.cam_x = orig_x;
    state.canvas.cam_y = orig_y;
    Ok(())
}