Skip to main content

ctt_intel_texture_compressor/
bc7.rs

1//! BC7 block compression — RGBA (high-quality).
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). Whether alpha is actually encoded
7//! depends on the [`EncodeSettings`]: the `opaque_*` presets set `channels: 3`
8//! (RGB only), while the `alpha_*` presets set `channels: 4` (full RGBA). In
9//! both cases the surface layout is the same 4-byte-per-pixel format.
10//!
11//! # Output
12//!
13//! Each 4×4 texel block is encoded into **16 bytes** (1 byte/pixel). BC7
14//! selects among 8 internal modes to balance color and alpha precision.
15
16use crate::RgbaSurface;
17use crate::bindings::kernel;
18
19#[derive(Debug, Copy, Clone)]
20pub struct EncodeSettings {
21    pub mode_selection: [bool; 4usize],
22    pub refine_iterations: [u32; 8usize],
23    pub skip_mode2: bool,
24    pub fast_skip_threshold_mode1: u32,
25    pub fast_skip_threshold_mode3: u32,
26    pub fast_skip_threshold_mode7: u32,
27    pub mode45_channel0: u32,
28    pub refine_iterations_channel: u32,
29    pub channels: i32,
30}
31
32#[must_use]
33pub fn calc_output_size(width: u32, height: u32) -> usize {
34    // BC7 uses a fixed block size of 16 bytes (128 bits) and a fixed tile size of 4x4 texels.
35    let block_count = (width.div_ceil(4) * height.div_ceil(4)) as usize;
36    block_count * 16
37}
38
39#[must_use]
40pub fn compress_blocks(settings: &EncodeSettings, surface: &RgbaSurface) -> Vec<u8> {
41    let output_size = calc_output_size(surface.width, surface.height);
42    let mut output = vec![0u8; output_size];
43    compress_blocks_into(settings, surface, &mut output);
44    output
45}
46
47/// Compresses an [`RgbaSurface`] into BC7 blocks.
48///
49/// The surface must contain `R8 G8 B8 A8` interleaved pixel data (4 bytes per
50/// pixel). Whether the encoder uses the alpha channel depends on
51/// `settings.channels` (3 = opaque, 4 = with alpha).
52///
53/// # Panics
54///
55/// Panics if `blocks.len()` does not equal [`calc_output_size`] for the given
56/// surface dimensions.
57pub fn compress_blocks_into(settings: &EncodeSettings, surface: &RgbaSurface, blocks: &mut [u8]) {
58    assert_eq!(
59        blocks.len(),
60        calc_output_size(surface.width, surface.height)
61    );
62    // SAFETY: The ISPC function does not mutate the source surface; the `*mut u8`
63    // pointer type is an artifact of the C header declaration.
64    let mut surface = kernel::rgba_surface {
65        width: surface.width as i32,
66        height: surface.height as i32,
67        stride: surface.stride as i32,
68        ptr: surface.data.as_ptr() as *mut u8,
69    };
70    let mut settings = kernel::bc7_enc_settings {
71        mode_selection: settings.mode_selection,
72        refineIterations: [
73            settings.refine_iterations[0] as i32,
74            settings.refine_iterations[1] as i32,
75            settings.refine_iterations[2] as i32,
76            settings.refine_iterations[3] as i32,
77            settings.refine_iterations[4] as i32,
78            settings.refine_iterations[5] as i32,
79            settings.refine_iterations[6] as i32,
80            settings.refine_iterations[7] as i32,
81        ],
82        skip_mode2: settings.skip_mode2,
83        fastSkipThreshold_mode1: settings.fast_skip_threshold_mode1 as i32,
84        fastSkipThreshold_mode3: settings.fast_skip_threshold_mode3 as i32,
85        fastSkipThreshold_mode7: settings.fast_skip_threshold_mode7 as i32,
86        mode45_channel0: settings.mode45_channel0 as i32,
87        refineIterations_channel: settings.refine_iterations_channel as i32,
88        channels: settings.channels,
89    };
90
91    unsafe {
92        kernel::CompressBlocksBC7_ispc(&mut surface, blocks.as_mut_ptr(), &mut settings);
93    }
94}
95
96pub fn opaque_ultra_fast_settings() -> EncodeSettings {
97    EncodeSettings {
98        channels: 3,
99        mode_selection: [false, false, false, true],
100        fast_skip_threshold_mode1: 3,
101        fast_skip_threshold_mode3: 1,
102        fast_skip_threshold_mode7: 0,
103        skip_mode2: true,
104        mode45_channel0: 0,
105        refine_iterations_channel: 0,
106        refine_iterations: [2, 2, 2, 1, 2, 2, 1, 0],
107    }
108}
109
110pub fn opaque_very_fast_settings() -> EncodeSettings {
111    EncodeSettings {
112        channels: 3,
113        mode_selection: [false, true, false, true],
114        fast_skip_threshold_mode1: 3,
115        fast_skip_threshold_mode3: 1,
116        fast_skip_threshold_mode7: 0,
117        skip_mode2: true,
118        mode45_channel0: 0,
119        refine_iterations_channel: 0,
120        refine_iterations: [2, 2, 2, 1, 2, 2, 1, 0],
121    }
122}
123
124pub fn opaque_fast_settings() -> EncodeSettings {
125    EncodeSettings {
126        channels: 3,
127        mode_selection: [false, true, false, true],
128        fast_skip_threshold_mode1: 12,
129        fast_skip_threshold_mode3: 4,
130        fast_skip_threshold_mode7: 0,
131        skip_mode2: true,
132        mode45_channel0: 0,
133        refine_iterations_channel: 0,
134        refine_iterations: [2, 2, 2, 1, 2, 2, 2, 0],
135    }
136}
137
138pub fn opaque_basic_settings() -> EncodeSettings {
139    EncodeSettings {
140        channels: 3,
141        mode_selection: [true, true, true, true],
142        fast_skip_threshold_mode1: 8 + 4,
143        fast_skip_threshold_mode3: 8,
144        fast_skip_threshold_mode7: 0,
145        skip_mode2: true,
146        mode45_channel0: 0,
147        refine_iterations_channel: 2,
148        refine_iterations: [2, 2, 2, 2, 2, 2, 2, 0],
149    }
150}
151
152pub fn opaque_slow_settings() -> EncodeSettings {
153    let more_refinement = 2;
154    EncodeSettings {
155        channels: 3,
156        mode_selection: [true, true, true, true],
157        fast_skip_threshold_mode1: 64,
158        fast_skip_threshold_mode3: 64,
159        fast_skip_threshold_mode7: 0,
160        skip_mode2: false,
161        mode45_channel0: 0,
162        refine_iterations_channel: 2 + more_refinement,
163        refine_iterations: [
164            2 + more_refinement,
165            2 + more_refinement,
166            2 + more_refinement,
167            2 + more_refinement,
168            2 + more_refinement,
169            2 + more_refinement,
170            2 + more_refinement,
171            0,
172        ],
173    }
174}
175
176pub fn alpha_ultra_fast_settings() -> EncodeSettings {
177    EncodeSettings {
178        channels: 4,
179        mode_selection: [false, false, true, true],
180        fast_skip_threshold_mode1: 0,
181        fast_skip_threshold_mode3: 0,
182        fast_skip_threshold_mode7: 4,
183        skip_mode2: true,
184        mode45_channel0: 3,
185        refine_iterations_channel: 1,
186        refine_iterations: [2, 1, 2, 1, 1, 1, 2, 2],
187    }
188}
189
190pub fn alpha_very_fast_settings() -> EncodeSettings {
191    EncodeSettings {
192        channels: 4,
193        mode_selection: [false, true, true, true],
194        fast_skip_threshold_mode1: 0,
195        fast_skip_threshold_mode3: 0,
196        fast_skip_threshold_mode7: 4,
197        skip_mode2: true,
198        mode45_channel0: 3,
199        refine_iterations_channel: 2,
200        refine_iterations: [2, 1, 2, 1, 2, 2, 2, 2],
201    }
202}
203
204pub fn alpha_fast_settings() -> EncodeSettings {
205    EncodeSettings {
206        channels: 4,
207        mode_selection: [false, true, true, true],
208        fast_skip_threshold_mode1: 4,
209        fast_skip_threshold_mode3: 4,
210        fast_skip_threshold_mode7: 8,
211        skip_mode2: true,
212        mode45_channel0: 3,
213        refine_iterations_channel: 2,
214        refine_iterations: [2, 1, 2, 1, 2, 2, 2, 2],
215    }
216}
217
218pub fn alpha_basic_settings() -> EncodeSettings {
219    EncodeSettings {
220        channels: 4,
221        mode_selection: [true, true, true, true],
222        fast_skip_threshold_mode1: 8 + 4,
223        fast_skip_threshold_mode3: 8,
224        fast_skip_threshold_mode7: 4,
225        skip_mode2: true,
226        mode45_channel0: 0,
227        refine_iterations_channel: 2,
228        refine_iterations: [2, 2, 2, 2, 2, 2, 2, 2],
229    }
230}
231
232pub fn alpha_slow_settings() -> EncodeSettings {
233    let more_refinement = 2;
234    EncodeSettings {
235        channels: 4,
236        mode_selection: [true, true, true, true],
237        fast_skip_threshold_mode1: 64,
238        fast_skip_threshold_mode3: 64,
239        fast_skip_threshold_mode7: 64,
240        skip_mode2: false,
241        mode45_channel0: 0,
242        refine_iterations_channel: 2 + more_refinement,
243        refine_iterations: [
244            2 + more_refinement,
245            2 + more_refinement,
246            2 + more_refinement,
247            2 + more_refinement,
248            2 + more_refinement,
249            2 + more_refinement,
250            2 + more_refinement,
251            2 + more_refinement,
252        ],
253    }
254}