1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200
use byteorder::{BigEndian, ByteOrder, LittleEndian}; pub(crate) const KTX_IDENTIFIER: [u8; 12] = [0xAB, 0x4B, 0x54, 0x58, 0x20, 0x31, 0x31, 0xBB, 0x0D, 0x0A, 0x1A, 0x0A]; /// KTX texture storage format parameters. /// /// See the [specification](https://www.khronos.org/opengles/sdk/tools/KTX/file_format_spec). pub trait KtxInfo { /// endianness contains the number 0x04030201 written as a 32 bit integer. If the file is little /// endian then this is represented as the bytes 0x01 0x02 0x03 0x04. If the file is big endian /// then this is represented as the bytes 0x04 0x03 0x02 0x01. When reading endianness as a 32 /// bit integer produces the value 0x04030201 then the endianness of the file matches the the /// endianness of the program that is reading the file and no conversion is necessary. When /// reading endianness as a 32 bit integer produces the value 0x01020304 then the endianness of /// the file is opposite the endianness of the program that is reading the file, and in that /// case the program reading the file must endian convert all header bytes and, if glTypeSize > /// 1, all texture data to the endianness of the program (i.e. a little endian program must /// convert from big endian, and a big endian program must convert to little endian). fn big_endian(&self) -> bool; /// For compressed textures, glType must equal 0. For uncompressed textures, glType specifies the type parameter passed to glTex{,Sub}Image*D, usually one of the values from table 8.2 of the OpenGL 4.4 specification [OPENGL44](https://www.khronos.org/opengles/sdk/tools/KTX/file_format_spec/#refsGL44) (UNSIGNED_BYTE, UNSIGNED_SHORT_5_6_5, etc.) fn gl_type(&self) -> u32; /// glTypeSize specifies the data type size that should be used when endianness conversion is /// required for the texture data stored in the file. If glType is not 0, this should be the /// size in bytes corresponding to glType. For texture data which does not depend on platform /// endianness, including compressed texture data, glTypeSize must equal 1. fn gl_type_size(&self) -> u32; /// For compressed textures, glFormat must equal 0. For uncompressed textures, glFormat specifies the format parameter passed to glTex{,Sub}Image*D, usually one of the values from table 8.3 of the OpenGL 4.4 specification [OPENGL44](https://www.khronos.org/opengles/sdk/tools/KTX/file_format_spec/#refsGL44) (RGB, RGBA, BGRA, etc.) fn gl_format(&self) -> u32; /// For compressed textures, glInternalFormat must equal the compressed internal format, usually /// one of the values from table 8.14 of the OpenGL 4.4 specification [OPENGL44]. For /// uncompressed textures, glInternalFormat specifies the internalformat parameter passed to /// glTexStorage*D or glTexImage*D, usually one of the sized internal formats from tables 8.12 & /// 8.13 of the OpenGL 4.4 specification [OPENGL44]. The sized format should be chosen to match /// the bit depth of the data provided. glInternalFormat is used when loading both compressed /// and uncompressed textures, except when loading into a context that does not support sized /// formats, such as an unextended OpenGL ES 2.0 context where the internalformat parameter is /// required to have the same value as the format parameter. fn gl_internal_format(&self) -> u32; /// For both compressed and uncompressed textures, glBaseInternalFormat specifies the base /// internal format of the texture, usually one of the values from table 8.11 of the OpenGL 4.4 /// specification [OPENGL44] (RGB, RGBA, ALPHA, etc.). For uncompressed textures, this value /// will be the same as glFormat and is used as the internalformat parameter when loading into a /// context that does not support sized formats, such as an unextended OpenGL ES 2.0 context. fn gl_base_internal_format(&self) -> u32; /// The width of the texture image for level 0, in pixels. No rounding to block sizes should be /// applied for block compressed textures. fn pixel_width(&self) -> u32; /// The height of the texture image for level 0, in pixels. No rounding to block sizes should be /// applied for block compressed textures. /// /// For 1D textures this must be 0. fn pixel_height(&self) -> u32; /// The depth of the texture image for level 0, in pixels. No rounding to block sizes should be /// applied for block compressed textures. /// /// For 1D textures this must be 0. For 2D and cube textures this must be 0. fn pixel_depth(&self) -> u32; /// numberOfArrayElements specifies the number of array elements. If the texture is not an array /// texture, numberOfArrayElements must equal 0. fn array_elements(&self) -> u32; /// numberOfFaces specifies the number of cubemap faces. For cubemaps and cubemap arrays this /// should be 6. For non cubemaps this should be 1. Cube map faces are stored in the order: +X, /// -X, +Y, -Y, +Z, -Z. /// /// Due to GL_OES_compressed_paletted_texture [OESCPT] not defining the interaction between /// cubemaps and its GL_PALETTE* formats, if `glInternalFormat` is one of its GL_PALETTE* /// format, numberOfFaces must be 1 fn faces(&self) -> u32; /// numberOfMipmapLevels must equal 1 for non-mipmapped textures. For mipmapped textures, it /// equals the number of mipmaps. Mipmaps are stored in order from largest size to smallest /// size. The first mipmap level is always level 0. A KTX file does not need to contain a /// complete mipmap pyramid. If numberOfMipmapLevels equals 0, it indicates that a full mipmap /// pyramid should be generated from level 0 at load time (this is usually not allowed for /// compressed formats). fn mipmap_levels(&self) -> u32; /// keyAndValueByteSize is the number of bytes of combined key and value data in one key/value /// pair following the header. This includes the size of the key, the NUL byte terminating the /// key, and all the bytes of data in the value. If the value is a UTF-8 string it should be NUL /// terminated and the keyAndValueByteSize should include the NUL character (but code that reads /// KTX files must not assume that value fields are NUL terminated). keyAndValueByteSize does /// not include the bytes in valuePadding. fn bytes_of_key_value_data(&self) -> u32; } /// KTX texture storage format header. Provides [`KtxInfo`](../header/trait.KtxInfo.html). #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] pub struct KtxHeader { big_endian: bool, gl_type: u32, gl_type_size: u32, gl_format: u32, gl_internal_format: u32, gl_base_internal_format: u32, pixel_width: u32, pixel_height: u32, pixel_depth: u32, array_elements: u32, faces: u32, mipmap_levels: u32, bytes_of_key_value_data: u32, } impl KtxHeader { /// Reads first 64 bytes to parse KTX header data, returns a `KtxHeader`. pub fn new(first_64_bytes: &[u8]) -> Self { debug_assert!(first_64_bytes.len() >= 64); debug_assert_eq!(&first_64_bytes[..12], &KTX_IDENTIFIER, "Not KTX"); let big_endian = first_64_bytes[12] == 4; let mut vals: [u32; 12] = <_>::default(); if big_endian { BigEndian::read_u32_into(&first_64_bytes[16..64], &mut vals); } else { LittleEndian::read_u32_into(&first_64_bytes[16..64], &mut vals); } Self { big_endian, gl_type: vals[0], gl_type_size: vals[1], gl_format: vals[2], gl_internal_format: vals[3], gl_base_internal_format: vals[4], pixel_width: vals[5], pixel_height: vals[6], pixel_depth: vals[7], array_elements: vals[8], faces: vals[9], mipmap_levels: vals[10], bytes_of_key_value_data: vals[11], } } } impl AsRef<KtxHeader> for KtxHeader { #[inline] fn as_ref(&self) -> &Self { self } } impl<T> KtxInfo for T where T: AsRef<KtxHeader>, { #[inline] fn big_endian(&self) -> bool { self.as_ref().big_endian } #[inline] fn gl_type(&self) -> u32 { self.as_ref().gl_type } #[inline] fn gl_type_size(&self) -> u32 { self.as_ref().gl_type_size } #[inline] fn gl_format(&self) -> u32 { self.as_ref().gl_format } #[inline] fn gl_internal_format(&self) -> u32 { self.as_ref().gl_internal_format } #[inline] fn gl_base_internal_format(&self) -> u32 { self.as_ref().gl_base_internal_format } #[inline] fn pixel_width(&self) -> u32 { self.as_ref().pixel_width } #[inline] fn pixel_height(&self) -> u32 { self.as_ref().pixel_height } #[inline] fn pixel_depth(&self) -> u32 { self.as_ref().pixel_depth } #[inline] fn array_elements(&self) -> u32 { self.as_ref().array_elements } #[inline] fn faces(&self) -> u32 { self.as_ref().faces } #[inline] fn mipmap_levels(&self) -> u32 { self.as_ref().mipmap_levels } #[inline] fn bytes_of_key_value_data(&self) -> u32 { self.as_ref().bytes_of_key_value_data } }