1use indexmap::IndexMap;
2
3use crate::{GLTF_Error, buffer, document::Document, error::Result, image};
4use std::{borrow::Cow, fs, io, path::Path};
5
6#[derive(Clone, Debug, Eq, Hash, PartialEq)]
7enum Scheme<'a> {
8 Data(Option<&'a str>, &'a str),
9 File(&'a str),
10 Relative(Cow<'a, str>),
11 Unsupported,
12}
13
14impl Scheme<'_> {
15 fn parse(uri: &str) -> Scheme<'_> {
16 if uri.contains(':') {
17 if let Some(rest) = uri.strip_prefix("data:") {
18 let mut it = rest.split(";base64,");
19
20 match (it.next(), it.next()) {
21 (match0_opt, Some(match1)) => Scheme::Data(match0_opt, match1),
22 (Some(match0), _) => Scheme::Data(None, match0),
23 _ => Scheme::Unsupported,
24 }
25 } else if let Some(rest) = uri.strip_prefix("file://") {
26 Scheme::File(rest)
27 } else if let Some(rest) = uri.strip_prefix("file:") {
28 Scheme::File(rest)
29 } else {
30 Scheme::Unsupported
31 }
32 } else {
33 Scheme::Relative(urlencoding::decode(uri).unwrap())
34 }
35 }
36
37 fn read(base: Option<&Path>, uri: &str) -> Result<Vec<u8>> {
38 match Scheme::parse(uri) {
39 Scheme::Data(_, base64) => base64::decode(base64).map_err(GLTF_Error::Base64),
42 Scheme::File(path) if base.is_some() => read_to_end(path),
43 Scheme::Relative(path) if base.is_some() => read_to_end(base.unwrap().join(&*path)),
44 Scheme::Unsupported => Err(GLTF_Error::UnsupportedScheme),
45 _ => Err(GLTF_Error::ExternalReferenceInSliceImport),
46 }
47 }
48}
49
50fn read_to_end<P>(path: P) -> Result<Vec<u8>>
51where
52 P: AsRef<Path>,
53{
54 use io::Read;
55 let file = fs::File::open(path.as_ref()).map_err(GLTF_Error::Io)?;
56 let length = file.metadata().map(|x| x.len() + 1).unwrap_or(0);
60 let mut reader = io::BufReader::new(file);
61 let mut data = Vec::with_capacity(length as usize);
62 reader.read_to_end(&mut data).map_err(GLTF_Error::Io)?;
63 Ok(data)
64}
65
66impl buffer::Data {
67 pub fn from_source(source: buffer::Source<'_>, base: Option<&Path>) -> Result<Self> {
68 Self::from_source_and_blob(source, base, &mut None)
69 }
70
71 pub fn from_source_and_blob(
72 source: buffer::Source<'_>,
73 base: Option<&Path>,
74 blob: &mut Option<Vec<u8>>,
75 ) -> Result<Self> {
76 let mut data = match source {
77 buffer::Source::Uri(uri) => Scheme::read(base, uri),
78 buffer::Source::Bin => blob.take().ok_or(GLTF_Error::MissingBlob),
79 }?;
80 while data.len() % 4 != 0 {
81 data.push(0);
82 }
83 Ok(buffer::Data(data))
84 }
85}
86
87pub fn import_buffers(
88 document: &Document,
89 base: Option<&Path>,
90 mut blob: Option<Vec<u8>>,
91) -> Result<IndexMap<String, buffer::Data>> {
92 let mut buffers = IndexMap::new();
93 for buffer in document.buffers() {
94 let index = buffer.index();
95 let data = buffer::Data::from_source_and_blob(buffer.source(), base, &mut blob)?;
96 if data.len() < buffer.length() {
97 return Err(GLTF_Error::BufferLength {
98 buffer: buffer.index().to_string(),
99 expected: buffer.length(),
100 actual: data.len(),
101 });
102 }
103 buffers.insert(index.to_string(), data);
104 }
105 Ok(buffers)
106}
107
108pub fn import_images(
109 document: &Document,
110 base: Option<&Path>,
111 buffer_data: &IndexMap<String, buffer::Data>,
112) -> Result<IndexMap<String, image::Data>> {
113 let mut images = IndexMap::new();
114 for image in document.images() {
115 let index = image.index();
116 let data = image::Data::from_source(image.source(), base, buffer_data)?;
117 images.insert(index.to_string(), data);
118 }
119 Ok(images)
120}
121
122impl image::Data {
123 pub fn from_source(
124 source: image::Source<'_>,
125 base: Option<&Path>,
126 buffer_data: &IndexMap<String, buffer::Data>,
127 ) -> Result<Self> {
128 let guess_format = |encoded_image: &[u8]| match image_crate::guess_format(encoded_image) {
129 Ok(image_crate::ImageFormat::Png) => Some(image_crate::ImageFormat::Png),
130 Ok(image_crate::ImageFormat::Jpeg) => Some(image_crate::ImageFormat::Jpeg),
131 Ok(image_crate::ImageFormat::Bmp) => Some(image_crate::ImageFormat::Bmp),
132 Ok(image_crate::ImageFormat::Gif) => Some(image_crate::ImageFormat::Gif),
133 _ => None,
134 };
135 let decoded_image = match source {
136 image::Source::Uri(uri) if base.is_some() => match Scheme::parse(uri) {
137 Scheme::Data(Some(annoying_case), base64) => {
138 let encoded_image = base64::decode(base64).map_err(GLTF_Error::Base64)?;
139 let encoded_format = match annoying_case {
140 "image/png" => image_crate::ImageFormat::Png,
141 "image/jpeg" => image_crate::ImageFormat::Jpeg,
142 "image/bmp" => image_crate::ImageFormat::Bmp,
143 "image/gif" => image_crate::ImageFormat::Gif,
144 _ => match guess_format(&encoded_image) {
145 Some(format) => format,
146 None => return Err(GLTF_Error::UnsupportedImageEncoding),
147 },
148 };
149 image_crate::load_from_memory_with_format(&encoded_image, encoded_format)?
150 }
151 Scheme::Unsupported => return Err(GLTF_Error::UnsupportedScheme),
152 _ => {
153 let encoded_image = Scheme::read(base, uri)?;
154 let encoded_format = image::Source::mime_type_format(uri).unwrap_or(
155 match guess_format(&encoded_image) {
156 Some(format) => format,
157 None => return Err(GLTF_Error::UnsupportedImageEncoding),
158 },
159 );
160 image_crate::load_from_memory_with_format(&encoded_image, encoded_format)?
161 }
162 },
163 image::Source::View { view, json } => {
164 let parent_buffer_data = &buffer_data[view.buffer().index()].0;
165 let begin = view.offset();
166 let end = begin + view.length();
167 let encoded_image = &parent_buffer_data[begin..end];
168 let encoded_format = match json.mime_type.as_str() {
169 "image/png" => image_crate::ImageFormat::Png,
170 "image/jpeg" => image_crate::ImageFormat::Jpeg,
171 "image/bmp" => image_crate::ImageFormat::Bmp,
172 "image/gif" => image_crate::ImageFormat::Gif,
173 _ => match guess_format(encoded_image) {
174 Some(format) => format,
175 None => return Err(GLTF_Error::UnsupportedImageEncoding),
176 },
177 };
178 image_crate::load_from_memory_with_format(encoded_image, encoded_format)?
179 }
180 _ => return Err(GLTF_Error::ExternalReferenceInSliceImport),
181 };
182 image::Data::new(decoded_image)
183 }
184}