Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
bevyengine
GitHub Repository: bevyengine/bevy
Path: blob/main/crates/bevy_image/src/ktx2.rs
6595 views
1
#[cfg(any(feature = "flate2", feature = "zstd_rust"))]
2
use std::io::Read;
3
4
#[cfg(feature = "basis-universal")]
5
use basis_universal::{
6
DecodeFlags, LowLevelUastcTranscoder, SliceParametersUastc, TranscoderBlockFormat,
7
};
8
use bevy_color::Srgba;
9
use bevy_utils::default;
10
#[cfg(any(feature = "flate2", feature = "zstd_rust", feature = "zstd_c"))]
11
use ktx2::SupercompressionScheme;
12
use ktx2::{
13
ChannelTypeQualifiers, ColorModel, DfdBlockBasic, DfdBlockHeaderBasic, DfdHeader, Header,
14
SampleInformation,
15
};
16
use wgpu_types::{
17
AstcBlock, AstcChannel, Extent3d, TextureDimension, TextureFormat, TextureViewDescriptor,
18
TextureViewDimension,
19
};
20
21
use super::{CompressedImageFormats, DataFormat, Image, TextureError, TranscodeFormat};
22
23
#[cfg(feature = "ktx2")]
24
pub 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
// Handle supercompression
46
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
// Identify the format
101
let texture_format = ktx2_get_texture_format(&ktx2, is_srgb).or_else(|error| match error {
102
// Transcode if needed and supported
103
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
// Next mip dimensions are half the current, minimum 1x1
117
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
// Next mip dimensions are half the current, minimum 1x1
134
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
// Texture is not a depth or stencil format, it is possible to pass `None` and unwrap
175
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
// NOTE: SliceParametersUastc does not implement Clone nor Copy so
193
// it has to be created per use
194
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
// ETC1S is a subset of ETC1 which is a subset of ETC2
221
// TODO: Implement transcoding
222
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
// Collect all level data into a contiguous buffer
249
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
// Assign the data and fill in the rest of the metadata now the possible
254
// error cases have been handled
255
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
// Note: we must give wgpu the logical texture dimensions, so it can correctly compute mip sizes.
260
// However this currently causes wgpu to panic if the dimensions arent a multiple of blocksize.
261
// See https://github.com/gfx-rs/wgpu/issues/7677 for more context.
262
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")]
302
pub 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
// NOTE: Rgba16Float should be transcoded to BC6H/ASTC_HDR. Neither are supported by
333
// basis-universal, nor is ASTC_HDR supported by wgpu
334
DataFormat::Rgb | DataFormat::Rgba => {
335
// NOTE: UASTC can be losslessly transcoded to ASTC4x4 and ASTC uses the same
336
// space as BC7 (128-bits per 4x4 texel block) so prefer ASTC over BC for
337
// transcoding speed and quality.
338
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")]
383
pub 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
411
enum DataType {
412
Unorm,
413
UnormSrgb,
414
Snorm,
415
Float,
416
Uint,
417
Sint,
418
}
419
420
// This can be obtained from core::mem::transmute::<f32, u32>(1.0f32). It is used for identifying
421
// normalized sample types as in Unorm or Snorm.
422
const F32_1_AS_U32: u32 = 1065353216;
423
424
fn sample_information_to_data_type(
425
sample: &SampleInformation,
426
is_srgb: bool,
427
) -> Result<DataType, TextureError> {
428
// Exponent flag not supported
429
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 lower bound of range is 0 then unorm, else if upper bound is 1.0f32 as u32
443
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")]
469
pub 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
// Only red channel allowed
479
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
// Only red and green channels allowed
546
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
// Only same bit length for all channels
555
assert_eq!(
556
sample_information[0].bit_length,
557
sample_information[1].bit_length
558
);
559
// Only same channel type qualifiers for all channels
560
assert_eq!(
561
sample_information[0].channel_type_qualifiers,
562
sample_information[1].channel_type_qualifiers
563
);
564
// Only same sample range for all channels
565
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
// Only RGBA or BGRA channels allowed
661
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
// Handle one special packed format
674
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
// Only same bit length for all channels
683
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
// NOTE: This is more about how you want to use the data so
734
// TextureFormat::Rgba8Uint is incorrect here
735
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
// NOTE: This is more about how you want to use the data so
749
// TextureFormat::Rgba8Sint is incorrect here
750
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
// Same number of channels in both texel block dimensions and sample info descriptions
888
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
// Only RGBA or BGRA channels allowed
895
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
// Only same bit length for all channels
900
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
// Only same channel type qualifiers for all channels
906
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
// Only same sample range for all channels
915
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
// FIXME: Red and green channels can be swapped for ATI2n/3Dc
1020
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
// ETC1 a subset of ETC2 only supporting Rgb8
1042
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")]
1191
pub 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)]
1488
mod tests {
1489
use crate::CompressedImageFormats;
1490
1491
use super::ktx2_buffer_to_image;
1492
1493
#[test]
1494
fn test_ktx_levels() {
1495
// R8UnormSrgb texture with 4x4 pixels data and 3 levels of mipmaps
1496
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
}
1519
1520