1use crate::buffer;
2use crate::image;
3use std::borrow::Cow;
4use std::{fs, io};
5
6use crate::{Document, Error, Gltf, Result};
7use base64::{Engine, engine::{general_purpose}};
8#[cfg(feature = "EXT_texture_webp")]
9use image_crate::ImageFormat::WebP;
10use image_crate::ImageFormat::{Jpeg, Png};
11use std::path::Path;
12
13type Import = (Document, Vec<buffer::Data>, Vec<image::Data>);
15
16#[derive(Clone, Debug, Eq, Hash, PartialEq)]
18enum Scheme<'a> {
19 Data(Option<&'a str>, &'a str),
21
22 File(&'a str),
26
27 Relative(Cow<'a, str>),
29
30 Unsupported,
32}
33
34impl<'a> Scheme<'a> {
35 fn parse(uri: &str) -> Scheme<'_> {
36 if uri.contains(':') {
37 if let Some(rest) = uri.strip_prefix("data:") {
38 let mut it = rest.split(";base64,");
39
40 match (it.next(), it.next()) {
41 (match0_opt, Some(match1)) => Scheme::Data(match0_opt, match1),
42 (Some(match0), _) => Scheme::Data(None, match0),
43 _ => Scheme::Unsupported,
44 }
45 } else if let Some(rest) = uri.strip_prefix("file://") {
46 Scheme::File(rest)
47 } else if let Some(rest) = uri.strip_prefix("file:") {
48 Scheme::File(rest)
49 } else {
50 Scheme::Unsupported
51 }
52 } else {
53 Scheme::Relative(urlencoding::decode(uri).unwrap())
54 }
55 }
56
57 fn read(base: Option<&Path>, uri: &str) -> Result<Vec<u8>> {
58 match Scheme::parse(uri) {
59 Scheme::Data(_, base64) => general_purpose::STANDARD.decode(base64).map_err(Error::Base64),
62 Scheme::File(path) if base.is_some() => read_to_end(path),
63 Scheme::Relative(path) if base.is_some() => read_to_end(base.unwrap().join(&*path)),
64 Scheme::Unsupported => Err(Error::UnsupportedScheme),
65 _ => Err(Error::ExternalReferenceInSliceImport),
66 }
67 }
68}
69
70fn read_to_end<P>(path: P) -> Result<Vec<u8>>
71where
72 P: AsRef<Path>,
73{
74 use io::Read;
75 let file = fs::File::open(path.as_ref()).map_err(Error::Io)?;
76 let length = file.metadata().map(|x| x.len() + 1).unwrap_or(0);
80 let mut reader = io::BufReader::new(file);
81 let mut data = Vec::with_capacity(length as usize);
82 reader.read_to_end(&mut data).map_err(Error::Io)?;
83 Ok(data)
84}
85
86impl buffer::Data {
87 pub fn from_source(source: buffer::Source<'_>, base: Option<&Path>) -> Result<Self> {
91 Self::from_source_and_blob(source, base, &mut None)
92 }
93
94 pub fn from_source_and_blob(
100 source: buffer::Source<'_>,
101 base: Option<&Path>,
102 blob: &mut Option<Vec<u8>>,
103 ) -> Result<Self> {
104 let mut data = match source {
105 buffer::Source::Uri(uri) => Scheme::read(base, uri),
106 buffer::Source::Bin => blob.take().ok_or(Error::MissingBlob),
107 }?;
108 while data.len() % 4 != 0 {
109 data.push(0);
110 }
111 Ok(buffer::Data(data))
112 }
113}
114
115pub fn import_buffers(
122 document: &Document,
123 base: Option<&Path>,
124 mut blob: Option<Vec<u8>>,
125) -> Result<Vec<buffer::Data>> {
126 let mut buffers = Vec::new();
127 for buffer in document.buffers() {
128 let data = buffer::Data::from_source_and_blob(buffer.source(), base, &mut blob)?;
129 if data.len() < buffer.length() {
130 return Err(Error::BufferLength {
131 buffer: buffer.index(),
132 expected: buffer.length(),
133 actual: data.len(),
134 });
135 }
136 buffers.push(data);
137 }
138 Ok(buffers)
139}
140
141impl image::Data {
142 pub fn from_source(
146 source: image::Source<'_>,
147 base: Option<&Path>,
148 buffer_data: &[buffer::Data],
149 ) -> Result<Self> {
150 #[cfg(feature = "guess_mime_type")]
151 let guess_format = |encoded_image: &[u8]| match image_crate::guess_format(encoded_image) {
152 Ok(image_crate::ImageFormat::Png) => Some(Png),
153 Ok(image_crate::ImageFormat::Jpeg) => Some(Jpeg),
154 #[cfg(feature = "EXT_texture_webp")]
155 Ok(image_crate::ImageFormat::WebP) => Some(WebP),
156 _ => None,
157 };
158 #[cfg(not(feature = "guess_mime_type"))]
159 let guess_format = |_encoded_image: &[u8]| None;
160 let decoded_image = match source {
161 image::Source::Uri { uri, mime_type } if base.is_some() => match Scheme::parse(uri) {
162 Scheme::Data(Some(annoying_case), base64) => {
163 let encoded_image = general_purpose::STANDARD.decode(base64).map_err(Error::Base64)?;
164 let encoded_format = match annoying_case {
165 "image/png" => Png,
166 "image/jpeg" => Jpeg,
167 #[cfg(feature = "EXT_texture_webp")]
168 "image/webp" => WebP,
169 _ => match guess_format(&encoded_image) {
170 Some(format) => format,
171 None => return Err(Error::UnsupportedImageEncoding),
172 },
173 };
174
175 image_crate::load_from_memory_with_format(&encoded_image, encoded_format)?
176 }
177 Scheme::Unsupported => return Err(Error::UnsupportedScheme),
178 _ => {
179 let encoded_image = Scheme::read(base, uri)?;
180 let encoded_format = match mime_type {
181 Some("image/png") => Png,
182 Some("image/jpeg") => Jpeg,
183 #[cfg(feature = "EXT_texture_webp")]
184 Some("image/webp") => WebP,
185 Some(_) => match guess_format(&encoded_image) {
186 Some(format) => format,
187 None => return Err(Error::UnsupportedImageEncoding),
188 },
189 None => match uri.rsplit('.').next() {
190 Some("png") => Png,
191 Some("jpg") | Some("jpeg") => Jpeg,
192 #[cfg(feature = "EXT_texture_webp")]
193 Some("webp") => WebP,
194 _ => match guess_format(&encoded_image) {
195 Some(format) => format,
196 None => return Err(Error::UnsupportedImageEncoding),
197 },
198 },
199 };
200 image_crate::load_from_memory_with_format(&encoded_image, encoded_format)?
201 }
202 },
203 image::Source::View { view, mime_type } => {
204 let parent_buffer_data = &buffer_data[view.buffer().index()].0;
205 let begin = view.offset();
206 let end = begin + view.length();
207 let encoded_image = &parent_buffer_data[begin..end];
208 let encoded_format = match mime_type {
209 "image/png" => Png,
210 "image/jpeg" => Jpeg,
211 #[cfg(feature = "EXT_texture_webp")]
212 "image/webp" => WebP,
213 _ => match guess_format(encoded_image) {
214 Some(format) => format,
215 None => return Err(Error::UnsupportedImageEncoding),
216 },
217 };
218 image_crate::load_from_memory_with_format(encoded_image, encoded_format)?
219 }
220 _ => return Err(Error::ExternalReferenceInSliceImport),
221 };
222
223 image::Data::new(decoded_image)
224 }
225}
226
227pub fn import_images(
234 document: &Document,
235 base: Option<&Path>,
236 buffer_data: &[buffer::Data],
237) -> Result<Vec<image::Data>> {
238 let mut images = Vec::new();
239 for image in document.images() {
240 images.push(image::Data::from_source(image.source(), base, buffer_data)?);
241 }
242 Ok(images)
243}
244
245fn import_impl(Gltf { document, blob }: Gltf, base: Option<&Path>) -> Result<Import> {
246 let buffer_data = import_buffers(&document, base, blob)?;
247 let image_data = import_images(&document, base, &buffer_data)?;
248 let import = (document, buffer_data, image_data);
249 Ok(import)
250}
251
252fn import_path(path: &Path) -> Result<Import> {
253 let base = path.parent().unwrap_or_else(|| Path::new("./"));
254 let file = fs::File::open(path).map_err(Error::Io)?;
255 let reader = io::BufReader::new(file);
256 import_impl(Gltf::from_reader(reader)?, Some(base))
257}
258
259pub fn import<P>(path: P) -> Result<Import>
287where
288 P: AsRef<Path>,
289{
290 import_path(path.as_ref())
291}
292
293fn import_slice_impl(slice: &[u8]) -> Result<Import> {
294 import_impl(Gltf::from_slice(slice)?, None)
295}
296
297pub fn import_slice<S>(slice: S) -> Result<Import>
325where
326 S: AsRef<[u8]>,
327{
328 import_slice_impl(slice.as_ref())
329}