Skip to main content

ctt_intel_texture_compressor/
etc1.rs

1//! ETC1 (Ericsson Texture Compression 1) block compression — RGB, no alpha.
2//!
3//! # Input format
4//!
5//! Expects an [`RgbaSurface`] with **`R8 G8 B8 A8` interleaved** pixel data
6//! (4 bytes per pixel, little-endian RGBA). Only the **R, G, B** channels are
7//! used; the alpha channel is read but ignored.
8//!
9//! # Output
10//!
11//! Each 4×4 texel block is encoded into **8 bytes** (0.5 bytes/pixel).
12
13use crate::RgbaSurface;
14use crate::bindings::kernel;
15
16#[derive(Debug, Copy, Clone)]
17pub struct EncodeSettings {
18    pub fast_skip_threshold: u32,
19}
20
21#[must_use]
22pub fn calc_output_size(width: u32, height: u32) -> usize {
23    // ETC1 uses a fixed block size of 8 bytes (64 bits) and a fixed tile size of 4x4 texels.
24    let block_count = (width.div_ceil(4) * height.div_ceil(4)) as usize;
25    block_count * 8
26}
27
28#[must_use]
29pub fn compress_blocks(settings: &EncodeSettings, surface: &RgbaSurface) -> Vec<u8> {
30    let output_size = calc_output_size(surface.width, surface.height);
31    let mut output = vec![0u8; output_size];
32    compress_blocks_into(settings, surface, &mut output);
33    output
34}
35
36/// Compresses an [`RgbaSurface`] into ETC1 blocks.
37///
38/// The surface must contain `R8 G8 B8 A8` interleaved pixel data (4 bytes per
39/// pixel). Only the R, G, and B channels are read; the alpha byte is present
40/// in the layout but ignored by the encoder.
41///
42/// # Panics
43///
44/// Panics if `blocks.len()` does not equal [`calc_output_size`] for the given
45/// surface dimensions.
46pub fn compress_blocks_into(settings: &EncodeSettings, surface: &RgbaSurface, blocks: &mut [u8]) {
47    assert_eq!(
48        blocks.len(),
49        calc_output_size(surface.width, surface.height)
50    );
51    // SAFETY: The ISPC function does not mutate the source surface; the `*mut u8`
52    // pointer type is an artifact of the C header declaration.
53    let mut surface = kernel::rgba_surface {
54        width: surface.width as i32,
55        height: surface.height as i32,
56        stride: surface.stride as i32,
57        ptr: surface.data.as_ptr() as *mut u8,
58    };
59    let mut settings = kernel::etc_enc_settings {
60        fastSkipThreshold: settings.fast_skip_threshold as i32,
61    };
62
63    unsafe {
64        kernel::CompressBlocksETC1_ispc(&mut surface, blocks.as_mut_ptr(), &mut settings);
65    }
66}
67
68pub fn slow_settings() -> EncodeSettings {
69    EncodeSettings {
70        fast_skip_threshold: 6,
71    }
72}