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
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
use crate::{
    image::{Image, Rect},
    PresentInput,
};

impl<'t> PresentInput<'t> {
    pub fn rect(&self) -> Rect {
        Rect {
            x: 0,
            y: 0,
            width: self.width,
            height: self.height,
        }
    }

    pub fn new_image(&self) -> Image {
        Image::new(self.width, self.height)
    }

    /// Blit the an area of the source image to the screen.
    ///
    /// The source rectangle is clipped to the source image.
    ///
    /// # Arguments
    ///
    /// * `dst_rect` - Where to blit the source image to on the screen.
    /// * `src_rect` - The area of the source image to blit.
    /// * `src_image` - The source image to blit from.
    /// * `paper` - The paper colour to use outside the source image.  This will
    ///   also be used as the ink colour.
    ///
    pub fn blit(&mut self, mut dst_rect: Rect, src_rect: Rect, src_image: &Image, paper: u32) {
        assert_eq!(dst_rect.width, src_rect.width);
        assert_eq!(dst_rect.height, src_rect.height);
        assert!(dst_rect.x >= 0 && dst_rect.y >= 0);
        assert!(dst_rect.x + dst_rect.width as i32 <= self.width as i32);
        assert!(dst_rect.y + dst_rect.height as i32 <= self.height as i32);

        // Clip the source rectangle to the source image and adjust the
        // destination rectangle accordingly.
        let (src_rect, src_offset) = src_rect.clip_within(src_image.width, src_image.height);
        dst_rect.width = src_rect.width;
        dst_rect.height = src_rect.height;

        if src_rect.width == 0 || src_rect.height == 0 {
            // Nothing to blit, so clear it
            self.clear(dst_rect, paper);
        } else {
            // Clear the top-left corner of the destination rectangle according
            // to any offset in the source rectangle.
            self.clear(
                Rect {
                    x: dst_rect.x,
                    y: dst_rect.y,
                    width: src_offset.x as u32,
                    height: src_offset.y as u32,
                },
                paper,
            );

            // Clear the top-right corner of the destination rectangle according
            // to any offset in the source rectangle.
            self.clear(
                Rect {
                    x: dst_rect.x + src_offset.x,
                    y: dst_rect.y,
                    width: dst_rect.width - src_offset.x as u32,
                    height: src_offset.y as u32,
                },
                paper,
            );

            // Clear the bottom-left corner of the destination rectangle according
            // to any offset in the source rectangle.
            self.clear(
                Rect {
                    x: dst_rect.x,
                    y: dst_rect.y + src_offset.y,
                    width: src_offset.x as u32,
                    height: dst_rect.height - src_offset.y as u32,
                },
                paper,
            );

            // Blit the image to the screen.
            self.blit_internal(
                Rect {
                    x: dst_rect.x + src_offset.x,
                    y: dst_rect.y + src_offset.y,
                    width: dst_rect.width - src_offset.x as u32,
                    height: dst_rect.height - src_offset.y as u32,
                },
                src_rect,
                src_image,
            );
        }
    }

    pub fn clear(&mut self, rect: Rect, paper: u32) {
        assert!(rect.x >= 0 && rect.y >= 0);
        assert!(rect.x + rect.width as i32 <= self.width as i32);
        assert!(rect.y + rect.height as i32 <= self.height as i32);

        let mut i = rect.y as usize * self.width as usize + rect.x as usize;
        for _ in 0..rect.height {
            self.fore_image[i..i + rect.width as usize].fill(paper);
            self.back_image[i..i + rect.width as usize].fill(paper);
            self.text_image[i..i + rect.width as usize].fill(0);
            i += self.width as usize;
        }
    }

    pub fn blit_internal(&mut self, dst_rect: Rect, src_rect: Rect, src_image: &Image) {
        assert_eq!(dst_rect.width, src_rect.width);
        assert_eq!(dst_rect.height, src_rect.height);
        assert!(dst_rect.x >= 0 && dst_rect.y >= 0);
        assert!(dst_rect.x + dst_rect.width as i32 <= self.width as i32);
        assert!(dst_rect.y + dst_rect.height as i32 <= self.height as i32);

        let mut dst_i = dst_rect.y as usize * self.width as usize + dst_rect.x as usize;
        let mut src_i = src_rect.y as usize * src_image.width as usize + src_rect.x as usize;
        for _ in 0..dst_rect.height {
            self.fore_image[dst_i..dst_i + dst_rect.width as usize]
                .copy_from_slice(&src_image.fore_image[src_i..src_i + dst_rect.width as usize]);
            self.back_image[dst_i..dst_i + dst_rect.width as usize]
                .copy_from_slice(&src_image.back_image[src_i..src_i + dst_rect.width as usize]);
            self.text_image[dst_i..dst_i + dst_rect.width as usize]
                .copy_from_slice(&src_image.text_image[src_i..src_i + dst_rect.width as usize]);
            dst_i += self.width as usize;
            src_i += src_image.width as usize;
        }
    }
}