ktx/
read.rs

1use crate::header::*;
2use byteorder::{BigEndian, ByteOrder, LittleEndian};
3use std::{
4    fmt,
5    io::{self, Read},
6};
7
8/// KTX texture storage format reader. Useful when reading from a file and/or compressed data.
9/// Provides [`KtxInfo`](../header/trait.KtxInfo.html).
10///
11/// # Example
12/// ```
13/// # use std::{io::BufReader, fs::File};
14/// # fn main() -> std::io::Result<()> {
15/// use ktx::*;
16/// # let mut buf_reader = BufReader::new(File::open("tests/babg-bc3.ktx")?);
17/// let mut decoder = ktx::Decoder::new(buf_reader)?;
18///
19/// assert_eq!(decoder.pixel_width(), 260);
20/// let texture_levels: Vec<Vec<u8>> = decoder.read_textures().collect();
21/// # Ok(()) }
22/// ```
23#[derive(Clone, Copy)]
24pub struct KtxDecoder<R> {
25    header: KtxHeader,
26    data: R,
27}
28
29impl<R> AsRef<KtxHeader> for KtxDecoder<R> {
30    #[inline]
31    fn as_ref(&self) -> &KtxHeader {
32        &self.header
33    }
34}
35
36impl<R> fmt::Debug for KtxDecoder<R> {
37    fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
38        fmt.debug_struct("KtxDecoder")
39            .field("header", &self.header)
40            .finish()
41    }
42}
43
44impl<R: io::Read> KtxDecoder<R> {
45    /// Reads KTX header data and returns a `KtxDecoder`.
46    #[inline]
47    pub fn new(mut data: R) -> io::Result<Self> {
48        let mut header_data = [0; 64];
49        data.read_exact(&mut header_data)?;
50        let header = KtxHeader::new(&header_data);
51        Ok(Self { header, data })
52    }
53
54    /// Consumes the `KtxDecoder` to returns an iterator reading texture levels starting at level 0.
55    #[inline]
56    pub fn read_textures(self) -> Textures<R> {
57        Textures {
58            header: self.header,
59            data: self.data,
60            next_level: 0,
61        }
62    }
63
64    /// Returns `KtxHeader`. Useful if this info is desired after consuming the `KtxDecoder`.
65    ///
66    /// # Example
67    /// ```
68    /// # use std::{io::BufReader, fs::File};
69    /// # use ktx::*;
70    /// # let mut reader = BufReader::new(File::open("tests/babg-bc3.ktx").unwrap());
71    /// # let mut decoder = ktx::Decoder::new(reader).unwrap();
72    /// let ktx_header = decoder.header();
73    /// assert_eq!(ktx_header.pixel_width(), 260);
74    /// ```
75    #[inline]
76    pub fn header(&self) -> KtxHeader {
77        self.header
78    }
79}
80
81/// Iterator that reads texture level data into `Vec<u8>`.
82///
83/// For cubemap textures each level will contain all 6 faces
84/// in order: +X, -X, +Y, -Y, +Z, -Z.
85#[derive(Debug)]
86pub struct Textures<R> {
87    header: KtxHeader,
88    data: R,
89    next_level: u32,
90}
91
92impl<R: io::Read> Iterator for Textures<R> {
93    type Item = Vec<u8>;
94
95    fn next(&mut self) -> Option<Self::Item> {
96        if self.next_level >= self.header.mipmap_levels() {
97            None
98        } else {
99            // skip key-value data
100            if self.next_level == 0 && self.header.bytes_of_key_value_data() != 0 {
101                let mut discard = Vec::with_capacity(self.header.bytes_of_key_value_data() as _);
102                self.data
103                    .by_ref()
104                    .take(self.header.bytes_of_key_value_data() as _)
105                    .read_to_end(&mut discard)
106                    .ok()?;
107            }
108
109            self.next_level += 1;
110            let mut level_len = {
111                let mut len = [0; 4];
112                self.data.read_exact(&mut len).ok()?;
113                if self.header.big_endian() {
114                    BigEndian::read_u32(&len)
115                } else {
116                    LittleEndian::read_u32(&len)
117                }
118            };
119
120            if self.header.array_elements() == 0 && self.header.faces() == 6 {
121                // Multiply for each face, see https://www.khronos.org/registry/KTX/specs/1.0/ktxspec_v1.html#2.16
122                level_len *= 6;
123            }
124
125            let mut level = Vec::with_capacity(level_len as _);
126            self.data
127                .by_ref()
128                .take(level_len as _)
129                .read_to_end(&mut level)
130                .ok()?;
131            Some(level)
132        }
133    }
134}
135
136impl<R: io::Read> std::iter::FusedIterator for Textures<R> {}