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