block_compression/
encode.rs

1//! CPU based encoding.
2
3#[cfg(feature = "bc15")]
4mod bc1_to_5;
5#[cfg(feature = "bc6h")]
6mod bc6h;
7#[cfg(feature = "bc7")]
8mod bc7;
9#[cfg(any(feature = "bc6h", feature = "bc7"))]
10mod common;
11
12#[cfg(feature = "bc15")]
13use self::bc1_to_5::BlockCompressorBC15;
14#[cfg(feature = "bc6h")]
15use self::bc6h::BlockCompressorBC6H;
16#[cfg(feature = "bc7")]
17use self::bc7::BlockCompressorBC7;
18#[cfg(feature = "bc6h")]
19use crate::BC6HSettings;
20#[cfg(feature = "bc7")]
21use crate::BC7Settings;
22#[cfg(any(feature = "bc15", feature = "bc6h", feature = "bc7"))]
23use crate::CompressionVariant;
24
25/// Compresses raw RGBA8 data into using a texture block compression format.
26///
27/// It supports BC1 through BC7 compression formats and provides CPU-based texture compression
28/// for RGBA8 data.
29///
30/// # Data Layout Requirements
31/// The input data must be in RGBA8 format (8 bits per channel, 32 bits per pixel). The data is
32/// expected to be in row-major order, with optional stride for padding between rows.
33///
34/// # Buffer Requirements
35/// The destination buffer must have sufficient capacity to store the compressed blocks.
36/// The required size can be calculated using [`CompressionVariant::blocks_byte_size()`].
37///
38/// For example:
39/// ```ignore
40/// let required_size = variant.blocks_byte_size(width, height);
41/// assert!(blocks_buffer.len() >= required_size);
42/// ```
43///
44/// # Arguments
45/// * `variation` - The block compression format to use
46/// * `rgba_data` - Source RGBA8 pixel data
47/// * `blocks_buffer` - Destination buffer for the compressed blocks
48/// * `width` - Width of the image in pixels
49/// * `height` - Height of the image in pixels
50/// * `stride` - Number of bytes per row in the source data (for padding).
51///   Must be `width * 4` for tightly packed RGBA data.
52///
53/// # Panics
54/// * If `width` or `height` is not a multiple of 4
55/// * If the destination `blocks_buffer` is too small to hold the compressed data
56///
57/// # Example
58/// ```
59/// use block_compression::{encode::compress_rgba8, CompressionVariant};
60///
61/// let rgba_data = vec![0u8; 256 * 256 * 4]; // Your RGBA data
62/// let width = 256;
63/// let height = 256;
64/// let stride = width * 4; // Tightly packed rows
65/// let variant = CompressionVariant::BC1;
66///
67/// let mut blocks_buffer = vec![0u8; variant.blocks_byte_size(width, height)];
68///
69/// compress_rgba8(
70///     variant,
71///     &rgba_data,
72///     &mut blocks_buffer,
73///     width,
74///     height,
75///     stride,
76/// );
77/// ```
78#[cfg(any(feature = "bc15", feature = "bc6h", feature = "bc7"))]
79#[cfg_attr(
80    docsrs,
81    doc(cfg(any(feature = "bc15", feature = "bc6h", feature = "bc7")))
82)]
83pub fn compress_rgba8(
84    variation: CompressionVariant,
85    rgba_data: &[u8],
86    blocks_buffer: &mut [u8],
87    width: u32,
88    height: u32,
89    stride: u32,
90) {
91    assert_eq!(height % 4, 0);
92    assert_eq!(width % 4, 0);
93
94    let required_size = variation.blocks_byte_size(width, height);
95
96    assert!(
97        blocks_buffer.len() >= required_size,
98        "blocks_buffer size ({}) is too small to hold compressed blocks. Required size: {}",
99        blocks_buffer.len(),
100        required_size
101    );
102
103    let stride = stride as usize;
104    let block_width = (width as usize).div_ceil(4);
105    let block_height = (height as usize).div_ceil(4);
106
107    match variation {
108        #[cfg(feature = "bc15")]
109        CompressionVariant::BC1 => {
110            compress_bc1(rgba_data, blocks_buffer, block_width, block_height, stride);
111        }
112        #[cfg(feature = "bc15")]
113        CompressionVariant::BC2 => {
114            compress_bc2(rgba_data, blocks_buffer, block_width, block_height, stride);
115        }
116        #[cfg(feature = "bc15")]
117        CompressionVariant::BC3 => {
118            compress_bc3(rgba_data, blocks_buffer, block_width, block_height, stride);
119        }
120        #[cfg(feature = "bc15")]
121        CompressionVariant::BC4 => {
122            compress_bc4(rgba_data, blocks_buffer, block_width, block_height, stride);
123        }
124        #[cfg(feature = "bc15")]
125        CompressionVariant::BC5 => {
126            compress_bc5(rgba_data, blocks_buffer, block_width, block_height, stride);
127        }
128        #[cfg(feature = "bc6h")]
129        CompressionVariant::BC6H(settings) => {
130            compress_bc6h_8bit(
131                rgba_data,
132                blocks_buffer,
133                block_width,
134                block_height,
135                stride,
136                &settings,
137            );
138        }
139        #[cfg(feature = "bc7")]
140        CompressionVariant::BC7(settings) => {
141            compress_bc7(
142                rgba_data,
143                blocks_buffer,
144                block_width,
145                block_height,
146                stride,
147                &settings,
148            );
149        }
150    }
151}
152
153/// Compresses raw RGBA16 (half-float) data using the BC6H texture block compression format.
154///
155/// It supports only BC6H compression format and provides CPU-based texture compression
156/// for RGBA16 (half-float) data.
157///
158/// # Data Layout Requirements
159/// The input data must be in RGBA16 format (16 bits per channel using half-float). The data is
160/// expected to be in row-major order, with optional stride for padding between rows.
161///
162/// # Buffer Requirements
163/// The destination buffer must have sufficient capacity to store the compressed blocks.
164/// The required size can be calculated using [`CompressionVariant::blocks_byte_size()`].
165///
166/// For example:
167/// ```ignore
168/// let required_size = variant.blocks_byte_size(width, height);
169/// assert!(blocks_buffer.len() >= required_size);
170/// ```
171///
172/// # Arguments
173/// * `variation` - The block compression format to use (must be BC6H)
174/// * `rgb_data` - Source RGBA16 pixel data in half-float format
175/// * `blocks_buffer` - Destination buffer for the compressed blocks
176/// * `width` - Width of the image in pixels
177/// * `height` - Height of the image in pixels
178/// * `stride` - Number of half-float elements per row in the source data (for padding).
179///   Must be `width * 4` for tightly packed RGBA data.
180///
181/// # Panics
182/// * If `width` or `height` is not a multiple of 4
183/// * If the destination `blocks_buffer` is too small to hold the compressed data
184/// * If `variation` is not `CompressionVariant::BC6H`
185///
186/// # Example
187/// ```
188/// use block_compression::{encode::compress_rgba16, BC6HSettings, CompressionVariant};
189/// use half::f16;
190///
191/// let rgba_data = vec![f16::ZERO; 256 * 256 * 4]; // Your RGBA16 data
192/// let width = 256;
193/// let height = 256;
194/// let stride = width * 4; // Tightly packed rows
195/// let settings = BC6HSettings::very_slow();
196/// let variant = CompressionVariant::BC6H(settings);
197///
198/// let mut blocks_buffer = vec![0u8; variant.blocks_byte_size(width, height)];
199///
200/// compress_rgba16(
201///     variant,
202///     &rgba_data,
203///     &mut blocks_buffer,
204///     width,
205///     height,
206///     stride,
207/// );
208/// ```
209#[cfg(feature = "bc6h")]
210#[cfg_attr(docsrs, doc(cfg(feature = "bc6h")))]
211pub fn compress_rgba16(
212    variation: CompressionVariant,
213    rgba_data: &[half::f16],
214    blocks_buffer: &mut [u8],
215    width: u32,
216    height: u32,
217    stride: u32,
218) {
219    assert_eq!(height % 4, 0);
220    assert_eq!(width % 4, 0);
221
222    let required_size = variation.blocks_byte_size(width, height);
223
224    assert!(
225        blocks_buffer.len() >= required_size,
226        "blocks_buffer size ({}) is too small to hold compressed blocks. Required size: {}",
227        blocks_buffer.len(),
228        required_size
229    );
230
231    let stride = stride as usize;
232    let block_width = (width as usize).div_ceil(4);
233    let block_height = (height as usize).div_ceil(4);
234
235    match variation {
236        CompressionVariant::BC6H(settings) => {
237            compress_bc6h_16bit(
238                rgba_data,
239                blocks_buffer,
240                block_width,
241                block_height,
242                stride,
243                &settings,
244            );
245        }
246        #[allow(unreachable_patterns)]
247        _ => {
248            panic!("only BC6H is supported for calling compress_rgba16");
249        }
250    }
251}
252
253#[cfg(feature = "bc15")]
254fn compress_bc1(
255    rgba_data: &[u8],
256    blocks_buffer: &mut [u8],
257    block_width: usize,
258    block_height: usize,
259    stride: usize,
260) {
261    for yy in 0..block_height {
262        for xx in 0..block_width {
263            let mut block_compressor = BlockCompressorBC15::default();
264
265            block_compressor.load_block_interleaved_rgba(rgba_data, xx, yy, stride);
266            let color_result = block_compressor.compress_block_bc1_core();
267            block_compressor.store_data(blocks_buffer, block_width, xx, yy, &color_result);
268        }
269    }
270}
271
272#[cfg(feature = "bc15")]
273fn compress_bc2(
274    rgba_data: &[u8],
275    blocks_buffer: &mut [u8],
276    block_width: usize,
277    block_height: usize,
278    stride: usize,
279) {
280    for yy in 0..block_height {
281        for xx in 0..block_width {
282            let mut block_compressor = BlockCompressorBC15::default();
283            let mut compressed_data = [0; 4];
284
285            let alpha_result = block_compressor.load_block_alpha_4bit(rgba_data, xx, yy, stride);
286
287            compressed_data[0] = alpha_result[0];
288            compressed_data[1] = alpha_result[1];
289
290            block_compressor.load_block_interleaved_rgba(rgba_data, xx, yy, stride);
291
292            let color_result = block_compressor.compress_block_bc1_core();
293            compressed_data[2] = color_result[0];
294            compressed_data[3] = color_result[1];
295
296            block_compressor.store_data(blocks_buffer, block_width, xx, yy, &compressed_data);
297        }
298    }
299}
300
301#[cfg(feature = "bc15")]
302fn compress_bc3(
303    rgba_data: &[u8],
304    blocks_buffer: &mut [u8],
305    block_width: usize,
306    block_height: usize,
307    stride: usize,
308) {
309    for yy in 0..block_height {
310        for xx in 0..block_width {
311            let mut block_compressor = BlockCompressorBC15::default();
312
313            let mut compressed_data = [0; 4];
314
315            block_compressor.load_block_interleaved_rgba(rgba_data, xx, yy, stride);
316
317            let alpha_result = block_compressor.compress_block_bc3_alpha();
318            compressed_data[0] = alpha_result[0];
319            compressed_data[1] = alpha_result[1];
320
321            let color_result = block_compressor.compress_block_bc1_core();
322            compressed_data[2] = color_result[0];
323            compressed_data[3] = color_result[1];
324
325            block_compressor.store_data(blocks_buffer, block_width, xx, yy, &compressed_data);
326        }
327    }
328}
329
330#[cfg(feature = "bc15")]
331fn compress_bc4(
332    rgba_data: &[u8],
333    blocks_buffer: &mut [u8],
334    block_width: usize,
335    block_height: usize,
336    stride: usize,
337) {
338    for yy in 0..block_height {
339        for xx in 0..block_width {
340            let mut block_compressor = BlockCompressorBC15::default();
341
342            let mut compressed_data = [0; 2];
343
344            block_compressor.load_block_r_8bit(rgba_data, xx, yy, stride);
345
346            let color_result = block_compressor.compress_block_bc3_alpha();
347            compressed_data[0] = color_result[0];
348            compressed_data[1] = color_result[1];
349
350            block_compressor.store_data(blocks_buffer, block_width, xx, yy, &compressed_data);
351        }
352    }
353}
354
355#[cfg(feature = "bc15")]
356fn compress_bc5(
357    rgba_data: &[u8],
358    blocks_buffer: &mut [u8],
359    block_width: usize,
360    block_height: usize,
361    stride: usize,
362) {
363    for yy in 0..block_height {
364        for xx in 0..block_width {
365            let mut block_compressor = BlockCompressorBC15::default();
366
367            let mut compressed_data = [0; 4];
368
369            block_compressor.load_block_r_8bit(rgba_data, xx, yy, stride);
370
371            let red_result = block_compressor.compress_block_bc3_alpha();
372            compressed_data[0] = red_result[0];
373            compressed_data[1] = red_result[1];
374
375            block_compressor.load_block_g_8bit(rgba_data, xx, yy, stride);
376
377            let green_result = block_compressor.compress_block_bc3_alpha();
378            compressed_data[2] = green_result[0];
379            compressed_data[3] = green_result[1];
380
381            block_compressor.store_data(blocks_buffer, block_width, xx, yy, &compressed_data);
382        }
383    }
384}
385
386#[cfg(feature = "bc6h")]
387fn compress_bc6h_8bit(
388    rgba_data: &[u8],
389    blocks_buffer: &mut [u8],
390    block_width: usize,
391    block_height: usize,
392    stride: usize,
393    settings: &BC6HSettings,
394) {
395    for yy in 0..block_height {
396        for xx in 0..block_width {
397            let mut block_compressor = BlockCompressorBC6H::new(settings);
398            block_compressor.load_block_interleaved_8bit(rgba_data, xx, yy, stride);
399            block_compressor.compress_bc6h_core();
400            block_compressor.store_data(blocks_buffer, block_width, xx, yy);
401        }
402    }
403}
404
405#[cfg(feature = "bc6h")]
406fn compress_bc6h_16bit(
407    rgba_data: &[half::f16],
408    blocks_buffer: &mut [u8],
409    block_width: usize,
410    block_height: usize,
411    stride: usize,
412    settings: &BC6HSettings,
413) {
414    for yy in 0..block_height {
415        for xx in 0..block_width {
416            let mut block_compressor = BlockCompressorBC6H::new(settings);
417            block_compressor.load_block_interleaved_16bit(rgba_data, xx, yy, stride);
418            block_compressor.compress_bc6h_core();
419            block_compressor.store_data(blocks_buffer, block_width, xx, yy);
420        }
421    }
422}
423
424#[cfg(feature = "bc7")]
425fn compress_bc7(
426    rgba_data: &[u8],
427    blocks_buffer: &mut [u8],
428    block_width: usize,
429    block_height: usize,
430    stride: usize,
431    settings: &BC7Settings,
432) {
433    for yy in 0..block_height {
434        for xx in 0..block_width {
435            let mut block_compressor = BlockCompressorBC7::new(settings);
436
437            block_compressor.load_block_interleaved_rgba(rgba_data, xx, yy, stride);
438            block_compressor.compute_opaque_err();
439            block_compressor.compress_block_bc7_core();
440            block_compressor.store_data(blocks_buffer, block_width, xx, yy);
441        }
442    }
443}