1#[cfg(any(feature = "flate2", feature = "ruzstd"))]
2use std::io::Read;
3
4#[cfg(feature = "basis-universal")]
5use basis_universal::{
6 DecodeFlags, LowLevelUastcTranscoder, SliceParametersUastc, TranscoderBlockFormat,
7};
8use bevy_color::Srgba;
9use bevy_utils::default;
10#[cfg(any(feature = "flate2", feature = "ruzstd"))]
11use ktx2::SupercompressionScheme;
12use ktx2::{
13 BasicDataFormatDescriptor, ChannelTypeQualifiers, ColorModel, DataFormatDescriptorHeader,
14 Header, SampleInformation,
15};
16use wgpu::{
17 AstcBlock, AstcChannel, Extent3d, TextureDimension, TextureFormat, TextureViewDescriptor,
18 TextureViewDimension,
19};
20
21use super::{CompressedImageFormats, DataFormat, Image, TextureError, TranscodeFormat};
22
23pub fn ktx2_buffer_to_image(
24 buffer: &[u8],
25 supported_compressed_formats: CompressedImageFormats,
26 is_srgb: bool,
27) -> Result<Image, TextureError> {
28 let ktx2 = ktx2::Reader::new(buffer)
29 .map_err(|err| TextureError::InvalidData(format!("Failed to parse ktx2 file: {err:?}")))?;
30 let Header {
31 pixel_width: width,
32 pixel_height: height,
33 pixel_depth: depth,
34 layer_count,
35 face_count,
36 level_count,
37 supercompression_scheme,
38 ..
39 } = ktx2.header();
40 let layer_count = layer_count.max(1);
41 let face_count = face_count.max(1);
42 let depth = depth.max(1);
43
44 let mut levels = Vec::new();
46 if let Some(supercompression_scheme) = supercompression_scheme {
47 for (_level, _level_data) in ktx2.levels().enumerate() {
48 match supercompression_scheme {
49 #[cfg(feature = "flate2")]
50 SupercompressionScheme::ZLIB => {
51 let mut decoder = flate2::bufread::ZlibDecoder::new(_level_data);
52 let mut decompressed = Vec::new();
53 decoder.read_to_end(&mut decompressed).map_err(|err| {
54 TextureError::SuperDecompressionError(format!(
55 "Failed to decompress {supercompression_scheme:?} for mip {_level}: {err:?}",
56 ))
57 })?;
58 levels.push(decompressed);
59 }
60 #[cfg(feature = "ruzstd")]
61 SupercompressionScheme::Zstandard => {
62 let mut cursor = std::io::Cursor::new(_level_data);
63 let mut decoder = ruzstd::StreamingDecoder::new(&mut cursor)
64 .map_err(|err| TextureError::SuperDecompressionError(err.to_string()))?;
65 let mut decompressed = Vec::new();
66 decoder.read_to_end(&mut decompressed).map_err(|err| {
67 TextureError::SuperDecompressionError(format!(
68 "Failed to decompress {supercompression_scheme:?} for mip {_level}: {err:?}",
69 ))
70 })?;
71 levels.push(decompressed);
72 }
73 _ => {
74 return Err(TextureError::SuperDecompressionError(format!(
75 "Unsupported supercompression scheme: {supercompression_scheme:?}",
76 )));
77 }
78 }
79 }
80 } else {
81 levels = ktx2.levels().map(|level| level.to_vec()).collect();
82 }
83
84 let texture_format = ktx2_get_texture_format(&ktx2, is_srgb).or_else(|error| match error {
86 TextureError::FormatRequiresTranscodingError(transcode_format) => {
88 let mut transcoded = vec![Vec::default(); levels.len()];
89 let texture_format = match transcode_format {
90 TranscodeFormat::R8UnormSrgb => {
91 let (mut original_width, mut original_height) = (width, height);
92
93 for (level, level_data) in levels.iter().enumerate() {
94 transcoded[level] = level_data
95 .iter()
96 .copied()
97 .map(|v| (Srgba::gamma_function(v as f32 / 255.) * 255.).floor() as u8)
98 .collect::<Vec<u8>>();
99
100 original_width = (original_width / 2).max(1);
102 original_height = (original_height / 2).max(1);
103 }
104
105 TextureFormat::R8Unorm
106 }
107 TranscodeFormat::Rg8UnormSrgb => {
108 let (mut original_width, mut original_height) = (width, height);
109
110 for (level, level_data) in levels.iter().enumerate() {
111 transcoded[level] = level_data
112 .iter()
113 .copied()
114 .map(|v| (Srgba::gamma_function(v as f32 / 255.) * 255.).floor() as u8)
115 .collect::<Vec<u8>>();
116
117 original_width = (original_width / 2).max(1);
119 original_height = (original_height / 2).max(1);
120 }
121
122 TextureFormat::Rg8Unorm
123 }
124 TranscodeFormat::Rgb8 => {
125 let mut rgba = vec![255u8; width as usize * height as usize * 4];
126 for (level, level_data) in levels.iter().enumerate() {
127 let n_pixels = (width as usize >> level).max(1) * (height as usize >> level).max(1);
128
129 let mut offset = 0;
130 for _layer in 0..layer_count {
131 for _face in 0..face_count {
132 for i in 0..n_pixels {
133 rgba[i * 4] = level_data[offset];
134 rgba[i * 4 + 1] = level_data[offset + 1];
135 rgba[i * 4 + 2] = level_data[offset + 2];
136 offset += 3;
137 }
138 transcoded[level].extend_from_slice(&rgba[0..n_pixels * 4]);
139 }
140 }
141 }
142
143 if is_srgb {
144 TextureFormat::Rgba8UnormSrgb
145 } else {
146 TextureFormat::Rgba8Unorm
147 }
148 }
149 #[cfg(feature = "basis-universal")]
150 TranscodeFormat::Uastc(data_format) => {
151 let (transcode_block_format, texture_format) =
152 get_transcoded_formats(supported_compressed_formats, data_format, is_srgb);
153 let texture_format_info = texture_format;
154 let (block_width_pixels, block_height_pixels) = (
155 texture_format_info.block_dimensions().0,
156 texture_format_info.block_dimensions().1,
157 );
158 let block_bytes = texture_format_info.block_copy_size(None).unwrap();
160
161 let transcoder = LowLevelUastcTranscoder::new();
162 for (level, level_data) in levels.iter().enumerate() {
163 let (level_width, level_height) = (
164 (width >> level as u32).max(1),
165 (height >> level as u32).max(1),
166 );
167 let (num_blocks_x, num_blocks_y) = (
168 ((level_width + block_width_pixels - 1) / block_width_pixels) .max(1),
169 ((level_height + block_height_pixels - 1) / block_height_pixels) .max(1),
170 );
171 let level_bytes = (num_blocks_x * num_blocks_y * block_bytes) as usize;
172
173 let mut offset = 0;
174 for _layer in 0..layer_count {
175 for _face in 0..face_count {
176 let slice_parameters = SliceParametersUastc {
179 num_blocks_x,
180 num_blocks_y,
181 has_alpha: false,
182 original_width: level_width,
183 original_height: level_height,
184 };
185 transcoder
186 .transcode_slice(
187 &level_data[offset..(offset + level_bytes)],
188 slice_parameters,
189 DecodeFlags::HIGH_QUALITY,
190 transcode_block_format,
191 )
192 .map(|mut transcoded_level| transcoded[level].append(&mut transcoded_level))
193 .map_err(|error| {
194 TextureError::SuperDecompressionError(format!(
195 "Failed to transcode mip level {level} from UASTC to {transcode_block_format:?}: {error:?}",
196 ))
197 })?;
198 offset += level_bytes;
199 }
200 }
201 }
202 texture_format
203 }
204 TranscodeFormat::Etc1s => {
207 let texture_format = if is_srgb {
208 TextureFormat::Etc2Rgb8UnormSrgb
209 } else {
210 TextureFormat::Etc2Rgb8Unorm
211 };
212 if !supported_compressed_formats.supports(texture_format) {
213 return Err(error);
214 }
215 transcoded = levels.to_vec();
216 texture_format
217 }
218 #[cfg(not(feature = "basis-universal"))]
219 _ => return Err(error),
220 };
221 levels = transcoded;
222 Ok(texture_format)
223 }
224 _ => Err(error),
225 })?;
226 if !supported_compressed_formats.supports(texture_format) {
227 return Err(TextureError::UnsupportedTextureFormat(format!(
228 "Format not supported by this GPU: {texture_format:?}",
229 )));
230 }
231
232 let texture_format_info = texture_format;
234 let (block_width_pixels, block_height_pixels) = (
235 texture_format_info.block_dimensions().0 as usize,
236 texture_format_info.block_dimensions().1 as usize,
237 );
238 let block_bytes = texture_format_info.block_copy_size(None).unwrap() as usize;
240
241 let mut wgpu_data = vec![Vec::default(); (layer_count * face_count) as usize];
242 for (level, level_data) in levels.iter().enumerate() {
243 let (level_width, level_height, level_depth) = (
244 (width as usize >> level).max(1),
245 (height as usize >> level).max(1),
246 (depth as usize >> level).max(1),
247 );
248 let (num_blocks_x, num_blocks_y) = (
249 ((level_width + block_width_pixels - 1) / block_width_pixels).max(1),
250 ((level_height + block_height_pixels - 1) / block_height_pixels).max(1),
251 );
252 let level_bytes = num_blocks_x * num_blocks_y * level_depth * block_bytes;
253
254 let mut index = 0;
255 for _layer in 0..layer_count {
256 for _face in 0..face_count {
257 let offset = index * level_bytes;
258 wgpu_data[index].extend_from_slice(&level_data[offset..(offset + level_bytes)]);
259 index += 1;
260 }
261 }
262 }
263
264 let mut image = Image::default();
267 image.texture_descriptor.format = texture_format;
268 image.data = wgpu_data.into_iter().flatten().collect::<Vec<_>>();
269 image.texture_descriptor.size = Extent3d {
270 width,
271 height,
272 depth_or_array_layers: if layer_count > 1 || face_count > 1 {
273 layer_count * face_count
274 } else {
275 depth
276 }
277 .max(1),
278 }
279 .physical_size(texture_format);
280 image.texture_descriptor.mip_level_count = level_count;
281 image.texture_descriptor.dimension = if depth > 1 {
282 TextureDimension::D3
283 } else if image.is_compressed() || height > 1 {
284 TextureDimension::D2
285 } else {
286 TextureDimension::D1
287 };
288 let mut dimension = None;
289 if face_count == 6 {
290 dimension = Some(if layer_count > 1 {
291 TextureViewDimension::CubeArray
292 } else {
293 TextureViewDimension::Cube
294 });
295 } else if layer_count > 1 {
296 dimension = Some(TextureViewDimension::D2Array);
297 } else if depth > 1 {
298 dimension = Some(TextureViewDimension::D3);
299 }
300 if dimension.is_some() {
301 image.texture_view_descriptor = Some(TextureViewDescriptor {
302 dimension,
303 ..default()
304 });
305 }
306 Ok(image)
307}
308
309#[cfg(feature = "basis-universal")]
310pub fn get_transcoded_formats(
311 supported_compressed_formats: CompressedImageFormats,
312 data_format: DataFormat,
313 is_srgb: bool,
314) -> (TranscoderBlockFormat, TextureFormat) {
315 match data_format {
316 DataFormat::Rrr => {
317 if supported_compressed_formats.contains(CompressedImageFormats::BC) {
318 (TranscoderBlockFormat::BC4, TextureFormat::Bc4RUnorm)
319 } else if supported_compressed_formats.contains(CompressedImageFormats::ETC2) {
320 (
321 TranscoderBlockFormat::ETC2_EAC_R11,
322 TextureFormat::EacR11Unorm,
323 )
324 } else {
325 (TranscoderBlockFormat::RGBA32, TextureFormat::R8Unorm)
326 }
327 }
328 DataFormat::Rrrg | DataFormat::Rg => {
329 if supported_compressed_formats.contains(CompressedImageFormats::BC) {
330 (TranscoderBlockFormat::BC5, TextureFormat::Bc5RgUnorm)
331 } else if supported_compressed_formats.contains(CompressedImageFormats::ETC2) {
332 (
333 TranscoderBlockFormat::ETC2_EAC_RG11,
334 TextureFormat::EacRg11Unorm,
335 )
336 } else {
337 (TranscoderBlockFormat::RGBA32, TextureFormat::Rg8Unorm)
338 }
339 }
340 DataFormat::Rgb | DataFormat::Rgba => {
343 if supported_compressed_formats.contains(CompressedImageFormats::ASTC_LDR) {
347 (
348 TranscoderBlockFormat::ASTC_4x4,
349 TextureFormat::Astc {
350 block: AstcBlock::B4x4,
351 channel: if is_srgb {
352 AstcChannel::UnormSrgb
353 } else {
354 AstcChannel::Unorm
355 },
356 },
357 )
358 } else if supported_compressed_formats.contains(CompressedImageFormats::BC) {
359 (
360 TranscoderBlockFormat::BC7,
361 if is_srgb {
362 TextureFormat::Bc7RgbaUnormSrgb
363 } else {
364 TextureFormat::Bc7RgbaUnorm
365 },
366 )
367 } else if supported_compressed_formats.contains(CompressedImageFormats::ETC2) {
368 (
369 TranscoderBlockFormat::ETC2_RGBA,
370 if is_srgb {
371 TextureFormat::Etc2Rgba8UnormSrgb
372 } else {
373 TextureFormat::Etc2Rgba8Unorm
374 },
375 )
376 } else {
377 (
378 TranscoderBlockFormat::RGBA32,
379 if is_srgb {
380 TextureFormat::Rgba8UnormSrgb
381 } else {
382 TextureFormat::Rgba8Unorm
383 },
384 )
385 }
386 }
387 }
388}
389
390pub fn ktx2_get_texture_format<Data: AsRef<[u8]>>(
391 ktx2: &ktx2::Reader<Data>,
392 is_srgb: bool,
393) -> Result<TextureFormat, TextureError> {
394 if let Some(format) = ktx2.header().format {
395 return ktx2_format_to_texture_format(format, is_srgb);
396 }
397
398 for data_format_descriptor in ktx2.data_format_descriptors() {
399 if data_format_descriptor.header == DataFormatDescriptorHeader::BASIC {
400 let basic_data_format_descriptor =
401 BasicDataFormatDescriptor::parse(data_format_descriptor.data)
402 .map_err(|err| TextureError::InvalidData(format!("KTX2: {err:?}")))?;
403 let sample_information = basic_data_format_descriptor
404 .sample_information()
405 .collect::<Vec<_>>();
406 return ktx2_dfd_to_texture_format(
407 &basic_data_format_descriptor,
408 &sample_information,
409 is_srgb,
410 );
411 }
412 }
413
414 Err(TextureError::UnsupportedTextureFormat(
415 "Unknown".to_string(),
416 ))
417}
418
419enum DataType {
420 Unorm,
421 UnormSrgb,
422 Snorm,
423 Float,
424 Uint,
425 Sint,
426}
427
428const F32_1_AS_U32: u32 = 1065353216;
431
432fn sample_information_to_data_type(
433 sample: &SampleInformation,
434 is_srgb: bool,
435) -> Result<DataType, TextureError> {
436 if sample
438 .channel_type_qualifiers
439 .contains(ChannelTypeQualifiers::EXPONENT)
440 {
441 return Err(TextureError::UnsupportedTextureFormat(
442 "Unsupported KTX2 channel type qualifier: exponent".to_string(),
443 ));
444 }
445 Ok(
446 if sample
447 .channel_type_qualifiers
448 .contains(ChannelTypeQualifiers::FLOAT)
449 {
450 if sample
452 .channel_type_qualifiers
453 .contains(ChannelTypeQualifiers::SIGNED)
454 {
455 if sample.upper == F32_1_AS_U32 {
456 DataType::Snorm
457 } else {
458 DataType::Float
459 }
460 } else if is_srgb {
461 DataType::UnormSrgb
462 } else {
463 DataType::Unorm
464 }
465 } else if sample
466 .channel_type_qualifiers
467 .contains(ChannelTypeQualifiers::SIGNED)
468 {
469 DataType::Sint
470 } else {
471 DataType::Uint
472 },
473 )
474}
475
476pub fn ktx2_dfd_to_texture_format(
477 data_format_descriptor: &BasicDataFormatDescriptor,
478 sample_information: &[SampleInformation],
479 is_srgb: bool,
480) -> Result<TextureFormat, TextureError> {
481 Ok(match data_format_descriptor.color_model {
482 Some(ColorModel::RGBSDA) => {
483 match sample_information.len() {
484 1 => {
485 if sample_information[0].channel_type != 0 {
487 return Err(TextureError::UnsupportedTextureFormat(
488 "Only red-component single-component KTX2 RGBSDA formats supported"
489 .to_string(),
490 ));
491 }
492
493 let sample = &sample_information[0];
494 let data_type = sample_information_to_data_type(sample, false)?;
495 match sample.bit_length {
496 8 => match data_type {
497 DataType::Unorm => TextureFormat::R8Unorm,
498 DataType::UnormSrgb => {
499 return Err(TextureError::UnsupportedTextureFormat(
500 "UnormSrgb not supported for R8".to_string(),
501 ));
502 }
503 DataType::Snorm => TextureFormat::R8Snorm,
504 DataType::Float => {
505 return Err(TextureError::UnsupportedTextureFormat(
506 "Float not supported for R8".to_string(),
507 ));
508 }
509 DataType::Uint => TextureFormat::R8Uint,
510 DataType::Sint => TextureFormat::R8Sint,
511 },
512 16 => match data_type {
513 DataType::Unorm => TextureFormat::R16Unorm,
514 DataType::UnormSrgb => {
515 return Err(TextureError::UnsupportedTextureFormat(
516 "UnormSrgb not supported for R16".to_string(),
517 ));
518 }
519 DataType::Snorm => TextureFormat::R16Snorm,
520 DataType::Float => TextureFormat::R16Float,
521 DataType::Uint => TextureFormat::R16Uint,
522 DataType::Sint => TextureFormat::R16Sint,
523 },
524 32 => match data_type {
525 DataType::Unorm => {
526 return Err(TextureError::UnsupportedTextureFormat(
527 "Unorm not supported for R32".to_string(),
528 ));
529 }
530 DataType::UnormSrgb => {
531 return Err(TextureError::UnsupportedTextureFormat(
532 "UnormSrgb not supported for R32".to_string(),
533 ));
534 }
535 DataType::Snorm => {
536 return Err(TextureError::UnsupportedTextureFormat(
537 "Snorm not supported for R32".to_string(),
538 ));
539 }
540 DataType::Float => TextureFormat::R32Float,
541 DataType::Uint => TextureFormat::R32Uint,
542 DataType::Sint => TextureFormat::R32Sint,
543 },
544 v => {
545 return Err(TextureError::UnsupportedTextureFormat(format!(
546 "Unsupported sample bit length for RGBSDA 1-channel format: {v}",
547 )));
548 }
549 }
550 }
551 2 => {
552 if sample_information[0].channel_type != 0
554 || sample_information[1].channel_type != 1
555 {
556 return Err(TextureError::UnsupportedTextureFormat(
557 "Only red-green-component two-component KTX2 RGBSDA formats supported"
558 .to_string(),
559 ));
560 }
561 assert_eq!(
563 sample_information[0].bit_length,
564 sample_information[1].bit_length
565 );
566 assert_eq!(
568 sample_information[0].channel_type_qualifiers,
569 sample_information[1].channel_type_qualifiers
570 );
571 assert_eq!(sample_information[0].lower, sample_information[1].lower);
573 assert_eq!(sample_information[0].upper, sample_information[1].upper);
574
575 let sample = &sample_information[0];
576 let data_type = sample_information_to_data_type(sample, false)?;
577 match sample.bit_length {
578 8 => match data_type {
579 DataType::Unorm => TextureFormat::Rg8Unorm,
580 DataType::UnormSrgb => {
581 return Err(TextureError::UnsupportedTextureFormat(
582 "UnormSrgb not supported for Rg8".to_string(),
583 ));
584 }
585 DataType::Snorm => TextureFormat::Rg8Snorm,
586 DataType::Float => {
587 return Err(TextureError::UnsupportedTextureFormat(
588 "Float not supported for Rg8".to_string(),
589 ));
590 }
591 DataType::Uint => TextureFormat::Rg8Uint,
592 DataType::Sint => TextureFormat::Rg8Sint,
593 },
594 16 => match data_type {
595 DataType::Unorm => TextureFormat::Rg16Unorm,
596 DataType::UnormSrgb => {
597 return Err(TextureError::UnsupportedTextureFormat(
598 "UnormSrgb not supported for Rg16".to_string(),
599 ));
600 }
601 DataType::Snorm => TextureFormat::Rg16Snorm,
602 DataType::Float => TextureFormat::Rg16Float,
603 DataType::Uint => TextureFormat::Rg16Uint,
604 DataType::Sint => TextureFormat::Rg16Sint,
605 },
606 32 => match data_type {
607 DataType::Unorm => {
608 return Err(TextureError::UnsupportedTextureFormat(
609 "Unorm not supported for Rg32".to_string(),
610 ));
611 }
612 DataType::UnormSrgb => {
613 return Err(TextureError::UnsupportedTextureFormat(
614 "UnormSrgb not supported for Rg32".to_string(),
615 ));
616 }
617 DataType::Snorm => {
618 return Err(TextureError::UnsupportedTextureFormat(
619 "Snorm not supported for Rg32".to_string(),
620 ));
621 }
622 DataType::Float => TextureFormat::Rg32Float,
623 DataType::Uint => TextureFormat::Rg32Uint,
624 DataType::Sint => TextureFormat::Rg32Sint,
625 },
626 v => {
627 return Err(TextureError::UnsupportedTextureFormat(format!(
628 "Unsupported sample bit length for RGBSDA 2-channel format: {v}",
629 )));
630 }
631 }
632 }
633 3 => {
634 if sample_information[0].channel_type == 0
635 && sample_information[0].bit_length == 11
636 && sample_information[1].channel_type == 1
637 && sample_information[1].bit_length == 11
638 && sample_information[2].channel_type == 2
639 && sample_information[2].bit_length == 10
640 {
641 TextureFormat::Rg11b10Float
642 } else if sample_information[0].channel_type == 0
643 && sample_information[0].bit_length == 9
644 && sample_information[1].channel_type == 1
645 && sample_information[1].bit_length == 9
646 && sample_information[2].channel_type == 2
647 && sample_information[2].bit_length == 9
648 {
649 TextureFormat::Rgb9e5Ufloat
650 } else if sample_information[0].channel_type == 0
651 && sample_information[0].bit_length == 8
652 && sample_information[1].channel_type == 1
653 && sample_information[1].bit_length == 8
654 && sample_information[2].channel_type == 2
655 && sample_information[2].bit_length == 8
656 {
657 return Err(TextureError::FormatRequiresTranscodingError(
658 TranscodeFormat::Rgb8,
659 ));
660 } else {
661 return Err(TextureError::UnsupportedTextureFormat(
662 "3-component formats not supported".to_string(),
663 ));
664 }
665 }
666 4 => {
667 let is_rgba = sample_information[0].channel_type == 0;
669 assert!(
670 sample_information[0].channel_type == 0
671 || sample_information[0].channel_type == 2
672 );
673 assert_eq!(sample_information[1].channel_type, 1);
674 assert_eq!(
675 sample_information[2].channel_type,
676 if is_rgba { 2 } else { 0 }
677 );
678 assert_eq!(sample_information[3].channel_type, 15);
679
680 if sample_information[0].bit_length == 10
682 && sample_information[1].bit_length == 10
683 && sample_information[2].bit_length == 10
684 && sample_information[3].bit_length == 2
685 {
686 return Ok(TextureFormat::Rgb10a2Unorm);
687 }
688
689 assert!(
691 sample_information[0].bit_length == sample_information[1].bit_length
692 && sample_information[0].bit_length == sample_information[2].bit_length
693 && sample_information[0].bit_length == sample_information[3].bit_length
694 );
695 assert!(
696 sample_information[0].lower == sample_information[1].lower
697 && sample_information[0].lower == sample_information[2].lower
698 && sample_information[0].lower == sample_information[3].lower
699 );
700 assert!(
701 sample_information[0].upper == sample_information[1].upper
702 && sample_information[0].upper == sample_information[2].upper
703 && sample_information[0].upper == sample_information[3].upper
704 );
705
706 let sample = &sample_information[0];
707 let data_type = sample_information_to_data_type(sample, is_srgb)?;
708 match sample.bit_length {
709 8 => match data_type {
710 DataType::Unorm => {
711 if is_rgba {
712 TextureFormat::Rgba8Unorm
713 } else {
714 TextureFormat::Bgra8Unorm
715 }
716 }
717 DataType::UnormSrgb => {
718 if is_rgba {
719 TextureFormat::Rgba8UnormSrgb
720 } else {
721 TextureFormat::Bgra8UnormSrgb
722 }
723 }
724 DataType::Snorm => {
725 if is_rgba {
726 TextureFormat::Rgba8Snorm
727 } else {
728 return Err(TextureError::UnsupportedTextureFormat(
729 "Bgra8 not supported for Snorm".to_string(),
730 ));
731 }
732 }
733 DataType::Float => {
734 return Err(TextureError::UnsupportedTextureFormat(
735 "Float not supported for Rgba8/Bgra8".to_string(),
736 ));
737 }
738 DataType::Uint => {
739 if is_rgba {
740 if is_srgb {
743 TextureFormat::Rgba8UnormSrgb
744 } else {
745 TextureFormat::Rgba8Unorm
746 }
747 } else {
748 return Err(TextureError::UnsupportedTextureFormat(
749 "Bgra8 not supported for Uint".to_string(),
750 ));
751 }
752 }
753 DataType::Sint => {
754 if is_rgba {
755 TextureFormat::Rgba8Snorm
758 } else {
759 return Err(TextureError::UnsupportedTextureFormat(
760 "Bgra8 not supported for Sint".to_string(),
761 ));
762 }
763 }
764 },
765 16 => match data_type {
766 DataType::Unorm => {
767 if is_rgba {
768 TextureFormat::Rgba16Unorm
769 } else {
770 return Err(TextureError::UnsupportedTextureFormat(
771 "Bgra16 not supported for Unorm".to_string(),
772 ));
773 }
774 }
775 DataType::UnormSrgb => {
776 return Err(TextureError::UnsupportedTextureFormat(
777 "UnormSrgb not supported for Rgba16/Bgra16".to_string(),
778 ));
779 }
780 DataType::Snorm => {
781 if is_rgba {
782 TextureFormat::Rgba16Snorm
783 } else {
784 return Err(TextureError::UnsupportedTextureFormat(
785 "Bgra16 not supported for Snorm".to_string(),
786 ));
787 }
788 }
789 DataType::Float => {
790 if is_rgba {
791 TextureFormat::Rgba16Float
792 } else {
793 return Err(TextureError::UnsupportedTextureFormat(
794 "Bgra16 not supported for Float".to_string(),
795 ));
796 }
797 }
798 DataType::Uint => {
799 if is_rgba {
800 TextureFormat::Rgba16Uint
801 } else {
802 return Err(TextureError::UnsupportedTextureFormat(
803 "Bgra16 not supported for Uint".to_string(),
804 ));
805 }
806 }
807 DataType::Sint => {
808 if is_rgba {
809 TextureFormat::Rgba16Sint
810 } else {
811 return Err(TextureError::UnsupportedTextureFormat(
812 "Bgra16 not supported for Sint".to_string(),
813 ));
814 }
815 }
816 },
817 32 => match data_type {
818 DataType::Unorm => {
819 return Err(TextureError::UnsupportedTextureFormat(
820 "Unorm not supported for Rgba32/Bgra32".to_string(),
821 ));
822 }
823 DataType::UnormSrgb => {
824 return Err(TextureError::UnsupportedTextureFormat(
825 "UnormSrgb not supported for Rgba32/Bgra32".to_string(),
826 ));
827 }
828 DataType::Snorm => {
829 return Err(TextureError::UnsupportedTextureFormat(
830 "Snorm not supported for Rgba32/Bgra32".to_string(),
831 ));
832 }
833 DataType::Float => {
834 if is_rgba {
835 TextureFormat::Rgba32Float
836 } else {
837 return Err(TextureError::UnsupportedTextureFormat(
838 "Bgra32 not supported for Float".to_string(),
839 ));
840 }
841 }
842 DataType::Uint => {
843 if is_rgba {
844 TextureFormat::Rgba32Uint
845 } else {
846 return Err(TextureError::UnsupportedTextureFormat(
847 "Bgra32 not supported for Uint".to_string(),
848 ));
849 }
850 }
851 DataType::Sint => {
852 if is_rgba {
853 TextureFormat::Rgba32Sint
854 } else {
855 return Err(TextureError::UnsupportedTextureFormat(
856 "Bgra32 not supported for Sint".to_string(),
857 ));
858 }
859 }
860 },
861 v => {
862 return Err(TextureError::UnsupportedTextureFormat(format!(
863 "Unsupported sample bit length for RGBSDA 4-channel format: {v}",
864 )));
865 }
866 }
867 }
868 v => {
869 return Err(TextureError::UnsupportedTextureFormat(format!(
870 "Unsupported channel count for RGBSDA format: {v}",
871 )));
872 }
873 }
874 }
875 Some(ColorModel::YUVSDA)
876 | Some(ColorModel::YIQSDA)
877 | Some(ColorModel::LabSDA)
878 | Some(ColorModel::CMYKA)
879 | Some(ColorModel::HSVAAng)
880 | Some(ColorModel::HSLAAng)
881 | Some(ColorModel::HSVAHex)
882 | Some(ColorModel::HSLAHex)
883 | Some(ColorModel::YCgCoA)
884 | Some(ColorModel::YcCbcCrc)
885 | Some(ColorModel::ICtCp)
886 | Some(ColorModel::CIEXYZ)
887 | Some(ColorModel::CIEXYY) => {
888 return Err(TextureError::UnsupportedTextureFormat(format!(
889 "{:?}",
890 data_format_descriptor.color_model
891 )));
892 }
893 Some(ColorModel::XYZW) => {
894 assert_eq!(
896 data_format_descriptor.texel_block_dimensions[0] as usize,
897 sample_information.len()
898 );
899 match sample_information.len() {
900 4 => {
901 assert_eq!(sample_information[0].channel_type, 0);
903 assert_eq!(sample_information[1].channel_type, 1);
904 assert_eq!(sample_information[2].channel_type, 2);
905 assert_eq!(sample_information[3].channel_type, 3);
906 assert!(
908 sample_information[0].bit_length == sample_information[1].bit_length
909 && sample_information[0].bit_length == sample_information[2].bit_length
910 && sample_information[0].bit_length == sample_information[3].bit_length
911 );
912 assert!(
914 sample_information[0].channel_type_qualifiers
915 == sample_information[1].channel_type_qualifiers
916 && sample_information[0].channel_type_qualifiers
917 == sample_information[2].channel_type_qualifiers
918 && sample_information[0].channel_type_qualifiers
919 == sample_information[3].channel_type_qualifiers
920 );
921 assert!(
923 sample_information[0].lower == sample_information[1].lower
924 && sample_information[0].lower == sample_information[2].lower
925 && sample_information[0].lower == sample_information[3].lower
926 );
927 assert!(
928 sample_information[0].upper == sample_information[1].upper
929 && sample_information[0].upper == sample_information[2].upper
930 && sample_information[0].upper == sample_information[3].upper
931 );
932
933 let sample = &sample_information[0];
934 let data_type = sample_information_to_data_type(sample, false)?;
935 match sample.bit_length {
936 8 => match data_type {
937 DataType::Unorm => TextureFormat::Rgba8Unorm,
938 DataType::UnormSrgb => {
939 return Err(TextureError::UnsupportedTextureFormat(
940 "UnormSrgb not supported for XYZW".to_string(),
941 ));
942 }
943 DataType::Snorm => TextureFormat::Rgba8Snorm,
944 DataType::Float => {
945 return Err(TextureError::UnsupportedTextureFormat(
946 "Float not supported for Rgba8/Bgra8".to_string(),
947 ));
948 }
949 DataType::Uint => TextureFormat::Rgba8Uint,
950 DataType::Sint => TextureFormat::Rgba8Sint,
951 },
952 16 => match data_type {
953 DataType::Unorm => TextureFormat::Rgba16Unorm,
954 DataType::UnormSrgb => {
955 return Err(TextureError::UnsupportedTextureFormat(
956 "UnormSrgb not supported for Rgba16/Bgra16".to_string(),
957 ));
958 }
959 DataType::Snorm => TextureFormat::Rgba16Snorm,
960 DataType::Float => TextureFormat::Rgba16Float,
961 DataType::Uint => TextureFormat::Rgba16Uint,
962 DataType::Sint => TextureFormat::Rgba16Sint,
963 },
964 32 => match data_type {
965 DataType::Unorm => {
966 return Err(TextureError::UnsupportedTextureFormat(
967 "Unorm not supported for Rgba32/Bgra32".to_string(),
968 ));
969 }
970 DataType::UnormSrgb => {
971 return Err(TextureError::UnsupportedTextureFormat(
972 "UnormSrgb not supported for Rgba32/Bgra32".to_string(),
973 ));
974 }
975 DataType::Snorm => {
976 return Err(TextureError::UnsupportedTextureFormat(
977 "Snorm not supported for Rgba32/Bgra32".to_string(),
978 ));
979 }
980 DataType::Float => TextureFormat::Rgba32Float,
981 DataType::Uint => TextureFormat::Rgba32Uint,
982 DataType::Sint => TextureFormat::Rgba32Sint,
983 },
984 v => {
985 return Err(TextureError::UnsupportedTextureFormat(format!(
986 "Unsupported sample bit length for XYZW 4-channel format: {v}",
987 )));
988 }
989 }
990 }
991 v => {
992 return Err(TextureError::UnsupportedTextureFormat(format!(
993 "Unsupported channel count for XYZW format: {v}",
994 )));
995 }
996 }
997 }
998 Some(ColorModel::BC1A) => {
999 if is_srgb {
1000 TextureFormat::Bc1RgbaUnormSrgb
1001 } else {
1002 TextureFormat::Bc1RgbaUnorm
1003 }
1004 }
1005 Some(ColorModel::BC2) => {
1006 if is_srgb {
1007 TextureFormat::Bc2RgbaUnormSrgb
1008 } else {
1009 TextureFormat::Bc2RgbaUnorm
1010 }
1011 }
1012 Some(ColorModel::BC3) => {
1013 if is_srgb {
1014 TextureFormat::Bc3RgbaUnormSrgb
1015 } else {
1016 TextureFormat::Bc3RgbaUnorm
1017 }
1018 }
1019 Some(ColorModel::BC4) => {
1020 if sample_information[0].lower == 0 {
1021 TextureFormat::Bc4RUnorm
1022 } else {
1023 TextureFormat::Bc4RSnorm
1024 }
1025 }
1026 Some(ColorModel::BC5) => {
1028 if sample_information[0].lower == 0 {
1029 TextureFormat::Bc5RgUnorm
1030 } else {
1031 TextureFormat::Bc5RgSnorm
1032 }
1033 }
1034 Some(ColorModel::BC6H) => {
1035 if sample_information[0].lower == 0 {
1036 TextureFormat::Bc6hRgbUfloat
1037 } else {
1038 TextureFormat::Bc6hRgbFloat
1039 }
1040 }
1041 Some(ColorModel::BC7) => {
1042 if is_srgb {
1043 TextureFormat::Bc7RgbaUnormSrgb
1044 } else {
1045 TextureFormat::Bc7RgbaUnorm
1046 }
1047 }
1048 Some(ColorModel::ETC1) => {
1050 if is_srgb {
1051 TextureFormat::Etc2Rgb8UnormSrgb
1052 } else {
1053 TextureFormat::Etc2Rgb8Unorm
1054 }
1055 }
1056 Some(ColorModel::ETC2) => match sample_information.len() {
1057 1 => {
1058 let sample = &sample_information[0];
1059 match sample.channel_type {
1060 0 => {
1061 if sample_information[0]
1062 .channel_type_qualifiers
1063 .contains(ChannelTypeQualifiers::SIGNED)
1064 {
1065 TextureFormat::EacR11Snorm
1066 } else {
1067 TextureFormat::EacR11Unorm
1068 }
1069 }
1070 2 => {
1071 if is_srgb {
1072 TextureFormat::Etc2Rgb8UnormSrgb
1073 } else {
1074 TextureFormat::Etc2Rgb8Unorm
1075 }
1076 }
1077 _ => {
1078 return Err(TextureError::UnsupportedTextureFormat(format!(
1079 "Invalid ETC2 sample channel type: {}",
1080 sample.channel_type
1081 )))
1082 }
1083 }
1084 }
1085 2 => {
1086 let sample0 = &sample_information[0];
1087 let sample1 = &sample_information[1];
1088 if sample0.channel_type == 0 && sample1.channel_type == 1 {
1089 if sample0
1090 .channel_type_qualifiers
1091 .contains(ChannelTypeQualifiers::SIGNED)
1092 {
1093 TextureFormat::EacRg11Snorm
1094 } else {
1095 TextureFormat::EacRg11Unorm
1096 }
1097 } else if sample0.channel_type == 2 && sample1.channel_type == 15 {
1098 if is_srgb {
1099 TextureFormat::Etc2Rgb8A1UnormSrgb
1100 } else {
1101 TextureFormat::Etc2Rgb8A1Unorm
1102 }
1103 } else if sample0.channel_type == 15 && sample1.channel_type == 2 {
1104 if is_srgb {
1105 TextureFormat::Etc2Rgba8UnormSrgb
1106 } else {
1107 TextureFormat::Etc2Rgba8Unorm
1108 }
1109 } else {
1110 return Err(TextureError::UnsupportedTextureFormat(format!(
1111 "Invalid ETC2 2-sample channel types: {} {}",
1112 sample0.channel_type, sample1.channel_type
1113 )));
1114 }
1115 }
1116 v => {
1117 return Err(TextureError::UnsupportedTextureFormat(format!(
1118 "Unsupported channel count for ETC2 format: {v}",
1119 )));
1120 }
1121 },
1122 Some(ColorModel::ASTC) => TextureFormat::Astc {
1123 block: match (
1124 data_format_descriptor.texel_block_dimensions[0],
1125 data_format_descriptor.texel_block_dimensions[1],
1126 ) {
1127 (4, 4) => AstcBlock::B4x4,
1128 (5, 4) => AstcBlock::B5x4,
1129 (5, 5) => AstcBlock::B5x5,
1130 (6, 5) => AstcBlock::B6x5,
1131 (8, 5) => AstcBlock::B8x5,
1132 (8, 8) => AstcBlock::B8x8,
1133 (10, 5) => AstcBlock::B10x5,
1134 (10, 6) => AstcBlock::B10x6,
1135 (10, 8) => AstcBlock::B10x8,
1136 (10, 10) => AstcBlock::B10x10,
1137 (12, 10) => AstcBlock::B12x10,
1138 (12, 12) => AstcBlock::B12x12,
1139 d => {
1140 return Err(TextureError::UnsupportedTextureFormat(format!(
1141 "Invalid ASTC dimension: {} x {}",
1142 d.0, d.1
1143 )))
1144 }
1145 },
1146 channel: if is_srgb {
1147 AstcChannel::UnormSrgb
1148 } else {
1149 AstcChannel::Unorm
1150 },
1151 },
1152 Some(ColorModel::ETC1S) => {
1153 return Err(TextureError::FormatRequiresTranscodingError(
1154 TranscodeFormat::Etc1s,
1155 ));
1156 }
1157 Some(ColorModel::PVRTC) => {
1158 return Err(TextureError::UnsupportedTextureFormat(
1159 "PVRTC is not supported".to_string(),
1160 ));
1161 }
1162 Some(ColorModel::PVRTC2) => {
1163 return Err(TextureError::UnsupportedTextureFormat(
1164 "PVRTC2 is not supported".to_string(),
1165 ));
1166 }
1167 Some(ColorModel::UASTC) => {
1168 return Err(TextureError::FormatRequiresTranscodingError(
1169 TranscodeFormat::Uastc(match sample_information[0].channel_type {
1170 0 => DataFormat::Rgb,
1171 3 => DataFormat::Rgba,
1172 4 => DataFormat::Rrr,
1173 5 => DataFormat::Rrrg,
1174 6 => DataFormat::Rg,
1175 channel_type => {
1176 return Err(TextureError::UnsupportedTextureFormat(format!(
1177 "Invalid KTX2 UASTC channel type: {channel_type}",
1178 )))
1179 }
1180 }),
1181 ));
1182 }
1183 None => {
1184 return Err(TextureError::UnsupportedTextureFormat(
1185 "Unspecified KTX2 color model".to_string(),
1186 ));
1187 }
1188 _ => {
1189 return Err(TextureError::UnsupportedTextureFormat(format!(
1190 "Unknown KTX2 color model: {:?}",
1191 data_format_descriptor.color_model
1192 )));
1193 }
1194 })
1195}
1196
1197pub fn ktx2_format_to_texture_format(
1198 ktx2_format: ktx2::Format,
1199 is_srgb: bool,
1200) -> Result<TextureFormat, TextureError> {
1201 Ok(match ktx2_format {
1202 ktx2::Format::R8_UNORM | ktx2::Format::R8_SRGB => {
1203 if is_srgb {
1204 return Err(TextureError::FormatRequiresTranscodingError(
1205 TranscodeFormat::R8UnormSrgb,
1206 ));
1207 }
1208 TextureFormat::R8Unorm
1209 }
1210 ktx2::Format::R8_SNORM => TextureFormat::R8Snorm,
1211 ktx2::Format::R8_UINT => TextureFormat::R8Uint,
1212 ktx2::Format::R8_SINT => TextureFormat::R8Sint,
1213 ktx2::Format::R8G8_UNORM | ktx2::Format::R8G8_SRGB => {
1214 if is_srgb {
1215 return Err(TextureError::FormatRequiresTranscodingError(
1216 TranscodeFormat::Rg8UnormSrgb,
1217 ));
1218 }
1219 TextureFormat::Rg8Unorm
1220 }
1221 ktx2::Format::R8G8_SNORM => TextureFormat::Rg8Snorm,
1222 ktx2::Format::R8G8_UINT => TextureFormat::Rg8Uint,
1223 ktx2::Format::R8G8_SINT => TextureFormat::Rg8Sint,
1224 ktx2::Format::R8G8B8_UNORM | ktx2::Format::R8G8B8_SRGB => {
1225 return Err(TextureError::FormatRequiresTranscodingError(
1226 TranscodeFormat::Rgb8,
1227 ));
1228 }
1229 ktx2::Format::R8G8B8A8_UNORM | ktx2::Format::R8G8B8A8_SRGB => {
1230 if is_srgb {
1231 TextureFormat::Rgba8UnormSrgb
1232 } else {
1233 TextureFormat::Rgba8Unorm
1234 }
1235 }
1236 ktx2::Format::R8G8B8A8_SNORM => TextureFormat::Rgba8Snorm,
1237 ktx2::Format::R8G8B8A8_UINT => TextureFormat::Rgba8Uint,
1238 ktx2::Format::R8G8B8A8_SINT => TextureFormat::Rgba8Sint,
1239 ktx2::Format::B8G8R8A8_UNORM | ktx2::Format::B8G8R8A8_SRGB => {
1240 if is_srgb {
1241 TextureFormat::Bgra8UnormSrgb
1242 } else {
1243 TextureFormat::Bgra8Unorm
1244 }
1245 }
1246 ktx2::Format::A2R10G10B10_UNORM_PACK32 => TextureFormat::Rgb10a2Unorm,
1247
1248 ktx2::Format::R16_UNORM => TextureFormat::R16Unorm,
1249 ktx2::Format::R16_SNORM => TextureFormat::R16Snorm,
1250 ktx2::Format::R16_UINT => TextureFormat::R16Uint,
1251 ktx2::Format::R16_SINT => TextureFormat::R16Sint,
1252 ktx2::Format::R16_SFLOAT => TextureFormat::R16Float,
1253 ktx2::Format::R16G16_UNORM => TextureFormat::Rg16Unorm,
1254 ktx2::Format::R16G16_SNORM => TextureFormat::Rg16Snorm,
1255 ktx2::Format::R16G16_UINT => TextureFormat::Rg16Uint,
1256 ktx2::Format::R16G16_SINT => TextureFormat::Rg16Sint,
1257 ktx2::Format::R16G16_SFLOAT => TextureFormat::Rg16Float,
1258
1259 ktx2::Format::R16G16B16A16_UNORM => TextureFormat::Rgba16Unorm,
1260 ktx2::Format::R16G16B16A16_SNORM => TextureFormat::Rgba16Snorm,
1261 ktx2::Format::R16G16B16A16_UINT => TextureFormat::Rgba16Uint,
1262 ktx2::Format::R16G16B16A16_SINT => TextureFormat::Rgba16Sint,
1263 ktx2::Format::R16G16B16A16_SFLOAT => TextureFormat::Rgba16Float,
1264 ktx2::Format::R32_UINT => TextureFormat::R32Uint,
1265 ktx2::Format::R32_SINT => TextureFormat::R32Sint,
1266 ktx2::Format::R32_SFLOAT => TextureFormat::R32Float,
1267 ktx2::Format::R32G32_UINT => TextureFormat::Rg32Uint,
1268 ktx2::Format::R32G32_SINT => TextureFormat::Rg32Sint,
1269 ktx2::Format::R32G32_SFLOAT => TextureFormat::Rg32Float,
1270
1271 ktx2::Format::R32G32B32A32_UINT => TextureFormat::Rgba32Uint,
1272 ktx2::Format::R32G32B32A32_SINT => TextureFormat::Rgba32Sint,
1273 ktx2::Format::R32G32B32A32_SFLOAT => TextureFormat::Rgba32Float,
1274
1275 ktx2::Format::B10G11R11_UFLOAT_PACK32 => TextureFormat::Rg11b10Float,
1276 ktx2::Format::E5B9G9R9_UFLOAT_PACK32 => TextureFormat::Rgb9e5Ufloat,
1277
1278 ktx2::Format::X8_D24_UNORM_PACK32 => TextureFormat::Depth24Plus,
1279 ktx2::Format::D32_SFLOAT => TextureFormat::Depth32Float,
1280
1281 ktx2::Format::D24_UNORM_S8_UINT => TextureFormat::Depth24PlusStencil8,
1282
1283 ktx2::Format::BC1_RGB_UNORM_BLOCK
1284 | ktx2::Format::BC1_RGB_SRGB_BLOCK
1285 | ktx2::Format::BC1_RGBA_UNORM_BLOCK
1286 | ktx2::Format::BC1_RGBA_SRGB_BLOCK => {
1287 if is_srgb {
1288 TextureFormat::Bc1RgbaUnormSrgb
1289 } else {
1290 TextureFormat::Bc1RgbaUnorm
1291 }
1292 }
1293 ktx2::Format::BC2_UNORM_BLOCK | ktx2::Format::BC2_SRGB_BLOCK => {
1294 if is_srgb {
1295 TextureFormat::Bc2RgbaUnormSrgb
1296 } else {
1297 TextureFormat::Bc2RgbaUnorm
1298 }
1299 }
1300 ktx2::Format::BC3_UNORM_BLOCK | ktx2::Format::BC3_SRGB_BLOCK => {
1301 if is_srgb {
1302 TextureFormat::Bc3RgbaUnormSrgb
1303 } else {
1304 TextureFormat::Bc3RgbaUnorm
1305 }
1306 }
1307 ktx2::Format::BC4_UNORM_BLOCK => TextureFormat::Bc4RUnorm,
1308 ktx2::Format::BC4_SNORM_BLOCK => TextureFormat::Bc4RSnorm,
1309 ktx2::Format::BC5_UNORM_BLOCK => TextureFormat::Bc5RgUnorm,
1310 ktx2::Format::BC5_SNORM_BLOCK => TextureFormat::Bc5RgSnorm,
1311 ktx2::Format::BC6H_UFLOAT_BLOCK => TextureFormat::Bc6hRgbUfloat,
1312 ktx2::Format::BC6H_SFLOAT_BLOCK => TextureFormat::Bc6hRgbFloat,
1313 ktx2::Format::BC7_UNORM_BLOCK | ktx2::Format::BC7_SRGB_BLOCK => {
1314 if is_srgb {
1315 TextureFormat::Bc7RgbaUnormSrgb
1316 } else {
1317 TextureFormat::Bc7RgbaUnorm
1318 }
1319 }
1320 ktx2::Format::ETC2_R8G8B8_UNORM_BLOCK | ktx2::Format::ETC2_R8G8B8_SRGB_BLOCK => {
1321 if is_srgb {
1322 TextureFormat::Etc2Rgb8UnormSrgb
1323 } else {
1324 TextureFormat::Etc2Rgb8Unorm
1325 }
1326 }
1327 ktx2::Format::ETC2_R8G8B8A1_UNORM_BLOCK | ktx2::Format::ETC2_R8G8B8A1_SRGB_BLOCK => {
1328 if is_srgb {
1329 TextureFormat::Etc2Rgb8A1UnormSrgb
1330 } else {
1331 TextureFormat::Etc2Rgb8A1Unorm
1332 }
1333 }
1334 ktx2::Format::ETC2_R8G8B8A8_UNORM_BLOCK | ktx2::Format::ETC2_R8G8B8A8_SRGB_BLOCK => {
1335 if is_srgb {
1336 TextureFormat::Etc2Rgba8UnormSrgb
1337 } else {
1338 TextureFormat::Etc2Rgba8Unorm
1339 }
1340 }
1341 ktx2::Format::EAC_R11_UNORM_BLOCK => TextureFormat::EacR11Unorm,
1342 ktx2::Format::EAC_R11_SNORM_BLOCK => TextureFormat::EacR11Snorm,
1343 ktx2::Format::EAC_R11G11_UNORM_BLOCK => TextureFormat::EacRg11Unorm,
1344 ktx2::Format::EAC_R11G11_SNORM_BLOCK => TextureFormat::EacRg11Snorm,
1345 ktx2::Format::ASTC_4x4_UNORM_BLOCK | ktx2::Format::ASTC_4x4_SRGB_BLOCK => {
1346 TextureFormat::Astc {
1347 block: AstcBlock::B4x4,
1348 channel: if is_srgb {
1349 AstcChannel::UnormSrgb
1350 } else {
1351 AstcChannel::Unorm
1352 },
1353 }
1354 }
1355 ktx2::Format::ASTC_5x4_UNORM_BLOCK | ktx2::Format::ASTC_5x4_SRGB_BLOCK => {
1356 TextureFormat::Astc {
1357 block: AstcBlock::B5x4,
1358 channel: if is_srgb {
1359 AstcChannel::UnormSrgb
1360 } else {
1361 AstcChannel::Unorm
1362 },
1363 }
1364 }
1365 ktx2::Format::ASTC_5x5_UNORM_BLOCK | ktx2::Format::ASTC_5x5_SRGB_BLOCK => {
1366 TextureFormat::Astc {
1367 block: AstcBlock::B5x5,
1368 channel: if is_srgb {
1369 AstcChannel::UnormSrgb
1370 } else {
1371 AstcChannel::Unorm
1372 },
1373 }
1374 }
1375 ktx2::Format::ASTC_6x5_UNORM_BLOCK | ktx2::Format::ASTC_6x5_SRGB_BLOCK => {
1376 TextureFormat::Astc {
1377 block: AstcBlock::B6x5,
1378 channel: if is_srgb {
1379 AstcChannel::UnormSrgb
1380 } else {
1381 AstcChannel::Unorm
1382 },
1383 }
1384 }
1385 ktx2::Format::ASTC_6x6_UNORM_BLOCK | ktx2::Format::ASTC_6x6_SRGB_BLOCK => {
1386 TextureFormat::Astc {
1387 block: AstcBlock::B6x6,
1388 channel: if is_srgb {
1389 AstcChannel::UnormSrgb
1390 } else {
1391 AstcChannel::Unorm
1392 },
1393 }
1394 }
1395 ktx2::Format::ASTC_8x5_UNORM_BLOCK | ktx2::Format::ASTC_8x5_SRGB_BLOCK => {
1396 TextureFormat::Astc {
1397 block: AstcBlock::B8x5,
1398 channel: if is_srgb {
1399 AstcChannel::UnormSrgb
1400 } else {
1401 AstcChannel::Unorm
1402 },
1403 }
1404 }
1405 ktx2::Format::ASTC_8x6_UNORM_BLOCK | ktx2::Format::ASTC_8x6_SRGB_BLOCK => {
1406 TextureFormat::Astc {
1407 block: AstcBlock::B8x6,
1408 channel: if is_srgb {
1409 AstcChannel::UnormSrgb
1410 } else {
1411 AstcChannel::Unorm
1412 },
1413 }
1414 }
1415 ktx2::Format::ASTC_8x8_UNORM_BLOCK | ktx2::Format::ASTC_8x8_SRGB_BLOCK => {
1416 TextureFormat::Astc {
1417 block: AstcBlock::B8x8,
1418 channel: if is_srgb {
1419 AstcChannel::UnormSrgb
1420 } else {
1421 AstcChannel::Unorm
1422 },
1423 }
1424 }
1425 ktx2::Format::ASTC_10x5_UNORM_BLOCK | ktx2::Format::ASTC_10x5_SRGB_BLOCK => {
1426 TextureFormat::Astc {
1427 block: AstcBlock::B10x5,
1428 channel: if is_srgb {
1429 AstcChannel::UnormSrgb
1430 } else {
1431 AstcChannel::Unorm
1432 },
1433 }
1434 }
1435 ktx2::Format::ASTC_10x6_UNORM_BLOCK | ktx2::Format::ASTC_10x6_SRGB_BLOCK => {
1436 TextureFormat::Astc {
1437 block: AstcBlock::B10x6,
1438 channel: if is_srgb {
1439 AstcChannel::UnormSrgb
1440 } else {
1441 AstcChannel::Unorm
1442 },
1443 }
1444 }
1445 ktx2::Format::ASTC_10x8_UNORM_BLOCK | ktx2::Format::ASTC_10x8_SRGB_BLOCK => {
1446 TextureFormat::Astc {
1447 block: AstcBlock::B10x8,
1448 channel: if is_srgb {
1449 AstcChannel::UnormSrgb
1450 } else {
1451 AstcChannel::Unorm
1452 },
1453 }
1454 }
1455 ktx2::Format::ASTC_10x10_UNORM_BLOCK | ktx2::Format::ASTC_10x10_SRGB_BLOCK => {
1456 TextureFormat::Astc {
1457 block: AstcBlock::B10x10,
1458 channel: if is_srgb {
1459 AstcChannel::UnormSrgb
1460 } else {
1461 AstcChannel::Unorm
1462 },
1463 }
1464 }
1465 ktx2::Format::ASTC_12x10_UNORM_BLOCK | ktx2::Format::ASTC_12x10_SRGB_BLOCK => {
1466 TextureFormat::Astc {
1467 block: AstcBlock::B12x10,
1468 channel: if is_srgb {
1469 AstcChannel::UnormSrgb
1470 } else {
1471 AstcChannel::Unorm
1472 },
1473 }
1474 }
1475 ktx2::Format::ASTC_12x12_UNORM_BLOCK | ktx2::Format::ASTC_12x12_SRGB_BLOCK => {
1476 TextureFormat::Astc {
1477 block: AstcBlock::B12x12,
1478 channel: if is_srgb {
1479 AstcChannel::UnormSrgb
1480 } else {
1481 AstcChannel::Unorm
1482 },
1483 }
1484 }
1485 _ => {
1486 return Err(TextureError::UnsupportedTextureFormat(format!(
1487 "{ktx2_format:?}"
1488 )))
1489 }
1490 })
1491}
1492
1493#[cfg(test)]
1494mod tests {
1495 use crate::texture::CompressedImageFormats;
1496
1497 use super::ktx2_buffer_to_image;
1498
1499 #[test]
1500 fn test_ktx_levels() {
1501 let buffer = vec![
1503 0xab, 0x4b, 0x54, 0x58, 0x20, 0x32, 0x30, 0xbb, 0x0d, 10, 0x1a, 10, 0x0f, 0, 0, 0, 1,
1504 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 3, 0, 0, 0, 0, 0,
1505 0, 0, 0x98, 0, 0, 0, 0x2c, 0, 0, 0, 0xc4, 0, 0, 0, 0x5c, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1506 0, 0, 0, 0, 0, 0, 0, 0, 0, 0x28, 1, 0, 0, 0, 0, 0, 0, 0x10, 0, 0, 0, 0, 0, 0, 0, 0x10,
1507 0, 0, 0, 0, 0, 0, 0, 0x24, 1, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0,
1508 0, 0, 0, 0x20, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0,
1509 0x2c, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0x28, 0, 1, 1, 2, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0,
1510 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0xff, 0, 0, 0, 0x12, 0, 0, 0, 0x4b, 0x54, 0x58,
1511 0x6f, 0x72, 0x69, 0x65, 0x6e, 0x74, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0, 0x72, 0x64, 0, 0,
1512 0, 0x10, 0, 0, 0, 0x4b, 0x54, 0x58, 0x73, 0x77, 0x69, 0x7a, 0x7a, 0x6c, 0x65, 0, 0x72,
1513 0x72, 0x72, 0x31, 0, 0x2c, 0, 0, 0, 0x4b, 0x54, 0x58, 0x77, 0x72, 0x69, 0x74, 0x65,
1514 0x72, 0, 0x74, 0x6f, 0x6b, 0x74, 0x78, 0x20, 0x76, 0x34, 0x2e, 0x33, 0x2e, 0x30, 0x7e,
1515 0x32, 0x38, 0x20, 0x2f, 0x20, 0x6c, 0x69, 0x62, 0x6b, 0x74, 0x78, 0x20, 0x76, 0x34,
1516 0x2e, 0x33, 0x2e, 0x30, 0x7e, 0x31, 0, 0x4a, 0, 0, 0, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a,
1517 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a, 0x4a,
1518 0x4a,
1519 ];
1520 let supported_compressed_formats = CompressedImageFormats::empty();
1521 let result = ktx2_buffer_to_image(&buffer, supported_compressed_formats, true);
1522 assert!(result.is_ok());
1523 }
1524}