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 R8,
13
14 R8G8,
16
17 R8G8B8,
19
20 R8G8B8A8,
22
23 R16,
25
26 R16G16,
28
29 R16G16B16,
31
32 R16G16B16A16,
34
35 R32G32B32FLOAT,
37
38 R32G32B32A32FLOAT,
40}
41#[derive(Clone, Debug)]
42pub enum Source<'a> {
43 Uri(&'a str),
44
45 View {
47 view: buffer::View<'a>,
49
50 json: &'a json::extensions::image::BinaryImage,
52 },
53}
54
55#[derive(Clone, Debug)]
57pub struct Image<'a> {
58 document: &'a Document,
60
61 index: &'a String,
63
64 json: &'a json::image::Image,
66}
67
68#[derive(Clone, Debug)]
69pub struct Data {
70 pub pixels: Vec<u8>,
72
73 pub format: Format,
75
76 pub width: u32,
78
79 pub height: u32,
81}
82#[derive(Clone, Debug)]
84pub struct Images<'a> {
85 pub(crate) iter: indexmap::map::Iter<'a, String, gltf_v1_json::Image>,
87
88 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 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 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 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}