gifski 1.10.0

pngquant-based GIF maker for nice-looking animGIFs
Documentation
// use crate::error::{CatResult, Error};
// use crate::GIFFrame;
// use crate::Settings;
// use crate::{Encoder, Repeat};
// use gifsicle::*;
// use std::io::Write;
// use std::ptr;

// pub(crate) struct Gifsicle<W> {
//     gfs: *mut Gif_Stream,
//     gif_writer: *mut Gif_Writer,
//     out: W,
//     info: Gif_CompressInfo,
// }

// impl<W> Gifsicle<W> {
//     pub fn new(loss: u32, out: W) -> Self {
//         unsafe {
//             let mut g = Self {
//                 gfs: ptr::null_mut(),
//                 gif_writer: ptr::null_mut(),
//                 info: std::mem::zeroed(),
//                 out,
//             };
//             Gif_InitCompressInfo(&mut g.info);
//             g.info.loss = loss as _;
//             g
//         }
//     }
// }

// impl<W: Write> Gifsicle<W> {
//     fn flush_writer(&mut self) -> CatResult<()> {
//         unsafe {
//             if (*self.gif_writer).pos > 0 {
// {                let buf_start = (*self.gif_writer).v.as_mut().ok_or(Error::Gifsicle)?;
//                                 let buf = std::slice::from_raw_parts(buf_start, (*self.gif_writer).pos as usize);
//                                 self.out.write_all(buf)?;
//                 }                (*self.gif_writer).pos = 0;
//             }
//         }
//         Ok(())
//     }
// }

// impl<W> Drop for Gifsicle<W> {
//     fn drop(&mut self) {
//         unsafe {
//             if !self.gif_writer.is_null() {
//                 Gif_IncrementalWriteComplete(self.gif_writer, self.gfs);
//             }
//             Gif_DeleteStream(self.gfs);
//         }
//     }
// }

// impl<W: Write> Encoder for Gifsicle<W> {
//     fn finish(&mut self) -> CatResult<()> {
//         if !self.gif_writer.is_null() {
//             self.flush_writer()?;
//             // fun fact: can't flush after the last write, because the writer gets freed,
//             // but the last write is literally just `;` (we don't use comments/extensions)
//             self.out.write_all(std::slice::from_ref(&b';'))?;
//             unsafe {
//                 Gif_IncrementalWriteComplete(self.gif_writer, self.gfs);
//             }
//             self.gif_writer = ptr::null_mut();
//         }
//         Ok(())
//     }
//     fn write_frame(&mut self, frame: GIFFrame, delay: u16, settings: &Settings) -> CatResult<()> {
//         let GIFFrame {left, top, pal, screen_width, screen_height, image, dispose, transparent_index} = frame;

//         if self.gfs.is_null() {
//             let gfs = unsafe {
//                 self.gfs = gifsicle::Gif_NewStream();
//                 self.gfs.as_mut().ok_or(Error::Gifsicle)?
//             };
//             gfs.screen_width = screen_width;
//             gfs.screen_height = screen_height;
//             // -1 is no looping, 0 is loop forever, else loop X number of times
//             // not sure the else will work.. I need to get gif::Repeat copy-able first to test.
//             gfs.loopcount = match settings.repeat {
//                 Repeat::Finite(0) => -1,
//                 Repeat::Infinite => 0,
//                 Repeat::Finite(x) => i64::from(x),
//             };
//             unsafe {
//                 self.gif_writer = Gif_IncrementalWriteFileInit(gfs, &self.info, ptr::null_mut());
//                 if self.gif_writer.is_null() {
//                     return Err(Error::Gifsicle);
//                 }
//             }
//         }

//         let g = unsafe {
//             Gif_NewImage().as_mut().ok_or(Error::Gifsicle)?
//         };
//         g.top = top;
//         g.left = left;
//         g.delay = delay;
//         g.width = image.width() as u16;
//         g.height = image.height() as u16;
//         g.disposal = match dispose {
//             gif::DisposalMethod::Any => Disposal::None,
//             gif::DisposalMethod::Keep => Disposal::Asis,
//             gif::DisposalMethod::Background => Disposal::Background,
//             gif::DisposalMethod::Previous => Disposal::Previous,
//         } as _;
//         g.transparent = transparent_index.map_or(-1, i16::from);

//         g.local = unsafe { Gif_NewFullColormap(0, pal.len() as _) }; // it's owned by the image
//         for c in &pal {
//             unsafe {
//                 Gif_AddColor(g.local, &mut Gif_Color {
//                     gfc_red: c.r,
//                     gfc_green: c.g,
//                     gfc_blue: c.b,

//                     haspixel: 0, // dunno?
//                     pixel: 0,
//                 }, -1);
//             }
//         }
//         unsafe {
//             if 0 == Gif_SetUncompressedImage(g, image.buf().as_ptr() as *mut u8, None, 0) {
//                 Gif_DeleteImage(g);
//                 return Err(Error::Gifsicle);
//             }
//             let res = Gif_IncrementalWriteImage(self.gif_writer, self.gfs, g);
//             Gif_DeleteImage(g);
//             if 0 == res {
//                 return Err(Error::Gifsicle);
//             }
//             self.flush_writer()?;
//         }
//         Ok(())
//     }
// }