1use crate::buffer;
2use crate::image;
3use std::borrow::Cow;
4use std::{fs, io};
5
6use crate::{Document, Error, Gltf, Result};
7use image_crate::ImageFormat::{Jpeg, Png};
8use std::path::Path;
9
10type Import = (Document, Vec<buffer::Data>, Vec<image::Data>);
12
13#[derive(Clone, Debug, Eq, Hash, PartialEq)]
15enum Scheme<'a> {
16 Data(Option<&'a str>, &'a str),
18
19 File(&'a str),
23
24 Relative(Cow<'a, str>),
26
27 Unsupported,
29}
30
31impl<'a> Scheme<'a> {
32 fn parse(uri: &str) -> Scheme<'_> {
33 if uri.contains(':') {
34 if let Some(rest) = uri.strip_prefix("data:") {
35 let mut it = rest.split(";base64,");
36
37 match (it.next(), it.next()) {
38 (match0_opt, Some(match1)) => Scheme::Data(match0_opt, match1),
39 (Some(match0), _) => Scheme::Data(None, match0),
40 _ => Scheme::Unsupported,
41 }
42 } else if let Some(rest) = uri.strip_prefix("file://") {
43 Scheme::File(rest)
44 } else if let Some(rest) = uri.strip_prefix("file:") {
45 Scheme::File(rest)
46 } else {
47 Scheme::Unsupported
48 }
49 } else {
50 Scheme::Relative(urlencoding::decode(uri).unwrap())
51 }
52 }
53
54 fn read(base: Option<&Path>, uri: &str) -> Result<Vec<u8>> {
55 match Scheme::parse(uri) {
56 Scheme::Data(_, base64) => base64::decode(base64).map_err(Error::Base64),
59 Scheme::File(path) if base.is_some() => read_to_end(path),
60 Scheme::Relative(path) if base.is_some() => read_to_end(base.unwrap().join(&*path)),
61 Scheme::Unsupported => Err(Error::UnsupportedScheme),
62 _ => Err(Error::ExternalReferenceInSliceImport),
63 }
64 }
65}
66
67fn read_to_end<P>(path: P) -> Result<Vec<u8>>
68where
69 P: AsRef<Path>,
70{
71 use io::Read;
72 let file = fs::File::open(path.as_ref()).map_err(Error::Io)?;
73 let length = file.metadata().map(|x| x.len() + 1).unwrap_or(0);
77 let mut reader = io::BufReader::new(file);
78 let mut data = Vec::with_capacity(length as usize);
79 reader.read_to_end(&mut data).map_err(Error::Io)?;
80 Ok(data)
81}
82
83impl buffer::Data {
84 pub fn from_source(source: buffer::Source<'_>, base: Option<&Path>) -> Result<Self> {
88 Self::from_source_and_blob(source, base, &mut None)
89 }
90
91 pub fn from_source_and_blob(
97 source: buffer::Source<'_>,
98 base: Option<&Path>,
99 blob: &mut Option<Vec<u8>>,
100 ) -> Result<Self> {
101 let mut data = match source {
102 buffer::Source::Uri(uri) => Scheme::read(base, uri),
103 buffer::Source::Bin => blob.take().ok_or(Error::MissingBlob),
104 }?;
105 while data.len() % 4 != 0 {
106 data.push(0);
107 }
108 Ok(buffer::Data(data))
109 }
110}
111
112pub fn import_buffers(
119 document: &Document,
120 base: Option<&Path>,
121 mut blob: Option<Vec<u8>>,
122) -> Result<Vec<buffer::Data>> {
123 let mut buffers = Vec::new();
124 for buffer in document.buffers() {
125 let data = buffer::Data::from_source_and_blob(buffer.source(), base, &mut blob)?;
126 if data.len() < buffer.length() {
127 return Err(Error::BufferLength {
128 buffer: buffer.index(),
129 expected: buffer.length(),
130 actual: data.len(),
131 });
132 }
133 buffers.push(data);
134 }
135 Ok(buffers)
136}
137
138impl image::Data {
139 pub fn from_source(
143 source: image::Source<'_>,
144 base: Option<&Path>,
145 buffer_data: &[buffer::Data],
146 ) -> Result<Self> {
147 #[cfg(feature = "guess_mime_type")]
148 let guess_format = |encoded_image: &[u8]| match image_crate::guess_format(encoded_image) {
149 Ok(image_crate::ImageFormat::Png) => Some(Png),
150 Ok(image_crate::ImageFormat::Jpeg) => Some(Jpeg),
151 _ => None,
152 };
153 #[cfg(not(feature = "guess_mime_type"))]
154 let guess_format = |_encoded_image: &[u8]| None;
155 let decoded_image = match source {
156 image::Source::Uri { uri, mime_type } if base.is_some() => match Scheme::parse(uri) {
157 Scheme::Data(Some(annoying_case), base64) => {
158 let encoded_image = base64::decode(base64).map_err(Error::Base64)?;
159 let encoded_format = match annoying_case {
160 "image/png" => Png,
161 "image/jpeg" => Jpeg,
162 _ => match guess_format(&encoded_image) {
163 Some(format) => format,
164 None => return Err(Error::UnsupportedImageEncoding),
165 },
166 };
167
168 image_crate::load_from_memory_with_format(&encoded_image, encoded_format)?
169 }
170 Scheme::Unsupported => return Err(Error::UnsupportedScheme),
171 _ => {
172 let encoded_image = Scheme::read(base, uri)?;
173 let encoded_format = match mime_type {
174 Some("image/png") => Png,
175 Some("image/jpeg") => Jpeg,
176 Some(_) => match guess_format(&encoded_image) {
177 Some(format) => format,
178 None => return Err(Error::UnsupportedImageEncoding),
179 },
180 None => match uri.rsplit('.').next() {
181 Some("png") => Png,
182 Some("jpg") | Some("jpeg") => Jpeg,
183 _ => match guess_format(&encoded_image) {
184 Some(format) => format,
185 None => return Err(Error::UnsupportedImageEncoding),
186 },
187 },
188 };
189 image_crate::load_from_memory_with_format(&encoded_image, encoded_format)?
190 }
191 },
192 image::Source::View { view, mime_type } => {
193 let parent_buffer_data = &buffer_data[view.buffer().index()].0;
194 let begin = view.offset();
195 let end = begin + view.length();
196 let encoded_image = &parent_buffer_data[begin..end];
197 let encoded_format = match mime_type {
198 "image/png" => Png,
199 "image/jpeg" => Jpeg,
200 _ => match guess_format(encoded_image) {
201 Some(format) => format,
202 None => return Err(Error::UnsupportedImageEncoding),
203 },
204 };
205 image_crate::load_from_memory_with_format(encoded_image, encoded_format)?
206 }
207 _ => return Err(Error::ExternalReferenceInSliceImport),
208 };
209
210 image::Data::new(decoded_image)
211 }
212}
213
214pub fn import_images(
221 document: &Document,
222 base: Option<&Path>,
223 buffer_data: &[buffer::Data],
224) -> Result<Vec<image::Data>> {
225 let mut images = Vec::new();
226 for image in document.images() {
227 images.push(image::Data::from_source(image.source(), base, buffer_data)?);
228 }
229 Ok(images)
230}
231
232fn import_impl(Gltf { document, blob }: Gltf, base: Option<&Path>) -> Result<Import> {
233 let buffer_data = import_buffers(&document, base, blob)?;
234 let image_data = import_images(&document, base, &buffer_data)?;
235 let import = (document, buffer_data, image_data);
236 Ok(import)
237}
238
239fn import_path(path: &Path) -> Result<Import> {
240 let base = path.parent().unwrap_or_else(|| Path::new("./"));
241 let file = fs::File::open(path).map_err(Error::Io)?;
242 let reader = io::BufReader::new(file);
243 import_impl(Gltf::from_reader(reader)?, Some(base))
244}
245
246pub fn import<P>(path: P) -> Result<Import>
274where
275 P: AsRef<Path>,
276{
277 import_path(path.as_ref())
278}
279
280fn import_slice_impl(slice: &[u8]) -> Result<Import> {
281 import_impl(Gltf::from_slice(slice)?, None)
282}
283
284pub fn import_slice<S>(slice: S) -> Result<Import>
312where
313 S: AsRef<[u8]>,
314{
315 import_slice_impl(slice.as_ref())
316}