use super::*;
use std::os::raw::{c_char, c_int, c_void};
use std::ptr;
use std::mem;
use std::slice;
use std::fs;
use std::io;
use std::sync::Arc;
use std::sync::Mutex;
use std::fs::File;
use std::ffi::CStr;
use std::path::{PathBuf, Path};
#[repr(C)]
#[derive(Copy, Clone)]
pub struct GifskiSettings {
pub width: u32,
pub height: u32,
pub quality: u8,
pub once: bool,
pub fast: bool,
}
#[repr(C)]
#[derive(Copy, Clone)]
pub struct ARGB8 {
pub a: u8,
pub r: u8,
pub g: u8,
pub b: u8,
}
pub struct GifskiHandle {
writer: Mutex<Option<Writer>>,
collector: Mutex<Option<Collector>>,
progress: Mutex<Option<ProgressCallback>>,
}
#[repr(C)]
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
#[allow(non_camel_case_types)]
pub enum GifskiError {
OK = 0,
NULL_ARG,
INVALID_STATE,
QUANT,
GIF,
THREAD_LOST,
NOT_FOUND,
PERMISSION_DENIED,
ALREADY_EXISTS,
INVALID_INPUT,
TIMED_OUT,
WRITE_ZERO,
INTERRUPTED,
UNEXPECTED_EOF,
ABORTED,
OTHER,
}
impl From<CatResult<()>> for GifskiError {
fn from(res: CatResult<()>) -> Self {
use crate::error::ErrorKind::*;
match res {
Ok(_) => GifskiError::OK,
Err(err) => match *err.kind() {
Quant(_) => GifskiError::QUANT,
Pal(_) => GifskiError::GIF,
ThreadSend => GifskiError::THREAD_LOST,
Io(ref err) => err.kind().into(),
_ => GifskiError::OTHER,
},
}
}
}
impl From<io::ErrorKind> for GifskiError {
fn from(res: io::ErrorKind) -> Self {
use std::io::ErrorKind as EK;
match res {
EK::NotFound => GifskiError::NOT_FOUND,
EK::PermissionDenied => GifskiError::PERMISSION_DENIED,
EK::AlreadyExists => GifskiError::ALREADY_EXISTS,
EK::InvalidInput | EK::InvalidData => GifskiError::INVALID_INPUT,
EK::TimedOut => GifskiError::TIMED_OUT,
EK::WriteZero => GifskiError::WRITE_ZERO,
EK::Interrupted => GifskiError::INTERRUPTED,
EK::UnexpectedEof => GifskiError::UNEXPECTED_EOF,
_ => GifskiError::OTHER,
}
}
}
#[no_mangle]
pub extern "C" fn gifski_new(settings: *const GifskiSettings) -> *const GifskiHandle {
let settings = unsafe {if let Some(s) = settings.as_ref() {s} else {
return ptr::null_mut();
}};
let s = Settings {
width: if settings.width > 0 {Some(settings.width)} else {None},
height: if settings.height > 0 {Some(settings.height)} else {None},
quality: settings.quality,
once: settings.once,
fast: settings.fast,
};
if let Ok((collector, writer)) = new(s) {
Arc::into_raw(Arc::new(GifskiHandle {
writer: Mutex::new(Some(writer)),
collector: Mutex::new(Some(collector)),
progress: Mutex::new(None),
}))
} else {
ptr::null_mut()
}
}
#[no_mangle]
pub extern "C" fn gifski_add_frame_png_file(handle: *const GifskiHandle, index: u32, file_path: *const c_char, delay: u16) -> GifskiError {
if file_path.is_null() {
return GifskiError::NULL_ARG;
}
let g = match unsafe { handle.as_ref() } {
Some(g) => g,
None => return GifskiError::NULL_ARG,
};
let path = if let Ok(s) = unsafe { CStr::from_ptr(file_path).to_str() } {
PathBuf::from(s)
} else {
return GifskiError::INVALID_INPUT;
};
if let Some(ref mut c) = *g.collector.lock().unwrap() {
c.add_frame_png_file(index as usize, path, delay).into()
} else {
eprintln!("frames can't be added any more, because gifski_end_adding_frames has been called already");
GifskiError::INVALID_STATE
}
}
#[no_mangle]
pub extern "C" fn gifski_add_frame_rgba(handle: *const GifskiHandle, index: u32, width: u32, height: u32, pixels: *const RGBA8, delay: u16) -> GifskiError {
if pixels.is_null() {
return GifskiError::NULL_ARG;
}
let pixels = unsafe {
slice::from_raw_parts(pixels, width as usize * height as usize)
};
add_frame_rgba(handle, index, ImgVec::new(pixels.to_owned(), width as usize, height as usize), delay)
}
fn add_frame_rgba(handle: *const GifskiHandle, index: u32, frame: ImgVec<RGBA8>, delay: u16) -> GifskiError {
let g = match unsafe { handle.as_ref() } {
Some(g) => g,
None => return GifskiError::NULL_ARG,
};
if let Some(ref mut c) = *g.collector.lock().unwrap() {
c.add_frame_rgba(index as usize, frame, delay).into()
} else {
eprintln!("frames can't be added any more, because gifski_end_adding_frames has been called already");
GifskiError::INVALID_STATE
}
}
#[no_mangle]
pub extern "C" fn gifski_add_frame_argb(handle: *const GifskiHandle, index: u32, width: u32, bytes_per_row: u32, height: u32, pixels: *const ARGB8, delay: u16) -> GifskiError {
if pixels.is_null() {
return GifskiError::NULL_ARG;
}
let width = width as usize;
let stride = bytes_per_row as usize / mem::size_of_val(unsafe{&*pixels});
if stride < width {
return GifskiError::INVALID_INPUT;
}
let pixels = unsafe {
slice::from_raw_parts(pixels, stride * height as usize)
};
add_frame_rgba(handle, index, ImgVec::new(pixels.chunks(stride).flat_map(|r| r[0..width].iter().map(|p| RGBA8 {
r: p.r,
g: p.g,
b: p.b,
a: p.a,
})).collect(), width as usize, height as usize), delay)
}
#[no_mangle]
pub extern "C" fn gifski_add_frame_rgb(handle: *const GifskiHandle, index: u32, width: u32, bytes_per_row: u32, height: u32, pixels: *const RGB8, delay: u16) -> GifskiError {
if pixels.is_null() {
return GifskiError::NULL_ARG;
}
let width = width as usize;
let stride = bytes_per_row as usize / mem::size_of_val(unsafe{&*pixels});
if stride < width {
return GifskiError::INVALID_INPUT;
}
let pixels = unsafe {
slice::from_raw_parts(pixels, stride * height as usize)
};
add_frame_rgba(handle, index, ImgVec::new(pixels.chunks(stride).flat_map(|r| r[0..width].iter().map(|&p| p.into())).collect(), width as usize, height as usize), delay)
}
#[no_mangle]
pub extern "C" fn gifski_end_adding_frames(handle: *const GifskiHandle) -> GifskiError {
let g = match unsafe { handle.as_ref() } {
Some(g) => g,
None => return GifskiError::NULL_ARG,
};
match g.collector.lock().unwrap().take() {
Some(_) => GifskiError::OK,
None => {
eprintln!("gifski_end_adding_frames has been called already");
GifskiError::INVALID_STATE
},
}
}
#[no_mangle]
pub extern "C" fn gifski_set_progress_callback(handle: *const GifskiHandle, cb: unsafe fn(*mut c_void) -> c_int, user_data: *mut c_void) {
let g = match unsafe { handle.as_ref() } {
Some(g) => g,
None => return,
};
*g.progress.lock().unwrap() = Some(ProgressCallback::new(cb, user_data));
}
#[no_mangle]
pub extern "C" fn gifski_write(handle: *const GifskiHandle, destination: *const c_char) -> GifskiError {
if destination.is_null() {
return GifskiError::NULL_ARG;
}
let g = match unsafe { handle.as_ref() } {
Some(g) => g,
None => return GifskiError::NULL_ARG,
};
let path = if let Ok(s) = unsafe { CStr::from_ptr(destination).to_str() } {
Path::new(s)
} else {
return GifskiError::INVALID_INPUT;
};
match File::create(path) {
Ok(file) => {
if let Some(writer) = g.writer.lock().unwrap().take() {
let mut cb;
let mut progress: &mut dyn ProgressReporter = &mut NoProgress {};
if let Some(tmp) = g.progress.lock().unwrap().take() {
cb = tmp;
progress = &mut cb;
}
match writer.write(file, progress).into() {
res @ GifskiError::OK |
res @ GifskiError::ALREADY_EXISTS => res,
err => {
let _ = fs::remove_file(path); err
},
}
} else {
eprintln!("gifski_write has been called already");
GifskiError::INVALID_STATE
}
},
Err(err) => err.kind().into(),
}
}
#[no_mangle]
pub extern "C" fn gifski_drop(g: *const GifskiHandle) {
if !g.is_null() {
unsafe {
Arc::from_raw(g);
}
}
}
#[test]
fn c() {
let g = gifski_new(&GifskiSettings {
width: 0, height: 0,
quality: 100,
once: false,
fast: true,
});
let rgb: *const RGB8 = ptr::null();
assert_eq!(3, mem::size_of_val(unsafe{&*rgb}));
assert!(!g.is_null());
assert_eq!(GifskiError::NULL_ARG, gifski_add_frame_rgba(g, 0, 1, 1, ptr::null(), 5));
fn cb(_: *mut c_void) -> c_int {
1
}
gifski_set_progress_callback(g, cb, ptr::null_mut());
assert_eq!(GifskiError::OK, gifski_add_frame_rgba(g, 0, 1, 1, &RGBA::new(0,0,0,0), 5));
assert_eq!(GifskiError::OK, gifski_add_frame_rgb(g, 1, 1, 3, 1, &RGB::new(0,0,0), 5));
assert_eq!(GifskiError::OK, gifski_end_adding_frames(g));
assert_eq!(GifskiError::INVALID_STATE, gifski_end_adding_frames(g));
gifski_drop(g);
}