gltf_v1/
image.rs

1use std::ops::Deref;
2
3use image_crate::DynamicImage;
4
5use crate::GLTF_Error;
6use crate::error::Result;
7use crate::{buffer, document::Document};
8
9#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
10pub enum Format {
11    /// Red only.
12    R8,
13
14    /// Red, green.
15    R8G8,
16
17    /// Red, green, blue.
18    R8G8B8,
19
20    /// Red, green, blue, alpha.
21    R8G8B8A8,
22
23    /// Red only (16 bits).
24    R16,
25
26    /// Red, green (16 bits).
27    R16G16,
28
29    /// Red, green, blue (16 bits).
30    R16G16B16,
31
32    /// Red, green, blue, alpha (16 bits).
33    R16G16B16A16,
34
35    /// Red, green, blue (32 bits float)
36    R32G32B32FLOAT,
37
38    /// Red, green, blue, alpha (32 bits float)
39    R32G32B32A32FLOAT,
40}
41#[derive(Clone, Debug)]
42pub enum Source<'a> {
43    Uri(&'a str),
44
45    /// Image data is contained in a buffer view.
46    View {
47        /// The buffer view containing the encoded image data.
48        view: buffer::View<'a>,
49
50        /// The image data MIME type.
51        json: &'a json::extensions::image::BinaryImage,
52    },
53}
54
55/// Image data used to create a texture.
56#[derive(Clone, Debug)]
57pub struct Image<'a> {
58    /// The parent `Document` struct.
59    document: &'a Document,
60
61    /// The corresponding JSON index.
62    index: &'a String,
63
64    /// The corresponding JSON struct.
65    json: &'a json::image::Image,
66}
67
68#[derive(Clone, Debug)]
69pub struct Data {
70    /// The image pixel data (8 bits per channel).
71    pub pixels: Vec<u8>,
72
73    /// The image pixel data format.
74    pub format: Format,
75
76    /// The image width in pixels.
77    pub width: u32,
78
79    /// The image height in pixels.
80    pub height: u32,
81}
82/// An `Iterator` that visits every accessor in a glTF asset.
83#[derive(Clone, Debug)]
84pub struct Images<'a> {
85    /// Internal accessor iterator.
86    pub(crate) iter: indexmap::map::Iter<'a, String, gltf_v1_json::Image>,
87
88    /// The internal root glTF object.
89    pub(crate) document: &'a Document,
90}
91
92impl ExactSizeIterator for Images<'_> {}
93impl<'a> Iterator for Images<'a> {
94    type Item = Image<'a>;
95
96    fn next(&mut self) -> Option<Self::Item> {
97        self.iter
98            .next()
99            .map(|(index, json)| Image::new(self.document, index, json))
100    }
101    fn size_hint(&self) -> (usize, Option<usize>) {
102        self.iter.size_hint()
103    }
104    fn count(self) -> usize {
105        self.iter.count()
106    }
107    fn last(self) -> Option<Self::Item> {
108        let document = self.document;
109        self.iter
110            .last()
111            .map(|(index, json)| Image::new(document, index, json))
112    }
113    fn nth(&mut self, n: usize) -> Option<Self::Item> {
114        self.iter
115            .nth(n)
116            .map(|(index, json)| Image::new(self.document, index, json))
117    }
118}
119
120impl<'a> Image<'a> {
121    /// Constructs an `Image` from owned data.
122    pub(crate) fn new(
123        document: &'a Document,
124        index: &'a String,
125        json: &'a json::image::Image,
126    ) -> Self {
127        Self {
128            document,
129            index,
130            json,
131        }
132    }
133    pub fn index(&self) -> &str {
134        self.index
135    }
136
137    /// Returns the image data source.
138    pub fn source(&self) -> Source<'a> {
139        #[cfg(feature = "KHR_binary_glTF")]
140        if let Some(image_extensions) = &self.json.extensions {
141            if let Some(binary) = &image_extensions.khr_binary_gltf {
142                let view = self
143                    .document
144                    .views()
145                    .find(|x| x.index() == binary.buffer_view.value())
146                    .unwrap();
147                return Source::View { view, json: binary };
148            }
149        }
150        let uri = self.json.uri.deref();
151        Source::Uri(uri)
152    }
153
154    pub fn name(&self) -> Option<&'a str> {
155        self.json.name.as_deref()
156    }
157}
158
159impl<'a> Source<'a> {
160    pub fn mime_type_format(uri: &'a str) -> Option<image_crate::ImageFormat> {
161        match uri.rsplit('.').next() {
162            Some("png") => Some(image_crate::ImageFormat::Png),
163            Some("jpg") | Some("jpeg") => Some(image_crate::ImageFormat::Jpeg),
164            Some("gif") => Some(image_crate::ImageFormat::Gif),
165            Some("bmp") => Some(image_crate::ImageFormat::Bmp),
166            _ => None,
167        }
168    }
169
170    pub fn mime_type(&self) -> Option<&'a str> {
171        match self {
172            Source::Uri(uri) => {
173                let format = Source::mime_type_format(uri);
174                format.map(|x| x.to_mime_type())
175            }
176            Source::View { view: _, json } => Some(json.mime_type.as_str()),
177        }
178    }
179}
180
181impl Data {
182    /// Note: We don't implement `From<DynamicImage>` since we don't want
183    /// to expose such functionality to the user.
184    pub(crate) fn new(image: DynamicImage) -> Result<Self> {
185        use image_crate::GenericImageView;
186        let format = match image {
187            DynamicImage::ImageLuma8(_) => Format::R8,
188            DynamicImage::ImageLumaA8(_) => Format::R8G8,
189            DynamicImage::ImageRgb8(_) => Format::R8G8B8,
190            DynamicImage::ImageRgba8(_) => Format::R8G8B8A8,
191            DynamicImage::ImageLuma16(_) => Format::R16,
192            DynamicImage::ImageLumaA16(_) => Format::R16G16,
193            DynamicImage::ImageRgb16(_) => Format::R16G16B16,
194            DynamicImage::ImageRgba16(_) => Format::R16G16B16A16,
195            DynamicImage::ImageRgb32F(_) => Format::R32G32B32FLOAT,
196            DynamicImage::ImageRgba32F(_) => Format::R32G32B32A32FLOAT,
197            image => return Err(GLTF_Error::UnsupportedImageFormat(image)),
198        };
199        let (width, height) = image.dimensions();
200        let pixels = image.into_bytes();
201        Ok(Data {
202            format,
203            width,
204            height,
205            pixels,
206        })
207    }
208}