From 81bd8abdbe41d3acbe1b0d6e30084787f18e871f Mon Sep 17 00:00:00 2001 From: Ivan Smirnov Date: Wed, 5 Jan 2022 17:07:30 +0300 Subject: [PATCH] Add `libqoi` helper crate that wraps qoi.h --- Cargo.toml | 2 +- libqoi/Cargo.toml | 16 +++++++++++++ libqoi/README.md | 2 ++ libqoi/build.rs | 18 +++++++++++++++ libqoi/src/lib.rs | 57 +++++++++++++++++++++++++++++++++++++++++++++++ libqoi/src/qoi.c | 1 + 6 files changed, 95 insertions(+), 1 deletion(-) create mode 100644 libqoi/Cargo.toml create mode 100644 libqoi/README.md create mode 100644 libqoi/build.rs create mode 100644 libqoi/src/lib.rs create mode 100644 libqoi/src/qoi.c diff --git a/Cargo.toml b/Cargo.toml index ee7be95..578fab1 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -26,7 +26,7 @@ reference = [] # follows reference encoder implementation precisely, but may be bytemuck = "1.7" [workspace] -members = ["bench"] +members = ["libqoi", "bench"] [dev-dependencies] anyhow = "1.0" diff --git a/libqoi/Cargo.toml b/libqoi/Cargo.toml new file mode 100644 index 0000000..4388d23 --- /dev/null +++ b/libqoi/Cargo.toml @@ -0,0 +1,16 @@ +[package] +name = "libqoi" +version = "0.1.0" +edition = "2018" +license = "MIT/Apache-2.0" +authors = ["Ivan Smirnov "] +description = "Bindings to the original qoi.h (for test purposes only)" +publish = false + +[dependencies] +anyhow = "1.0" +c_vec = "2.0" +libc = "0.2" + +[build-dependencies] +cc = "1.0" diff --git a/libqoi/README.md b/libqoi/README.md new file mode 100644 index 0000000..e7c8dcb --- /dev/null +++ b/libqoi/README.md @@ -0,0 +1,2 @@ +This crate provides direct bindings around the original `qoi.h`, +and is only used for testing and benchmarking purposes. diff --git a/libqoi/build.rs b/libqoi/build.rs new file mode 100644 index 0000000..f69cb6a --- /dev/null +++ b/libqoi/build.rs @@ -0,0 +1,18 @@ +use std::env; +use std::fs; +use std::path::PathBuf; + +fn main() { + let out_dir = PathBuf::from(env::var("OUT_DIR").unwrap()); + let out_src = out_dir.join("qoi.c"); + fs::write(&out_src, "#include \"qoi.h\"\n").unwrap(); + + cc::Build::new() + .file(&out_src) + .include("../ext/qoi") + .define("QOI_NO_STDIO", None) + .define("QOI_IMPLEMENTATION", None) + .flag("-Wno-unsequenced") + .opt_level(3) + .compile("qoi"); +} diff --git a/libqoi/src/lib.rs b/libqoi/src/lib.rs new file mode 100644 index 0000000..56d94c1 --- /dev/null +++ b/libqoi/src/lib.rs @@ -0,0 +1,57 @@ +use anyhow::{ensure, Result}; +use c_vec::CVec; +use libc::{c_int, c_void}; + +mod ffi { + use libc::{c_int, c_uchar, c_uint, c_void}; + + #[derive(Debug, Copy, Clone, Default)] + #[repr(C)] + #[allow(non_camel_case_types)] + pub struct qoi_desc { + pub width: c_uint, + pub height: c_uint, + pub channels: c_uchar, + pub colorspace: c_uchar, + } + + extern "C" { + pub fn qoi_encode( + data: *const c_void, desc: *const qoi_desc, out_len: *mut c_int, + ) -> *mut c_void; + pub fn qoi_decode( + data: *const c_void, size: c_int, desc: *mut qoi_desc, channels: c_int, + ) -> *mut c_void; + } +} + +pub use ffi::qoi_desc; + +pub fn qoi_encode(data: &[u8], width: u32, height: u32, channels: u8) -> Result> { + let desc = + qoi_desc { width: width as _, height: height as _, channels: channels as _, colorspace: 0 }; + let mut out_len: c_int = 0; + let out_ptr = + unsafe { ffi::qoi_encode(data.as_ptr().cast(), &desc as *const _, &mut out_len as *mut _) }; + ensure!(!out_ptr.is_null(), "qoi.h: qoi_encode() returned null pointer"); + ensure!(out_len > 0, "qoi.h: qoi_encode() returned non-positive out_len"); + let vec = unsafe { + CVec::new_with_dtor(out_ptr.cast::(), out_len as _, |p| libc::free(p.cast::())) + }; + Ok(vec) +} + +pub fn qoi_decode(data: &[u8], channels: u8) -> Result<(qoi_desc, CVec)> { + let mut desc = qoi_desc::default(); + let out_ptr = unsafe { + ffi::qoi_decode(data.as_ptr() as _, data.len() as _, &mut desc as *mut _, channels as _) + }; + ensure!(!out_ptr.is_null(), "qoi.h: qoi_decode() returned null pointer"); + let out_len = (desc.width as usize) + .saturating_mul(desc.height as usize) + .saturating_mul(channels as usize); + let vec = unsafe { + CVec::new_with_dtor(out_ptr.cast::(), out_len, |p| libc::free(p.cast::())) + }; + Ok((desc, vec)) +} diff --git a/libqoi/src/qoi.c b/libqoi/src/qoi.c new file mode 100644 index 0000000..2027fd9 --- /dev/null +++ b/libqoi/src/qoi.c @@ -0,0 +1 @@ +#include "qoi.h"