Skip to content
Snippets Groups Projects
image.rs 3.84 KiB
Newer Older
use std::path::{Path, PathBuf};
alexdevteam's avatar
alexdevteam committed

use xml::attribute::OwnedAttribute;
alexdevteam's avatar
alexdevteam committed

alexdevteam's avatar
alexdevteam committed
use crate::{error::TiledError, properties::Color, util::*};
alexdevteam's avatar
alexdevteam committed

/// A reference to an image stored somewhere within the filesystem.
alexdevteam's avatar
alexdevteam committed
#[derive(Debug, PartialEq, Eq, Clone)]
pub struct Image {
    /// The **uncanonicalized** filepath of the image, starting from the path given to load the file
    /// this image is in. See the example for more details.
alexdevteam's avatar
alexdevteam committed
    ///
    /// ## Note
    /// The crate does not currently support embedded images (Even though Tiled
    /// does not allow creating maps with embedded image data, the TMX format does; [source])
    ///
    /// Currently, the crate is not prepared to handle anything but OS paths. Using VFS is a hard
    /// task that involves a lot of messy path manipulation. [Tracking issue]
    ///
alexdevteam's avatar
alexdevteam committed
    /// [source]: https://doc.mapeditor.org/en/stable/reference/tmx-map-format/#image
    /// [Tracking issue]: https://github.com/mapeditor/rs-tiled/issues/37
    ///
    /// ## Example
    /// ```
    /// use std::path::Path;
    /// use std::fs::File;
    /// use tiled::*;
    ///
    /// # fn main() -> Result<(), Box<dyn std::error::Error>> {
    /// let map = Map::parse_file(
    ///     "assets/folder/tiled_relative_paths.tmx",
    ///     &mut FilesystemResourceCache::new(),
    /// )?;
    ///
    /// let image_layer = match map
    ///     .layers()
    ///     .find(|layer| layer.name() == "image")
    ///     .unwrap()
    ///     .layer_type()
    /// {
    ///     LayerType::ImageLayer(layer) => layer,
    ///     _ => panic!(),
    /// };
    ///
    /// // Image layer has an image with the source attribute set to "../tilesheet.png"
    /// // Given the information we gave to the `parse_file` function, the image source should be
    /// // "assets/folder/../tilesheet.png". The filepath is not canonicalized.
    /// let image_source = &image_layer.image().unwrap().source;
    ///
    /// assert_eq!(
    ///     image_source,
    ///     Path::new("assets/folder/../tilesheet.png")
    /// );
    ///
    /// // If you are using the OS's filesystem, figuring out the real path of the image is as easy
    /// // as canonicalizing the path. If you are using some sort of VFS, this task is much harder
    /// // since std::path is meant to be used with the OS. This will be fixed in the future!
    /// let image_source = image_source.canonicalize()?;
    /// assert!(File::open(image_source).is_ok());
    /// # Ok(())
    /// # }
    /// ```
    /// Check the assets/tiled_relative_paths.tmx file at the crate root to see the structure of the
    /// file this example is referring to.
alexdevteam's avatar
alexdevteam committed
    // TODO: Embedded images
    // TODO: Figure out how to serve crate users paths in a better way
alexdevteam's avatar
alexdevteam committed
    pub source: PathBuf,
    /// The width in pixels of the image.
alexdevteam's avatar
alexdevteam committed
    pub width: i32,
    /// The height in pixels of the image.
alexdevteam's avatar
alexdevteam committed
    pub height: i32,
    /// A color that should be interpreted as transparent (0 alpha), if any.
alexdevteam's avatar
alexdevteam committed
    pub transparent_colour: Option<Color>,
alexdevteam's avatar
alexdevteam committed
}

impl Image {
    pub(crate) fn new(
        parser: &mut impl Iterator<Item = XmlEventResult>,
alexdevteam's avatar
alexdevteam committed
        attrs: Vec<OwnedAttribute>,
alexdevteam's avatar
alexdevteam committed
        path_relative_to: impl AsRef<Path>,
alexdevteam's avatar
alexdevteam committed
    ) -> Result<Image, TiledError> {
        let (c, (s, w, h)) = get_attrs!(
            attrs,
            optionals: [
                ("trans", trans, |v:String| v.parse().ok()),
            ],
            required: [
                ("source", source, |v| Some(v)),
                ("width", width, |v:String| v.parse().ok()),
                ("height", height, |v:String| v.parse().ok()),
            ],
alexdevteam's avatar
alexdevteam committed
            TiledError::MalformedAttributes("Image must have a source, width and height with correct types".to_string())
alexdevteam's avatar
alexdevteam committed
        );

Alejandro Perea's avatar
Alejandro Perea committed
        parse_tag!(parser, "image", { });
alexdevteam's avatar
alexdevteam committed
        Ok(Image {
alexdevteam's avatar
alexdevteam committed
            source: path_relative_to.as_ref().join(s),
alexdevteam's avatar
alexdevteam committed
            width: w,
            height: h,
            transparent_colour: c,
        })
    }
}