block_compression\decode/
block.rs

1//! Direct Rust port the "bcdec.h - v0.98"
2//!
3//! <https://github.com/iOrange/bcdec/blob/main/bcdec.h>
4//!
5//! # CREDITS
6//!
7//! Aras Pranckevicius (@aras-p)
8//! - BC1/BC3 decoders optimizations (up to 3x the speed)
9//! - BC6H/BC7 bits pulling routines optimizations
10//! - optimized BC6H by moving unquantize out of the loop
11//! - Split BC6H decompression function into 'half' and
12//!   'float' variants
13//!
14//! Michael Schmidt (@RunDevelopment)
15//! - Found better "magic" coefficients for integer interpolation
16//!   of reference colors in BC1 color block, that match with
17//!   the floating point interpolation. This also made it faster
18//!   than integer division by 3!
19//!
20//! # License
21//!
22//! This is free and unencumbered software released into the public domain.
23//!
24//! Anyone is free to copy, modify, publish, use, compile, sell, or
25//! distribute this software, either in source code form or as a compiled
26//! binary, for any purpose, commercial or non-commercial, and by any
27//! means.
28//!
29//! In jurisdictions that recognize copyright laws, the author or authors
30//! of this software dedicate any and all copyright interest in the
31//! software to the public domain. We make this dedication for the benefit
32//! of the public at large and to the detriment of our heirs and
33//! successors. We intend this dedication to be an overt act of
34//! relinquishment in perpetuity of all present and future rights to this
35//! software under copyright law.
36//!
37//! THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
38//! EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
39//! MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
40//! IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
41//! OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
42//! ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
43//! OTHER DEALINGS IN THE SOFTWARE.
44//!
45//! For more information, please refer to <https://unlicense.org>
46
47/// Decodes a BC1 block by reading 8 bytes from `compressed_block` and writing the RGBA8 data into `decompressed_block` with `destination_pitch` many bytes per output row.
48#[cfg(feature = "bc15")]
49#[inline(always)]
50pub fn decode_block_bc1(
51    compressed_block: &[u8],
52    decompressed_block: &mut [u8],
53    destination_pitch: usize,
54) {
55    decode_color_block::<false>(compressed_block, decompressed_block, destination_pitch);
56}
57
58/// Decodes a BC2 block by reading 16 bytes from `compressed_block` and writing the RGBA8 data into `decompressed_block` with `destination_pitch` many bytes per output row.
59#[cfg(feature = "bc15")]
60#[inline(always)]
61pub fn decode_block_bc2(
62    compressed_block: &[u8],
63    decompressed_block: &mut [u8],
64    destination_pitch: usize,
65) {
66    decode_color_block::<true>(
67        &compressed_block[8..],
68        decompressed_block,
69        destination_pitch,
70    );
71    decode_sharp_alpha_block(compressed_block, decompressed_block, destination_pitch);
72}
73
74/// Decodes a BC3 block by reading 16 bytes from `compressed_block` and writing the RGBA8 data into `decompressed_block` with `destination_pitch` many bytes per output row.
75#[cfg(feature = "bc15")]
76#[inline(always)]
77pub fn decode_block_bc3(
78    compressed_block: &[u8],
79    decompressed_block: &mut [u8],
80    destination_pitch: usize,
81) {
82    decode_color_block::<true>(
83        &compressed_block[8..],
84        decompressed_block,
85        destination_pitch,
86    );
87    decode_smooth_alpha_block::<4>(
88        compressed_block,
89        &mut decompressed_block[3..],
90        destination_pitch,
91    );
92}
93
94/// Decodes a BC4 block by reading 8 bytes from `compressed_block` and writing the R8 data into `decompressed_block` with `destination_pitch` many bytes per output row.
95#[cfg(feature = "bc15")]
96#[inline(always)]
97pub fn decode_block_bc4(
98    compressed_block: &[u8],
99    decompressed_block: &mut [u8],
100    destination_pitch: usize,
101) {
102    decode_smooth_alpha_block::<1>(compressed_block, decompressed_block, destination_pitch);
103}
104
105/// Decodes a BC5 block by reading 16 bytes from `compressed_block` and writing the RG8 data into `decompressed_block` with `destination_pitch` many bytes per output row.
106#[cfg(feature = "bc15")]
107#[inline(always)]
108pub fn decode_block_bc5(
109    compressed_block: &[u8],
110    decompressed_block: &mut [u8],
111    destination_pitch: usize,
112) {
113    decode_smooth_alpha_block::<2>(compressed_block, decompressed_block, destination_pitch);
114    decode_smooth_alpha_block::<2>(
115        &compressed_block[8..],
116        &mut decompressed_block[1..],
117        destination_pitch,
118    );
119}
120
121/// Decompresses a BC1/DXT1 color block
122#[cfg(feature = "bc15")]
123#[inline(always)]
124fn decode_color_block<const OPAQUE_MODE: bool>(
125    compressed_block: &[u8],
126    decompressed_block: &mut [u8],
127    destination_pitch: usize,
128) {
129    let mut ref_colors = [0u32; 4];
130    let c0 = u16::from_le_bytes([compressed_block[0], compressed_block[1]]);
131    let c1 = u16::from_le_bytes([compressed_block[2], compressed_block[3]]);
132
133    // Unpack 565 ref colors
134    let r0 = (c0 >> 11) & 0x1F;
135    let g0 = (c0 >> 5) & 0x3F;
136    let b0 = c0 & 0x1F;
137
138    let r1 = (c1 >> 11) & 0x1F;
139    let g1 = (c1 >> 5) & 0x3F;
140    let b1 = c1 & 0x1F;
141
142    // Expand 565 ref colors to 888
143    let r = (r0 as u32 * 527 + 23) >> 6;
144    let g = (g0 as u32 * 259 + 33) >> 6;
145    let b = (b0 as u32 * 527 + 23) >> 6;
146    ref_colors[0] = 0xFF000000 | (b << 16) | (g << 8) | r;
147
148    let r = (r1 as u32 * 527 + 23) >> 6;
149    let g = (g1 as u32 * 259 + 33) >> 6;
150    let b = (b1 as u32 * 527 + 23) >> 6;
151    ref_colors[1] = 0xFF000000 | (b << 16) | (g << 8) | r;
152
153    if c0 > c1 || OPAQUE_MODE {
154        // Standard BC1 mode (also BC3 color block uses ONLY this mode)
155        // color_2 = 2/3*color_0 + 1/3*color_1
156        // color_3 = 1/3*color_0 + 2/3*color_1
157        let r = ((2 * r0 as u32 + r1 as u32) * 351 + 61) >> 7;
158        let g = ((2 * g0 as u32 + g1 as u32) * 2763 + 1039) >> 11;
159        let b = ((2 * b0 as u32 + b1 as u32) * 351 + 61) >> 7;
160        ref_colors[2] = 0xFF000000 | (b << 16) | (g << 8) | r;
161
162        let r = ((r0 as u32 + r1 as u32 * 2) * 351 + 61) >> 7;
163        let g = ((g0 as u32 + g1 as u32 * 2) * 2763 + 1039) >> 11;
164        let b = ((b0 as u32 + b1 as u32 * 2) * 351 + 61) >> 7;
165        ref_colors[3] = 0xFF000000 | (b << 16) | (g << 8) | r;
166    } else {
167        // Quite rare BC1A mode
168        // color_2 = 1/2*color_0 + 1/2*color_1
169        // color_3 = 0
170        let r = ((r0 as u32 + r1 as u32) * 1053 + 125) >> 8;
171        let g = ((g0 as u32 + g1 as u32) * 4145 + 1019) >> 11;
172        let b = ((b0 as u32 + b1 as u32) * 1053 + 125) >> 8;
173        ref_colors[2] = 0xFF000000 | (b << 16) | (g << 8) | r;
174        ref_colors[3] = 0x00000000;
175    }
176
177    let mut color_indices = u32::from_le_bytes([
178        compressed_block[4],
179        compressed_block[5],
180        compressed_block[6],
181        compressed_block[7],
182    ]);
183
184    // Fill out the decompressed color block
185    for i in 0..4 {
186        for j in 0..4 {
187            let idx = color_indices & 0x03;
188            let offset = j * 4;
189            let color = ref_colors[idx as usize];
190
191            decompressed_block[i * destination_pitch + offset..][..4]
192                .copy_from_slice(&color.to_le_bytes());
193
194            color_indices >>= 2;
195        }
196    }
197}
198
199/// Decodes a BC2/DXT3 alpha block (sharp transitions)
200#[cfg(feature = "bc15")]
201#[inline(always)]
202fn decode_sharp_alpha_block(
203    compressed_block: &[u8],
204    decompressed_block: &mut [u8],
205    destination_pitch: usize,
206) {
207    for i in 0..4 {
208        for j in 0..4 {
209            let byte_index = i * 2 + (j / 2);
210            let shift = (j % 2) * 4;
211            let alpha_value = (compressed_block[byte_index] >> shift) & 0x0F;
212            decompressed_block[i * destination_pitch + j * 4 + 3] = alpha_value * 17;
213        }
214    }
215}
216
217/// Decodes a BC2/DXT3 alpha block (smooth transitions)
218#[cfg(feature = "bc15")]
219#[inline(always)]
220#[rustfmt::skip]
221fn decode_smooth_alpha_block<const PIXEL_SIZE: usize>(
222    compressed_block: &[u8],
223    decompressed_block: &mut [u8],
224    destination_pitch: usize,
225) {
226    let block = u64::from_le_bytes(compressed_block[0..8].try_into().unwrap());
227
228    let mut alpha = [0u8; 8];
229    alpha[0] = (block & 0xFF) as u8;
230    alpha[1] = ((block >> 8) & 0xFF) as u8;
231
232    if alpha[0] > alpha[1] {
233        // 6 interpolated alpha values
234        alpha[2] = ((6 * alpha[0] as u16 +     alpha[1] as u16) / 7) as u8;   // 6/7*alpha_0 + 1/7*alpha_1
235        alpha[3] = ((5 * alpha[0] as u16 + 2 * alpha[1] as u16) / 7) as u8;   // 5/7*alpha_0 + 2/7*alpha_1
236        alpha[4] = ((4 * alpha[0] as u16 + 3 * alpha[1] as u16) / 7) as u8;   // 4/7*alpha_0 + 3/7*alpha_1
237        alpha[5] = ((3 * alpha[0] as u16 + 4 * alpha[1] as u16) / 7) as u8;   // 3/7*alpha_0 + 4/7*alpha_1
238        alpha[6] = ((2 * alpha[0] as u16 + 5 * alpha[1] as u16) / 7) as u8;   // 2/7*alpha_0 + 5/7*alpha_1
239        alpha[7] = ((    alpha[0] as u16 + 6 * alpha[1] as u16) / 7) as u8;   // 1/7*alpha_0 + 6/7*alpha_1
240    } else {
241        // 4 interpolated alpha values
242        alpha[2] = ((4 * alpha[0] as u16 +     alpha[1] as u16) / 5) as u8;   // 4/5*alpha_0 + 1/5*alpha_1
243        alpha[3] = ((3 * alpha[0] as u16 + 2 * alpha[1] as u16) / 5) as u8;   // 3/5*alpha_0 + 2/5*alpha_1
244        alpha[4] = ((2 * alpha[0] as u16 + 3 * alpha[1] as u16) / 5) as u8;   // 2/5*alpha_0 + 3/5*alpha_1
245        alpha[5] = ((    alpha[0] as u16 + 4 * alpha[1] as u16) / 5) as u8;   // 1/5*alpha_0 + 4/5*alpha_1
246        alpha[6] = 0x00;
247        alpha[7] = 0xFF;
248    }
249
250    let mut indices = block >> 16;
251
252    for i in 0..4 {
253        for j in 0..4 {
254            decompressed_block[i * destination_pitch + j * PIXEL_SIZE] = alpha[(indices & 0x07) as usize];
255            indices >>= 3;
256        }
257    }
258}
259
260/// Decodes a BC7 block by reading 16 bytes from `compressed_block` and writing the RGB16F data (half float) into `decompressed_block` with `destination_pitch` many bytes per output row.
261#[cfg(feature = "bc6h")]
262pub fn decode_block_bc6h(
263    compressed_block: &[u8],
264    decompressed_block: &mut [half::f16],
265    destination_pitch: usize,
266    is_signed: bool,
267) {
268    use half::f16;
269
270    static ACTUAL_BITS_COUNT: &[[u8; 14]; 4] = &[
271        [10, 7, 11, 11, 11, 9, 8, 8, 8, 6, 10, 11, 12, 16], // W
272        [5, 6, 5, 4, 4, 5, 6, 5, 5, 6, 10, 9, 8, 4],        // dR
273        [5, 6, 4, 5, 4, 5, 5, 6, 5, 6, 10, 9, 8, 4],        // dG
274        [5, 6, 4, 4, 5, 5, 5, 5, 6, 6, 10, 9, 8, 4],        // dB
275    ];
276
277    // There are 32 possible partition sets for a two-region tile.
278    // Each 4x4 block represents a single shape.
279    //Here also every fix-up index has MSB bit set.
280    static PARTITION_SETS: &[[[u8; 4]; 4]; 32] = &[
281        [[128, 0, 1, 1], [0, 0, 1, 1], [0, 0, 1, 1], [0, 0, 1, 129]], //  0
282        [[128, 0, 0, 1], [0, 0, 0, 1], [0, 0, 0, 1], [0, 0, 0, 129]], //  1
283        [[128, 1, 1, 1], [0, 1, 1, 1], [0, 1, 1, 1], [0, 1, 1, 129]], //  2
284        [[128, 0, 0, 1], [0, 0, 1, 1], [0, 0, 1, 1], [0, 1, 1, 129]], //  3
285        [[128, 0, 0, 0], [0, 0, 0, 1], [0, 0, 0, 1], [0, 0, 1, 129]], //  4
286        [[128, 0, 1, 1], [0, 1, 1, 1], [0, 1, 1, 1], [1, 1, 1, 129]], //  5
287        [[128, 0, 0, 1], [0, 0, 1, 1], [0, 1, 1, 1], [1, 1, 1, 129]], //  6
288        [[128, 0, 0, 0], [0, 0, 0, 1], [0, 0, 1, 1], [0, 1, 1, 129]], //  7
289        [[128, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 1], [0, 0, 1, 129]], //  8
290        [[128, 0, 1, 1], [0, 1, 1, 1], [1, 1, 1, 1], [1, 1, 1, 129]], //  9
291        [[128, 0, 0, 0], [0, 0, 0, 1], [0, 1, 1, 1], [1, 1, 1, 129]], // 10
292        [[128, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 1], [0, 1, 1, 129]], // 11
293        [[128, 0, 0, 1], [0, 1, 1, 1], [1, 1, 1, 1], [1, 1, 1, 129]], // 12
294        [[128, 0, 0, 0], [0, 0, 0, 0], [1, 1, 1, 1], [1, 1, 1, 129]], // 13
295        [[128, 0, 0, 0], [1, 1, 1, 1], [1, 1, 1, 1], [1, 1, 1, 129]], // 14
296        [[128, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [1, 1, 1, 129]], // 15
297        [[128, 0, 0, 0], [1, 0, 0, 0], [1, 1, 1, 0], [1, 1, 1, 129]], // 16
298        [[128, 1, 129, 1], [0, 0, 0, 1], [0, 0, 0, 0], [0, 0, 0, 0]], // 17
299        [[128, 0, 0, 0], [0, 0, 0, 0], [129, 0, 0, 0], [1, 1, 1, 0]], // 18
300        [[128, 1, 129, 1], [0, 0, 1, 1], [0, 0, 0, 1], [0, 0, 0, 0]], // 19
301        [[128, 0, 129, 1], [0, 0, 0, 1], [0, 0, 0, 0], [0, 0, 0, 0]], // 20
302        [[128, 0, 0, 0], [1, 0, 0, 0], [129, 1, 0, 0], [1, 1, 1, 0]], // 21
303        [[128, 0, 0, 0], [0, 0, 0, 0], [129, 0, 0, 0], [1, 1, 0, 0]], // 22
304        [[128, 1, 1, 1], [0, 0, 1, 1], [0, 0, 1, 1], [0, 0, 0, 129]], // 23
305        [[128, 0, 129, 1], [0, 0, 0, 1], [0, 0, 0, 1], [0, 0, 0, 0]], // 24
306        [[128, 0, 0, 0], [1, 0, 0, 0], [129, 0, 0, 0], [1, 1, 0, 0]], // 25
307        [[128, 1, 129, 0], [0, 1, 1, 0], [0, 1, 1, 0], [0, 1, 1, 0]], // 26
308        [[128, 0, 129, 1], [0, 1, 1, 0], [0, 1, 1, 0], [1, 1, 0, 0]], // 27
309        [[128, 0, 0, 1], [0, 1, 1, 1], [129, 1, 1, 0], [1, 0, 0, 0]], // 28
310        [[128, 0, 0, 0], [1, 1, 1, 1], [129, 1, 1, 1], [0, 0, 0, 0]], // 29
311        [[128, 1, 129, 1], [0, 0, 0, 1], [1, 0, 0, 0], [1, 1, 1, 0]], // 30
312        [[128, 0, 129, 1], [1, 0, 0, 1], [1, 0, 0, 1], [1, 1, 0, 0]], // 31
313    ];
314
315    const WEIGHT3: &[i32] = &[0, 9, 18, 27, 37, 46, 55, 64];
316    const WEIGHT4: &[i32] = &[0, 4, 9, 13, 17, 21, 26, 30, 34, 38, 43, 47, 51, 55, 60, 64];
317
318    let mut bstream = BitStream::new(compressed_block);
319
320    let mut r = [0i32; 4];
321    let mut g = [0i32; 4];
322    let mut b = [0i32; 4];
323
324    let mut mode = bstream.read_bits(2);
325    if mode > 1 {
326        mode |= bstream.read_bits(3) << 2;
327    }
328
329    // modes >= 11 (10 in my code) are using 0 one, others will read it from the bitstream
330    let mut partition = 0;
331
332    match mode {
333        // Mode 1
334        0b00 => {
335            // Partition indices: 46 bits
336            // Partition: 5 bits
337            // Color Endpoints: 75 bits (10.555, 10.555, 10.555)
338            g[2] |= bstream.read_bit_i32() << 4; // gy[4]
339            b[2] |= bstream.read_bit_i32() << 4; // by[4]
340            b[3] |= bstream.read_bit_i32() << 4; // bz[4]
341            r[0] |= bstream.read_bits_i32(10); // rw[9:0]
342            g[0] |= bstream.read_bits_i32(10); // gw[9:0]
343            b[0] |= bstream.read_bits_i32(10); // bw[9:0]
344            r[1] |= bstream.read_bits_i32(5); // rx[4:0]
345            g[3] |= bstream.read_bit_i32() << 4; // gz[4]
346            g[2] |= bstream.read_bits_i32(4); // gy[3:0]
347            g[1] |= bstream.read_bits_i32(5); // gx[4:0]
348            b[3] |= bstream.read_bit_i32(); // bz[0]
349            g[3] |= bstream.read_bits_i32(4); // gz[3:0]
350            b[1] |= bstream.read_bits_i32(5); // bx[4:0]
351            b[3] |= bstream.read_bit_i32() << 1; // bz[1]
352            b[2] |= bstream.read_bits_i32(4); // by[3:0]
353            r[2] |= bstream.read_bits_i32(5); // ry[4:0]
354            b[3] |= bstream.read_bit_i32() << 2; // bz[2]
355            r[3] |= bstream.read_bits_i32(5); // rz[4:0]
356            b[3] |= bstream.read_bit_i32() << 3; // bz[3]
357            partition = bstream.read_bits_i32(5); // d[4:0]
358            mode = 0;
359        }
360
361        // Mode 2
362        0b01 => {
363            // Partition indices: 46 bits
364            // Partition: 5 bits
365            // Color Endpoints: 75 bits (7666, 7666, 7666)
366            g[2] |= bstream.read_bit_i32() << 5; // gy[5]
367            g[3] |= bstream.read_bit_i32() << 4; // gz[4]
368            g[3] |= bstream.read_bit_i32() << 5; // gz[5]
369            r[0] |= bstream.read_bits_i32(7); // rw[6:0]
370            b[3] |= bstream.read_bit_i32(); // bz[0]
371            b[3] |= bstream.read_bit_i32() << 1; // bz[1]
372            b[2] |= bstream.read_bit_i32() << 4; // by[4]
373            g[0] |= bstream.read_bits_i32(7); // gw[6:0]
374            b[2] |= bstream.read_bit_i32() << 5; // by[5]
375            b[3] |= bstream.read_bit_i32() << 2; // bz[2]
376            g[2] |= bstream.read_bit_i32() << 4; // gy[4]
377            b[0] |= bstream.read_bits_i32(7); // bw[6:0]
378            b[3] |= bstream.read_bit_i32() << 3; // bz[3]
379            b[3] |= bstream.read_bit_i32() << 5; // bz[5]
380            b[3] |= bstream.read_bit_i32() << 4; // bz[4]
381            r[1] |= bstream.read_bits_i32(6); // rx[5:0]
382            g[2] |= bstream.read_bits_i32(4); // gy[3:0]
383            g[1] |= bstream.read_bits_i32(6); // gx[5:0]
384            g[3] |= bstream.read_bits_i32(4); // gz[3:0]
385            b[1] |= bstream.read_bits_i32(6); // bx[5:0]
386            b[2] |= bstream.read_bits_i32(4); // by[3:0]
387            r[2] |= bstream.read_bits_i32(6); // ry[5:0]
388            r[3] |= bstream.read_bits_i32(6); // rz[5:0]
389            partition = bstream.read_bits_i32(5); // d[4:0]
390            mode = 1;
391        }
392
393        // Mode 3
394        0b00010 => {
395            // Partition indices: 46 bits
396            // Partition: 5 bits
397            // Color Endpoints: 72 bits (11.555, 11.444, 11.444)
398            r[0] |= bstream.read_bits_i32(10); // rw[9:0]
399            g[0] |= bstream.read_bits_i32(10); // gw[9:0]
400            b[0] |= bstream.read_bits_i32(10); // bw[9:0]
401            r[1] |= bstream.read_bits_i32(5); // rx[4:0]
402            r[0] |= bstream.read_bit_i32() << 10; // rw[10]
403            g[2] |= bstream.read_bits_i32(4); // gy[3:0]
404            g[1] |= bstream.read_bits_i32(4); // gx[3:0]
405            g[0] |= bstream.read_bit_i32() << 10; // gw[10]
406            b[3] |= bstream.read_bit_i32(); // bz[0]
407            g[3] |= bstream.read_bits_i32(4); // gz[3:0]
408            b[1] |= bstream.read_bits_i32(4); // bx[3:0]
409            b[0] |= bstream.read_bit_i32() << 10; // bw[10]
410            b[3] |= bstream.read_bit_i32() << 1; // bz[1]
411            b[2] |= bstream.read_bits_i32(4); // by[3:0]
412            r[2] |= bstream.read_bits_i32(5); // ry[4:0]
413            b[3] |= bstream.read_bit_i32() << 2; // bz[2]
414            r[3] |= bstream.read_bits_i32(5); // rz[4:0]
415            b[3] |= bstream.read_bit_i32() << 3; // bz[3]
416            partition = bstream.read_bits_i32(5); // d[4:0]
417            mode = 2;
418        }
419        // Mode 4
420        0b00110 => {
421            // Partition indices: 46 bits
422            // Partition: 5 bits
423            // Color Endpoints: 72 bits (11.444, 11.555, 11.444)
424            r[0] |= bstream.read_bits_i32(10); // rw[9:0]
425            g[0] |= bstream.read_bits_i32(10); // gw[9:0]
426            b[0] |= bstream.read_bits_i32(10); // bw[9:0]
427            r[1] |= bstream.read_bits_i32(4); // rx[3:0]
428            r[0] |= bstream.read_bit_i32() << 10; // rw[10]
429            g[3] |= bstream.read_bit_i32() << 4; // gz[4]
430            g[2] |= bstream.read_bits_i32(4); // gy[3:0]
431            g[1] |= bstream.read_bits_i32(5); // gx[4:0]
432            g[0] |= bstream.read_bit_i32() << 10; // gw[10]
433            g[3] |= bstream.read_bits_i32(4); // gz[3:0]
434            b[1] |= bstream.read_bits_i32(4); // bx[3:0]
435            b[0] |= bstream.read_bit_i32() << 10; // bw[10]
436            b[3] |= bstream.read_bit_i32() << 1; // bz[1]
437            b[2] |= bstream.read_bits_i32(4); // by[3:0]
438            r[2] |= bstream.read_bits_i32(4); // ry[3:0]
439            b[3] |= bstream.read_bit_i32(); // bz[0]
440            b[3] |= bstream.read_bit_i32() << 2; // bz[2]
441            r[3] |= bstream.read_bits_i32(4); // rz[3:0]
442            g[2] |= bstream.read_bit_i32() << 4; // gy[4]
443            b[3] |= bstream.read_bit_i32() << 3; // bz[3]
444            partition = bstream.read_bits_i32(5); // d[4:0]
445            mode = 3;
446        }
447        // Mode 5
448        0b01010 => {
449            // Partition indices: 46 bits
450            // Partition: 5 bits
451            // Color Endpoints: 72 bits (11.444, 11.444, 11.555)
452            r[0] |= bstream.read_bits_i32(10); // rw[9:0]
453            g[0] |= bstream.read_bits_i32(10); // gw[9:0]
454            b[0] |= bstream.read_bits_i32(10); // bw[9:0]
455            r[1] |= bstream.read_bits_i32(4); // rx[3:0]
456            r[0] |= bstream.read_bit_i32() << 10; // rw[10]
457            b[2] |= bstream.read_bit_i32() << 4; // by[4]
458            g[2] |= bstream.read_bits_i32(4); // gy[3:0]
459            g[1] |= bstream.read_bits_i32(4); // gx[3:0]
460            g[0] |= bstream.read_bit_i32() << 10; // gw[10]
461            b[3] |= bstream.read_bit_i32(); // bz[0]
462            g[3] |= bstream.read_bits_i32(4); // gz[3:0]
463            b[1] |= bstream.read_bits_i32(5); // bx[4:0]
464            b[0] |= bstream.read_bit_i32() << 10; // bw[10]
465            b[2] |= bstream.read_bits_i32(4); // by[3:0]
466            r[2] |= bstream.read_bits_i32(4); // ry[3:0]
467            b[3] |= bstream.read_bit_i32() << 1; // bz[1]
468            b[3] |= bstream.read_bit_i32() << 2; // bz[2]
469            r[3] |= bstream.read_bits_i32(4); // rz[3:0]
470            b[3] |= bstream.read_bit_i32() << 4; // bz[4]
471            b[3] |= bstream.read_bit_i32() << 3; // bz[3]
472            partition = bstream.read_bits_i32(5); // d[4:0]
473            mode = 4;
474        }
475        // Mode 6
476        0b01110 => {
477            // Partition indices: 46 bits
478            // Partition: 5 bits
479            // Color Endpoints: 72 bits (9555, 9555, 9555)
480            r[0] |= bstream.read_bits_i32(9); // rw[8:0]
481            b[2] |= bstream.read_bit_i32() << 4; // by[4]
482            g[0] |= bstream.read_bits_i32(9); // gw[8:0]
483            g[2] |= bstream.read_bit_i32() << 4; // gy[4]
484            b[0] |= bstream.read_bits_i32(9); // bw[8:0]
485            b[3] |= bstream.read_bit_i32() << 4; // bz[4]
486            r[1] |= bstream.read_bits_i32(5); // rx[4:0]
487            g[3] |= bstream.read_bit_i32() << 4; // gz[4]
488            g[2] |= bstream.read_bits_i32(4); // gy[3:0]
489            g[1] |= bstream.read_bits_i32(5); // gx[4:0]
490            b[3] |= bstream.read_bit_i32(); // bz[0]
491            g[3] |= bstream.read_bits_i32(4); // gx[3:0]
492            b[1] |= bstream.read_bits_i32(5); // bx[4:0]
493            b[3] |= bstream.read_bit_i32() << 1; // bz[1]
494            b[2] |= bstream.read_bits_i32(4); // by[3:0]
495            r[2] |= bstream.read_bits_i32(5); // ry[4:0]
496            b[3] |= bstream.read_bit_i32() << 2; // bz[2]
497            r[3] |= bstream.read_bits_i32(5); // rz[4:0]
498            b[3] |= bstream.read_bit_i32() << 3; // bz[3]
499            partition = bstream.read_bits_i32(5); // d[4:0]
500            mode = 5;
501        }
502        // Mode 7
503        0b10010 => {
504            // Partition indices: 46 bits
505            // Partition: 5 bits
506            // Color Endpoints: 72 bits (8666, 8555, 8555)
507            r[0] |= bstream.read_bits_i32(8); // rw[7:0]
508            g[3] |= bstream.read_bit_i32() << 4; // gz[4]
509            b[2] |= bstream.read_bit_i32() << 4; // by[4]
510            g[0] |= bstream.read_bits_i32(8); // gw[7:0]
511            b[3] |= bstream.read_bit_i32() << 2; // bz[2]
512            g[2] |= bstream.read_bit_i32() << 4; // gy[4]
513            b[0] |= bstream.read_bits_i32(8); // bw[7:0]
514            b[3] |= bstream.read_bit_i32() << 3; // bz[3]
515            b[3] |= bstream.read_bit_i32() << 4; // bz[4]
516            r[1] |= bstream.read_bits_i32(6); // rx[5:0]
517            g[2] |= bstream.read_bits_i32(4); // gy[3:0]
518            g[1] |= bstream.read_bits_i32(5); // gx[4:0]
519            b[3] |= bstream.read_bit_i32(); // bz[0]
520            g[3] |= bstream.read_bits_i32(4); // gz[3:0]
521            b[1] |= bstream.read_bits_i32(5); // bx[4:0]
522            b[3] |= bstream.read_bit_i32() << 1; // bz[1]
523            b[2] |= bstream.read_bits_i32(4); // by[3:0]
524            r[2] |= bstream.read_bits_i32(6); // ry[5:0]
525            r[3] |= bstream.read_bits_i32(6); // rz[5:0]
526            partition = bstream.read_bits_i32(5); // d[4:0]
527            mode = 6;
528        }
529        // Mode 8
530        0b10110 => {
531            // Partition indices: 46 bits
532            // Partition: 5 bits
533            // Color Endpoints: 72 bits (8555, 8666, 8555)
534            r[0] |= bstream.read_bits_i32(8); // rw[7:0]
535            b[3] |= bstream.read_bit_i32(); // bz[0]
536            b[2] |= bstream.read_bit_i32() << 4; // by[4]
537            g[0] |= bstream.read_bits_i32(8); // gw[7:0]
538            g[2] |= bstream.read_bit_i32() << 5; // gy[5]
539            g[2] |= bstream.read_bit_i32() << 4; // gy[4]
540            b[0] |= bstream.read_bits_i32(8); // bw[7:0]
541            g[3] |= bstream.read_bit_i32() << 5; // gz[5]
542            b[3] |= bstream.read_bit_i32() << 4; // bz[4]
543            r[1] |= bstream.read_bits_i32(5); // rx[4:0]
544            g[3] |= bstream.read_bit_i32() << 4; // gz[4]
545            g[2] |= bstream.read_bits_i32(4); // gy[3:0]
546            g[1] |= bstream.read_bits_i32(6); // gx[5:0]
547            g[3] |= bstream.read_bits_i32(4); // zx[3:0]
548            b[1] |= bstream.read_bits_i32(5); // bx[4:0]
549            b[3] |= bstream.read_bit_i32() << 1; // bz[1]
550            b[2] |= bstream.read_bits_i32(4); // by[3:0]
551            r[2] |= bstream.read_bits_i32(5); // ry[4:0]
552            b[3] |= bstream.read_bit_i32() << 2; // bz[2]
553            r[3] |= bstream.read_bits_i32(5); // rz[4:0]
554            b[3] |= bstream.read_bit_i32() << 3; // bz[3]
555            partition = bstream.read_bits_i32(5); // d[4:0]
556            mode = 7;
557        }
558        // Mode 9
559        0b11010 => {
560            // Partition indices: 46 bits
561            // Partition: 5 bits
562            // Color Endpoints: 72 bits (8555, 8555, 8666)
563            r[0] |= bstream.read_bits_i32(8); // rw[7:0]
564            b[3] |= bstream.read_bit_i32() << 1; // bz[1]
565            b[2] |= bstream.read_bit_i32() << 4; // by[4]
566            g[0] |= bstream.read_bits_i32(8); // gw[7:0]
567            b[2] |= bstream.read_bit_i32() << 5; // by[5]
568            g[2] |= bstream.read_bit_i32() << 4; // gy[4]
569            b[0] |= bstream.read_bits_i32(8); // bw[7:0]
570            b[3] |= bstream.read_bit_i32() << 5; // bz[5]
571            b[3] |= bstream.read_bit_i32() << 4; // bz[4]
572            r[1] |= bstream.read_bits_i32(5); // bw[4:0]
573            g[3] |= bstream.read_bit_i32() << 4; // gz[4]
574            g[2] |= bstream.read_bits_i32(4); // gy[3:0]
575            g[1] |= bstream.read_bits_i32(5); // gx[4:0]
576            b[3] |= bstream.read_bit_i32(); // bz[0]
577            g[3] |= bstream.read_bits_i32(4); // gz[3:0]
578            b[1] |= bstream.read_bits_i32(6); // bx[5:0]
579            b[2] |= bstream.read_bits_i32(4); // by[3:0]
580            r[2] |= bstream.read_bits_i32(5); // ry[4:0]
581            b[3] |= bstream.read_bit_i32() << 2; // bz[2]
582            r[3] |= bstream.read_bits_i32(5); // rz[4:0]
583            b[3] |= bstream.read_bit_i32() << 3; // bz[3]
584            partition = bstream.read_bits_i32(5); // d[4:0]
585            mode = 8;
586        }
587        // Mode 10
588        0b11110 => {
589            // Partition indices: 46 bits
590            // Partition: 5 bits
591            // Color Endpoints: 72 bits (6666, 6666, 6666)
592            r[0] |= bstream.read_bits_i32(6); // rw[5:0]
593            g[3] |= bstream.read_bit_i32() << 4; // gz[4]
594            b[3] |= bstream.read_bit_i32(); // bz[0]
595            b[3] |= bstream.read_bit_i32() << 1; // bz[1]
596            b[2] |= bstream.read_bit_i32() << 4; // by[4]
597            g[0] |= bstream.read_bits_i32(6); // gw[5:0]
598            g[2] |= bstream.read_bit_i32() << 5; // gy[5]
599            b[2] |= bstream.read_bit_i32() << 5; // by[5]
600            b[3] |= bstream.read_bit_i32() << 2; // bz[2]
601            g[2] |= bstream.read_bit_i32() << 4; // gy[4]
602            b[0] |= bstream.read_bits_i32(6); // bw[5:0]
603            g[3] |= bstream.read_bit_i32() << 5; // gz[5]
604            b[3] |= bstream.read_bit_i32() << 3; // bz[3]
605            b[3] |= bstream.read_bit_i32() << 5; // bz[5]
606            b[3] |= bstream.read_bit_i32() << 4; // bz[4]
607            r[1] |= bstream.read_bits_i32(6); // rx[5:0]
608            g[2] |= bstream.read_bits_i32(4); // gy[3:0]
609            g[1] |= bstream.read_bits_i32(6); // gx[5:0]
610            g[3] |= bstream.read_bits_i32(4); // gz[3:0]
611            b[1] |= bstream.read_bits_i32(6); // bx[5:0]
612            b[2] |= bstream.read_bits_i32(4); // by[3:0]
613            r[2] |= bstream.read_bits_i32(6); // ry[5:0]
614            r[3] |= bstream.read_bits_i32(6); // rz[5:0]
615            partition = bstream.read_bits_i32(5); // d[4:0]
616            mode = 9;
617        }
618        // Mode 11
619        0b00011 => {
620            // Partition indices: 63 bits
621            // Partition: 0 bits
622            // Color Endpoints: 60 bits (10.10, 10.10, 10.10)
623            r[0] |= bstream.read_bits_i32(10); // rw[9:0]
624            g[0] |= bstream.read_bits_i32(10); // gw[9:0]
625            b[0] |= bstream.read_bits_i32(10); // bw[9:0]
626            r[1] |= bstream.read_bits_i32(10); // rx[9:0]
627            g[1] |= bstream.read_bits_i32(10); // gx[9:0]
628            b[1] |= bstream.read_bits_i32(10); // bx[9:0]
629            mode = 10;
630        }
631        // Mode 12
632        0b00111 => {
633            // Partition indices: 63 bits
634            // Partition: 0 bits
635            // Color Endpoints: 60 bits (11.9, 11.9, 11.9)
636            r[0] |= bstream.read_bits_i32(10); // rw[9:0]
637            g[0] |= bstream.read_bits_i32(10); // gw[9:0]
638            b[0] |= bstream.read_bits_i32(10); // bw[9:0]
639            r[1] |= bstream.read_bits_i32(9); // rx[8:0]
640            r[0] |= bstream.read_bit_i32() << 10; // rw[10]
641            g[1] |= bstream.read_bits_i32(9); // gx[8:0]
642            g[0] |= bstream.read_bit_i32() << 10; // gw[10]
643            b[1] |= bstream.read_bits_i32(9); // bx[8:0]
644            b[0] |= bstream.read_bit_i32() << 10; // bw[10]
645            mode = 11;
646        }
647        // Mode 13
648        0b01011 => {
649            // Partition indices: 63 bits
650            // Partition: 0 bits
651            // Color Endpoints: 60 bits (12.8, 12.8, 12.8)
652            r[0] |= bstream.read_bits_i32(10); // rw[9:0]
653            g[0] |= bstream.read_bits_i32(10); // gw[9:0]
654            b[0] |= bstream.read_bits_i32(10); // bw[9:0]
655            r[1] |= bstream.read_bits_i32(8); // rx[7:0]
656            r[0] |= bstream.read_bits_reversed(2) << 10; // rx[10:11]
657            g[1] |= bstream.read_bits_i32(8); // gx[7:0]
658            g[0] |= bstream.read_bits_reversed(2) << 10; // gx[10:11]
659            b[1] |= bstream.read_bits_i32(8); // bx[7:0]
660            b[0] |= bstream.read_bits_reversed(2) << 10; // bx[10:11]
661            mode = 12;
662        }
663        // Mode 14
664        0b01111 => {
665            // Partition indices: 63 bits
666            // Partition: 0 bits
667            // Color Endpoints: 60 bits (16.4, 16.4, 16.4)
668            r[0] |= bstream.read_bits_i32(10); // rw[9:0]
669            g[0] |= bstream.read_bits_i32(10); // gw[9:0]
670            b[0] |= bstream.read_bits_i32(10); // bw[9:0]
671            r[1] |= bstream.read_bits_i32(4); // rx[3:0]
672            r[0] |= bstream.read_bits_reversed(6) << 10; // rw[10:15]
673            g[1] |= bstream.read_bits_i32(4); // gx[3:0]
674            g[0] |= bstream.read_bits_reversed(6) << 10; // gw[10:15]
675            b[1] |= bstream.read_bits_i32(4); // bx[3:0]
676            b[0] |= bstream.read_bits_reversed(6) << 10; // bw[10:15]
677            mode = 13;
678        }
679        _ => {
680            // Modes 10011, 10111, 11011, and 11111 (not shown) are reserved.
681            // Do not use these in your encoder. If the hardware is passed blocks
682            // with one of these modes specified, the resulting decompressed block
683            // must contain all zeroes in all channels except for the alpha channel.
684            for i in 0..4 {
685                let start = i * destination_pitch;
686                let end = start + 4 * 3;
687                decompressed_block[start..end].fill(f16::ZERO);
688            }
689
690            return;
691        }
692    }
693
694    let num_partitions = if mode >= 10 { 0 } else { 1 };
695
696    let actual_bits0_mode = ACTUAL_BITS_COUNT[0][mode as usize] as i32;
697    if is_signed {
698        r[0] = extend_sign(r[0], actual_bits0_mode);
699        g[0] = extend_sign(g[0], actual_bits0_mode);
700        b[0] = extend_sign(b[0], actual_bits0_mode);
701    }
702
703    // Mode 11 (like Mode 10) does not use delta compression,
704    // and instead stores both color endpoints explicitly.
705    if mode != 9 && mode != 10 || is_signed {
706        for i in 1..(num_partitions + 1) * 2 {
707            r[i] = extend_sign(r[i], ACTUAL_BITS_COUNT[1][mode as usize] as i32);
708            g[i] = extend_sign(g[i], ACTUAL_BITS_COUNT[2][mode as usize] as i32);
709            b[i] = extend_sign(b[i], ACTUAL_BITS_COUNT[3][mode as usize] as i32);
710        }
711    }
712
713    if mode != 9 && mode != 10 {
714        for i in 1..(num_partitions + 1) * 2 {
715            r[i] = transform_inverse(r[i], r[0], actual_bits0_mode, is_signed);
716            g[i] = transform_inverse(g[i], g[0], actual_bits0_mode, is_signed);
717            b[i] = transform_inverse(b[i], b[0], actual_bits0_mode, is_signed);
718        }
719    }
720
721    for i in 0..(num_partitions + 1) * 2 {
722        r[i] = unquantize(r[i], actual_bits0_mode, is_signed);
723        g[i] = unquantize(g[i], actual_bits0_mode, is_signed);
724        b[i] = unquantize(b[i], actual_bits0_mode, is_signed);
725    }
726
727    let weights = if mode >= 10 { WEIGHT4 } else { WEIGHT3 };
728
729    for i in 0..4 {
730        for j in 0..4 {
731            let mut partition_set = if mode >= 10 {
732                if i | j == 0 {
733                    128i32
734                } else {
735                    0i32
736                }
737            } else {
738                PARTITION_SETS[partition as usize][i][j] as i32
739            };
740
741            let mut index_bits = if mode >= 10 { 4 } else { 3 };
742
743            // fix-up index is specified with one less bit
744            // The fix-up index for subset 0 is always index 0
745            if (partition_set & 0x80) != 0 {
746                index_bits -= 1;
747            }
748            partition_set &= 0x01;
749
750            let index = bstream.read_bits_i32(index_bits);
751
752            let ep_i = (partition_set * 2) as usize;
753            let out = i * destination_pitch + j * 3;
754
755            decompressed_block[out] = f16::from_bits(finish_unquantize(
756                interpolate(r[ep_i], r[ep_i + 1], weights, index),
757                is_signed,
758            ));
759            decompressed_block[out + 1] = f16::from_bits(finish_unquantize(
760                interpolate(g[ep_i], g[ep_i + 1], weights, index),
761                is_signed,
762            ));
763            decompressed_block[out + 2] = f16::from_bits(finish_unquantize(
764                interpolate(b[ep_i], b[ep_i + 1], weights, index),
765                is_signed,
766            ));
767        }
768    }
769}
770
771/// Decodes a BC6H block by reading 16 bytes from `compressed_block` and writing the RGB32F data into `decompressed_block` with `destination_pitch` many bytes per output row.
772#[cfg(feature = "bc6h")]
773#[inline(always)]
774pub fn decode_block_bc6h_float(
775    compressed_block: &[u8],
776    decompressed_block: &mut [f32],
777    destination_pitch: usize,
778    is_signed: bool,
779) {
780    let mut block = [half::f16::ZERO; 48];
781    decode_block_bc6h(compressed_block, &mut block, 12, is_signed);
782
783    let mut decompressed = decompressed_block;
784
785    for i in 0..4 {
786        for j in 0..4 {
787            let offset = i * 12 + j * 3;
788            let pixel_offset = j * 3;
789
790            decompressed[pixel_offset] = block[offset].to_f32();
791            decompressed[pixel_offset + 1] = block[offset + 1].to_f32();
792            decompressed[pixel_offset + 2] = block[offset + 2].to_f32();
793        }
794        decompressed = &mut decompressed[destination_pitch..];
795    }
796}
797
798/// Decodes a BC7 block by reading 16 bytes from `compressed_block` and writing the RGBA8 data into `decompressed_block` with `destination_pitch` many bytes per output row.
799#[cfg(feature = "bc7")]
800#[allow(clippy::needless_range_loop)]
801pub fn decode_block_bc7(
802    compressed_block: &[u8],
803    decompressed_block: &mut [u8],
804    destination_pitch: usize,
805) {
806    static ACTUAL_BITS_COUNT: &[[u8; 8]; 2] = &[
807        [4, 6, 5, 7, 5, 7, 7, 5], // RGBA
808        [0, 0, 0, 0, 6, 8, 7, 5], // Alpha
809    ];
810
811    // There are 64 possible partition sets for a two-region tile.
812    // Each 4x4 block represents a single shape.
813    // Here also every fix-up index has MSB bit set.
814    static PARTITION_SETS: &[[[[u8; 4]; 4]; 64]; 2] = &[
815        [
816            // Partition table for 2-subset BPTC
817            [[128, 0, 1, 1], [0, 0, 1, 1], [0, 0, 1, 1], [0, 0, 1, 129]], //  0
818            [[128, 0, 0, 1], [0, 0, 0, 1], [0, 0, 0, 1], [0, 0, 0, 129]], //  1
819            [[128, 1, 1, 1], [0, 1, 1, 1], [0, 1, 1, 1], [0, 1, 1, 129]], //  2
820            [[128, 0, 0, 1], [0, 0, 1, 1], [0, 0, 1, 1], [0, 1, 1, 129]], //  3
821            [[128, 0, 0, 0], [0, 0, 0, 1], [0, 0, 0, 1], [0, 0, 1, 129]], //  4
822            [[128, 0, 1, 1], [0, 1, 1, 1], [0, 1, 1, 1], [1, 1, 1, 129]], //  5
823            [[128, 0, 0, 1], [0, 0, 1, 1], [0, 1, 1, 1], [1, 1, 1, 129]], //  6
824            [[128, 0, 0, 0], [0, 0, 0, 1], [0, 0, 1, 1], [0, 1, 1, 129]], //  7
825            [[128, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 1], [0, 0, 1, 129]], //  8
826            [[128, 0, 1, 1], [0, 1, 1, 1], [1, 1, 1, 1], [1, 1, 1, 129]], //  9
827            [[128, 0, 0, 0], [0, 0, 0, 1], [0, 1, 1, 1], [1, 1, 1, 129]], // 10
828            [[128, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 1], [0, 1, 1, 129]], // 11
829            [[128, 0, 0, 1], [0, 1, 1, 1], [1, 1, 1, 1], [1, 1, 1, 129]], // 12
830            [[128, 0, 0, 0], [0, 0, 0, 0], [1, 1, 1, 1], [1, 1, 1, 129]], // 13
831            [[128, 0, 0, 0], [1, 1, 1, 1], [1, 1, 1, 1], [1, 1, 1, 129]], // 14
832            [[128, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [1, 1, 1, 129]], // 15
833            [[128, 0, 0, 0], [1, 0, 0, 0], [1, 1, 1, 0], [1, 1, 1, 129]], // 16
834            [[128, 1, 129, 1], [0, 0, 0, 1], [0, 0, 0, 0], [0, 0, 0, 0]], // 17
835            [[128, 0, 0, 0], [0, 0, 0, 0], [129, 0, 0, 0], [1, 1, 1, 0]], // 18
836            [[128, 1, 129, 1], [0, 0, 1, 1], [0, 0, 0, 1], [0, 0, 0, 0]], // 19
837            [[128, 0, 129, 1], [0, 0, 0, 1], [0, 0, 0, 0], [0, 0, 0, 0]], // 20
838            [[128, 0, 0, 0], [1, 0, 0, 0], [129, 1, 0, 0], [1, 1, 1, 0]], // 21
839            [[128, 0, 0, 0], [0, 0, 0, 0], [129, 0, 0, 0], [1, 1, 0, 0]], // 22
840            [[128, 1, 1, 1], [0, 0, 1, 1], [0, 0, 1, 1], [0, 0, 0, 129]], // 23
841            [[128, 0, 129, 1], [0, 0, 0, 1], [0, 0, 0, 1], [0, 0, 0, 0]], // 24
842            [[128, 0, 0, 0], [1, 0, 0, 0], [129, 0, 0, 0], [1, 1, 0, 0]], // 25
843            [[128, 1, 129, 0], [0, 1, 1, 0], [0, 1, 1, 0], [0, 1, 1, 0]], // 26
844            [[128, 0, 129, 1], [0, 1, 1, 0], [0, 1, 1, 0], [1, 1, 0, 0]], // 27
845            [[128, 0, 0, 1], [0, 1, 1, 1], [129, 1, 1, 0], [1, 0, 0, 0]], // 28
846            [[128, 0, 0, 0], [1, 1, 1, 1], [129, 1, 1, 1], [0, 0, 0, 0]], // 29
847            [[128, 1, 129, 1], [0, 0, 0, 1], [1, 0, 0, 0], [1, 1, 1, 0]], // 30
848            [[128, 0, 129, 1], [1, 0, 0, 1], [1, 0, 0, 1], [1, 1, 0, 0]], // 31
849            [[128, 1, 0, 1], [0, 1, 0, 1], [0, 1, 0, 1], [0, 1, 0, 129]], // 32
850            [[128, 0, 0, 0], [1, 1, 1, 1], [0, 0, 0, 0], [1, 1, 1, 129]], // 33
851            [[128, 1, 0, 1], [1, 0, 129, 0], [0, 1, 0, 1], [1, 0, 1, 0]], // 34
852            [[128, 0, 1, 1], [0, 0, 1, 1], [129, 1, 0, 0], [1, 1, 0, 0]], // 35
853            [[128, 0, 129, 1], [1, 1, 0, 0], [0, 0, 1, 1], [1, 1, 0, 0]], // 36
854            [[128, 1, 0, 1], [0, 1, 0, 1], [129, 0, 1, 0], [1, 0, 1, 0]], // 37
855            [[128, 1, 1, 0], [1, 0, 0, 1], [0, 1, 1, 0], [1, 0, 0, 129]], // 38
856            [[128, 1, 0, 1], [1, 0, 1, 0], [1, 0, 1, 0], [0, 1, 0, 129]], // 39
857            [[128, 1, 129, 1], [0, 0, 1, 1], [1, 1, 0, 0], [1, 1, 1, 0]], // 40
858            [[128, 0, 0, 1], [0, 0, 1, 1], [129, 1, 0, 0], [1, 0, 0, 0]], // 41
859            [[128, 0, 129, 1], [0, 0, 1, 0], [0, 1, 0, 0], [1, 1, 0, 0]], // 42
860            [[128, 0, 129, 1], [1, 0, 1, 1], [1, 1, 0, 1], [1, 1, 0, 0]], // 43
861            [[128, 1, 129, 0], [1, 0, 0, 1], [1, 0, 0, 1], [0, 1, 1, 0]], // 44
862            [[128, 0, 1, 1], [1, 1, 0, 0], [1, 1, 0, 0], [0, 0, 1, 129]], // 45
863            [[128, 1, 1, 0], [0, 1, 1, 0], [1, 0, 0, 1], [1, 0, 0, 129]], // 46
864            [[128, 0, 0, 0], [0, 1, 129, 0], [0, 1, 1, 0], [0, 0, 0, 0]], // 47
865            [[128, 1, 0, 0], [1, 1, 129, 0], [0, 1, 0, 0], [0, 0, 0, 0]], // 48
866            [[128, 0, 129, 0], [0, 1, 1, 1], [0, 0, 1, 0], [0, 0, 0, 0]], // 49
867            [[128, 0, 0, 0], [0, 0, 129, 0], [0, 1, 1, 1], [0, 0, 1, 0]], // 50
868            [[128, 0, 0, 0], [0, 1, 0, 0], [129, 1, 1, 0], [0, 1, 0, 0]], // 51
869            [[128, 1, 1, 0], [1, 1, 0, 0], [1, 0, 0, 1], [0, 0, 1, 129]], // 52
870            [[128, 0, 1, 1], [0, 1, 1, 0], [1, 1, 0, 0], [1, 0, 0, 129]], // 53
871            [[128, 1, 129, 0], [0, 0, 1, 1], [1, 0, 0, 1], [1, 1, 0, 0]], // 54
872            [[128, 0, 129, 1], [1, 0, 0, 1], [1, 1, 0, 0], [0, 1, 1, 0]], // 55
873            [[128, 1, 1, 0], [1, 1, 0, 0], [1, 1, 0, 0], [1, 0, 0, 129]], // 56
874            [[128, 1, 1, 0], [0, 0, 1, 1], [0, 0, 1, 1], [1, 0, 0, 129]], // 57
875            [[128, 1, 1, 1], [1, 1, 1, 0], [1, 0, 0, 0], [0, 0, 0, 129]], // 58
876            [[128, 0, 0, 1], [1, 0, 0, 0], [1, 1, 1, 0], [0, 1, 1, 129]], // 59
877            [[128, 0, 0, 0], [1, 1, 1, 1], [0, 0, 1, 1], [0, 0, 1, 129]], // 60
878            [[128, 0, 129, 1], [0, 0, 1, 1], [1, 1, 1, 1], [0, 0, 0, 0]], // 61
879            [[128, 0, 129, 0], [0, 0, 1, 0], [1, 1, 1, 0], [1, 1, 1, 0]], // 62
880            [[128, 1, 0, 0], [0, 1, 0, 0], [0, 1, 1, 1], [0, 1, 1, 129]], // 63
881        ],
882        [
883            // Partition table for 3-subset BPTC
884            [[128, 0, 1, 129], [0, 0, 1, 1], [0, 2, 2, 1], [2, 2, 2, 130]], //  0
885            [[128, 0, 0, 129], [0, 0, 1, 1], [130, 2, 1, 1], [2, 2, 2, 1]], //  1
886            [[128, 0, 0, 0], [2, 0, 0, 1], [130, 2, 1, 1], [2, 2, 1, 129]], //  2
887            [[128, 2, 2, 130], [0, 0, 2, 2], [0, 0, 1, 1], [0, 1, 1, 129]], //  3
888            [[128, 0, 0, 0], [0, 0, 0, 0], [129, 1, 2, 2], [1, 1, 2, 130]], //  4
889            [[128, 0, 1, 129], [0, 0, 1, 1], [0, 0, 2, 2], [0, 0, 2, 130]], //  5
890            [[128, 0, 2, 130], [0, 0, 2, 2], [1, 1, 1, 1], [1, 1, 1, 129]], //  6
891            [[128, 0, 1, 1], [0, 0, 1, 1], [130, 2, 1, 1], [2, 2, 1, 129]], //  7
892            [[128, 0, 0, 0], [0, 0, 0, 0], [129, 1, 1, 1], [2, 2, 2, 130]], //  8
893            [[128, 0, 0, 0], [1, 1, 1, 1], [129, 1, 1, 1], [2, 2, 2, 130]], //  9
894            [[128, 0, 0, 0], [1, 1, 129, 1], [2, 2, 2, 2], [2, 2, 2, 130]], // 10
895            [[128, 0, 1, 2], [0, 0, 129, 2], [0, 0, 1, 2], [0, 0, 1, 130]], // 11
896            [[128, 1, 1, 2], [0, 1, 129, 2], [0, 1, 1, 2], [0, 1, 1, 130]], // 12
897            [[128, 1, 2, 2], [0, 129, 2, 2], [0, 1, 2, 2], [0, 1, 2, 130]], // 13
898            [[128, 0, 1, 129], [0, 1, 1, 2], [1, 1, 2, 2], [1, 2, 2, 130]], // 14
899            [[128, 0, 1, 129], [2, 0, 0, 1], [130, 2, 0, 0], [2, 2, 2, 0]], // 15
900            [[128, 0, 0, 129], [0, 0, 1, 1], [0, 1, 1, 2], [1, 1, 2, 130]], // 16
901            [[128, 1, 1, 129], [0, 0, 1, 1], [130, 0, 0, 1], [2, 2, 0, 0]], // 17
902            [[128, 0, 0, 0], [1, 1, 2, 2], [129, 1, 2, 2], [1, 1, 2, 130]], // 18
903            [[128, 0, 2, 130], [0, 0, 2, 2], [0, 0, 2, 2], [1, 1, 1, 129]], // 19
904            [[128, 1, 1, 129], [0, 1, 1, 1], [0, 2, 2, 2], [0, 2, 2, 130]], // 20
905            [[128, 0, 0, 129], [0, 0, 0, 1], [130, 2, 2, 1], [2, 2, 2, 1]], // 21
906            [[128, 0, 0, 0], [0, 0, 129, 1], [0, 1, 2, 2], [0, 1, 2, 130]], // 22
907            [[128, 0, 0, 0], [1, 1, 0, 0], [130, 2, 129, 0], [2, 2, 1, 0]], // 23
908            [[128, 1, 2, 130], [0, 129, 2, 2], [0, 0, 1, 1], [0, 0, 0, 0]], // 24
909            [[128, 0, 1, 2], [0, 0, 1, 2], [129, 1, 2, 2], [2, 2, 2, 130]], // 25
910            [[128, 1, 1, 0], [1, 2, 130, 1], [129, 2, 2, 1], [0, 1, 1, 0]], // 26
911            [[128, 0, 0, 0], [0, 1, 129, 0], [1, 2, 130, 1], [1, 2, 2, 1]], // 27
912            [[128, 0, 2, 2], [1, 1, 0, 2], [129, 1, 0, 2], [0, 0, 2, 130]], // 28
913            [[128, 1, 1, 0], [0, 129, 1, 0], [2, 0, 0, 2], [2, 2, 2, 130]], // 29
914            [[128, 0, 1, 1], [0, 1, 2, 2], [0, 1, 130, 2], [0, 0, 1, 129]], // 30
915            [[128, 0, 0, 0], [2, 0, 0, 0], [130, 2, 1, 1], [2, 2, 2, 129]], // 31
916            [[128, 0, 0, 0], [0, 0, 0, 2], [129, 1, 2, 2], [1, 2, 2, 130]], // 32
917            [[128, 2, 2, 130], [0, 0, 2, 2], [0, 0, 1, 2], [0, 0, 1, 129]], // 33
918            [[128, 0, 1, 129], [0, 0, 1, 2], [0, 0, 2, 2], [0, 2, 2, 130]], // 34
919            [[128, 1, 2, 0], [0, 129, 2, 0], [0, 1, 130, 0], [0, 1, 2, 0]], // 35
920            [[128, 0, 0, 0], [1, 1, 129, 1], [2, 2, 130, 2], [0, 0, 0, 0]], // 36
921            [[128, 1, 2, 0], [1, 2, 0, 1], [130, 0, 129, 2], [0, 1, 2, 0]], // 37
922            [[128, 1, 2, 0], [2, 0, 1, 2], [129, 130, 0, 1], [0, 1, 2, 0]], // 38
923            [[128, 0, 1, 1], [2, 2, 0, 0], [1, 1, 130, 2], [0, 0, 1, 129]], // 39
924            [[128, 0, 1, 1], [1, 1, 130, 2], [2, 2, 0, 0], [0, 0, 1, 129]], // 40
925            [[128, 1, 0, 129], [0, 1, 0, 1], [2, 2, 2, 2], [2, 2, 2, 130]], // 41
926            [[128, 0, 0, 0], [0, 0, 0, 0], [130, 1, 2, 1], [2, 1, 2, 129]], // 42
927            [[128, 0, 2, 2], [1, 129, 2, 2], [0, 0, 2, 2], [1, 1, 2, 130]], // 43
928            [[128, 0, 2, 130], [0, 0, 1, 1], [0, 0, 2, 2], [0, 0, 1, 129]], // 44
929            [[128, 2, 2, 0], [1, 2, 130, 1], [0, 2, 2, 0], [1, 2, 2, 129]], // 45
930            [[128, 1, 0, 1], [2, 2, 130, 2], [2, 2, 2, 2], [0, 1, 0, 129]], // 46
931            [[128, 0, 0, 0], [2, 1, 2, 1], [130, 1, 2, 1], [2, 1, 2, 129]], // 47
932            [[128, 1, 0, 129], [0, 1, 0, 1], [0, 1, 0, 1], [2, 2, 2, 130]], // 48
933            [[128, 2, 2, 130], [0, 1, 1, 1], [0, 2, 2, 2], [0, 1, 1, 129]], // 49
934            [[128, 0, 0, 2], [1, 129, 1, 2], [0, 0, 0, 2], [1, 1, 1, 130]], // 50
935            [[128, 0, 0, 0], [2, 129, 1, 2], [2, 1, 1, 2], [2, 1, 1, 130]], // 51
936            [[128, 2, 2, 2], [0, 129, 1, 1], [0, 1, 1, 1], [0, 2, 2, 130]], // 52
937            [[128, 0, 0, 2], [1, 1, 1, 2], [129, 1, 1, 2], [0, 0, 0, 130]], // 53
938            [[128, 1, 1, 0], [0, 129, 1, 0], [0, 1, 1, 0], [2, 2, 2, 130]], // 54
939            [[128, 0, 0, 0], [0, 0, 0, 0], [2, 1, 129, 2], [2, 1, 1, 130]], // 55
940            [[128, 1, 1, 0], [0, 129, 1, 0], [2, 2, 2, 2], [2, 2, 2, 130]], // 56
941            [[128, 0, 2, 2], [0, 0, 1, 1], [0, 0, 129, 1], [0, 0, 2, 130]], // 57
942            [[128, 0, 2, 2], [1, 1, 2, 2], [129, 1, 2, 2], [0, 0, 2, 130]], // 58
943            [[128, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [2, 129, 1, 130]], // 59
944            [[128, 0, 0, 130], [0, 0, 0, 1], [0, 0, 0, 2], [0, 0, 0, 129]], // 60
945            [[128, 2, 2, 2], [1, 2, 2, 2], [0, 2, 2, 2], [129, 2, 2, 130]], // 61
946            [[128, 1, 0, 129], [2, 2, 2, 2], [2, 2, 2, 2], [2, 2, 2, 130]], // 62
947            [[128, 1, 1, 129], [2, 0, 1, 1], [130, 2, 0, 1], [2, 2, 2, 0]], // 63
948        ],
949    ];
950
951    static WEIGHT2: &[i32] = &[0, 21, 43, 64];
952    static WEIGHT3: &[i32] = &[0, 9, 18, 27, 37, 46, 55, 64];
953    static WEIGHT4: &[i32] = &[0, 4, 9, 13, 17, 21, 26, 30, 34, 38, 43, 47, 51, 55, 60, 64];
954
955    const MODE_HAS_P_BITS: u8 = 0b11001011;
956
957    let mut bstream = BitStream::new(compressed_block);
958
959    // Find mode
960    let mut mode = 0;
961    while mode < 8 && bstream.read_bit() == 0 {
962        mode += 1;
963    }
964
965    // Unexpected mode, clear the block (transparent black)
966    if mode >= 8 {
967        for i in 0..4 {
968            for j in 0..4 {
969                let offset = i * destination_pitch + j * 4;
970                decompressed_block[offset..offset + 4].copy_from_slice(&[0, 0, 0, 0]);
971            }
972        }
973        return;
974    }
975
976    let mut partition = 0;
977    let mut num_partitions = 1;
978    let mut rotation = 0;
979    let mut index_selection_bit = 0;
980
981    if mode == 0 || mode == 1 || mode == 2 || mode == 3 || mode == 7 {
982        num_partitions = if mode == 0 || mode == 2 { 3 } else { 2 };
983        partition = bstream.read_bits(if mode == 0 { 4 } else { 6 }) as usize;
984    }
985
986    let num_endpoints = num_partitions * 2;
987
988    if mode == 4 || mode == 5 {
989        rotation = bstream.read_bits(2);
990        if mode == 4 {
991            index_selection_bit = bstream.read_bit();
992        }
993    }
994
995    // Extract endpoints
996    let mut endpoints = [[0i32; 4]; 6];
997
998    // RGB
999    for i in 0..3 {
1000        for j in 0..num_endpoints {
1001            endpoints[j][i] = bstream.read_bits(ACTUAL_BITS_COUNT[0][mode as usize] as u32) as i32;
1002        }
1003    }
1004
1005    // Alpha (if any)
1006    if ACTUAL_BITS_COUNT[1][mode as usize] > 0 {
1007        for j in 0..num_endpoints {
1008            endpoints[j][3] = bstream.read_bits(ACTUAL_BITS_COUNT[1][mode as usize] as u32) as i32;
1009        }
1010    }
1011
1012    // Fully decode endpoints
1013    // Handle modes that have P-bits
1014    if mode == 0 || mode == 1 || mode == 3 || mode == 6 || mode == 7 {
1015        // Component-wise left-shift
1016        for endpoint in endpoints.iter_mut().take(num_endpoints) {
1017            for component in endpoint.iter_mut() {
1018                *component <<= 1;
1019            }
1020        }
1021
1022        // If P-bit is shared
1023        if mode == 1 {
1024            let i = bstream.read_bit() as i32;
1025            let j = bstream.read_bit() as i32;
1026
1027            // RGB component-wise insert pbits
1028            for k in 0..3 {
1029                endpoints[0][k] |= i;
1030                endpoints[1][k] |= i;
1031                endpoints[2][k] |= j;
1032                endpoints[3][k] |= j;
1033            }
1034        } else if MODE_HAS_P_BITS & (1 << mode) != 0 {
1035            // Unique P-bit per endpoint
1036            for endpoint in endpoints.iter_mut().take(num_endpoints) {
1037                let j = bstream.read_bit() as i32;
1038                for component in endpoint.iter_mut() {
1039                    *component |= j;
1040                }
1041            }
1042        }
1043    }
1044
1045    // Fully decode endpoints
1046    // Component-wise precision adjustment
1047    for i in 0..num_endpoints {
1048        // Get color components precision including pbit
1049        let j = ACTUAL_BITS_COUNT[0][mode as usize] + ((MODE_HAS_P_BITS >> mode) & 1);
1050
1051        // RGB components
1052        for k in 0..3 {
1053            // Left shift endpoint components so that their MSB lies in bit 7
1054            endpoints[i][k] <<= 8 - j;
1055            // Replicate each component's MSB into the LSBs revealed by the left-shift operation
1056            endpoints[i][k] |= endpoints[i][k] >> j as i32;
1057        }
1058
1059        // Get alpha component precision including pbit
1060        let j = ACTUAL_BITS_COUNT[1][mode as usize] + ((MODE_HAS_P_BITS >> mode) & 1);
1061
1062        // Alpha component
1063        endpoints[i][3] <<= 8 - j;
1064        endpoints[i][3] |= endpoints[i][3] >> j as i32;
1065    }
1066
1067    // If this mode does not explicitly define the alpha component, set alpha to 255 (1.0)
1068    if ACTUAL_BITS_COUNT[1][mode as usize] == 0 {
1069        for endpoint in endpoints.iter_mut().take(num_endpoints) {
1070            endpoint[3] = 0xFF;
1071        }
1072    }
1073
1074    // Determine weights tables
1075    let index_bits = match mode {
1076        0 | 1 => 3,
1077        6 => 4,
1078        _ => 2,
1079    };
1080
1081    let index_bits2 = match mode {
1082        4 => 3,
1083        5 => 2,
1084        _ => 0,
1085    };
1086
1087    let weights = match index_bits {
1088        2 => WEIGHT2,
1089        3 => WEIGHT3,
1090        _ => WEIGHT4,
1091    };
1092
1093    let weights2 = match index_bits2 {
1094        2 => WEIGHT2,
1095        _ => WEIGHT3,
1096    };
1097
1098    // Collect indices in two passes
1099    // Pass #1: collecting color indices
1100    let mut indices = [[0i32; 4]; 4];
1101    for i in 0..4 {
1102        for j in 0..4 {
1103            let partition_set = if num_partitions == 1 {
1104                if i | j == 0 {
1105                    128
1106                } else {
1107                    0
1108                }
1109            } else {
1110                PARTITION_SETS[num_partitions - 2][partition][i][j]
1111            };
1112
1113            let mut idx_bits = match mode {
1114                0 | 1 => 3,
1115                6 => 4,
1116                _ => 2,
1117            };
1118
1119            // Fix-up index is specified with one less bit
1120            // The fix-up index for subset 0 is always index 0
1121            if partition_set & 0x80 != 0 {
1122                idx_bits -= 1;
1123            }
1124
1125            indices[i][j] = bstream.read_bits(idx_bits) as i32;
1126        }
1127    }
1128
1129    // Pass #2: reading alpha indices (if any) and interpolating & rotating
1130    for i in 0..4 {
1131        for j in 0..4 {
1132            let partition_set = if num_partitions == 1 {
1133                if i | j == 0 {
1134                    128
1135                } else {
1136                    0
1137                }
1138            } else {
1139                PARTITION_SETS[num_partitions - 2][partition][i][j]
1140            };
1141            let partition_set = (partition_set & 0x03) as usize;
1142
1143            let index = indices[i][j];
1144
1145            let (mut r, mut g, mut b, mut a) = if index_bits2 == 0 {
1146                // No secondary index bits
1147                (
1148                    interpolate(
1149                        endpoints[partition_set * 2][0],
1150                        endpoints[partition_set * 2 + 1][0],
1151                        weights,
1152                        index,
1153                    ),
1154                    interpolate(
1155                        endpoints[partition_set * 2][1],
1156                        endpoints[partition_set * 2 + 1][1],
1157                        weights,
1158                        index,
1159                    ),
1160                    interpolate(
1161                        endpoints[partition_set * 2][2],
1162                        endpoints[partition_set * 2 + 1][2],
1163                        weights,
1164                        index,
1165                    ),
1166                    interpolate(
1167                        endpoints[partition_set * 2][3],
1168                        endpoints[partition_set * 2 + 1][3],
1169                        weights,
1170                        index,
1171                    ),
1172                )
1173            } else {
1174                let index2 = bstream.read_bits(if i | j == 0 {
1175                    index_bits2 - 1
1176                } else {
1177                    index_bits2
1178                }) as i32;
1179
1180                if index_selection_bit == 0 {
1181                    (
1182                        interpolate(
1183                            endpoints[partition_set * 2][0],
1184                            endpoints[partition_set * 2 + 1][0],
1185                            weights,
1186                            index,
1187                        ),
1188                        interpolate(
1189                            endpoints[partition_set * 2][1],
1190                            endpoints[partition_set * 2 + 1][1],
1191                            weights,
1192                            index,
1193                        ),
1194                        interpolate(
1195                            endpoints[partition_set * 2][2],
1196                            endpoints[partition_set * 2 + 1][2],
1197                            weights,
1198                            index,
1199                        ),
1200                        interpolate(
1201                            endpoints[partition_set * 2][3],
1202                            endpoints[partition_set * 2 + 1][3],
1203                            weights2,
1204                            index2,
1205                        ),
1206                    )
1207                } else {
1208                    (
1209                        interpolate(
1210                            endpoints[partition_set * 2][0],
1211                            endpoints[partition_set * 2 + 1][0],
1212                            weights2,
1213                            index2,
1214                        ),
1215                        interpolate(
1216                            endpoints[partition_set * 2][1],
1217                            endpoints[partition_set * 2 + 1][1],
1218                            weights2,
1219                            index2,
1220                        ),
1221                        interpolate(
1222                            endpoints[partition_set * 2][2],
1223                            endpoints[partition_set * 2 + 1][2],
1224                            weights2,
1225                            index2,
1226                        ),
1227                        interpolate(
1228                            endpoints[partition_set * 2][3],
1229                            endpoints[partition_set * 2 + 1][3],
1230                            weights,
1231                            index,
1232                        ),
1233                    )
1234                }
1235            };
1236
1237            // Handle rotation
1238            match rotation {
1239                1 => std::mem::swap(&mut a, &mut r), // 01 – Block format is Scalar(R) Vector(AGB) - swap A and R
1240                2 => std::mem::swap(&mut a, &mut g), // 10 – Block format is Scalar(G) Vector(RAB) - swap A and G
1241                3 => std::mem::swap(&mut a, &mut b), // 11 - Block format is Scalar(B) Vector(RGA) - swap A and B
1242                _ => {}
1243            }
1244
1245            let offset = i * destination_pitch + j * 4;
1246            decompressed_block[offset] = r as u8;
1247            decompressed_block[offset + 1] = g as u8;
1248            decompressed_block[offset + 2] = b as u8;
1249            decompressed_block[offset + 3] = a as u8;
1250        }
1251    }
1252}
1253
1254#[cfg(any(feature = "bc6h", feature = "bc7"))]
1255#[inline]
1256fn interpolate(a: i32, b: i32, weights: &[i32], index: i32) -> i32 {
1257    (a * (64 - weights[index as usize]) + b * weights[index as usize] + 32) >> 6
1258}
1259
1260#[cfg(feature = "bc6h")]
1261#[inline]
1262fn extend_sign(val: i32, bits: i32) -> i32 {
1263    // http://graphics.stanford.edu/~seander/bithacks.html#VariableSignExtend
1264    (val << (32 - bits)) >> (32 - bits)
1265}
1266
1267#[cfg(feature = "bc6h")]
1268#[inline]
1269fn transform_inverse(val: i32, a0: i32, bits: i32, is_signed: bool) -> i32 {
1270    // If the precision of A0 is "p" bits, then the transform algorithm is:
1271    // B0 = (B0 + A0) & ((1 << p) - 1)
1272    let transformed = (val + a0) & ((1 << bits) - 1);
1273    if is_signed {
1274        extend_sign(transformed, bits)
1275    } else {
1276        transformed
1277    }
1278}
1279
1280#[cfg(feature = "bc6h")]
1281#[inline]
1282fn unquantize(val: i32, bits: i32, is_signed: bool) -> i32 {
1283    if !is_signed {
1284        if bits >= 15 {
1285            val
1286        } else if val == 0 {
1287            0
1288        } else if val == ((1 << bits) - 1) {
1289            0xFFFF
1290        } else {
1291            ((val << 16) + 0x8000) >> bits
1292        }
1293    } else if bits >= 16 {
1294        val
1295    } else {
1296        let (s, v) = if val < 0 { (true, -val) } else { (false, val) };
1297
1298        let unq = if v == 0 {
1299            0
1300        } else if v >= ((1 << (bits - 1)) - 1) {
1301            0x7FFF
1302        } else {
1303            ((v << 15) + 0x4000) >> (bits - 1)
1304        };
1305
1306        if s {
1307            -unq
1308        } else {
1309            unq
1310        }
1311    }
1312}
1313
1314#[cfg(feature = "bc6h")]
1315#[inline]
1316fn finish_unquantize(val: i32, is_signed: bool) -> u16 {
1317    if !is_signed {
1318        // Scale the magnitude by 31 / 64
1319        ((val * 31) >> 6) as u16
1320    } else {
1321        // Scale the magnitude by 31 / 32
1322        let scaled = if val < 0 {
1323            -(((-val) * 31) >> 5)
1324        } else {
1325            (val * 31) >> 5
1326        };
1327
1328        let (sign_bit, magnitude) = if scaled < 0 {
1329            (0x8000, -scaled)
1330        } else {
1331            (0, scaled)
1332        };
1333
1334        (sign_bit | magnitude) as u16
1335    }
1336}
1337
1338/// Internal bitstream helper for reading bits from compressed data
1339#[cfg(any(feature = "bc6h", feature = "bc7"))]
1340#[derive(Debug, Clone, Copy)]
1341struct BitStream {
1342    low: u64,
1343    high: u64,
1344}
1345
1346#[cfg(any(feature = "bc6h", feature = "bc7"))]
1347impl BitStream {
1348    /// Create a new bitstream from raw data.
1349    #[inline]
1350    fn new(data: &[u8]) -> Self {
1351        let low = u64::from_le_bytes(data[0..8].try_into().unwrap());
1352        let high = u64::from_le_bytes(data[8..16].try_into().unwrap());
1353        Self { low, high }
1354    }
1355
1356    #[cfg(feature = "bc7")]
1357    #[inline]
1358    fn read_bit(&mut self) -> u32 {
1359        self.read_bits(1)
1360    }
1361
1362    #[cfg(feature = "bc6h")]
1363    #[inline]
1364    fn read_bit_i32(&mut self) -> i32 {
1365        self.read_bits(1) as i32
1366    }
1367
1368    #[inline]
1369    pub fn read_bits(&mut self, num_bits: u32) -> u32 {
1370        let mask = (1u64 << num_bits) - 1;
1371        // Read the low N bits.
1372        let bits = (self.low & mask) as u32;
1373        self.low >>= num_bits;
1374
1375        // Put the low N bits of "high" into the high 64-N bits of "low".
1376        self.low |= (self.high & mask) << (64 - num_bits);
1377        self.high >>= num_bits;
1378
1379        bits
1380    }
1381
1382    #[cfg(feature = "bc6h")]
1383    #[inline]
1384    pub fn read_bits_i32(&mut self, num_bits: u32) -> i32 {
1385        self.read_bits(num_bits) as i32
1386    }
1387
1388    #[cfg(feature = "bc6h")]
1389    #[inline]
1390    fn read_bits_reversed(&mut self, num_bits: u32) -> i32 {
1391        let mut bits = self.read_bits_i32(num_bits);
1392        // Reverse the bits.
1393        let mut result = 0;
1394
1395        (0..num_bits).for_each(|_| {
1396            result <<= 1;
1397            result |= bits & 1;
1398            bits >>= 1;
1399        });
1400
1401        result
1402    }
1403}
1404
1405#[cfg(test)]
1406mod tests {
1407    use super::*;
1408
1409    fn test_block(
1410        decode_block: fn(&[u8], &mut [u8], usize),
1411        pitch: usize,
1412        compressed_block: &[u8],
1413        expected_output: &[u8],
1414        name: &str,
1415    ) {
1416        let mut decoded = [0u8; 64];
1417        decode_block(compressed_block, &mut decoded, pitch);
1418
1419        for y in 0..4 {
1420            let start = y * pitch;
1421            let end = start + pitch;
1422            assert_eq!(
1423                &decoded[start..end],
1424                &expected_output[start..end],
1425                "{name}: Mismatch at row {y}",
1426            );
1427        }
1428    }
1429
1430    #[test]
1431    fn test_bc1_block_black() {
1432        let compressed_block = [0u8; 8];
1433        let expected_output = [
1434            0x0, 0x0, 0x0, 0xFF, 0x0, 0x0, 0x0, 0xFF, 0x0, 0x0, 0x0, 0xFF, 0x0, 0x0, 0x0, 0xFF,
1435            0x0, 0x0, 0x0, 0xFF, 0x0, 0x0, 0x0, 0xFF, 0x0, 0x0, 0x0, 0xFF, 0x0, 0x0, 0x0, 0xFF,
1436            0x0, 0x0, 0x0, 0xFF, 0x0, 0x0, 0x0, 0xFF, 0x0, 0x0, 0x0, 0xFF, 0x0, 0x0, 0x0, 0xFF,
1437            0x0, 0x0, 0x0, 0xFF, 0x0, 0x0, 0x0, 0xFF, 0x0, 0x0, 0x0, 0xFF, 0x0, 0x0, 0x0, 0xFF,
1438        ];
1439        test_block(
1440            decode_block_bc1,
1441            16,
1442            &compressed_block,
1443            &expected_output,
1444            "Black block",
1445        );
1446    }
1447
1448    #[test]
1449    fn test_bc1_block_red() {
1450        let compressed_block = [0x00, 0xF8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00];
1451        let expected_output = [
1452            0xFF, 0x0, 0x0, 0xFF, 0xFF, 0x0, 0x0, 0xFF, 0xFF, 0x0, 0x0, 0xFF, 0xFF, 0x0, 0x0, 0xFF,
1453            0xFF, 0x0, 0x0, 0xFF, 0xFF, 0x0, 0x0, 0xFF, 0xFF, 0x0, 0x0, 0xFF, 0xFF, 0x0, 0x0, 0xFF,
1454            0xFF, 0x0, 0x0, 0xFF, 0xFF, 0x0, 0x0, 0xFF, 0xFF, 0x0, 0x0, 0xFF, 0xFF, 0x0, 0x0, 0xFF,
1455            0xFF, 0x0, 0x0, 0xFF, 0xFF, 0x0, 0x0, 0xFF, 0xFF, 0x0, 0x0, 0xFF, 0xFF, 0x0, 0x0, 0xFF,
1456        ];
1457        test_block(
1458            decode_block_bc1,
1459            16,
1460            &compressed_block,
1461            &expected_output,
1462            "Red block",
1463        );
1464    }
1465
1466    #[test]
1467    fn test_bc1_block_gradient() {
1468        let compressed_block = [0x00, 0xF8, 0xE0, 0x07, 0x55, 0x55, 0x55, 0x55];
1469        let expected_output = [
1470            0x0, 0xFF, 0x0, 0xFF, 0x0, 0xFF, 0x0, 0xFF, 0x0, 0xFF, 0x0, 0xFF, 0x0, 0xFF, 0x0, 0xFF,
1471            0x0, 0xFF, 0x0, 0xFF, 0x0, 0xFF, 0x0, 0xFF, 0x0, 0xFF, 0x0, 0xFF, 0x0, 0xFF, 0x0, 0xFF,
1472            0x0, 0xFF, 0x0, 0xFF, 0x0, 0xFF, 0x0, 0xFF, 0x0, 0xFF, 0x0, 0xFF, 0x0, 0xFF, 0x0, 0xFF,
1473            0x0, 0xFF, 0x0, 0xFF, 0x0, 0xFF, 0x0, 0xFF, 0x0, 0xFF, 0x0, 0xFF, 0x0, 0xFF, 0x0, 0xFF,
1474        ];
1475        test_block(
1476            decode_block_bc1,
1477            16,
1478            &compressed_block,
1479            &expected_output,
1480            "Gradient block",
1481        );
1482    }
1483
1484    #[test]
1485    fn test_bc2_alpha_gradient() {
1486        let compressed_block = [
1487            0x10, 0x32, 0x54, 0x76, 0x98, 0xBA, 0xDC, 0xFE, 0x00, 0xF8, 0x00, 0x00, 0x00, 0x00,
1488            0x00, 0x00,
1489        ];
1490        let expected_output = [
1491            0xFF, 0x0, 0x0, 0x0, 0xFF, 0x0, 0x0, 0x11, 0xFF, 0x0, 0x0, 0x22, 0xFF, 0x0, 0x0, 0x33,
1492            0xFF, 0x0, 0x0, 0x44, 0xFF, 0x0, 0x0, 0x55, 0xFF, 0x0, 0x0, 0x66, 0xFF, 0x0, 0x0, 0x77,
1493            0xFF, 0x0, 0x0, 0x88, 0xFF, 0x0, 0x0, 0x99, 0xFF, 0x0, 0x0, 0xAA, 0xFF, 0x0, 0x0, 0xBB,
1494            0xFF, 0x0, 0x0, 0xCC, 0xFF, 0x0, 0x0, 0xDD, 0xFF, 0x0, 0x0, 0xEE, 0xFF, 0x0, 0x0, 0xFF,
1495        ];
1496        test_block(
1497            decode_block_bc2,
1498            16,
1499            &compressed_block,
1500            &expected_output,
1501            "Alpha gradient",
1502        );
1503    }
1504
1505    #[test]
1506    fn test_bc2_alpha_half_transparent() {
1507        let compressed_block = [
1508            0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x77, 0x00, 0xF8, 0x00, 0x00, 0x00, 0x00,
1509            0x00, 0x00,
1510        ];
1511        let expected_output = [
1512            0xFF, 0x0, 0x0, 0x77, 0xFF, 0x0, 0x0, 0x77, 0xFF, 0x0, 0x0, 0x77, 0xFF, 0x0, 0x0, 0x77,
1513            0xFF, 0x0, 0x0, 0x77, 0xFF, 0x0, 0x0, 0x77, 0xFF, 0x0, 0x0, 0x77, 0xFF, 0x0, 0x0, 0x77,
1514            0xFF, 0x0, 0x0, 0x77, 0xFF, 0x0, 0x0, 0x77, 0xFF, 0x0, 0x0, 0x77, 0xFF, 0x0, 0x0, 0x77,
1515            0xFF, 0x0, 0x0, 0x77, 0xFF, 0x0, 0x0, 0x77, 0xFF, 0x0, 0x0, 0x77, 0xFF, 0x0, 0x0, 0x77,
1516        ];
1517        test_block(
1518            decode_block_bc2,
1519            16,
1520            &compressed_block,
1521            &expected_output,
1522            "Half transparent",
1523        );
1524    }
1525
1526    #[test]
1527    fn test_bc3_solid_black() {
1528        let compressed_block = [
1529            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
1530            0x00, 0x00,
1531        ];
1532        let expected_output = [
1533            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
1534            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
1535            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
1536            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
1537        ];
1538        test_block(
1539            decode_block_bc3,
1540            16,
1541            &compressed_block,
1542            &expected_output,
1543            "Solid black with full alpha",
1544        );
1545    }
1546
1547    #[test]
1548    fn test_bc3_transparent_red() {
1549        let compressed_block = [
1550            0x00, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x00, 0x00, 0x00,
1551            0x00, 0x00,
1552        ];
1553        let expected_output = [
1554            0xFF, 0x0, 0x0, 0x0, 0xFF, 0x0, 0x0, 0x0, 0xFF, 0x0, 0x0, 0x0, 0xFF, 0x0, 0x0, 0x0,
1555            0xFF, 0x0, 0x0, 0x0, 0xFF, 0x0, 0x0, 0x0, 0xFF, 0x0, 0x0, 0x0, 0xFF, 0x0, 0x0, 0x0,
1556            0xFF, 0x0, 0x0, 0x0, 0xFF, 0x0, 0x0, 0x0, 0xFF, 0x0, 0x0, 0x0, 0xFF, 0x0, 0x0, 0x0,
1557            0xFF, 0x0, 0x0, 0x0, 0xFF, 0x0, 0x0, 0x0, 0xFF, 0x0, 0x0, 0x0, 0xFF, 0x0, 0x0, 0x0,
1558        ];
1559        test_block(
1560            decode_block_bc3,
1561            16,
1562            &compressed_block,
1563            &expected_output,
1564            "Transparent red",
1565        );
1566    }
1567
1568    #[test]
1569    fn test_bc3_alpha_gradient() {
1570        let compressed_block = [
1571            0x00, 0xFF, 0xFF, 0xFF, 0x55, 0x55, 0x55, 0x55, 0x00, 0xF8, 0x00, 0x00, 0x00, 0x00,
1572            0x00, 0x00,
1573        ];
1574        let expected_output = [
1575            0xFF, 0x0, 0x0, 0xFF, 0xFF, 0x0, 0x0, 0xFF, 0xFF, 0x0, 0x0, 0xFF, 0xFF, 0x0, 0x0, 0xFF,
1576            0xFF, 0x0, 0x0, 0xFF, 0xFF, 0x0, 0x0, 0x66, 0xFF, 0x0, 0x0, 0xCC, 0xFF, 0x0, 0x0, 0x33,
1577            0xFF, 0x0, 0x0, 0xCC, 0xFF, 0x0, 0x0, 0x33, 0xFF, 0x0, 0x0, 0xCC, 0xFF, 0x0, 0x0, 0x33,
1578            0xFF, 0x0, 0x0, 0xCC, 0xFF, 0x0, 0x0, 0x33, 0xFF, 0x0, 0x0, 0xCC, 0xFF, 0x0, 0x0, 0x33,
1579        ];
1580        test_block(
1581            decode_block_bc3,
1582            16,
1583            &compressed_block,
1584            &expected_output,
1585            "Red with alpha gradient",
1586        );
1587    }
1588
1589    #[test]
1590    fn test_bc3_color_alpha_gradient() {
1591        let compressed_block = [
1592            0x00, 0xFF, 0xFF, 0xFF, 0x55, 0x55, 0x55, 0x55, 0x00, 0xF8, 0xE0, 0x07, 0x55, 0x55,
1593            0x55, 0x55,
1594        ];
1595        let expected_output = [
1596            0x0, 0xFF, 0x0, 0xFF, 0x0, 0xFF, 0x0, 0xFF, 0x0, 0xFF, 0x0, 0xFF, 0x0, 0xFF, 0x0, 0xFF,
1597            0x0, 0xFF, 0x0, 0xFF, 0x0, 0xFF, 0x0, 0x66, 0x0, 0xFF, 0x0, 0xCC, 0x0, 0xFF, 0x0, 0x33,
1598            0x0, 0xFF, 0x0, 0xCC, 0x0, 0xFF, 0x0, 0x33, 0x0, 0xFF, 0x0, 0xCC, 0x0, 0xFF, 0x0, 0x33,
1599            0x0, 0xFF, 0x0, 0xCC, 0x0, 0xFF, 0x0, 0x33, 0x0, 0xFF, 0x0, 0xCC, 0x0, 0xFF, 0x0, 0x33,
1600        ];
1601        test_block(
1602            decode_block_bc3,
1603            16,
1604            &compressed_block,
1605            &expected_output,
1606            "Color and alpha gradients",
1607        );
1608    }
1609
1610    #[test]
1611    fn test_bc3_semi_transparent() {
1612        let compressed_block = [
1613            0x80, 0x80, 0xFF, 0xFF, 0xAA, 0xAA, 0xAA, 0xAA, 0x00, 0xF8, 0x00, 0x00, 0x00, 0x00,
1614            0x00, 0x00,
1615        ];
1616        let expected_output = [
1617            0xFF, 0x0, 0x0, 0xFF, 0xFF, 0x0, 0x0, 0xFF, 0xFF, 0x0, 0x0, 0xFF, 0xFF, 0x0, 0x0, 0xFF,
1618            0xFF, 0x0, 0x0, 0xFF, 0xFF, 0x0, 0x0, 0x80, 0xFF, 0x0, 0x0, 0x80, 0xFF, 0x0, 0x0, 0x80,
1619            0xFF, 0x0, 0x0, 0x80, 0xFF, 0x0, 0x0, 0x80, 0xFF, 0x0, 0x0, 0x80, 0xFF, 0x0, 0x0, 0x80,
1620            0xFF, 0x0, 0x0, 0x80, 0xFF, 0x0, 0x0, 0x80, 0xFF, 0x0, 0x0, 0x80, 0xFF, 0x0, 0x0, 0x80,
1621        ];
1622        test_block(
1623            decode_block_bc3,
1624            16,
1625            &compressed_block,
1626            &expected_output,
1627            "Semi-transparent red",
1628        );
1629    }
1630
1631    #[test]
1632    fn test_bc4_gradient() {
1633        let compressed_block = [0x00, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00];
1634        let expected_output = [
1635            0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
1636            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
1637            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
1638            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
1639        ];
1640        test_block(
1641            decode_block_bc4,
1642            4,
1643            &compressed_block,
1644            &expected_output,
1645            "BC4 gradient",
1646        );
1647    }
1648
1649    #[test]
1650    fn test_bc4_interpolated() {
1651        let compressed_block = [0x00, 0xFF, 0x92, 0x24, 0x49, 0x92, 0x00, 0x00];
1652        let expected_output = [
1653            0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x33, 0x0, 0x0, 0x0, 0x0,
1654            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
1655            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
1656            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
1657        ];
1658        test_block(
1659            decode_block_bc4,
1660            4,
1661            &compressed_block,
1662            &expected_output,
1663            "BC4 interpolated",
1664        );
1665    }
1666
1667    #[test]
1668    fn test_bc5_gradient() {
1669        let compressed_block = [
1670            0x00, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0xFF, 0xFF, 0x00, 0x00,
1671            0x00, 0x00,
1672        ];
1673        let expected_output = [
1674            0xFF, 0x24, 0xFF, 0x24, 0xFF, 0x24, 0xFF, 0x24, 0xFF, 0x24, 0xFF, 0x0, 0x0, 0xFF, 0x0,
1675            0xFF, 0x0, 0xFF, 0x0, 0xFF, 0x0, 0xFF, 0x0, 0xFF, 0x0, 0xFF, 0x0, 0xFF, 0x0, 0xFF, 0x0,
1676            0xFF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
1677            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
1678        ];
1679        test_block(
1680            decode_block_bc5,
1681            8,
1682            &compressed_block,
1683            &expected_output,
1684            "BC5 gradient",
1685        );
1686    }
1687
1688    #[test]
1689    fn test_bc5_interpolated() {
1690        let compressed_block = [
1691            0x00, 0xFF, 0x92, 0x24, 0x49, 0x92, 0x00, 0x00, 0xFF, 0x00, 0x92, 0x24, 0x49, 0x92,
1692            0x00, 0x00,
1693        ];
1694        let expected_output = [
1695            0x33, 0xDA, 0x33, 0xDA, 0x33, 0xDA, 0x33, 0xDA, 0x33, 0xDA, 0x33, 0xDA, 0x33, 0xDA,
1696            0x33, 0xDA, 0x33, 0xDA, 0x33, 0xDA, 0x33, 0xDA, 0x0, 0xFF, 0x0, 0xFF, 0x0, 0xFF, 0x0,
1697            0xFF, 0x0, 0xFF, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
1698            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
1699            0x0,
1700        ];
1701        test_block(
1702            decode_block_bc5,
1703            8,
1704            &compressed_block,
1705            &expected_output,
1706            "BC5 interpolated",
1707        );
1708    }
1709
1710    #[cfg(feature = "bc6h")]
1711    #[test]
1712    fn test_bc6h_block_0() {
1713        use half::f16;
1714
1715        let compressed_block = [
1716            0x40, 0xAF, 0xF6, 0x0B, 0xFD, 0x2E, 0xFF, 0xFF, 0x11, 0x71, 0x10, 0xA1, 0x21, 0xF2,
1717            0x33, 0x73,
1718        ];
1719        let expected_output = [
1720            f16::from_bits(0x5BAB),
1721            f16::from_bits(0x84B9),
1722            f16::from_bits(0xDBE9),
1723            f16::from_bits(0x5BA2),
1724            f16::from_bits(0x84F6),
1725            f16::from_bits(0xDBF1),
1726            f16::from_bits(0x5B99),
1727            f16::from_bits(0x8533),
1728            f16::from_bits(0xDBFA),
1729            f16::from_bits(0x5D9B),
1730            f16::from_bits(0x8307),
1731            f16::from_bits(0xD847),
1732            f16::from_bits(0x5B7E),
1733            f16::from_bits(0x85F0),
1734            f16::from_bits(0xDC15),
1735            f16::from_bits(0x5BA2),
1736            f16::from_bits(0x84F6),
1737            f16::from_bits(0xDBF1),
1738            f16::from_bits(0x5CC3),
1739            f16::from_bits(0x81E8),
1740            f16::from_bits(0xD8D6),
1741            f16::from_bits(0x5D9B),
1742            f16::from_bits(0x8307),
1743            f16::from_bits(0xD847),
1744            f16::from_bits(0x5BA2),
1745            f16::from_bits(0x84F6),
1746            f16::from_bits(0xDBF1),
1747            f16::from_bits(0x5B6D),
1748            f16::from_bits(0x866B),
1749            f16::from_bits(0xDC27),
1750            f16::from_bits(0x5C27),
1751            f16::from_bits(0x8117),
1752            f16::from_bits(0xD93F),
1753            f16::from_bits(0x5CC3),
1754            f16::from_bits(0x81E8),
1755            f16::from_bits(0xD8D6),
1756            f16::from_bits(0x5BA2),
1757            f16::from_bits(0x84F6),
1758            f16::from_bits(0xDBF1),
1759            f16::from_bits(0x5CFE),
1760            f16::from_bits(0x8235),
1761            f16::from_bits(0xD8AF),
1762            f16::from_bits(0x5C5B),
1763            f16::from_bits(0x815C),
1764            f16::from_bits(0xD91C),
1765            f16::from_bits(0x5D66),
1766            f16::from_bits(0x82C1),
1767            f16::from_bits(0xD869),
1768        ];
1769
1770        let mut decoded = [f16::ZERO; 48];
1771        decode_block_bc6h(&compressed_block, &mut decoded, 12, true);
1772
1773        assert_eq!(&decoded[..], &expected_output[..], "BC6H block mismatch");
1774    }
1775
1776    #[test]
1777    #[rustfmt::skip]
1778    fn test_bc6h_block_0_float() {
1779        let compressed_block = [
1780            0x40, 0xAF, 0xF6, 0x0B, 0xFD, 0x2E, 0xFF, 0xFF, 0x11, 0x71, 0x10, 0xA1, 0x21, 0xF2,
1781            0x33, 0x73,
1782        ];
1783
1784        let expected_output: [f32; 48] = [
1785            245.375, -0.000072062016, -253.125, 244.25, -0.0000756979, -254.125, 243.125, -0.00007933378, -255.25, 358.75, -0.0000461936, -136.875, 239.75, -0.00009059906, -261.25, 244.25,
1786            -0.0000756979, -254.125, 304.75, -0.000029087067, -154.75, 358.75, -0.0000461936, -136.875, 244.25, -0.0000756979, -254.125, 237.625, -0.00009793043, -265.75, 265.75, -0.000016629696,
1787            -167.875, 304.75, -0.000029087067, -154.75, 244.25, -0.0000756979, -254.125, 319.5, -0.000033676624, -149.875, 278.75, -0.000020742416, -163.5, 345.5, -0.000042021275, -141.125
1788        ];
1789
1790        let mut decoded = [0.0_f32; 48];
1791        decode_block_bc6h_float(&compressed_block, &mut decoded, 12, true);
1792
1793        assert_eq!(&decoded[..], &expected_output[..], "BC6H block mismatch");
1794    }
1795
1796    #[test]
1797    fn test_bc7_block_0() {
1798        let compressed_block = [
1799            0x40, 0xAF, 0xF6, 0xB, 0xFD, 0x2E, 0xFF, 0xFF, 0x11, 0x71, 0x10, 0xA1, 0x21, 0xF2,
1800            0x33, 0x73,
1801        ];
1802        let expected_output = [
1803            0xBD, 0xBF, 0xBF, 0xFF, 0xBD, 0xBD, 0xBD, 0xFF, 0xBD, 0xBF, 0xBF, 0xFF, 0xBD, 0xBD,
1804            0xBD, 0xFF, 0xBD, 0xBD, 0xBD, 0xFF, 0xBC, 0xBB, 0xB9, 0xFF, 0xBB, 0xB9, 0xB7, 0xFF,
1805            0xBB, 0xB9, 0xB7, 0xFF, 0xBB, 0xB9, 0xB7, 0xFF, 0xB9, 0xB1, 0xAC, 0xFF, 0x0, 0x0, 0x0,
1806            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
1807            0x0, 0x0, 0x0, 0x0,
1808        ];
1809        test_block(
1810            decode_block_bc7,
1811            8,
1812            &compressed_block,
1813            &expected_output,
1814            "BC7 block 0",
1815        );
1816    }
1817
1818    #[test]
1819    fn test_bc7_block_1() {
1820        let compressed_block = [
1821            0xC0, 0x8C, 0xEF, 0xA2, 0xBB, 0xDC, 0xFE, 0x7F, 0x6C, 0x55, 0x6A, 0x34, 0x4F, 0x0,
1822            0x5D, 0x0,
1823        ];
1824        let expected_output = [
1825            0x50, 0x4A, 0x48, 0xFE, 0x50, 0x4A, 0x48, 0xFE, 0x64, 0x5D, 0x59, 0xFE, 0x50, 0x4A,
1826            0x48, 0xFE, 0x7C, 0x74, 0x6E, 0xFE, 0x46, 0x41, 0x3F, 0xFE, 0x72, 0x6A, 0x65, 0xFE,
1827            0x4A, 0x45, 0x43, 0xFE, 0x32, 0x2E, 0x2E, 0xFE, 0x32, 0x2E, 0x2E, 0xFE, 0x0, 0x0, 0x0,
1828            0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0,
1829            0x0, 0x0, 0x0, 0x0,
1830        ];
1831        test_block(
1832            decode_block_bc7,
1833            8,
1834            &compressed_block,
1835            &expected_output,
1836            "BC7 block 1",
1837        );
1838    }
1839}