#![warn(missing_docs)]
use std::{convert::TryInto, mem::MaybeUninit, ops::Deref, ops::DerefMut, ptr::NonNull};
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub enum Error {
BadBlockSize,
BadContext,
BadCpuFloat,
BadCpuIsa,
BadFlags,
BadParam,
BadPreset,
BadProfile,
BadSwizzle,
NotImplemented,
OutOfMem,
Unknown,
}
fn error_code_to_result(code: astcenc_sys::astcenc_error) -> Result<(), Error> {
match code {
astcenc_sys::astcenc_error_ASTCENC_SUCCESS => Ok(()),
astcenc_sys::astcenc_error_ASTCENC_ERR_BAD_BLOCK_SIZE => Err(Error::BadBlockSize),
astcenc_sys::astcenc_error_ASTCENC_ERR_BAD_CONTEXT => Err(Error::BadContext),
astcenc_sys::astcenc_error_ASTCENC_ERR_BAD_CPU_FLOAT => Err(Error::BadCpuFloat),
astcenc_sys::astcenc_error_ASTCENC_ERR_BAD_CPU_ISA => Err(Error::BadCpuIsa),
astcenc_sys::astcenc_error_ASTCENC_ERR_BAD_FLAGS => Err(Error::BadFlags),
astcenc_sys::astcenc_error_ASTCENC_ERR_BAD_PARAM => Err(Error::BadParam),
astcenc_sys::astcenc_error_ASTCENC_ERR_BAD_PRESET => Err(Error::BadPreset),
astcenc_sys::astcenc_error_ASTCENC_ERR_BAD_PROFILE => Err(Error::BadProfile),
astcenc_sys::astcenc_error_ASTCENC_ERR_BAD_SWIZZLE => Err(Error::BadSwizzle),
astcenc_sys::astcenc_error_ASTCENC_ERR_NOT_IMPLEMENTED => Err(Error::NotImplemented),
astcenc_sys::astcenc_error_ASTCENC_ERR_OUT_OF_MEM => Err(Error::OutOfMem),
_ => Err(Error::Unknown),
}
}
pub struct Context {
inner: NonNull<astcenc_sys::astcenc_context>,
config: Config,
}
unsafe impl Sync for Context {}
unsafe impl Send for Context {}
impl Default for Context {
fn default() -> Self {
Self::new(Config::default()).unwrap()
}
}
#[derive(Default, Copy, Clone, PartialEq, Eq, Hash)]
pub struct Extents {
pub x: u32,
pub y: u32,
pub z: u32,
}
impl Extents {
pub fn default_block_size() -> Self {
Self::new(4, 4)
}
pub fn new(x: u32, y: u32) -> Self {
Self { x, y, z: 1 }
}
pub fn new_3d(x: u32, y: u32, z: u32) -> Self {
Self { x, y, z }
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum Preset {
Fast,
Medium,
Thorough,
Exhaustive,
}
impl Default for Preset {
fn default() -> Self {
Self::Medium
}
}
impl Preset {
fn into_sys(self) -> astcenc_sys::astcenc_preset {
match self {
Preset::Fast => astcenc_sys::astcenc_preset_ASTCENC_PRE_FAST,
Preset::Medium => astcenc_sys::astcenc_preset_ASTCENC_PRE_MEDIUM,
Preset::Thorough => astcenc_sys::astcenc_preset_ASTCENC_PRE_THOROUGH,
Preset::Exhaustive => astcenc_sys::astcenc_preset_ASTCENC_PRE_EXHAUSTIVE,
}
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub enum Profile {
HdrRgba,
HdrRgbLdrA,
LdrRgba,
LdrSrgb,
}
impl Default for Profile {
fn default() -> Self {
Self::LdrRgba
}
}
impl Profile {
fn into_sys(self) -> astcenc_sys::astcenc_profile {
match self {
Self::HdrRgba => astcenc_sys::astcenc_profile_ASTCENC_PRF_HDR,
Self::HdrRgbLdrA => astcenc_sys::astcenc_profile_ASTCENC_PRF_HDR_RGB_LDR_A,
Self::LdrRgba => astcenc_sys::astcenc_profile_ASTCENC_PRF_LDR,
Self::LdrSrgb => astcenc_sys::astcenc_profile_ASTCENC_PRF_LDR_SRGB,
}
}
}
pub struct Config {
inner: astcenc_sys::astcenc_config,
}
impl Default for Config {
fn default() -> Self {
ConfigBuilder::default().build().unwrap()
}
}
#[derive(Clone, Hash)]
pub struct ConfigBuilder {
profile: Profile,
preset: Preset,
block_size: Extents,
}
impl Default for ConfigBuilder {
fn default() -> Self {
Self {
profile: Profile::default(),
preset: Preset::default(),
block_size: Extents::default_block_size(),
}
}
}
impl ConfigBuilder {
pub fn new() -> Self {
Self::default()
}
pub fn profile(&mut self, profile: Profile) -> &mut Self {
self.profile = profile;
self
}
pub fn with_profile(mut self, profile: Profile) -> Self {
self.profile(profile);
self
}
pub fn preset(&mut self, preset: Preset) -> &mut Self {
self.preset = preset;
self
}
pub fn with_preset(mut self, preset: Preset) -> Self {
self.preset(preset);
self
}
pub fn block_size(&mut self, block_size: Extents) -> &mut Self {
self.block_size = block_size;
self
}
pub fn with_block_size(mut self, block_size: Extents) -> Self {
self.block_size(block_size);
self
}
pub fn build(self) -> Result<Config, Error> {
let mut cfg: MaybeUninit<astcenc_sys::astcenc_config> = MaybeUninit::uninit();
error_code_to_result(unsafe {
astcenc_sys::astcenc_config_init(
self.profile.into_sys(),
self.block_size.x,
self.block_size.y,
self.block_size.z,
self.preset.into_sys(),
Flags::default().into_sys(),
cfg.as_mut_ptr(),
)
})?;
Ok(Config {
inner: unsafe { cfg.assume_init() },
})
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub enum Type {
F16,
F32,
U8,
}
impl Type {
fn into_sys(self) -> astcenc_sys::astcenc_type {
match self {
Self::F16 => astcenc_sys::astcenc_type_ASTCENC_TYPE_F16,
Self::F32 => astcenc_sys::astcenc_type_ASTCENC_TYPE_F32,
Self::U8 => astcenc_sys::astcenc_type_ASTCENC_TYPE_U8,
}
}
}
pub trait DataType: Sized {
const TYPE: Type;
fn as_u8s(array: &[Self]) -> &[u8];
fn as_u8s_mut(array: &mut [Self]) -> &mut [u8];
}
impl DataType for u8 {
const TYPE: Type = Type::U8;
fn as_u8s(array: &[Self]) -> &[u8] {
array
}
fn as_u8s_mut(array: &mut [Self]) -> &mut [u8] {
array
}
}
impl DataType for f32 {
const TYPE: Type = Type::F32;
fn as_u8s(array: &[Self]) -> &[u8] {
unsafe { std::mem::transmute(array) }
}
fn as_u8s_mut(array: &mut [Self]) -> &mut [u8] {
unsafe { std::mem::transmute(array) }
}
}
impl DataType for half::f16 {
const TYPE: Type = Type::F16;
fn as_u8s(array: &[Self]) -> &[u8] {
unsafe { std::mem::transmute(array) }
}
fn as_u8s_mut(array: &mut [Self]) -> &mut [u8] {
unsafe { std::mem::transmute(array) }
}
}
#[derive(Default)]
pub struct Image<T> {
pub extents: Extents,
pub border_padding: u32,
pub data: T,
}
impl<D, T> Image<T>
where
T: Deref<Target = [D]>,
D: DataType,
{
fn as_sys(&self) -> astcenc_sys::astcenc_image {
astcenc_sys::astcenc_image {
dim_x: self.extents.x,
dim_y: self.extents.y,
dim_z: self.extents.z,
dim_pad: self.border_padding,
data_type: D::TYPE.into_sys(),
data: self.data.as_ptr() as *mut _,
}
}
}
impl<D, T> Image<T>
where
T: DerefMut<Target = [D]>,
D: DataType,
{
fn as_sys_mut(&mut self) -> astcenc_sys::astcenc_image {
astcenc_sys::astcenc_image {
dim_x: self.extents.x,
dim_y: self.extents.y,
dim_z: self.extents.z,
dim_pad: self.border_padding,
data_type: D::TYPE.into_sys(),
data: self.data.as_mut_ptr() as *mut _,
}
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
pub enum Selector {
Red,
Green,
Blue,
Alpha,
Z,
One,
Zero,
}
impl Selector {
fn into_sys(self) -> astcenc_sys::astcenc_swz {
match self {
Self::Red => astcenc_sys::astcenc_swz_ASTCENC_SWZ_R,
Self::Green => astcenc_sys::astcenc_swz_ASTCENC_SWZ_G,
Self::Blue => astcenc_sys::astcenc_swz_ASTCENC_SWZ_B,
Self::Alpha => astcenc_sys::astcenc_swz_ASTCENC_SWZ_A,
Self::Z => astcenc_sys::astcenc_swz_ASTCENC_SWZ_Z,
Self::One => astcenc_sys::astcenc_swz_ASTCENC_SWZ_1,
Self::Zero => astcenc_sys::astcenc_swz_ASTCENC_SWZ_0,
}
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct Swizzle {
pub r: Selector,
pub g: Selector,
pub b: Selector,
pub a: Selector,
}
impl Swizzle {
pub fn rrr1() -> Self {
Self {
r: Selector::Red,
g: Selector::Red,
b: Selector::Red,
a: Selector::One,
}
}
pub fn rrrg() -> Self {
Self {
r: Selector::Red,
g: Selector::Red,
b: Selector::Red,
a: Selector::Green,
}
}
pub fn rgb1() -> Self {
Self {
r: Selector::Red,
g: Selector::Green,
b: Selector::Blue,
a: Selector::One,
}
}
pub fn rgba() -> Self {
Self {
r: Selector::Red,
g: Selector::Green,
b: Selector::Blue,
a: Selector::Alpha,
}
}
fn into_sys(self) -> astcenc_sys::astcenc_swizzle {
astcenc_sys::astcenc_swizzle {
r: self.r.into_sys(),
g: self.g.into_sys(),
b: self.b.into_sys(),
a: self.a.into_sys(),
}
}
}
impl Context {
pub fn new(config: Config) -> Result<Self, Error> {
const THREADS: usize = 1;
let mut cfg: MaybeUninit<*mut astcenc_sys::astcenc_context> = MaybeUninit::uninit();
error_code_to_result(unsafe {
astcenc_sys::astcenc_context_alloc(&config.inner, THREADS as _, cfg.as_mut_ptr())
})?;
Ok(Self {
inner: unsafe { NonNull::new(cfg.assume_init()).ok_or(Error::Unknown)? },
config,
})
}
pub fn compress<D, T>(&mut self, image: &Image<T>, swizzle: Swizzle) -> Result<Vec<u8>, Error>
where
D: DataType,
T: Deref<Target = [D]>,
{
const BYTES_PER_BLOCK: usize = 16;
if image.data.as_ref().len()
!= (image.extents.x * image.extents.y * image.extents.z * 4) as usize
{
return Err(Error::BadParam);
}
let blocks_x =
(image.extents.x + self.config.inner.block_x - 1) / self.config.inner.block_x;
let blocks_y =
(image.extents.y + self.config.inner.block_y - 1) / self.config.inner.block_y;
let blocks_z =
(image.extents.z + self.config.inner.block_z - 1) / self.config.inner.block_z;
let bytes = blocks_x as u64 * blocks_y as u64 * blocks_z as u64 * BYTES_PER_BLOCK as u64;
let mut out = Vec::with_capacity(bytes as usize);
let image_sys = image.as_sys();
error_code_to_result(unsafe {
astcenc_sys::astcenc_compress_image(
self.inner.as_mut(),
&image_sys as *const _ as *mut _,
swizzle.into_sys(),
out.as_mut_ptr(),
bytes,
0,
)
})?;
unsafe { out.set_len(bytes.try_into().map_err(|_| Error::OutOfMem)?) };
self.reset()?;
Ok(out)
}
pub fn decompress_into<D, T>(
&mut self,
data: &[u8],
out: &mut Image<T>,
swizzle: Swizzle,
) -> Result<(), Error>
where
D: DataType,
T: DerefMut<Target = [D]>,
{
error_code_to_result(unsafe {
astcenc_sys::astcenc_decompress_image(
self.inner.as_mut(),
data.as_ptr(),
data.len() as u64,
&mut out.as_sys_mut(),
swizzle.into_sys(),
)
})
}
pub fn decompress<D>(
&mut self,
data: &[u8],
extents: Extents,
border_padding: u32,
swizzle: Swizzle,
) -> Result<Image<Vec<D>>, Error>
where
D: DataType,
{
let size = (extents.x * extents.y * extents.z * 4) as usize;
let mut out = Image {
extents,
border_padding,
data: Vec::with_capacity(size),
};
error_code_to_result(unsafe {
astcenc_sys::astcenc_decompress_image(
self.inner.as_mut(),
data.as_ptr(),
data.len() as u64,
&mut out.as_sys_mut(),
swizzle.into_sys(),
)
})?;
unsafe { out.data.set_len(size) };
Ok(out)
}
fn reset(&mut self) -> Result<(), Error> {
error_code_to_result(unsafe { astcenc_sys::astcenc_compress_reset(self.inner.as_mut()) })
}
}
bitflags::bitflags! {
pub struct Flags: std::os::raw::c_uint {
const DECOMPRESS_ONLY = astcenc_sys::ASTCENC_FLG_DECOMPRESS_ONLY;
const MAP_MASK = astcenc_sys::ASTCENC_FLG_MAP_MASK;
const MAP_NORMAL = astcenc_sys::ASTCENC_FLG_MAP_NORMAL;
const USE_ALPHA_WEIGHT = astcenc_sys::ASTCENC_FLG_USE_ALPHA_WEIGHT;
const USE_PERCEPTUAL = astcenc_sys::ASTCENC_FLG_USE_PERCEPTUAL;
}
}
impl Flags {
fn into_sys(self) -> std::os::raw::c_uint {
self.bits
}
}
impl Default for Flags {
fn default() -> Self {
Flags::USE_ALPHA_WEIGHT
}
}
#[cfg(test)]
mod tests {
#[test]
fn linked_correctly() {
let mut img = super::Image::<Vec<u8>>::default();
let mut ctx = super::Context::default();
let swz = super::Swizzle::rgba();
let data = ctx.compress(&img, swz).unwrap();
ctx.decompress_into(&data, &mut img, swz).unwrap();
}
}