ktx2/
lib.rs

1//! Parser for the [ktx2](https://github.khronos.org/KTX-Specification/) texture container format.
2//!
3//! ## Features
4//! - [x] Async reading
5//! - [x] Parsing
6//! - [x] Validating
7//! - [x] [Data format description](https://github.khronos.org/KTX-Specification/#_data_format_descriptor)
8//! - [ ] [Key/value data](https://github.khronos.org/KTX-Specification/#_keyvalue_data)
9//
10//! ## Example
11//! ```rust
12//! // Crate instance of reader. This validates the header
13//! # let file = include_bytes!("../data/test_tex.ktx2");
14//! let mut reader = ktx2::Reader::new(file).expect("Can't create reader"); // Crate instance of reader.
15//!
16//! // Get general texture information.
17//! let header = reader.header();
18//!
19//! // Read iterator over slices of each mipmap level.
20//! let levels = reader.levels().collect::<Vec<_>>();
21//! # let _ = (header, levels);
22//! ```
23
24#![no_std]
25
26#[cfg(feature = "std")]
27extern crate std;
28
29mod enums;
30mod error;
31
32pub use crate::{
33    enums::{ColorModel, ColorPrimaries, Format, SupercompressionScheme, TransferFunction},
34    error::ParseError,
35};
36
37use core::convert::TryInto;
38
39/// Decodes KTX2 texture data
40pub struct Reader<Data: AsRef<[u8]>> {
41    input: Data,
42}
43
44impl<Data: AsRef<[u8]>> Reader<Data> {
45    /// Decode KTX2 data from `input`
46    pub fn new(input: Data) -> Result<Self, ParseError> {
47        if input.as_ref().len() < Header::LENGTH {
48            return Err(ParseError::UnexpectedEnd);
49        }
50        if !input.as_ref().starts_with(&KTX2_MAGIC) {
51            return Err(ParseError::BadMagic);
52        }
53        let header_data = input.as_ref()[0..Header::LENGTH].try_into().unwrap();
54        let header = Header::from_bytes(header_data);
55        header.validate()?;
56
57        if (header.dfd_byte_offset + header.dfd_byte_length) as usize >= input.as_ref().len() {
58            return Err(ParseError::UnexpectedEnd);
59        }
60
61        let result = Self { input };
62        result.level_index()?; // Check index integrity
63
64        // Check level data integrity
65        let trailing = result.level_index().unwrap().max_by_key(|l| l.offset).unwrap();
66        if trailing.offset + trailing.length_bytes > result.input.as_ref().len() as u64 {
67            return Err(ParseError::UnexpectedEnd);
68        }
69
70        Ok(result)
71    }
72
73    fn level_index(&self) -> ParseResult<impl ExactSizeIterator<Item = LevelIndex> + '_> {
74        let level_count = self.header().level_count.max(1) as usize;
75
76        let level_index_end_byte = Header::LENGTH + level_count * LevelIndex::LENGTH;
77        let level_index_bytes = self
78            .input
79            .as_ref()
80            .get(Header::LENGTH..level_index_end_byte)
81            .ok_or(ParseError::UnexpectedEnd)?;
82        Ok(level_index_bytes
83            .chunks_exact(LevelIndex::LENGTH)
84            .map(LevelIndex::from_bytes))
85    }
86
87    /// Access underlying raw bytes
88    pub fn data(&self) -> &[u8] {
89        self.input.as_ref()
90    }
91
92    /// Container-level metadata
93    pub fn header(&self) -> Header {
94        let bytes = self.input.as_ref()[0..Header::LENGTH].try_into().unwrap();
95        Header::from_bytes(bytes)
96    }
97
98    /// Iterator over the texture's mip levels
99    pub fn levels(&self) -> impl ExactSizeIterator<Item = &[u8]> + '_ {
100        self.level_index()
101            .unwrap()
102            .map(move |level| &self.input.as_ref()[level.offset as usize..(level.offset + level.length_bytes) as usize])
103    }
104
105    pub fn supercompression_global_data(&self) -> &[u8] {
106        let header = self.header();
107        let start = header.sgd_byte_offset as usize;
108        let end = (header.sgd_byte_offset + header.sgd_byte_length) as usize;
109        &self.input.as_ref()[start..end]
110    }
111
112    pub fn data_format_descriptors(&self) -> impl Iterator<Item = DataFormatDescriptor> {
113        let header = self.header();
114        let start = header.dfd_byte_offset as usize;
115        let end = (header.dfd_byte_offset + header.dfd_byte_length) as usize;
116        DataFormatDescriptorIterator {
117            // start + 4 to skip the data format descriptors total length
118            data: &self.input.as_ref()[start + 4..end],
119        }
120    }
121}
122
123struct DataFormatDescriptorIterator<'data> {
124    data: &'data [u8],
125}
126
127impl<'data> Iterator for DataFormatDescriptorIterator<'data> {
128    type Item = DataFormatDescriptor<'data>;
129
130    fn next(&mut self) -> Option<Self::Item> {
131        if self.data.len() < DataFormatDescriptorHeader::LENGTH {
132            return None;
133        }
134        DataFormatDescriptorHeader::parse(&self.data[..DataFormatDescriptorHeader::LENGTH]).map_or(
135            None,
136            |(header, descriptor_block_size)| {
137                if descriptor_block_size == 0 || self.data.len() < descriptor_block_size {
138                    return None;
139                }
140                let data = &self.data[DataFormatDescriptorHeader::LENGTH..descriptor_block_size];
141                self.data = &self.data[descriptor_block_size..];
142                Some(DataFormatDescriptor { header, data })
143            },
144        )
145    }
146}
147
148/// Identifier, expected in start of input texture data.
149const KTX2_MAGIC: [u8; 12] = [0xAB, 0x4B, 0x54, 0x58, 0x20, 0x32, 0x30, 0xBB, 0x0D, 0x0A, 0x1A, 0x0A];
150
151/// Result of parsing data operation.
152type ParseResult<T> = Result<T, ParseError>;
153
154/// Container-level metadata
155#[derive(Copy, Clone, Eq, PartialEq, Debug)]
156pub struct Header {
157    pub format: Option<Format>,
158    pub type_size: u32,
159    pub pixel_width: u32,
160    pub pixel_height: u32,
161    pub pixel_depth: u32,
162    pub layer_count: u32,
163    pub face_count: u32,
164    pub level_count: u32,
165    pub supercompression_scheme: Option<SupercompressionScheme>,
166    dfd_byte_offset: u32,
167    dfd_byte_length: u32,
168    kvd_byte_offset: u32,
169    kvd_byte_length: u32,
170    sgd_byte_offset: u64,
171    sgd_byte_length: u64,
172}
173
174impl Header {
175    const LENGTH: usize = 80;
176
177    fn from_bytes(data: &[u8; Self::LENGTH]) -> Self {
178        Self {
179            format: Format::new(u32::from_le_bytes(data[12..16].try_into().unwrap())),
180            type_size: u32::from_le_bytes(data[16..20].try_into().unwrap()),
181            pixel_width: u32::from_le_bytes(data[20..24].try_into().unwrap()),
182            pixel_height: u32::from_le_bytes(data[24..28].try_into().unwrap()),
183            pixel_depth: u32::from_le_bytes(data[28..32].try_into().unwrap()),
184            layer_count: u32::from_le_bytes(data[32..36].try_into().unwrap()),
185            face_count: u32::from_le_bytes(data[36..40].try_into().unwrap()),
186            level_count: u32::from_le_bytes(data[40..44].try_into().unwrap()),
187            supercompression_scheme: SupercompressionScheme::new(u32::from_le_bytes(data[44..48].try_into().unwrap())),
188            dfd_byte_offset: u32::from_le_bytes(data[48..52].try_into().unwrap()),
189            dfd_byte_length: u32::from_le_bytes(data[52..56].try_into().unwrap()),
190            kvd_byte_offset: u32::from_le_bytes(data[56..60].try_into().unwrap()),
191            kvd_byte_length: u32::from_le_bytes(data[60..64].try_into().unwrap()),
192            sgd_byte_offset: u64::from_le_bytes(data[64..72].try_into().unwrap()),
193            sgd_byte_length: u64::from_le_bytes(data[72..80].try_into().unwrap()),
194        }
195    }
196
197    fn validate(&self) -> ParseResult<()> {
198        if self.pixel_width == 0 {
199            return Err(ParseError::ZeroWidth);
200        }
201        if self.face_count == 0 {
202            return Err(ParseError::ZeroFaceCount);
203        }
204        Ok(())
205    }
206}
207
208#[derive(Debug, Eq, PartialEq, Copy, Clone)]
209struct LevelIndex {
210    offset: u64,
211    length_bytes: u64,
212    uncompressed_length_bytes: u64,
213}
214
215impl LevelIndex {
216    const LENGTH: usize = 24;
217
218    pub fn from_bytes(data: &[u8]) -> Self {
219        Self {
220            offset: u64::from_le_bytes(data[0..8].try_into().unwrap()),
221            length_bytes: u64::from_le_bytes(data[8..16].try_into().unwrap()),
222            uncompressed_length_bytes: u64::from_le_bytes(data[16..24].try_into().unwrap()),
223        }
224    }
225}
226
227bitflags::bitflags! {
228    #[repr(transparent)]
229    pub struct ChannelTypeQualifiers: u32 {
230        const LINEAR        = (1 << 0);
231        const EXPONENT      = (1 << 1);
232        const SIGNED        = (1 << 2);
233        const FLOAT         = (1 << 3);
234    }
235}
236
237bitflags::bitflags! {
238    #[derive(Default)]
239    #[repr(transparent)]
240    pub struct DataFormatFlags: u32 {
241        const STRAIGHT_ALPHA             = 0;
242        const ALPHA_PREMULTIPLIED        = (1 << 0);
243    }
244}
245
246#[derive(Debug, PartialEq, Eq)]
247pub struct DataFormatDescriptorHeader {
248    pub vendor_id: u32,       //: 17;
249    pub descriptor_type: u32, //: 15;
250    pub version_number: u32,  //: 16;
251}
252
253impl DataFormatDescriptorHeader {
254    const LENGTH: usize = 8;
255
256    pub const BASIC: Self = Self {
257        vendor_id: 0,
258        descriptor_type: 0,
259        version_number: 2,
260    };
261
262    fn parse(bytes: &[u8]) -> Result<(Self, usize), ParseError> {
263        let mut offset = 0;
264
265        let v = bytes_to_u32(bytes, &mut offset)?;
266        let vendor_id = shift_and_mask_lower(0, 17, v);
267        let descriptor_type = shift_and_mask_lower(17, 15, v);
268
269        let v = bytes_to_u32(bytes, &mut offset)?;
270        let version_number = shift_and_mask_lower(0, 16, v);
271        let descriptor_block_size = shift_and_mask_lower(16, 16, v);
272
273        Ok((
274            Self {
275                vendor_id,
276                descriptor_type,
277                version_number,
278            },
279            descriptor_block_size as usize,
280        ))
281    }
282}
283
284pub struct DataFormatDescriptor<'data> {
285    pub header: DataFormatDescriptorHeader,
286    pub data: &'data [u8],
287}
288
289pub struct BasicDataFormatDescriptor<'data> {
290    /// None means Unspecified or is an otherwise unknown value
291    pub color_model: Option<ColorModel>, //: 8;
292    /// None means Unspecified or is an otherwise unknown value
293    pub color_primaries: Option<ColorPrimaries>, //: 8;
294    /// None means Unspecified or is an otherwise unknown value
295    pub transfer_function: Option<TransferFunction>, //: 8;
296    pub flags: DataFormatFlags,           //: 8;
297    pub texel_block_dimensions: [u32; 4], //: 8 x 4;
298    pub bytes_planes: [u32; 8],           //: 8 x 8;
299    sample_data: &'data [u8],
300}
301
302impl<'data> BasicDataFormatDescriptor<'data> {
303    pub fn parse(bytes: &'data [u8]) -> Result<Self, ParseError> {
304        let mut offset = 0;
305
306        let v = bytes_to_u32(bytes, &mut offset)?;
307        let model = shift_and_mask_lower(0, 8, v);
308        let primaries = shift_and_mask_lower(8, 8, v);
309        let transfer = shift_and_mask_lower(16, 8, v);
310        let flags = shift_and_mask_lower(24, 8, v);
311
312        let v = bytes_to_u32(bytes, &mut offset)?;
313        let texel_block_dimensions = [
314            shift_and_mask_lower(0, 8, v) + 1,
315            shift_and_mask_lower(8, 8, v) + 1,
316            shift_and_mask_lower(16, 8, v) + 1,
317            shift_and_mask_lower(24, 8, v) + 1,
318        ];
319
320        let v = bytes_to_u32(bytes, &mut offset)?;
321        let mut bytes_planes = [0u32; 8];
322        bytes_planes[0] = shift_and_mask_lower(0, 8, v);
323        bytes_planes[1] = shift_and_mask_lower(8, 8, v);
324        bytes_planes[2] = shift_and_mask_lower(16, 8, v);
325        bytes_planes[3] = shift_and_mask_lower(24, 8, v);
326
327        let v = bytes_to_u32(bytes, &mut offset)?;
328        bytes_planes[4] = shift_and_mask_lower(0, 8, v);
329        bytes_planes[5] = shift_and_mask_lower(8, 8, v);
330        bytes_planes[6] = shift_and_mask_lower(16, 8, v);
331        bytes_planes[7] = shift_and_mask_lower(24, 8, v);
332
333        Ok(Self {
334            color_model: ColorModel::new(model),
335            color_primaries: ColorPrimaries::new(primaries),
336            transfer_function: TransferFunction::new(transfer),
337            flags: DataFormatFlags::from_bits_truncate(flags),
338            texel_block_dimensions,
339            bytes_planes,
340            sample_data: &bytes[offset..],
341        })
342    }
343
344    pub fn sample_information(&self) -> impl Iterator<Item = SampleInformation> + 'data {
345        SampleInformationIterator { data: self.sample_data }
346    }
347}
348
349struct SampleInformationIterator<'data> {
350    data: &'data [u8],
351}
352
353impl<'data> Iterator for SampleInformationIterator<'data> {
354    type Item = SampleInformation;
355
356    fn next(&mut self) -> Option<Self::Item> {
357        if self.data.len() < SampleInformation::LENGTH {
358            return None;
359        }
360        SampleInformation::parse(&self.data[..SampleInformation::LENGTH]).map_or(None, |sample_information| {
361            self.data = &self.data[SampleInformation::LENGTH..];
362            Some(sample_information)
363        })
364    }
365}
366
367#[derive(Debug)]
368pub struct SampleInformation {
369    pub bit_offset: u32,                                //: 16;
370    pub bit_length: u32,                                //: 8;
371    pub channel_type: u32,                              //: 4;
372    pub channel_type_qualifiers: ChannelTypeQualifiers, //: 4;
373    pub sample_positions: [u32; 4],                     //: 8 x 4;
374    pub lower: u32,                                     //;
375    pub upper: u32,                                     //;
376}
377
378impl SampleInformation {
379    const LENGTH: usize = 16;
380
381    fn parse(bytes: &[u8]) -> Result<Self, ParseError> {
382        let mut offset = 0;
383
384        let v = bytes_to_u32(bytes, &mut offset)?;
385        let bit_offset = shift_and_mask_lower(0, 16, v);
386        let bit_length = shift_and_mask_lower(16, 8, v) + 1;
387        let channel_type = shift_and_mask_lower(24, 4, v);
388        let channel_type_qualifiers = ChannelTypeQualifiers::from_bits_truncate(shift_and_mask_lower(28, 4, v));
389
390        let v = bytes_to_u32(bytes, &mut offset)?;
391        let sample_positions = [
392            shift_and_mask_lower(0, 8, v),
393            shift_and_mask_lower(8, 8, v),
394            shift_and_mask_lower(16, 8, v),
395            shift_and_mask_lower(24, 8, v),
396        ];
397        let lower = bytes_to_u32(bytes, &mut offset)?;
398        let upper = bytes_to_u32(bytes, &mut offset)?;
399
400        Ok(Self {
401            bit_offset,
402            bit_length,
403            channel_type,
404            channel_type_qualifiers,
405            sample_positions,
406            lower,
407            upper,
408        })
409    }
410}
411
412fn bytes_to_u32(bytes: &[u8], offset: &mut usize) -> Result<u32, ParseError> {
413    let v = u32::from_le_bytes(
414        bytes
415            .get(*offset..*offset + 4)
416            .ok_or(ParseError::UnexpectedEnd)?
417            .try_into()
418            .unwrap(),
419    );
420    *offset += 4;
421    Ok(v)
422}
423
424fn shift_and_mask_lower(shift: u32, mask: u32, value: u32) -> u32 {
425    (value >> shift) & ((1 << mask) - 1)
426}