use core::fmt;
use std::{
ffi::{c_int, c_uchar, c_void},
mem, ptr, slice,
string::FromUtf8Error,
};
pub use sixel_sys::status;
pub use sixel_sys::status::Status;
pub use sixel_sys::DiffusionMethod;
pub use sixel_sys::PixelFormat;
use sixel_sys::{
sixel_dither_destroy, sixel_dither_initialize, sixel_dither_new,
sixel_dither_set_diffusion_type, sixel_dither_set_pixelformat, sixel_encode,
sixel_output_destroy, sixel_output_new, sixel_output_set_encode_policy, Dither, EncodePolicy,
MethodForLargest, Output,
};
#[derive(Debug)]
pub enum SixelError {
Sixel(Status),
Utf8(FromUtf8Error),
}
impl fmt::Display for SixelError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
SixelError::Sixel(status) => write!(f, "Sixel error code {0}", status),
SixelError::Utf8(utf8_error) => utf8_error.fmt(f),
}
}
}
impl SixelError {
pub fn from_status(value: c_int) -> Result<(), Self> {
match value {
status::OK => Ok(()),
code => Err(SixelError::Sixel(code)),
}
}
}
const DEPTH_ALWAYS_IGNORED: i32 = 24;
pub fn sixel_string(
bytes: &[u8],
width: i32,
height: i32,
pixelformat: PixelFormat,
method_for_diffuse: DiffusionMethod,
) -> Result<String, SixelError> {
let mut sixel_data: Vec<i8> = Vec::new();
let sixel_data_ptr: *mut c_void = &mut sixel_data as *mut _ as *mut c_void;
let mut output: *mut Output = ptr::null_mut() as *mut _;
let output_ptr: *mut *mut Output = &mut output as *mut _;
let mut dither: *mut Dither = ptr::null_mut() as *mut _;
let dither_ptr: *mut *mut Dither = &mut dither as *mut _;
let pixels = bytes.as_ptr() as *mut c_uchar;
unsafe extern "C" fn callback(
data: *mut ::std::os::raw::c_char,
size: ::std::os::raw::c_int,
priv_: *mut ::std::os::raw::c_void,
) -> ::std::os::raw::c_int {
let sixel_data: &mut Vec<i8> = &mut *(priv_ as *mut Vec<i8>);
let data_slice: &mut [i8] =
slice::from_raw_parts_mut(if data.is_null() { return 1 } else { data }, size as usize);
sixel_data.append(&mut data_slice.to_vec());
status::OK
}
unsafe {
SixelError::from_status(sixel_output_new(
output_ptr,
Some(callback),
sixel_data_ptr,
ptr::null_mut(),
))?;
sixel_output_set_encode_policy(output, EncodePolicy::Auto);
SixelError::from_status(sixel_dither_new(dither_ptr, 256, ptr::null_mut()))?;
SixelError::from_status(sixel_dither_initialize(
dither,
pixels,
width,
height,
pixelformat,
MethodForLargest::Auto,
sixel_sys::MethodForRepColor::Auto,
sixel_sys::QualityMode::Auto,
))?;
sixel_dither_set_pixelformat(dither, pixelformat);
sixel_dither_set_diffusion_type(dither, method_for_diffuse);
SixelError::from_status(sixel_encode(
pixels,
width,
height,
DEPTH_ALWAYS_IGNORED,
dither,
output,
))?;
sixel_output_destroy(output);
sixel_dither_destroy(dither);
String::from_utf8(mem::transmute(sixel_data)).map_err(SixelError::Utf8)
}
}