1use rgb::bytemuck;
2use super::Error;
3use crate::disposal::Disposal;
4use imgref::{Img, ImgRef, ImgVec};
5use rgb::{RGB8, RGBA8};
6use std::io;
7
8pub struct Screen {
12 internal_pixels: ImgVec<RGBA8>,
14
15 global_pal: Option<[RGB8; 256]>,
16 next_disposal: Disposal,
17}
18
19impl Screen {
20 #[must_use]
25 pub fn new_decoder<T: io::Read>(reader: &gif::Decoder<T>) -> Self {
26 let w = reader.width();
27 let h = reader.height();
28 let pal = reader.global_palette().map(bytemuck::cast_slice);
29 Self::new(w.into(), h.into(), pal)
30 }
31
32 #[inline]
36 #[must_use]
37 pub fn new(width: usize, height: usize, global_pal: Option<&[RGB8]>) -> Self {
38 Screen {
39 internal_pixels: Img::new(vec![RGBA8::default(); width * height], width, height),
40 global_pal: global_pal.map(|g| std::array::from_fn(move |i| g.get(i).copied().unwrap_or_default())),
41 next_disposal: Disposal::default(),
42 }
43 }
44
45 pub fn blit_frame(&mut self, frame: &gif::Frame<'_>) -> Result<(), Error> {
49 let local_pal = frame.palette.as_deref().map(bytemuck::cast_slice);
50 self.blit(local_pal, frame.dispose,
51 frame.left, frame.top,
52 ImgRef::new(&frame.buffer, frame.width.into(), frame.height.into()), frame.transparent)
53 }
54
55 pub fn blit(&mut self, local_pal: Option<&[RGB8]>, method: gif::DisposalMethod, left: u16, top: u16, buffer: ImgRef<'_, u8>, transparent: Option<u8>) -> Result<(), Error> {
57 self.dispose_only().then_blit(local_pal, method, left, top, buffer, transparent)?;
58 Ok(())
59 }
60
61 fn blit_without_dispose(&mut self, local_pal: Option<&[RGB8]>, method: gif::DisposalMethod, left: u16, top: u16, buffer: ImgRef<'_, u8>, transparent: Option<u8>) -> Result<(), Error> {
62 self.next_disposal = Disposal::new(method, left, top, buffer.width() as u16, buffer.height() as u16, self.internal_pixels.as_ref());
63
64 let pal_slice = local_pal.or(self.global_pal.as_ref().map(|p| &p[..])).ok_or(Error::NoPalette)?;
65 let pal: [_; 256] = std::array::from_fn(|i| {
66 pal_slice.get(i).copied().unwrap_or_default()
67 });
68
69 for (dst, src) in self.internal_pixels.sub_image_mut(left.into(), top.into(), buffer.width(), buffer.height()).pixels_mut().zip(buffer.pixels()) {
70 if Some(src) == transparent {
71 continue;
72 }
73 *dst = pal[src as usize].with_alpha(255);
74 }
75 Ok(())
76 }
77
78 #[inline(always)]
80 pub fn pixels_rgba(&mut self) -> ImgRef<'_, RGBA8> {
81 self.internal_pixels.as_ref()
82 }
83
84 #[deprecated(note = "use pixels_rgba() instead. This method will return a different type in the next version")]
86 pub fn pixels(&mut self) -> ImgRef<'_, RGBA8> {
87 self.pixels_rgba()
88 }
89
90 #[inline]
110 pub fn dispose_only(&mut self) -> TempDisposedStateScreen<'_> {
111 self.next_disposal.dispose(self.internal_pixels.as_mut());
112 TempDisposedStateScreen(self)
113 }
114
115 #[must_use]
116 pub fn width(&self) -> usize {
117 self.internal_pixels.width()
118 }
119
120 #[must_use]
121 pub fn height(&self) -> usize {
122 self.internal_pixels.height()
123 }
124}
125
126#[must_use]
128pub struct TempDisposedStateScreen<'screen>(&'screen mut Screen);
129
130impl Drop for TempDisposedStateScreen<'_> {
132 fn drop(&mut self) {
133 }
134}
135
136impl<'s, > TempDisposedStateScreen<'s> {
137 #[inline(always)]
138 pub fn then_blit(self, local_pal: Option<&[RGB8]>, method: gif::DisposalMethod, left: u16, top: u16, buffer: ImgRef<'_, u8>, transparent: Option<u8>) -> Result<(), Error> {
139 self.0.blit_without_dispose(local_pal, method, left, top, buffer, transparent)
140 }
141
142 #[inline(always)]
144 pub fn pixels_rgba(&mut self) -> ImgRef<'_, RGBA8> {
145 self.0.internal_pixels.as_ref()
146 }
147
148 #[deprecated(note = "use pixels_rgba() instead. This method will return a different type in the next version")]
150 pub fn pixels(&mut self) -> ImgRef<'_, RGBA8> {
151 self.pixels_rgba()
152 }
153}