use super::Error;
use crate::disposal::Disposal;
use imgref::{Img, ImgRef, ImgVec};
use rgb::FromSlice;
use rgb::{RGB8, RGBA8};
use std::io;
pub struct Screen {
internal_pixels: ImgVec<RGBA8>,
global_pal: Option<[RGB8; 256]>,
next_disposal: Disposal,
}
impl Screen {
#[must_use]
pub fn new_decoder<T: io::Read>(reader: &gif::Decoder<T>) -> Self {
let w = reader.width();
let h = reader.height();
let pal = reader.global_palette().map(|pal| pal.as_rgb());
Self::new(w.into(), h.into(), pal)
}
#[inline]
#[must_use]
pub fn new(width: usize, height: usize, global_pal: Option<&[RGB8]>) -> Self {
Screen {
internal_pixels: Img::new(vec![RGBA8::default(); width * height], width, height),
global_pal: global_pal.map(|g| std::array::from_fn(move |i| g.get(i).copied().unwrap_or_default())),
next_disposal: Disposal::default(),
}
}
pub fn blit_frame(&mut self, frame: &gif::Frame<'_>) -> Result<(), Error> {
let local_pal = frame.palette.as_deref().map(|p| p.as_rgb());
self.blit(local_pal.map(|p| &p[..]), frame.dispose,
frame.left, frame.top,
ImgRef::new(&frame.buffer, frame.width.into(), frame.height.into()), frame.transparent)
}
pub fn blit(&mut self, local_pal: Option<&[RGB8]>, method: gif::DisposalMethod, left: u16, top: u16, buffer: ImgRef<'_, u8>, transparent: Option<u8>) -> Result<(), Error> {
self.dispose_only().then_blit(local_pal, method, left, top, buffer, transparent)?;
Ok(())
}
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> {
self.next_disposal = Disposal::new(method, left, top, buffer.width() as u16, buffer.height() as u16, self.internal_pixels.as_ref());
let pal_slice = local_pal.or(self.global_pal.as_ref().map(|p| &p[..])).ok_or(Error::NoPalette)?;
let pal: [_; 256] = std::array::from_fn(|i| {
pal_slice.get(i).copied().unwrap_or_default()
});
for (dst, src) in self.internal_pixels.sub_image_mut(left.into(), top.into(), buffer.width(), buffer.height()).pixels_mut().zip(buffer.pixels()) {
if Some(src) == transparent {
continue;
}
*dst = pal[src as usize].alpha(255);
}
Ok(())
}
#[inline(always)]
pub fn pixels_rgba(&mut self) -> ImgRef<'_, RGBA8> {
self.internal_pixels.as_ref()
}
#[deprecated(note = "use pixels_rgba() instead. This method will return a different type in the next version")]
pub fn pixels(&mut self) -> ImgRef<'_, RGBA8> {
self.pixels_rgba()
}
#[inline]
pub fn dispose_only(&mut self) -> TempDisposedStateScreen<'_> {
self.next_disposal.dispose(self.internal_pixels.as_mut());
TempDisposedStateScreen(self)
}
#[must_use]
pub fn width(&self) -> usize {
self.internal_pixels.width()
}
#[must_use]
pub fn height(&self) -> usize {
self.internal_pixels.height()
}
}
#[must_use]
pub struct TempDisposedStateScreen<'screen>(&'screen mut Screen);
impl Drop for TempDisposedStateScreen<'_> {
fn drop(&mut self) {
}
}
impl<'s, > TempDisposedStateScreen<'s> {
#[inline(always)]
pub fn then_blit(self, local_pal: Option<&[RGB8]>, method: gif::DisposalMethod, left: u16, top: u16, buffer: ImgRef<'_, u8>, transparent: Option<u8>) -> Result<(), Error> {
self.0.blit_without_dispose(local_pal, method, left, top, buffer, transparent)
}
#[inline(always)]
pub fn pixels_rgba(&mut self) -> ImgRef<'_, RGBA8> {
self.0.internal_pixels.as_ref()
}
#[deprecated(note = "use pixels_rgba() instead. This method will return a different type in the next version")]
pub fn pixels(&mut self) -> ImgRef<'_, RGBA8> {
self.pixels_rgba()
}
}