mesh_loader/collada/
image.rs

1use super::*;
2
3/// The `<library_images>` element.
4///
5/// See the specifications ([1.4], [1.5]) for details.
6///
7/// [1.4]: https://www.khronos.org/files/collada_spec_1_4.pdf#page=278
8/// [1.5]: https://www.khronos.org/files/collada_spec_1_5.pdf#page=327
9#[derive(Default)]
10pub(super) struct LibraryImages<'a> {
11    // /// The unique identifier of this element.
12    // pub(super) id: Option<&'a str>,
13    // /// The name of this element.
14    // pub(super) name: Option<&'a str>,
15    pub(super) images: HashMap<&'a str, Image<'a>>,
16}
17
18/// The `<image>` element.
19///
20/// See the specifications ([1.4], [1.5]) for details.
21///
22/// [1.4]: https://www.khronos.org/files/collada_spec_1_4.pdf#page=268
23/// [1.5]: https://www.khronos.org/files/collada_spec_1_5.pdf#page=310
24pub(super) struct Image<'a> {
25    /// The unique identifier of this element.
26    pub(super) id: &'a str,
27    // /// The name of this element.
28    // pub(super) name: Option<&'a str>,
29    // /// The image format.
30    // pub(super) format: Option<&'a str>,
31    // /// The height of the image in pixels.
32    // pub(super) height: Option<u32>,
33    // /// The width of the image in pixels.
34    // pub(super) width: Option<u32>,
35    // /// The depth of the image in pixels. A 2-D image has a depth of 1, which is the default.
36    // pub(super) depth: u32,
37    /// An embedded image data or an external image file.
38    pub(super) source: ImageSource<'a>,
39}
40
41/// An embedded image data or an external image file.
42pub(super) enum ImageSource<'a> {
43    /// An embedded image data.
44    Data(Vec<u8>),
45    /// An external image file.
46    InitFrom(&'a str),
47    Skip,
48}
49
50// -----------------------------------------------------------------------------
51// Parsing
52
53pub(super) fn parse_library_images<'a>(
54    cx: &mut Context<'a>,
55    node: xml::Node<'a, '_>,
56) -> io::Result<()> {
57    debug_assert_eq!(node.tag_name().name(), "library_images");
58    // cx.library_images.id = node.attribute("id");
59    // cx.library_images.name = node.attribute("name");
60
61    for node in node.element_children() {
62        match node.tag_name().name() {
63            "image" => {
64                let image = parse_image(cx, node)?;
65                cx.library_images.images.insert(image.id, image);
66            }
67            "asset" | "extra" => { /* skip */ }
68            _ => return Err(error::unexpected_child_elem(node)),
69        }
70    }
71
72    // The specification says <library_images> has 1 or more <image> elements,
73    // but some exporters write empty <library_images/> tags.
74
75    Ok(())
76}
77
78fn parse_image<'a>(cx: &Context<'a>, node: xml::Node<'a, '_>) -> io::Result<Image<'a>> {
79    debug_assert_eq!(node.tag_name().name(), "image");
80    let id = node.required_attribute("id")?;
81    // let name = node.attribute("name");
82    let is_1_4 = cx.version.is_1_4();
83    if is_1_4 {
84        // let mut format = node.attribute("format");
85        let _height: Option<u32> = node.parse_attribute("height")?;
86        let _width: Option<u32> = node.parse_attribute("width")?;
87        let _depth: u32 = node.parse_attribute("depth")?.unwrap_or(1);
88    } else {
89        // let sid = node.attribute("sid");
90    }
91    let mut source = None;
92
93    for node in node.element_children() {
94        let tag_name = node.tag_name().name();
95        match tag_name {
96            "init_from" => {
97                if is_1_4 {
98                    source = Some(ImageSource::InitFrom(node.trimmed_text()));
99                    continue;
100                }
101                for node in node.element_children() {
102                    match node.tag_name().name() {
103                        "ref" => {
104                            source = Some(ImageSource::InitFrom(node.trimmed_text()));
105                        }
106                        "hex" => {
107                            // format = node.attribute("format");
108                            let data = hex::decode(node.trimmed_text().as_bytes())?;
109                            source = Some(ImageSource::Data(data));
110                        }
111                        _ => {}
112                    }
113                }
114            }
115            "data" if is_1_4 => {
116                let data = hex::decode(node.trimmed_text().as_bytes())?;
117                source = Some(ImageSource::Data(data));
118            }
119            "asset" | "extra" => { /* skip */ }
120            _ if is_1_4 => return Err(error::unexpected_child_elem(node)),
121            _ => {}
122        }
123    }
124
125    let source = match source {
126        Some(source) => source,
127        None => {
128            if is_1_4 {
129                bail!(
130                    "<{}> element must be contain <data> or <init_from> element ({})",
131                    node.tag_name().name(),
132                    node.node_location()
133                )
134            }
135            // 1.5 has <create_*> elements, but many applications ignore them.
136            ImageSource::Skip
137        }
138    };
139
140    Ok(Image {
141        id,
142        // name,
143        // format,
144        // height,
145        // width,
146        // depth,
147        source,
148    })
149}