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 gif;
use disposal::Disposal;
use rgb::*;
use subimage::Subimage;
use std::io;
use std::error::Error;
pub struct Screen {
pub pixels: Vec<RGBA8>,
pub width: usize,
pub height: usize,
global_pal: Option<Vec<RGBA8>>,
bg_color: RGBA8,
disposal: Disposal<RGBA8>,
}
impl Screen {
pub fn new<T: io::Read>(reader: &gif::Reader<T>) -> Self {
let pal = reader.global_palette().map(|palette_bytes| to_rgba(palette_bytes));
let pixels = reader.width() as usize * reader.height() as usize;
let bg_color = if let (Some(bg_index), Some(pal)) = (reader.bg_color(), pal.as_ref()) {
pal[bg_index]
} else {
RGBA8::new(0,0,0,0)
};
Screen {
pixels: vec![bg_color; pixels],
width: reader.width() as usize,
height: reader.height() as usize,
global_pal: pal,
bg_color: bg_color,
disposal: Disposal::default(),
}
}
pub fn blit(&mut self, frame: &gif::Frame) -> Result<(), Box<Error>> {
let local_pal : Option<Vec<_>> = frame.palette.as_ref().map(|bytes| to_rgba(bytes));
let pal = local_pal.as_ref().or(self.global_pal.as_ref()).ok_or("the frame must have _some_ palette")?;
self.disposal.dispose(&mut self.pixels, self.width, self.bg_color);
self.disposal = Disposal::new(&frame, &self.pixels, self.width);
for (dst, &src) in self.pixels.iter_mut().subimage(frame.left as usize, frame.top as usize, frame.width as usize, frame.height as usize, self.width).zip(frame.buffer.iter()) {
if let Some(transparent) = frame.transparent {
if src == transparent {
continue;
}
}
*dst = pal[src as usize];
}
Ok(())
}
}
fn to_rgba(palette_bytes: &[u8]) -> Vec<RGBA8> {
palette_bytes.chunks(3).map(|byte| RGBA8{r:byte[0], g:byte[1], b:byte[2], a:255}).collect()
}