From ce73c547cca92179ce3dba64538cf93209d3d1a1 Mon Sep 17 00:00:00 2001
From: Alejandro Perea <alexpro820@gmail.com>
Date: Wed, 9 Feb 2022 16:44:34 +0100
Subject: [PATCH] Add layer tint color; Fix `Color` parsing issues (#150)

* Fix #148

* Close #85

* Close #149

* Fix just one small issue

* Update changelog
---
 CHANGELOG.md                  |  5 ++++
 assets/tiled_image_layers.tmx |  4 +--
 src/layers.rs                 |  8 ++++-
 src/properties.rs             | 55 +++++++++++++++++++++++------------
 tests/lib.rs                  | 25 +++++++++++++++-
 5 files changed, 75 insertions(+), 22 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 83a04f3..fbbe474 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -9,6 +9,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 ### Added
 - `Tileset::source` for obtaining where the tileset actually came from.
 - `Tileset::columns`.
+- `Color::alpha`.
 - `Layer::id`, `Layer::width`, `Layer::height`, `Layer::parallax_x` and `Layer::parallax_y`.
 - Support for 'object'-type properties.
 - Support for multiline string properties.
@@ -39,6 +40,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
 - Bumped `zstd` to `0.9`.
 - Fixed markdown formatting in the `CONTRIBUTORS` file.
 
+### Fixed
+- `Color` parsing.
+
+
 ## [0.9.5] - 2021-05-02
 ### Added
 - Support for file properties.
diff --git a/assets/tiled_image_layers.tmx b/assets/tiled_image_layers.tmx
index 6ee077b..6c3128f 100644
--- a/assets/tiled_image_layers.tmx
+++ b/assets/tiled_image_layers.tmx
@@ -3,8 +3,8 @@
  <tileset firstgid="1" name="tilesheet" tilewidth="32" tileheight="32" tilecount="84" columns="14">
   <image source="tilesheet.png" width="448" height="192"/>
  </tileset>
- <imagelayer id="1" name="Image Layer 1"/>
- <imagelayer id="2" name="Image Layer 2">
+ <imagelayer id="1" name="Image Layer 1" tintcolor="#12345678"/>
+ <imagelayer id="2" name="Image Layer 2" tintcolor="123456">
   <image source="tilesheet.png" width="448" height="192"/>
  </imagelayer>
 </map>
diff --git a/src/layers.rs b/src/layers.rs
index b5c0b65..e75f345 100644
--- a/src/layers.rs
+++ b/src/layers.rs
@@ -68,6 +68,7 @@ pub struct Layer {
     pub parallax_x: f32,
     pub parallax_y: f32,
     pub opacity: f32,
+    pub tint_color: Option<Color>,
     pub properties: Properties,
     pub layer_type: LayerType,
 }
@@ -80,10 +81,14 @@ impl Layer {
         infinite: bool,
         path_relative_to: Option<&Path>,
     ) -> Result<Self, TiledError> {
-        let ((opacity, visible, offset_x, offset_y, parallax_x, parallax_y, name, id), ()) = get_attrs!(
+        let (
+            (opacity, tint_color, visible, offset_x, offset_y, parallax_x, parallax_y, name, id),
+            (),
+        ) = get_attrs!(
             attrs,
             optionals: [
                 ("opacity", opacity, |v:String| v.parse().ok()),
+                ("tintcolor", tint_color, |v:String| v.parse().ok()),
                 ("visible", visible, |v:String| v.parse().ok().map(|x:i32| x == 1)),
                 ("offsetx", offset_x, |v:String| v.parse().ok()),
                 ("offsety", offset_y, |v:String| v.parse().ok()),
@@ -120,6 +125,7 @@ impl Layer {
             parallax_x: parallax_x.unwrap_or(1.0),
             parallax_y: parallax_y.unwrap_or(1.0),
             opacity: opacity.unwrap_or(1.0),
+            tint_color,
             name: name.unwrap_or_default(),
             id: id.unwrap_or(0),
             properties,
diff --git a/src/properties.rs b/src/properties.rs
index 7e8c0bd..949046c 100644
--- a/src/properties.rs
+++ b/src/properties.rs
@@ -1,38 +1,57 @@
 use std::{collections::HashMap, io::Read, str::FromStr};
 
-use xml::{EventReader, attribute::OwnedAttribute, reader::XmlEvent};
+use xml::{attribute::OwnedAttribute, reader::XmlEvent, EventReader};
 
 use crate::{
-    error::{ParseTileError, TiledError},
+    error::TiledError,
     util::{get_attrs, parse_tag},
 };
 
 #[derive(Debug, PartialEq, Eq, Copy, Clone)]
 pub struct Color {
+    pub alpha: u8,
     pub red: u8,
     pub green: u8,
     pub blue: u8,
 }
 
 impl FromStr for Color {
-    type Err = ParseTileError;
+    type Err = ();
 
-    fn from_str(s: &str) -> Result<Color, ParseTileError> {
+    fn from_str(s: &str) -> Result<Color, Self::Err> {
         let s = if s.starts_with("#") { &s[1..] } else { s };
-        if s.len() != 6 {
-            return Err(ParseTileError::ColorError);
-        }
-        let r = u8::from_str_radix(&s[0..2], 16);
-        let g = u8::from_str_radix(&s[2..4], 16);
-        let b = u8::from_str_radix(&s[4..6], 16);
-        if r.is_ok() && g.is_ok() && b.is_ok() {
-            return Ok(Color {
-                red: r.unwrap(),
-                green: g.unwrap(),
-                blue: b.unwrap(),
-            });
+        match s.len() {
+            6 => {
+                let r = u8::from_str_radix(&s[0..2], 16);
+                let g = u8::from_str_radix(&s[2..4], 16);
+                let b = u8::from_str_radix(&s[4..6], 16);
+                match (r, g, b) {
+                    (Ok(red), Ok(green), Ok(blue)) => Ok(Color {
+                        alpha: 0xFF,
+                        red,
+                        green,
+                        blue,
+                    }),
+                    _ => Err(()),
+                }
+            }
+            8 => {
+                let a = u8::from_str_radix(&s[0..2], 16);
+                let r = u8::from_str_radix(&s[2..4], 16);
+                let g = u8::from_str_radix(&s[4..6], 16);
+                let b = u8::from_str_radix(&s[6..8], 16);
+                match (a, r, g, b) {
+                    (Ok(alpha), Ok(red), Ok(green), Ok(blue)) => Ok(Color {
+                        alpha,
+                        red,
+                        green,
+                        blue,
+                    }),
+                    _ => Err(()),
+                }
+            }
+            _ => Err(()),
         }
-        Err(ParseTileError::ColorError)
     }
 }
 
@@ -105,7 +124,7 @@ pub(crate) fn parse_properties<R: Read>(
                 TiledError::MalformedAttributes("property must have a name and a value".to_string())
             );
             let t = t.unwrap_or("string".into());
-            
+
             let v = match v_attr {
                 Some(val) => val,
                 None => {
diff --git a/tests/lib.rs b/tests/lib.rs
index 4954b84..ad8839f 100644
--- a/tests/lib.rs
+++ b/tests/lib.rs
@@ -1,6 +1,6 @@
 use std::path::Path;
 use std::{fs::File, path::PathBuf};
-use tiled::{LayerData, Map, PropertyValue, TiledError, Tileset};
+use tiled::{Color, LayerData, Map, PropertyValue, TiledError, Tileset};
 use tiled::{LayerType, ObjectLayer, TileLayer};
 
 fn as_tile_layer(layer: &LayerType) -> &TileLayer {
@@ -256,3 +256,26 @@ fn test_object_property() {
     };
     assert_eq!(3, prop_value);
 }
+
+#[test]
+fn test_tint_color() {
+    let r = Map::parse_file("assets/tiled_image_layers.tmx").unwrap();
+    assert_eq!(
+        r.layers[0].tint_color,
+        Some(Color {
+            alpha: 0x12,
+            red: 0x34,
+            green: 0x56,
+            blue: 0x78
+        })
+    );
+    assert_eq!(
+        r.layers[1].tint_color,
+        Some(Color {
+            alpha: 0xFF,
+            red: 0x12,
+            green: 0x34,
+            blue: 0x56
+        })
+    );
+}
-- 
GitLab