mod dispatch;
use dispatch::dispatch;
#[derive(Debug, Copy, Clone)]
pub struct Surface<'a> {
pub data: &'a [u8],
pub width: u32,
pub height: u32,
}
impl<'a> Surface<'a> {
pub fn new(data: &'a [u8], width: u32, height: u32) -> Self {
assert!(width > 0 && height > 0, "width and height must be non-zero");
assert!(
width.is_multiple_of(4),
"width {width} must be a multiple of 4"
);
assert!(
height.is_multiple_of(4),
"height {height} must be a multiple of 4"
);
let required = (width as usize)
.checked_mul(height as usize)
.and_then(|wh| wh.checked_mul(4))
.expect("width * height * 4 overflows usize");
assert!(
data.len() >= required,
"data length {} is less than width * height * 4 ({required})",
data.len()
);
Self {
data,
width,
height,
}
}
fn blocks(&self) -> u32 {
(self.width / 4) * (self.height / 4)
}
}
#[must_use]
pub fn output_size_64bpb(width: u32, height: u32) -> usize {
let block_count = (width.div_ceil(4) * height.div_ceil(4)) as usize;
block_count * 8
}
#[must_use]
pub fn output_size_128bpb(width: u32, height: u32) -> usize {
let block_count = (width.div_ceil(4) * height.div_ceil(4)) as usize;
block_count * 16
}
pub mod etc1 {
use super::*;
#[must_use]
pub fn compress_blocks(surface: &Surface) -> Vec<u8> {
let size = output_size_64bpb(surface.width, surface.height);
let mut output = vec![0u8; size];
compress_blocks_into(surface, &mut output);
output
}
pub fn compress_blocks_into(surface: &Surface, output: &mut [u8]) {
assert_eq!(
output.len(),
output_size_64bpb(surface.width, surface.height)
);
unsafe {
(dispatch().compress_etc1_rgb)(
surface.data.as_ptr().cast(),
output.as_mut_ptr().cast(),
surface.blocks(),
surface.width as usize,
);
}
}
#[must_use]
pub fn compress_blocks_dither(surface: &Surface) -> Vec<u8> {
let size = output_size_64bpb(surface.width, surface.height);
let mut output = vec![0u8; size];
compress_blocks_dither_into(surface, &mut output);
output
}
pub fn compress_blocks_dither_into(surface: &Surface, output: &mut [u8]) {
assert_eq!(
output.len(),
output_size_64bpb(surface.width, surface.height)
);
unsafe {
(dispatch().compress_etc1_rgb_dither)(
surface.data.as_ptr().cast(),
output.as_mut_ptr().cast(),
surface.blocks(),
surface.width as usize,
);
}
}
}
pub mod etc2_rgb {
use super::*;
#[must_use]
pub fn compress_blocks(surface: &Surface, use_heuristics: bool) -> Vec<u8> {
let size = output_size_64bpb(surface.width, surface.height);
let mut output = vec![0u8; size];
compress_blocks_into(surface, use_heuristics, &mut output);
output
}
pub fn compress_blocks_into(surface: &Surface, use_heuristics: bool, output: &mut [u8]) {
assert_eq!(
output.len(),
output_size_64bpb(surface.width, surface.height)
);
unsafe {
(dispatch().compress_etc2_rgb)(
surface.data.as_ptr().cast(),
output.as_mut_ptr().cast(),
surface.blocks(),
surface.width as usize,
use_heuristics,
);
}
}
}
pub mod etc2_rgba {
use super::*;
#[must_use]
pub fn compress_blocks(surface: &Surface, use_heuristics: bool) -> Vec<u8> {
let size = output_size_128bpb(surface.width, surface.height);
let mut output = vec![0u8; size];
compress_blocks_into(surface, use_heuristics, &mut output);
output
}
pub fn compress_blocks_into(surface: &Surface, use_heuristics: bool, output: &mut [u8]) {
assert_eq!(
output.len(),
output_size_128bpb(surface.width, surface.height)
);
unsafe {
(dispatch().compress_etc2_rgba)(
surface.data.as_ptr().cast(),
output.as_mut_ptr().cast(),
surface.blocks(),
surface.width as usize,
use_heuristics,
);
}
}
}
pub mod eac_r {
use super::*;
#[must_use]
pub fn compress_blocks(surface: &Surface) -> Vec<u8> {
let size = output_size_64bpb(surface.width, surface.height);
let mut output = vec![0u8; size];
compress_blocks_into(surface, &mut output);
output
}
pub fn compress_blocks_into(surface: &Surface, output: &mut [u8]) {
assert_eq!(
output.len(),
output_size_64bpb(surface.width, surface.height)
);
unsafe {
(dispatch().compress_eac_r)(
surface.data.as_ptr().cast(),
output.as_mut_ptr().cast(),
surface.blocks(),
surface.width as usize,
);
}
}
}
pub mod eac_rg {
use super::*;
#[must_use]
pub fn compress_blocks(surface: &Surface) -> Vec<u8> {
let size = output_size_128bpb(surface.width, surface.height);
let mut output = vec![0u8; size];
compress_blocks_into(surface, &mut output);
output
}
pub fn compress_blocks_into(surface: &Surface, output: &mut [u8]) {
assert_eq!(
output.len(),
output_size_128bpb(surface.width, surface.height)
);
unsafe {
(dispatch().compress_eac_rg)(
surface.data.as_ptr().cast(),
output.as_mut_ptr().cast(),
surface.blocks(),
surface.width as usize,
);
}
}
}
pub mod bc1 {
use super::*;
#[must_use]
pub fn compress_blocks(surface: &Surface) -> Vec<u8> {
let size = output_size_64bpb(surface.width, surface.height);
let mut output = vec![0u8; size];
compress_blocks_into(surface, &mut output);
output
}
pub fn compress_blocks_into(surface: &Surface, output: &mut [u8]) {
assert_eq!(
output.len(),
output_size_64bpb(surface.width, surface.height)
);
unsafe {
(dispatch().compress_bc1)(
surface.data.as_ptr().cast(),
output.as_mut_ptr().cast(),
surface.blocks(),
surface.width as usize,
);
}
}
#[must_use]
pub fn compress_blocks_dither(surface: &Surface) -> Vec<u8> {
let size = output_size_64bpb(surface.width, surface.height);
let mut output = vec![0u8; size];
compress_blocks_dither_into(surface, &mut output);
output
}
pub fn compress_blocks_dither_into(surface: &Surface, output: &mut [u8]) {
assert_eq!(
output.len(),
output_size_64bpb(surface.width, surface.height)
);
unsafe {
(dispatch().compress_bc1_dither)(
surface.data.as_ptr().cast(),
output.as_mut_ptr().cast(),
surface.blocks(),
surface.width as usize,
);
}
}
}
pub mod bc3 {
use super::*;
#[must_use]
pub fn compress_blocks(surface: &Surface) -> Vec<u8> {
let size = output_size_128bpb(surface.width, surface.height);
let mut output = vec![0u8; size];
compress_blocks_into(surface, &mut output);
output
}
pub fn compress_blocks_into(surface: &Surface, output: &mut [u8]) {
assert_eq!(
output.len(),
output_size_128bpb(surface.width, surface.height)
);
unsafe {
(dispatch().compress_bc3)(
surface.data.as_ptr().cast(),
output.as_mut_ptr().cast(),
surface.blocks(),
surface.width as usize,
);
}
}
}
pub mod bc4 {
use super::*;
#[must_use]
pub fn compress_blocks(surface: &Surface) -> Vec<u8> {
let size = output_size_64bpb(surface.width, surface.height);
let mut output = vec![0u8; size];
compress_blocks_into(surface, &mut output);
output
}
pub fn compress_blocks_into(surface: &Surface, output: &mut [u8]) {
assert_eq!(
output.len(),
output_size_64bpb(surface.width, surface.height)
);
unsafe {
(dispatch().compress_bc4)(
surface.data.as_ptr().cast(),
output.as_mut_ptr().cast(),
surface.blocks(),
surface.width as usize,
);
}
}
}
pub mod bc5 {
use super::*;
#[must_use]
pub fn compress_blocks(surface: &Surface) -> Vec<u8> {
let size = output_size_128bpb(surface.width, surface.height);
let mut output = vec![0u8; size];
compress_blocks_into(surface, &mut output);
output
}
pub fn compress_blocks_into(surface: &Surface, output: &mut [u8]) {
assert_eq!(
output.len(),
output_size_128bpb(surface.width, surface.height)
);
unsafe {
(dispatch().compress_bc5)(
surface.data.as_ptr().cast(),
output.as_mut_ptr().cast(),
surface.blocks(),
surface.width as usize,
);
}
}
}
pub mod decode {
use super::*;
fn output_pixel_size(width: u32, height: u32) -> usize {
(width as usize) * (height as usize) * 4
}
#[must_use]
pub fn decode_rgb(data: &[u8], width: u32, height: u32) -> Vec<u8> {
let size = output_pixel_size(width, height);
let mut output = vec![0u8; size];
decode_rgb_into(data, width, height, &mut output);
output
}
pub fn decode_rgb_into(data: &[u8], width: u32, height: u32, output: &mut [u8]) {
assert_eq!(output.len(), output_pixel_size(width, height));
assert!(data.len() >= output_size_64bpb(width, height));
unsafe {
(dispatch().decode_rgb)(
data.as_ptr().cast(),
output.as_mut_ptr().cast(),
width as i32,
height as i32,
);
}
}
#[must_use]
pub fn decode_rgba(data: &[u8], width: u32, height: u32) -> Vec<u8> {
let size = output_pixel_size(width, height);
let mut output = vec![0u8; size];
decode_rgba_into(data, width, height, &mut output);
output
}
pub fn decode_rgba_into(data: &[u8], width: u32, height: u32, output: &mut [u8]) {
assert_eq!(output.len(), output_pixel_size(width, height));
assert!(data.len() >= output_size_128bpb(width, height));
unsafe {
(dispatch().decode_rgba)(
data.as_ptr().cast(),
output.as_mut_ptr().cast(),
width as i32,
height as i32,
);
}
}
#[must_use]
pub fn decode_r(data: &[u8], width: u32, height: u32, is_signed: bool) -> Vec<u8> {
let size = output_pixel_size(width, height);
let mut output = vec![0u8; size];
decode_r_into(data, width, height, is_signed, &mut output);
output
}
pub fn decode_r_into(data: &[u8], width: u32, height: u32, is_signed: bool, output: &mut [u8]) {
assert_eq!(output.len(), output_pixel_size(width, height));
assert!(data.len() >= output_size_64bpb(width, height));
unsafe {
(dispatch().decode_r)(
data.as_ptr().cast(),
output.as_mut_ptr().cast(),
width as i32,
height as i32,
is_signed,
);
}
}
#[must_use]
pub fn decode_rg(data: &[u8], width: u32, height: u32, is_signed: bool) -> Vec<u8> {
let size = output_pixel_size(width, height);
let mut output = vec![0u8; size];
decode_rg_into(data, width, height, is_signed, &mut output);
output
}
pub fn decode_rg_into(
data: &[u8],
width: u32,
height: u32,
is_signed: bool,
output: &mut [u8],
) {
assert_eq!(output.len(), output_pixel_size(width, height));
assert!(data.len() >= output_size_128bpb(width, height));
unsafe {
(dispatch().decode_rg)(
data.as_ptr().cast(),
output.as_mut_ptr().cast(),
width as i32,
height as i32,
is_signed,
);
}
}
#[must_use]
pub fn decode_bc1(data: &[u8], width: u32, height: u32) -> Vec<u8> {
let size = output_pixel_size(width, height);
let mut output = vec![0u8; size];
decode_bc1_into(data, width, height, &mut output);
output
}
pub fn decode_bc1_into(data: &[u8], width: u32, height: u32, output: &mut [u8]) {
assert_eq!(output.len(), output_pixel_size(width, height));
assert!(data.len() >= output_size_64bpb(width, height));
unsafe {
(dispatch().decode_bc1)(
data.as_ptr().cast(),
output.as_mut_ptr().cast(),
width as i32,
height as i32,
);
}
}
#[must_use]
pub fn decode_bc3(data: &[u8], width: u32, height: u32) -> Vec<u8> {
let size = output_pixel_size(width, height);
let mut output = vec![0u8; size];
decode_bc3_into(data, width, height, &mut output);
output
}
pub fn decode_bc3_into(data: &[u8], width: u32, height: u32, output: &mut [u8]) {
assert_eq!(output.len(), output_pixel_size(width, height));
assert!(data.len() >= output_size_128bpb(width, height));
unsafe {
(dispatch().decode_bc3)(
data.as_ptr().cast(),
output.as_mut_ptr().cast(),
width as i32,
height as i32,
);
}
}
#[must_use]
pub fn decode_bc4(data: &[u8], width: u32, height: u32) -> Vec<u8> {
let size = output_pixel_size(width, height);
let mut output = vec![0u8; size];
decode_bc4_into(data, width, height, &mut output);
output
}
pub fn decode_bc4_into(data: &[u8], width: u32, height: u32, output: &mut [u8]) {
assert_eq!(output.len(), output_pixel_size(width, height));
assert!(data.len() >= output_size_64bpb(width, height));
unsafe {
(dispatch().decode_bc4)(
data.as_ptr().cast(),
output.as_mut_ptr().cast(),
width as i32,
height as i32,
);
}
}
#[must_use]
pub fn decode_bc5(data: &[u8], width: u32, height: u32) -> Vec<u8> {
let size = output_pixel_size(width, height);
let mut output = vec![0u8; size];
decode_bc5_into(data, width, height, &mut output);
output
}
pub fn decode_bc5_into(data: &[u8], width: u32, height: u32, output: &mut [u8]) {
assert_eq!(output.len(), output_pixel_size(width, height));
assert!(data.len() >= output_size_128bpb(width, height));
unsafe {
(dispatch().decode_bc5)(
data.as_ptr().cast(),
output.as_mut_ptr().cast(),
width as i32,
height as i32,
);
}
}
#[must_use]
pub fn decode_bc7(data: &[u8], width: u32, height: u32) -> Vec<u8> {
let size = output_pixel_size(width, height);
let mut output = vec![0u8; size];
decode_bc7_into(data, width, height, &mut output);
output
}
pub fn decode_bc7_into(data: &[u8], width: u32, height: u32, output: &mut [u8]) {
assert_eq!(output.len(), output_pixel_size(width, height));
assert!(data.len() >= output_size_128bpb(width, height));
unsafe {
(dispatch().decode_bc7)(
data.as_ptr().cast(),
output.as_mut_ptr().cast(),
width as i32,
height as i32,
);
}
}
}