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