1#[cfg(any(feature = "bc15", feature = "bc6h", feature = "bc7"))]
4mod block;
5
6#[cfg(feature = "bc7")]
7#[cfg_attr(docsrs, doc(cfg(feature = "bc7")))]
8pub use self::block::decode_block_bc7;
9#[cfg(feature = "bc15")]
10#[cfg_attr(docsrs, doc(cfg(feature = "bc15")))]
11pub use self::block::{
12 decode_block_bc1, decode_block_bc2, decode_block_bc3, decode_block_bc4, decode_block_bc5,
13};
14#[cfg(feature = "bc6h")]
15#[cfg_attr(docsrs, doc(cfg(feature = "bc6h")))]
16pub use self::block::{decode_block_bc6h, decode_block_bc6h_float};
17#[cfg(feature = "bc6h")]
18use crate::BC6HSettings;
19#[cfg(feature = "bc7")]
20use crate::BC7Settings;
21#[cfg(any(feature = "bc15", feature = "bc6h", feature = "bc7"))]
22use crate::CompressionVariant;
23
24#[cfg(any(feature = "bc15", feature = "bc6h", feature = "bc7"))]
26trait BlockRgba8Decoder {
27 fn decode_block_rgba8(compressed: &[u8], decompressed: &mut [u8], pitch: usize);
28 fn block_byte_size() -> u32;
29}
30
31#[cfg(feature = "bc6h")]
33trait BlockRgba16fDecoder {
34 fn decode_block_rgba16f(compressed: &[u8], decompressed: &mut [half::f16], pitch: usize);
35 fn block_byte_size() -> u32;
36}
37
38#[cfg(feature = "bc6h")]
40trait BlockRgba32fDecoder {
41 fn decode_block_rgba32f(compressed: &[u8], decompressed: &mut [f32], pitch: usize);
42 fn block_byte_size() -> u32;
43}
44
45#[cfg(feature = "bc15")]
46struct BC1Decoder;
47#[cfg(feature = "bc15")]
48struct BC2Decoder;
49#[cfg(feature = "bc15")]
50struct BC3Decoder;
51#[cfg(feature = "bc15")]
52struct BC4Decoder;
53#[cfg(feature = "bc15")]
54struct BC5Decoder;
55#[cfg(feature = "bc6h")]
56struct BC6HDecoder;
57#[cfg(feature = "bc7")]
58struct BC7Decoder;
59
60#[cfg(feature = "bc15")]
61impl BlockRgba8Decoder for BC1Decoder {
62 #[inline(always)]
63 fn decode_block_rgba8(compressed: &[u8], decompressed: &mut [u8], pitch: usize) {
64 decode_block_bc1(compressed, decompressed, pitch)
65 }
66
67 fn block_byte_size() -> u32 {
68 CompressionVariant::BC1.block_byte_size()
69 }
70}
71
72#[cfg(feature = "bc15")]
73impl BlockRgba8Decoder for BC2Decoder {
74 #[inline(always)]
75 fn decode_block_rgba8(compressed: &[u8], decompressed: &mut [u8], pitch: usize) {
76 decode_block_bc2(compressed, decompressed, pitch)
77 }
78
79 fn block_byte_size() -> u32 {
80 CompressionVariant::BC2.block_byte_size()
81 }
82}
83
84#[cfg(feature = "bc15")]
85impl BlockRgba8Decoder for BC3Decoder {
86 #[inline(always)]
87 fn decode_block_rgba8(compressed: &[u8], decompressed: &mut [u8], pitch: usize) {
88 decode_block_bc3(compressed, decompressed, pitch)
89 }
90
91 fn block_byte_size() -> u32 {
92 CompressionVariant::BC3.block_byte_size()
93 }
94}
95
96#[cfg(feature = "bc15")]
97impl BlockRgba8Decoder for BC4Decoder {
98 #[inline(always)]
99 fn decode_block_rgba8(compressed: &[u8], decompressed: &mut [u8], pitch: usize) {
100 const PITCH: usize = 4;
101 let mut buffer = [0u8; 16];
102 decode_block_bc4(compressed, &mut buffer, 4);
103
104 for y in 0..4 {
106 for x in 0..4 {
107 let out_pos = y * pitch + x * 4;
108 let in_pos = y * PITCH + x;
109
110 decompressed[out_pos] = buffer[in_pos];
111 decompressed[out_pos + 1] = 0;
112 decompressed[out_pos + 2] = 0;
113 decompressed[out_pos + 3] = 0;
114 }
115 }
116 }
117
118 fn block_byte_size() -> u32 {
119 CompressionVariant::BC4.block_byte_size()
120 }
121}
122
123#[cfg(feature = "bc15")]
124impl BlockRgba8Decoder for BC5Decoder {
125 #[inline(always)]
126 fn decode_block_rgba8(compressed: &[u8], decompressed: &mut [u8], pitch: usize) {
127 const PITCH: usize = 8;
128 let mut buffer = [0u8; 32];
129 decode_block_bc5(compressed, &mut buffer, PITCH);
130
131 for y in 0..4 {
133 for x in 0..4 {
134 let out_pos = y * pitch + x * 4;
135 let in_pos = y * PITCH + x * 2;
136
137 decompressed[out_pos] = buffer[in_pos];
138 decompressed[out_pos + 1] = buffer[in_pos + 1];
139 decompressed[out_pos + 2] = 0;
140 decompressed[out_pos + 3] = 0;
141 }
142 }
143 }
144
145 fn block_byte_size() -> u32 {
146 CompressionVariant::BC5.block_byte_size()
147 }
148}
149
150#[cfg(feature = "bc6h")]
151fn linear_to_srgb(linear: f32) -> u8 {
152 let v = if linear <= 0.0031308 {
153 linear * 12.92
154 } else {
155 1.055 * linear.powf(1.0 / 2.4) - 0.055
156 };
157
158 (v.clamp(0.0, 1.0) * 255.0).round() as u8
159}
160
161#[cfg(feature = "bc6h")]
162impl BlockRgba8Decoder for BC6HDecoder {
163 #[inline(always)]
164 fn decode_block_rgba8(compressed: &[u8], decompressed: &mut [u8], pitch: usize) {
165 const PITCH: usize = 12;
166 let mut buffer = [0.0_f32; 48];
167 decode_block_bc6h_float(compressed, &mut buffer, PITCH, false);
168
169 for y in 0..4 {
171 for x in 0..4 {
172 let out_pos = y * pitch + x * 4;
173 let in_pos = y * PITCH + x * 3;
174
175 decompressed[out_pos] = linear_to_srgb(buffer[in_pos]) as _;
176 decompressed[out_pos + 1] = linear_to_srgb(buffer[in_pos + 1]) as _;
177 decompressed[out_pos + 2] = linear_to_srgb(buffer[in_pos + 2]) as _;
178 decompressed[out_pos + 3] = 0;
179 }
180 }
181 }
182
183 fn block_byte_size() -> u32 {
184 CompressionVariant::BC6H(BC6HSettings::basic()).block_byte_size()
185 }
186}
187
188#[cfg(feature = "bc7")]
189impl BlockRgba8Decoder for BC7Decoder {
190 #[inline(always)]
191 fn decode_block_rgba8(compressed: &[u8], decompressed: &mut [u8], pitch: usize) {
192 decode_block_bc7(compressed, decompressed, pitch)
193 }
194
195 fn block_byte_size() -> u32 {
196 CompressionVariant::BC7(BC7Settings::alpha_basic()).block_byte_size()
197 }
198}
199
200#[cfg(any(feature = "bc15", feature = "bc6h", feature = "bc7"))]
201fn decompress_rgba8<D: BlockRgba8Decoder>(
202 width: u32,
203 height: u32,
204 blocks_data: &[u8],
205 rgba_data: &mut [u8],
206) {
207 let blocks_x = width.div_ceil(4);
208 let blocks_y = height.div_ceil(4);
209 let block_byte_size = D::block_byte_size() as usize;
210 let output_row_pitch = width as usize * 4; for by in 0..blocks_y {
213 for bx in 0..blocks_x {
214 let block_index = (by * blocks_x + bx) as usize;
215 let block_offset = block_index * block_byte_size;
216
217 if block_offset + block_byte_size > blocks_data.len() {
218 break;
219 }
220
221 let output_offset = (by * 4 * output_row_pitch as u32 + bx * 16) as usize;
222
223 if output_offset < rgba_data.len() {
224 D::decode_block_rgba8(
225 &blocks_data[block_offset..block_offset + block_byte_size],
226 &mut rgba_data[output_offset..],
227 output_row_pitch,
228 );
229 }
230 }
231 }
232}
233
234#[cfg(feature = "bc6h")]
235impl BlockRgba16fDecoder for BC6HDecoder {
236 #[inline(always)]
237 fn decode_block_rgba16f(compressed: &[u8], decompressed: &mut [half::f16], pitch: usize) {
238 decode_block_bc6h(compressed, decompressed, pitch, false);
239 }
240
241 fn block_byte_size() -> u32 {
242 CompressionVariant::BC6H(BC6HSettings::basic()).block_byte_size()
243 }
244}
245
246#[cfg(feature = "bc6h")]
247fn decompress_rgba16f<D: BlockRgba16fDecoder>(
248 width: u32,
249 height: u32,
250 blocks_data: &[u8],
251 rgba_data: &mut [half::f16],
252) {
253 let blocks_x = width.div_ceil(4);
254 let blocks_y = height.div_ceil(4);
255 let block_byte_size = D::block_byte_size() as usize;
256 let output_row_pitch = width as usize * 4; for by in 0..blocks_y {
259 for bx in 0..blocks_x {
260 let block_index = (by * blocks_x + bx) as usize;
261 let block_offset = block_index * block_byte_size;
262
263 if block_offset + block_byte_size > blocks_data.len() {
264 break;
265 }
266
267 let output_offset = (by * 4 * output_row_pitch as u32 + bx * 16) as usize;
268
269 if output_offset < rgba_data.len() {
270 D::decode_block_rgba16f(
271 &blocks_data[block_offset..block_offset + block_byte_size],
272 &mut rgba_data[output_offset..],
273 output_row_pitch,
274 );
275 }
276 }
277 }
278}
279
280#[cfg(feature = "bc6h")]
281impl BlockRgba32fDecoder for BC6HDecoder {
282 #[inline(always)]
283 fn decode_block_rgba32f(compressed: &[u8], decompressed: &mut [f32], pitch: usize) {
284 decode_block_bc6h_float(compressed, decompressed, pitch, false);
285 }
286
287 fn block_byte_size() -> u32 {
288 CompressionVariant::BC6H(BC6HSettings::basic()).block_byte_size()
289 }
290}
291
292#[cfg(feature = "bc6h")]
293fn decompress_rgba32f<D: BlockRgba32fDecoder>(
294 width: u32,
295 height: u32,
296 blocks_data: &[u8],
297 rgba_data: &mut [f32],
298) {
299 let blocks_x = width.div_ceil(4);
300 let blocks_y = height.div_ceil(4);
301 let block_byte_size = D::block_byte_size() as usize;
302 let output_row_pitch = width as usize * 4; for by in 0..blocks_y {
305 for bx in 0..blocks_x {
306 let block_index = (by * blocks_x + bx) as usize;
307 let block_offset = block_index * block_byte_size;
308
309 if block_offset + block_byte_size > blocks_data.len() {
310 break;
311 }
312
313 let output_offset = (by * 4 * output_row_pitch as u32 + bx * 16) as usize;
314
315 if output_offset < rgba_data.len() {
316 D::decode_block_rgba32f(
317 &blocks_data[block_offset..block_offset + block_byte_size],
318 &mut rgba_data[output_offset..],
319 output_row_pitch,
320 );
321 }
322 }
323 }
324}
325
326#[cfg(any(feature = "bc15", feature = "bc6h", feature = "bc7"))]
332#[cfg_attr(
333 docsrs,
334 doc(cfg(any(feature = "bc15", feature = "bc6h", feature = "bc7")))
335)]
336pub fn decompress_blocks_as_rgba8(
337 variant: CompressionVariant,
338 width: u32,
339 height: u32,
340 blocks_data: &[u8],
341 rgba_data: &mut [u8],
342) {
343 let expected_input_size = variant.blocks_byte_size(width, height);
344 assert_eq!(
345 blocks_data.len(),
346 expected_input_size,
347 "the input bitstream slice has not the expected size"
348 );
349
350 let expected_output_size = width as usize * height as usize * 4;
351 assert_eq!(
352 rgba_data.len(),
353 expected_output_size,
354 "the output slice has not the expected size"
355 );
356
357 match variant {
358 #[cfg(feature = "bc15")]
359 CompressionVariant::BC1 => {
360 decompress_rgba8::<BC1Decoder>(width, height, blocks_data, rgba_data)
361 }
362 #[cfg(feature = "bc15")]
363 CompressionVariant::BC2 => {
364 decompress_rgba8::<BC2Decoder>(width, height, blocks_data, rgba_data)
365 }
366 #[cfg(feature = "bc15")]
367 CompressionVariant::BC3 => {
368 decompress_rgba8::<BC3Decoder>(width, height, blocks_data, rgba_data)
369 }
370 #[cfg(feature = "bc15")]
371 CompressionVariant::BC4 => {
372 decompress_rgba8::<BC4Decoder>(width, height, blocks_data, rgba_data)
373 }
374 #[cfg(feature = "bc15")]
375 CompressionVariant::BC5 => {
376 decompress_rgba8::<BC5Decoder>(width, height, blocks_data, rgba_data)
377 }
378 #[cfg(feature = "bc6h")]
379 CompressionVariant::BC6H(..) => {
380 decompress_rgba8::<BC6HDecoder>(width, height, blocks_data, rgba_data)
381 }
382 #[cfg(feature = "bc7")]
383 CompressionVariant::BC7(..) => {
384 decompress_rgba8::<BC7Decoder>(width, height, blocks_data, rgba_data)
385 }
386 }
387}
388
389#[cfg(feature = "bc6h")]
396#[cfg_attr(docsrs, doc(cfg(feature = "bc6h")))]
397pub fn decompress_blocks_as_rgba16f(
398 variant: CompressionVariant,
399 width: u32,
400 height: u32,
401 blocks_data: &[u8],
402 rgba_data: &mut [half::f16],
403) {
404 let expected_input_size = variant.blocks_byte_size(width, height);
405
406 assert_eq!(
407 blocks_data.len(),
408 expected_input_size,
409 "the input bitstream slice has not the expected size"
410 );
411
412 let expected_output_size = width as usize * height as usize * 4;
413 assert_eq!(
414 rgba_data.len(),
415 expected_output_size,
416 "the output slice has not the expected size"
417 );
418
419 match variant {
420 CompressionVariant::BC6H(..) => {
421 decompress_rgba16f::<BC6HDecoder>(width, height, blocks_data, rgba_data)
422 }
423 #[allow(unreachable_patterns)]
424 _ => {
425 panic!("unsupported compression variant");
426 }
427 }
428}
429
430#[cfg(feature = "bc6h")]
437#[cfg_attr(docsrs, doc(cfg(feature = "bc6h")))]
438pub fn decompress_blocks_as_rgba32f(
439 variant: CompressionVariant,
440 width: u32,
441 height: u32,
442 blocks_data: &[u8],
443 rgba_data: &mut [f32],
444) {
445 let expected_input_size = variant.blocks_byte_size(width, height);
446 assert_eq!(
447 blocks_data.len(),
448 expected_input_size,
449 "the input bitstream slice has not the expected size"
450 );
451
452 let expected_output_size = width as usize * height as usize * 4;
453 assert_eq!(
454 rgba_data.len(),
455 expected_output_size,
456 "the output slice has not the expected size"
457 );
458
459 match variant {
460 CompressionVariant::BC6H(..) => {
461 decompress_rgba32f::<BC6HDecoder>(width, height, blocks_data, rgba_data)
462 }
463 #[allow(unreachable_patterns)]
464 _ => {
465 panic!("unsupported compression variant");
466 }
467 }
468}