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