#![allow(clippy::missing_safety_doc)]
use std::cell::RefCell;
use std::ffi::{CStr, CString, c_char, c_int, c_uint};
use std::path::PathBuf;
use std::ptr;
use std::slice;
use crate::{
EncodeFrame, Encoder, Error, Frame, Image, KnownFormat, Loader, MemoryFormat, SandboxSelector,
Texture,
};
#[repr(C)]
pub struct GlycinNgLoader {
inner: Option<Loader>,
}
#[repr(C)]
pub struct GlycinNgImage {
inner: Image,
}
thread_local! {
static LAST_ERROR: RefCell<Option<CString>> = const { RefCell::new(None) };
}
fn set_error<E: std::fmt::Display>(e: E) {
let msg = CString::new(e.to_string()).unwrap_or_else(|_| CString::new("error").unwrap());
LAST_ERROR.with(|cell| *cell.borrow_mut() = Some(msg));
}
fn clear_error() {
LAST_ERROR.with(|cell| *cell.borrow_mut() = None);
}
#[unsafe(no_mangle)]
pub extern "C" fn glycin_ng_last_error() -> *const c_char {
LAST_ERROR.with(|cell| {
cell.borrow()
.as_ref()
.map(|s| s.as_ptr())
.unwrap_or(ptr::null())
})
}
#[unsafe(no_mangle)]
pub extern "C" fn glycin_ng_clear_last_error() {
clear_error();
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn glycin_ng_loader_free(loader: *mut GlycinNgLoader) {
if !loader.is_null() {
drop(unsafe { Box::from_raw(loader) });
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn glycin_ng_loader_new_path(path: *const c_char) -> *mut GlycinNgLoader {
clear_error();
if path.is_null() {
set_error("path is null");
return ptr::null_mut();
}
let cstr = unsafe { CStr::from_ptr(path) };
let pb = PathBuf::from(cstr.to_string_lossy().as_ref());
let loader = Loader::new_path(pb);
Box::into_raw(Box::new(GlycinNgLoader {
inner: Some(loader),
}))
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn glycin_ng_loader_new_bytes(
data: *const u8,
len: usize,
) -> *mut GlycinNgLoader {
clear_error();
if data.is_null() && len != 0 {
set_error("data is null but len is non-zero");
return ptr::null_mut();
}
let bytes: Vec<u8> = if len == 0 {
Vec::new()
} else {
unsafe { slice::from_raw_parts(data, len) }.to_vec()
};
let loader = Loader::new_bytes(bytes);
Box::into_raw(Box::new(GlycinNgLoader {
inner: Some(loader),
}))
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn glycin_ng_loader_sandbox(
loader: *mut GlycinNgLoader,
landlock: c_int,
seccomp: c_int,
rlimit: c_int,
strict: c_int,
) -> c_int {
clear_error();
let Some(handle) = (unsafe { loader.as_mut() }) else {
set_error("loader is null");
return -1;
};
let Some(inner) = handle.inner.take() else {
set_error("loader has already been consumed");
return -1;
};
let selector = SandboxSelector {
landlock: landlock != 0,
seccomp: seccomp != 0,
rlimit: rlimit != 0,
strict: strict != 0,
};
handle.inner = Some(inner.sandbox_selector(selector));
0
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn glycin_ng_loader_format_hint(
loader: *mut GlycinNgLoader,
format: c_uint,
) -> c_int {
clear_error();
let Some(handle) = (unsafe { loader.as_mut() }) else {
set_error("loader is null");
return -1;
};
let Some(inner) = handle.inner.take() else {
set_error("loader has already been consumed");
return -1;
};
let f = match c_uint_to_format(format) {
Some(f) => f,
None => {
handle.inner = Some(inner);
set_error("unknown format constant");
return -1;
}
};
handle.inner = Some(inner.format_hint(f));
0
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn glycin_ng_loader_apply_transformations(
loader: *mut GlycinNgLoader,
apply: c_int,
) -> c_int {
modify_loader(loader, |l| l.apply_transformations(apply != 0))
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn glycin_ng_loader_render_size_hint(
loader: *mut GlycinNgLoader,
width: u32,
height: u32,
) -> c_int {
modify_loader(loader, |l| l.render_size_hint(width, height))
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn glycin_ng_loader_set_max_width(
loader: *mut GlycinNgLoader,
max_width: u32,
) -> c_int {
modify_loader(loader, |l| {
let mut lim = l.current_limits();
lim.max_width = max_width;
l.limits(lim)
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn glycin_ng_loader_set_max_height(
loader: *mut GlycinNgLoader,
max_height: u32,
) -> c_int {
modify_loader(loader, |l| {
let mut lim = l.current_limits();
lim.max_height = max_height;
l.limits(lim)
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn glycin_ng_loader_set_max_pixels(
loader: *mut GlycinNgLoader,
max_pixels: u64,
) -> c_int {
modify_loader(loader, |l| {
let mut lim = l.current_limits();
lim.max_pixels = max_pixels;
l.limits(lim)
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn glycin_ng_loader_set_max_frames(
loader: *mut GlycinNgLoader,
max_frames: u32,
) -> c_int {
modify_loader(loader, |l| {
let mut lim = l.current_limits();
lim.max_frames = max_frames;
l.limits(lim)
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn glycin_ng_loader_set_max_animation_seconds(
loader: *mut GlycinNgLoader,
seconds: u64,
) -> c_int {
modify_loader(loader, |l| {
let mut lim = l.current_limits();
lim.max_animation_duration = std::time::Duration::from_secs(seconds);
l.limits(lim)
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn glycin_ng_loader_set_decode_memory_mib(
loader: *mut GlycinNgLoader,
mib: u64,
) -> c_int {
modify_loader(loader, |l| {
let mut lim = l.current_limits();
lim.decode_memory_mib = mib;
l.limits(lim)
})
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn glycin_ng_loader_set_decode_cpu_seconds(
loader: *mut GlycinNgLoader,
seconds: u64,
) -> c_int {
modify_loader(loader, |l| {
let mut lim = l.current_limits();
lim.decode_cpu_seconds = seconds;
l.limits(lim)
})
}
fn modify_loader<F: FnOnce(Loader) -> Loader>(loader: *mut GlycinNgLoader, mutate: F) -> c_int {
clear_error();
let Some(handle) = (unsafe { loader.as_mut() }) else {
set_error("loader is null");
return -1;
};
let Some(inner) = handle.inner.take() else {
set_error("loader has already been consumed");
return -1;
};
handle.inner = Some(mutate(inner));
0
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn glycin_ng_loader_load(loader: *mut GlycinNgLoader) -> *mut GlycinNgImage {
clear_error();
if loader.is_null() {
set_error("loader is null");
return ptr::null_mut();
}
let mut handle = unsafe { Box::from_raw(loader) };
let Some(inner) = handle.inner.take() else {
set_error("loader has already been consumed");
return ptr::null_mut();
};
match inner.load() {
Ok(image) => Box::into_raw(Box::new(GlycinNgImage { inner: image })),
Err(e) => {
set_error(e);
ptr::null_mut()
}
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn glycin_ng_image_free(image: *mut GlycinNgImage) {
if !image.is_null() {
drop(unsafe { Box::from_raw(image) });
}
}
fn image_ref<'a>(image: *const GlycinNgImage) -> Option<&'a Image> {
unsafe { image.as_ref() }.map(|h| &h.inner)
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn glycin_ng_image_width(image: *const GlycinNgImage) -> u32 {
image_ref(image).map(|i| i.width()).unwrap_or(0)
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn glycin_ng_image_height(image: *const GlycinNgImage) -> u32 {
image_ref(image).map(|i| i.height()).unwrap_or(0)
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn glycin_ng_image_frame_count(image: *const GlycinNgImage) -> usize {
image_ref(image).map(|i| i.frames().len()).unwrap_or(0)
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn glycin_ng_image_is_animated(image: *const GlycinNgImage) -> c_int {
image_ref(image)
.map(|i| i.is_animated() as c_int)
.unwrap_or(0)
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn glycin_ng_image_orientation(image: *const GlycinNgImage) -> u16 {
image_ref(image)
.map(|i| i.orientation().exif_value())
.unwrap_or(1)
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn glycin_ng_image_format_name(image: *const GlycinNgImage) -> *const c_char {
let Some(img) = image_ref(image) else {
return ptr::null();
};
format_name_cstr(img.format_name())
}
fn format_name_cstr(name: &'static str) -> *const c_char {
const NAMES: &[(&str, &CStr)] = &[
("png", c"png"),
("jpeg", c"jpeg"),
("gif", c"gif"),
("webp", c"webp"),
("tiff", c"tiff"),
("bmp", c"bmp"),
("ico", c"ico"),
("tga", c"tga"),
("qoi", c"qoi"),
("exr", c"exr"),
("pnm", c"pnm"),
("dds", c"dds"),
("jxl", c"jxl"),
("svg", c"svg"),
];
for (rust, cstr) in NAMES {
if *rust == name {
return cstr.as_ptr();
}
}
ptr::null()
}
fn frame_ref<'a>(image: *const GlycinNgImage, index: usize) -> Option<&'a Frame> {
let img = image_ref(image)?;
img.frames().get(index)
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn glycin_ng_image_texture(
image: *const GlycinNgImage,
index: usize,
) -> *const Texture {
match frame_ref(image, index) {
Some(frame) => frame.texture() as *const Texture,
None => ptr::null(),
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn glycin_ng_image_frame_delay_ms(
image: *const GlycinNgImage,
index: usize,
) -> u64 {
frame_ref(image, index)
.and_then(|f| f.delay())
.map(|d| d.as_millis() as u64)
.unwrap_or(0)
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn glycin_ng_texture_width(texture: *const Texture) -> u32 {
unsafe { texture.as_ref() }.map(|t| t.width()).unwrap_or(0)
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn glycin_ng_texture_height(texture: *const Texture) -> u32 {
unsafe { texture.as_ref() }.map(|t| t.height()).unwrap_or(0)
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn glycin_ng_texture_stride(texture: *const Texture) -> u32 {
unsafe { texture.as_ref() }.map(|t| t.stride()).unwrap_or(0)
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn glycin_ng_texture_format(texture: *const Texture) -> c_uint {
let Some(t) = (unsafe { texture.as_ref() }) else {
return GLYCIN_NG_FORMAT_UNKNOWN;
};
memory_format_to_c_uint(t.format())
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn glycin_ng_texture_data(texture: *const Texture) -> *const u8 {
unsafe { texture.as_ref() }
.map(|t| t.data().as_ptr())
.unwrap_or(ptr::null())
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn glycin_ng_texture_data_len(texture: *const Texture) -> usize {
unsafe { texture.as_ref() }
.map(|t| t.data().len())
.unwrap_or(0)
}
pub const GLYCIN_NG_FORMAT_UNKNOWN: c_uint = 0;
pub const GLYCIN_NG_FORMAT_G8: c_uint = 1;
pub const GLYCIN_NG_FORMAT_G8A8: c_uint = 2;
pub const GLYCIN_NG_FORMAT_G8A8_PRE: c_uint = 3;
pub const GLYCIN_NG_FORMAT_G16: c_uint = 4;
pub const GLYCIN_NG_FORMAT_G16A16: c_uint = 5;
pub const GLYCIN_NG_FORMAT_G16A16_PRE: c_uint = 6;
pub const GLYCIN_NG_FORMAT_R8G8B8: c_uint = 10;
pub const GLYCIN_NG_FORMAT_R8G8B8A8: c_uint = 11;
pub const GLYCIN_NG_FORMAT_R8G8B8A8_PRE: c_uint = 12;
pub const GLYCIN_NG_FORMAT_B8G8R8: c_uint = 13;
pub const GLYCIN_NG_FORMAT_B8G8R8A8: c_uint = 14;
pub const GLYCIN_NG_FORMAT_B8G8R8A8_PRE: c_uint = 15;
pub const GLYCIN_NG_FORMAT_A8R8G8B8: c_uint = 16;
pub const GLYCIN_NG_FORMAT_A8R8G8B8_PRE: c_uint = 17;
pub const GLYCIN_NG_FORMAT_A8B8G8R8: c_uint = 18;
pub const GLYCIN_NG_FORMAT_R16G16B16: c_uint = 20;
pub const GLYCIN_NG_FORMAT_R16G16B16A16: c_uint = 21;
pub const GLYCIN_NG_FORMAT_R16G16B16A16_PRE: c_uint = 22;
pub const GLYCIN_NG_FORMAT_R16G16B16_F: c_uint = 23;
pub const GLYCIN_NG_FORMAT_R16G16B16A16_F: c_uint = 24;
pub const GLYCIN_NG_FORMAT_R32G32B32_F: c_uint = 25;
pub const GLYCIN_NG_FORMAT_R32G32B32A32_F: c_uint = 26;
pub const GLYCIN_NG_FORMAT_R32G32B32A32_F_PRE: c_uint = 27;
fn memory_format_to_c_uint(f: MemoryFormat) -> c_uint {
match f {
MemoryFormat::G8 => GLYCIN_NG_FORMAT_G8,
MemoryFormat::G8a8 => GLYCIN_NG_FORMAT_G8A8,
MemoryFormat::G8a8Premultiplied => GLYCIN_NG_FORMAT_G8A8_PRE,
MemoryFormat::G16 => GLYCIN_NG_FORMAT_G16,
MemoryFormat::G16a16 => GLYCIN_NG_FORMAT_G16A16,
MemoryFormat::G16a16Premultiplied => GLYCIN_NG_FORMAT_G16A16_PRE,
MemoryFormat::R8g8b8 => GLYCIN_NG_FORMAT_R8G8B8,
MemoryFormat::R8g8b8a8 => GLYCIN_NG_FORMAT_R8G8B8A8,
MemoryFormat::R8g8b8a8Premultiplied => GLYCIN_NG_FORMAT_R8G8B8A8_PRE,
MemoryFormat::B8g8r8 => GLYCIN_NG_FORMAT_B8G8R8,
MemoryFormat::B8g8r8a8 => GLYCIN_NG_FORMAT_B8G8R8A8,
MemoryFormat::B8g8r8a8Premultiplied => GLYCIN_NG_FORMAT_B8G8R8A8_PRE,
MemoryFormat::A8r8g8b8 => GLYCIN_NG_FORMAT_A8R8G8B8,
MemoryFormat::A8r8g8b8Premultiplied => GLYCIN_NG_FORMAT_A8R8G8B8_PRE,
MemoryFormat::A8b8g8r8 => GLYCIN_NG_FORMAT_A8B8G8R8,
MemoryFormat::R16g16b16 => GLYCIN_NG_FORMAT_R16G16B16,
MemoryFormat::R16g16b16a16 => GLYCIN_NG_FORMAT_R16G16B16A16,
MemoryFormat::R16g16b16a16Premultiplied => GLYCIN_NG_FORMAT_R16G16B16A16_PRE,
MemoryFormat::R16g16b16Float => GLYCIN_NG_FORMAT_R16G16B16_F,
MemoryFormat::R16g16b16a16Float => GLYCIN_NG_FORMAT_R16G16B16A16_F,
MemoryFormat::R32g32b32Float => GLYCIN_NG_FORMAT_R32G32B32_F,
MemoryFormat::R32g32b32a32Float => GLYCIN_NG_FORMAT_R32G32B32A32_F,
MemoryFormat::R32g32b32a32FloatPremultiplied => GLYCIN_NG_FORMAT_R32G32B32A32_F_PRE,
}
}
fn c_uint_to_memory_format(value: c_uint) -> Option<MemoryFormat> {
Some(match value {
GLYCIN_NG_FORMAT_G8 => MemoryFormat::G8,
GLYCIN_NG_FORMAT_G8A8 => MemoryFormat::G8a8,
GLYCIN_NG_FORMAT_G8A8_PRE => MemoryFormat::G8a8Premultiplied,
GLYCIN_NG_FORMAT_G16 => MemoryFormat::G16,
GLYCIN_NG_FORMAT_G16A16 => MemoryFormat::G16a16,
GLYCIN_NG_FORMAT_G16A16_PRE => MemoryFormat::G16a16Premultiplied,
GLYCIN_NG_FORMAT_R8G8B8 => MemoryFormat::R8g8b8,
GLYCIN_NG_FORMAT_R8G8B8A8 => MemoryFormat::R8g8b8a8,
GLYCIN_NG_FORMAT_R8G8B8A8_PRE => MemoryFormat::R8g8b8a8Premultiplied,
GLYCIN_NG_FORMAT_B8G8R8 => MemoryFormat::B8g8r8,
GLYCIN_NG_FORMAT_B8G8R8A8 => MemoryFormat::B8g8r8a8,
GLYCIN_NG_FORMAT_B8G8R8A8_PRE => MemoryFormat::B8g8r8a8Premultiplied,
GLYCIN_NG_FORMAT_A8R8G8B8 => MemoryFormat::A8r8g8b8,
GLYCIN_NG_FORMAT_A8R8G8B8_PRE => MemoryFormat::A8r8g8b8Premultiplied,
GLYCIN_NG_FORMAT_A8B8G8R8 => MemoryFormat::A8b8g8r8,
GLYCIN_NG_FORMAT_R16G16B16 => MemoryFormat::R16g16b16,
GLYCIN_NG_FORMAT_R16G16B16A16 => MemoryFormat::R16g16b16a16,
GLYCIN_NG_FORMAT_R16G16B16A16_PRE => MemoryFormat::R16g16b16a16Premultiplied,
GLYCIN_NG_FORMAT_R16G16B16_F => MemoryFormat::R16g16b16Float,
GLYCIN_NG_FORMAT_R16G16B16A16_F => MemoryFormat::R16g16b16a16Float,
GLYCIN_NG_FORMAT_R32G32B32_F => MemoryFormat::R32g32b32Float,
GLYCIN_NG_FORMAT_R32G32B32A32_F => MemoryFormat::R32g32b32a32Float,
GLYCIN_NG_FORMAT_R32G32B32A32_F_PRE => MemoryFormat::R32g32b32a32FloatPremultiplied,
_ => return None,
})
}
pub const GLYCIN_NG_KFMT_PNG: c_uint = 1;
pub const GLYCIN_NG_KFMT_JPEG: c_uint = 2;
pub const GLYCIN_NG_KFMT_GIF: c_uint = 3;
pub const GLYCIN_NG_KFMT_WEBP: c_uint = 4;
pub const GLYCIN_NG_KFMT_TIFF: c_uint = 5;
pub const GLYCIN_NG_KFMT_BMP: c_uint = 6;
pub const GLYCIN_NG_KFMT_ICO: c_uint = 7;
pub const GLYCIN_NG_KFMT_TGA: c_uint = 8;
pub const GLYCIN_NG_KFMT_QOI: c_uint = 9;
pub const GLYCIN_NG_KFMT_EXR: c_uint = 10;
pub const GLYCIN_NG_KFMT_PNM: c_uint = 11;
pub const GLYCIN_NG_KFMT_DDS: c_uint = 12;
pub const GLYCIN_NG_KFMT_JXL: c_uint = 13;
pub const GLYCIN_NG_KFMT_SVG: c_uint = 14;
fn c_uint_to_format(value: c_uint) -> Option<KnownFormat> {
Some(match value {
GLYCIN_NG_KFMT_PNG => KnownFormat::Png,
GLYCIN_NG_KFMT_JPEG => KnownFormat::Jpeg,
GLYCIN_NG_KFMT_GIF => KnownFormat::Gif,
GLYCIN_NG_KFMT_WEBP => KnownFormat::WebP,
GLYCIN_NG_KFMT_TIFF => KnownFormat::Tiff,
GLYCIN_NG_KFMT_BMP => KnownFormat::Bmp,
GLYCIN_NG_KFMT_ICO => KnownFormat::Ico,
GLYCIN_NG_KFMT_TGA => KnownFormat::Tga,
GLYCIN_NG_KFMT_QOI => KnownFormat::Qoi,
GLYCIN_NG_KFMT_EXR => KnownFormat::Exr,
GLYCIN_NG_KFMT_PNM => KnownFormat::Pnm,
GLYCIN_NG_KFMT_DDS => KnownFormat::Dds,
GLYCIN_NG_KFMT_JXL => KnownFormat::Jxl,
GLYCIN_NG_KFMT_SVG => KnownFormat::Svg,
_ => return None,
})
}
fn format_to_c_uint(f: KnownFormat) -> c_uint {
match f {
KnownFormat::Png => GLYCIN_NG_KFMT_PNG,
KnownFormat::Jpeg => GLYCIN_NG_KFMT_JPEG,
KnownFormat::Gif => GLYCIN_NG_KFMT_GIF,
KnownFormat::WebP => GLYCIN_NG_KFMT_WEBP,
KnownFormat::Tiff => GLYCIN_NG_KFMT_TIFF,
KnownFormat::Bmp => GLYCIN_NG_KFMT_BMP,
KnownFormat::Ico => GLYCIN_NG_KFMT_ICO,
KnownFormat::Tga => GLYCIN_NG_KFMT_TGA,
KnownFormat::Qoi => GLYCIN_NG_KFMT_QOI,
KnownFormat::Exr => GLYCIN_NG_KFMT_EXR,
KnownFormat::Pnm => GLYCIN_NG_KFMT_PNM,
KnownFormat::Dds => GLYCIN_NG_KFMT_DDS,
KnownFormat::Jxl => GLYCIN_NG_KFMT_JXL,
KnownFormat::Svg => GLYCIN_NG_KFMT_SVG,
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn glycin_ng_known_format_from_mime(mime: *const c_char) -> c_uint {
if mime.is_null() {
return 0;
}
let Ok(s) = (unsafe { CStr::from_ptr(mime) }).to_str() else {
return 0;
};
KnownFormat::from_mime_type(s)
.map(format_to_c_uint)
.unwrap_or(0)
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn glycin_ng_known_format_from_extension(ext: *const c_char) -> c_uint {
if ext.is_null() {
return 0;
}
let Ok(s) = (unsafe { CStr::from_ptr(ext) }).to_str() else {
return 0;
};
KnownFormat::from_extension(s)
.map(format_to_c_uint)
.unwrap_or(0)
}
#[repr(C)]
pub struct GlycinNgEncoder {
inner: Encoder,
}
#[repr(C)]
pub struct GlycinNgEncodedImage {
bytes: Vec<u8>,
}
#[unsafe(no_mangle)]
pub extern "C" fn glycin_ng_encoder_new(format: c_uint) -> *mut GlycinNgEncoder {
clear_error();
let Some(target) = c_uint_to_format(format) else {
set_error("unknown known-format constant");
return ptr::null_mut();
};
match Encoder::new(target) {
Ok(enc) => Box::into_raw(Box::new(GlycinNgEncoder { inner: enc })),
Err(e) => {
set_error(e);
ptr::null_mut()
}
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn glycin_ng_encoder_free(encoder: *mut GlycinNgEncoder) {
if !encoder.is_null() {
drop(unsafe { Box::from_raw(encoder) });
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn glycin_ng_encoder_set_quality(encoder: *mut GlycinNgEncoder, quality: u8) {
if let Some(handle) = unsafe { encoder.as_mut() } {
handle.inner.set_quality(quality);
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn glycin_ng_encoder_set_compression(
encoder: *mut GlycinNgEncoder,
compression: u8,
) {
if let Some(handle) = unsafe { encoder.as_mut() } {
handle.inner.set_compression(compression);
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn glycin_ng_encoder_set_icc_profile(
encoder: *mut GlycinNgEncoder,
data: *const u8,
len: usize,
) -> c_int {
clear_error();
let Some(handle) = (unsafe { encoder.as_mut() }) else {
set_error("encoder is null");
return -1;
};
if data.is_null() && len != 0 {
set_error("icc data is null but len is non-zero");
return -1;
}
let icc = if data.is_null() || len == 0 {
None
} else {
Some(unsafe { slice::from_raw_parts(data, len) }.to_vec())
};
handle.inner.set_icc_profile(icc);
0
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn glycin_ng_encoder_add_metadata(
encoder: *mut GlycinNgEncoder,
key: *const c_char,
value: *const c_char,
) -> c_int {
clear_error();
let Some(handle) = (unsafe { encoder.as_mut() }) else {
set_error("encoder is null");
return -1;
};
if key.is_null() || value.is_null() {
set_error("key or value is null");
return -1;
}
let k = match unsafe { CStr::from_ptr(key) }.to_str() {
Ok(s) => s.to_string(),
Err(_) => {
set_error("metadata key is not valid UTF-8");
return -1;
}
};
let v = match unsafe { CStr::from_ptr(value) }.to_str() {
Ok(s) => s.to_string(),
Err(_) => {
set_error("metadata value is not valid UTF-8");
return -1;
}
};
handle.inner.add_metadata(k, v);
0
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn glycin_ng_encoder_add_frame(
encoder: *mut GlycinNgEncoder,
width: u32,
height: u32,
stride: u32,
format: c_uint,
data: *const u8,
data_len: usize,
) -> c_int {
clear_error();
let Some(handle) = (unsafe { encoder.as_mut() }) else {
set_error("encoder is null");
return -1;
};
let Some(mf) = c_uint_to_memory_format(format) else {
set_error("unknown memory-format constant");
return -1;
};
if data.is_null() && data_len != 0 {
set_error("data is null but data_len is non-zero");
return -1;
}
let bytes: Vec<u8> = if data.is_null() || data_len == 0 {
Vec::new()
} else {
unsafe { slice::from_raw_parts(data, data_len) }.to_vec()
};
handle.inner.add_frame(EncodeFrame {
width,
height,
stride,
format: mf,
data: bytes,
});
0
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn glycin_ng_encoder_encode(
encoder: *mut GlycinNgEncoder,
) -> *mut GlycinNgEncodedImage {
clear_error();
let Some(handle) = (unsafe { encoder.as_mut() }) else {
set_error("encoder is null");
return ptr::null_mut();
};
match handle.inner.encode() {
Ok(bytes) => Box::into_raw(Box::new(GlycinNgEncodedImage { bytes })),
Err(e) => {
set_error(e);
ptr::null_mut()
}
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn glycin_ng_encoded_image_free(image: *mut GlycinNgEncodedImage) {
if !image.is_null() {
drop(unsafe { Box::from_raw(image) });
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn glycin_ng_encoded_image_data(
image: *const GlycinNgEncodedImage,
) -> *const u8 {
unsafe { image.as_ref() }
.map(|i| i.bytes.as_ptr())
.unwrap_or(ptr::null())
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn glycin_ng_encoded_image_len(image: *const GlycinNgEncodedImage) -> usize {
unsafe { image.as_ref() }
.map(|i| i.bytes.len())
.unwrap_or(0)
}
#[allow(dead_code)]
fn _dummy(_: &Error) {}