libktx_rs/
sources.rs

1// Copyright (C) 2021 Paolo Jovon <paolo.jovon@gmail.com>
2// SPDX-License-Identifier: Apache-2.0
3
4//! [`crate::texture::TextureSource`] implementations for reading (or creating) [`Texture`]s from.
5
6use crate::{
7    enums::{CreateStorage, TextureCreateFlags},
8    stream::{RWSeekable, RustKtxStream},
9    sys::{self},
10    texture::{Texture, TextureSource},
11    KtxError,
12};
13use std::{
14    convert::TryInto,
15    marker::PhantomData,
16    sync::{Arc, Mutex},
17};
18
19/// [`Texture`] creation info common to KTX1 and KTX2.
20#[derive(Debug, Clone, PartialEq, Eq)]
21pub struct CommonCreateInfo {
22    pub create_storage: CreateStorage,
23    pub base_width: u32,
24    pub base_height: u32,
25    pub base_depth: u32,
26    pub num_dimensions: u32,
27    pub num_levels: u32,
28    pub num_layers: u32,
29    pub num_faces: u32,
30    pub is_array: bool,
31    pub generate_mipmaps: bool,
32}
33
34impl Default for CommonCreateInfo {
35    fn default() -> Self {
36        CommonCreateInfo {
37            create_storage: CreateStorage::AllocStorage,
38            base_width: 1,
39            base_height: 1,
40            base_depth: 1,
41            num_dimensions: 1,
42            num_levels: 1,
43            num_layers: 1,
44            num_faces: 1,
45            is_array: false,
46            generate_mipmaps: false,
47        }
48    }
49}
50
51/// [`Texture`] creation info for KTX1 textures ([`crate::texture::Ktx1`]).  
52/// This is also a [`TextureSource`], which creates a new KTX1 texture according to `self`.
53#[derive(Debug, Clone, PartialEq, Eq)]
54pub struct Ktx1CreateInfo {
55    pub gl_internal_format: u32,
56    pub common: CommonCreateInfo,
57}
58
59impl Default for Ktx1CreateInfo {
60    fn default() -> Self {
61        Ktx1CreateInfo {
62            gl_internal_format: 0x8058, // GL_RGBA8
63            common: Default::default(),
64        }
65    }
66}
67
68fn try_create_texture<'a, S, C>(source: S, create_fn: C) -> Result<Texture<'a>, KtxError>
69where
70    S: TextureSource<'a> + 'a,
71    C: FnOnce(S) -> (S, sys::ktx_error_code_e, *mut sys::ktxTexture),
72{
73    let (source, err, handle) = (create_fn)(source);
74    if err == sys::ktx_error_code_e_KTX_SUCCESS && !handle.is_null() {
75        Ok(Texture {
76            source: Box::new(source),
77            handle,
78            handle_phantom: PhantomData,
79        })
80    } else {
81        Err(err.try_into().unwrap_or(KtxError::InvalidOperation))
82    }
83}
84
85impl<'a> TextureSource<'a> for Ktx1CreateInfo {
86    fn create_texture(self) -> Result<Texture<'a>, KtxError> {
87        let mut sys_create_info = sys::ktxTextureCreateInfo {
88            glInternalformat: self.gl_internal_format,
89            vkFormat: 0,
90            pDfd: std::ptr::null_mut(),
91            baseWidth: self.common.base_width,
92            baseHeight: self.common.base_height,
93            baseDepth: self.common.base_depth,
94            numDimensions: self.common.num_dimensions,
95            numLevels: self.common.num_levels,
96            numLayers: self.common.num_layers,
97            numFaces: self.common.num_faces,
98            isArray: self.common.is_array,
99            generateMipmaps: self.common.generate_mipmaps,
100        };
101
102        try_create_texture(self, |source| {
103            let mut handle: *mut sys::ktxTexture = std::ptr::null_mut();
104            let handle_ptr: *mut *mut sys::ktxTexture = &mut handle;
105
106            let err = unsafe {
107                sys::ktxTexture1_Create(
108                    &mut sys_create_info,
109                    source.common.create_storage as u32,
110                    handle_ptr as *mut *mut sys::ktxTexture1,
111                )
112            };
113            (source, err, handle)
114        })
115    }
116}
117
118/// [`Texture`] creation info for KTX2 textures ([`crate::texture::Ktx2`]).  
119/// This is also a [`TextureSource`], which creates a new KTX2 texture according to `self`.
120#[derive(Debug, Clone, PartialEq, Eq)]
121pub struct Ktx2CreateInfo {
122    pub vk_format: u32,
123    pub dfd: Option<Vec<u32>>,
124    pub common: CommonCreateInfo,
125}
126
127impl Default for Ktx2CreateInfo {
128    fn default() -> Self {
129        Ktx2CreateInfo {
130            vk_format: 37, // VK_R8G8B8A8_UNORM
131            dfd: None,
132            common: Default::default(),
133        }
134    }
135}
136
137impl<'a> TextureSource<'a> for Ktx2CreateInfo {
138    fn create_texture(mut self) -> Result<Texture<'a>, KtxError> {
139        // SAFETY: the contents of the Vec will not change or move around memory
140        // - libKTX does not modify the given DFD pointer
141        //   (but then, why no `const` in the C API pointer?)
142        // - The Vec's data is read-only from now on (= no reallocations are possible)
143        let dfd_ptr = match &mut self.dfd {
144            Some(dfd_data) => dfd_data.as_mut_ptr() as *mut u32,
145            None => std::ptr::null_mut(),
146        };
147
148        let mut sys_create_info = sys::ktxTextureCreateInfo {
149            glInternalformat: 0,
150            vkFormat: self.vk_format,
151            pDfd: dfd_ptr,
152            baseWidth: self.common.base_width,
153            baseHeight: self.common.base_height,
154            baseDepth: self.common.base_depth,
155            numDimensions: self.common.num_dimensions,
156            numLevels: self.common.num_levels,
157            numLayers: self.common.num_layers,
158            numFaces: self.common.num_faces,
159            isArray: self.common.is_array,
160            generateMipmaps: self.common.generate_mipmaps,
161        };
162
163        try_create_texture(self, |source| {
164            let mut handle: *mut sys::ktxTexture = std::ptr::null_mut();
165            let handle_ptr: *mut *mut sys::ktxTexture = &mut handle;
166
167            let err = unsafe {
168                sys::ktxTexture2_Create(
169                    &mut sys_create_info,
170                    source.common.create_storage as u32,
171                    handle_ptr as *mut *mut sys::ktxTexture2,
172                )
173            };
174            (source, err, handle)
175        })
176    }
177}
178
179/// [`TextureSource`] for reading a texture from a [`RustKtxStream`].
180#[derive(Debug)]
181pub struct StreamSource<'a, T: RWSeekable + ?Sized + 'a> {
182    stream: Arc<Mutex<RustKtxStream<'a, T>>>,
183    texture_create_flags: TextureCreateFlags,
184}
185
186impl<'a, T: RWSeekable + ?Sized + 'a> StreamSource<'a, T> {
187    /// Creates a new stream texture source from the given [`RustKtxStream`] and texture creation flags.
188    pub fn new(
189        inner: Arc<Mutex<RustKtxStream<'a, T>>>,
190        texture_create_flags: TextureCreateFlags,
191    ) -> Self {
192        StreamSource {
193            stream: inner,
194            texture_create_flags,
195        }
196    }
197
198    /// Destroys `self`, giving back the inner [`RustKtxStream`] that was passed on construction.
199    pub fn into_inner(self) -> Arc<Mutex<RustKtxStream<'a, T>>> {
200        self.stream
201    }
202}
203
204impl<'a, T: RWSeekable + ?Sized + 'a> TextureSource<'a> for StreamSource<'a, T> {
205    fn create_texture(self) -> Result<Texture<'a>, KtxError> {
206        try_create_texture(self, |source| {
207            let mut handle: *mut sys::ktxTexture = std::ptr::null_mut();
208            let handle_ptr: *mut *mut sys::ktxTexture = &mut handle;
209
210            let err = unsafe {
211                sys::ktxTexture_CreateFromStream(
212                    source
213                        .stream
214                        .lock()
215                        .expect("Inner stream is poisoned")
216                        .ktx_stream(),
217                    source.texture_create_flags.bits(),
218                    handle_ptr,
219                )
220            };
221            (source, err, handle)
222        })
223    }
224}