diff --git a/CHANGELOG.md b/CHANGELOG.md index 518d4f55020d0914803814c07ebb079d1c389353..f7c54b10edb4fc9dd2192781dd2ab8a3dab06881 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,7 +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`. -- `layers::Layer::id`. +- `Layer::id`, `Layer::width` and `Layer::height`. - Support for 'object'-type properties. - Documentation for map members. - Tests for `tiled_base64_zstandard.tmx`. @@ -33,6 +33,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - `Tile` has been moved into the `tile` module. - `Tileset` has been moved into the `tileset` module. - `Map::get_tileset_by_gid` -> `Map::tileset_by_gid` +- `Layer::tiles` changed from `Vec<Vec<LayerTile>>` to `Vec<LayerTile>`. - Tile now has `image` instead of `images`. ([Issue comment](https://github.com/mapeditor/rs-tiled/issues/103#issuecomment-940773123)) - Tileset now has `image` instead of `images`. - `Image::source` is now a `PathBuf` instead of a `String`. diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index bca6c44c5ec72403234ea0ee62de8cd17da4233e..0ad8f4cb323fb28dd0fc094b29e69c8caeacbbf0 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -1,2 +1,3 @@ * Matthew Hall * Kevin Balz +* Thorbjørn Lindeijer \ No newline at end of file diff --git a/src/layers.rs b/src/layers.rs index 883426a03d4d2c47176f0d420c3982a022f1422b..2f801e2985eb1f58ce24e689c42c46a281f3e14a 100644 --- a/src/layers.rs +++ b/src/layers.rs @@ -45,6 +45,8 @@ impl LayerTile { #[derive(Debug, PartialEq, Clone)] pub struct Layer { pub name: String, + pub width: u32, + pub height: u32, pub opacity: f32, pub visible: bool, pub offset_x: f32, @@ -63,11 +65,10 @@ impl Layer { pub(crate) fn new<R: Read>( parser: &mut EventReader<R>, attrs: Vec<OwnedAttribute>, - width: u32, layer_index: u32, infinite: bool, ) -> Result<Layer, TiledError> { - let ((o, v, ox, oy, n, id), ()) = get_attrs!( + let ((o, v, ox, oy, n, id), (w, h)) = get_attrs!( attrs, optionals: [ ("opacity", opacity, |v:String| v.parse().ok()), @@ -77,9 +78,11 @@ impl Layer { ("name", name, |v| Some(v)), ("id", id, |v:String| v.parse().ok()), ], - required: [], - // this error should never happen since there are no required attrs - TiledError::MalformedAttributes("layer parsing error".to_string()) + required: [ + ("width", width, |v: String| v.parse().ok()), + ("height", height, |v: String| v.parse().ok()), + ], + TiledError::MalformedAttributes("layer parsing error, width and height attributes required".to_string()) ); let mut tiles: LayerData = LayerData::Finite(Default::default()); let mut properties = HashMap::new(); @@ -88,7 +91,7 @@ impl Layer { if infinite { tiles = parse_infinite_data(parser, attrs)?; } else { - tiles = parse_data(parser, attrs, width)?; + tiles = parse_data(parser, attrs)?; } Ok(()) }, @@ -100,6 +103,8 @@ impl Layer { Ok(Layer { name: n.unwrap_or(String::new()), + width: w, + height: h, opacity: o.unwrap_or(1.0), visible: v.unwrap_or(true), offset_x: ox.unwrap_or(0.0), @@ -111,9 +116,10 @@ impl Layer { }) } } + #[derive(Debug, PartialEq, Clone)] pub enum LayerData { - Finite(Vec<Vec<LayerTile>>), + Finite(Vec<LayerTile>), Infinite(HashMap<(i32, i32), Chunk>), } @@ -185,7 +191,7 @@ pub struct Chunk { pub y: i32, pub width: u32, pub height: u32, - pub tiles: Vec<Vec<LayerTile>>, + pub tiles: Vec<LayerTile>, } impl Chunk { @@ -207,7 +213,7 @@ impl Chunk { TiledError::MalformedAttributes("layer must have a name".to_string()) ); - let tiles = parse_data_line(encoding, compression, parser, width)?; + let tiles = parse_data_line(encoding, compression, parser)?; Ok(Chunk { x, diff --git a/src/map.rs b/src/map.rs index e22ff65f977b797ed717b5095d287249e3ff5110..52081e31c2a369c9ab10d2a954439e32eae61921 100644 --- a/src/map.rs +++ b/src/map.rs @@ -112,7 +112,7 @@ impl Map { Ok(()) }, "layer" => |attrs| { - layers.push(Layer::new(parser, attrs, w, layer_index, infinite.unwrap_or(false))?); + layers.push(Layer::new(parser, attrs, layer_index, infinite.unwrap_or(false))?); layer_index += 1; Ok(()) }, diff --git a/src/util.rs b/src/util.rs index 50d28e5ebf87d4282ad27ef3b52fb54c677331c2..500cf3c8c091b66a1facfecd93f06b3b8cd37ee1 100644 --- a/src/util.rs +++ b/src/util.rs @@ -109,7 +109,6 @@ pub(crate) fn parse_infinite_data<R: Read>( pub(crate) fn parse_data<R: Read>( parser: &mut EventReader<R>, attrs: Vec<OwnedAttribute>, - width: u32, ) -> Result<LayerData, TiledError> { let ((e, c), ()) = get_attrs!( attrs, @@ -121,7 +120,7 @@ pub(crate) fn parse_data<R: Read>( TiledError::MalformedAttributes("data must have an encoding and a compression".to_string()) ); - let tiles = parse_data_line(e, c, parser, width)?; + let tiles = parse_data_line(e, c, parser)?; Ok(LayerData::Finite(tiles)) } @@ -130,8 +129,7 @@ pub(crate) fn parse_data_line<R: Read>( encoding: Option<String>, compression: Option<String>, parser: &mut EventReader<R>, - width: u32, -) -> Result<Vec<Vec<LayerTile>>, TiledError> { +) -> Result<Vec<LayerTile>, TiledError> { match (encoding, compression) { (None, None) => { return Err(TiledError::Other( @@ -139,26 +137,26 @@ pub(crate) fn parse_data_line<R: Read>( )) } (Some(e), None) => match e.as_ref() { - "base64" => return parse_base64(parser).map(|v| convert_to_tile(&v, width)), - "csv" => return decode_csv(width, parser), + "base64" => return parse_base64(parser).map(|v| convert_to_tiles(&v)), + "csv" => return decode_csv(parser), e => return Err(TiledError::Other(format!("Unknown encoding format {}", e))), }, (Some(e), Some(c)) => match (e.as_ref(), c.as_ref()) { ("base64", "zlib") => { return parse_base64(parser) .and_then(decode_zlib) - .map(|v| convert_to_tile(&v, width)) + .map(|v| convert_to_tiles(&v)) } ("base64", "gzip") => { return parse_base64(parser) .and_then(decode_gzip) - .map(|v| convert_to_tile(&v, width)) + .map(|v| convert_to_tiles(&v)) } #[cfg(feature = "zstd")] ("base64", "zstd") => { return parse_base64(parser) .and_then(decode_zstd) - .map(|v| convert_to_tile(&v, width)) + .map(|v| convert_to_tiles(&v)) } (e, c) => { return Err(TiledError::Other(format!( @@ -225,24 +223,17 @@ pub(crate) fn decode_zstd(data: Vec<u8>) -> Result<Vec<u8>, TiledError> { } pub(crate) fn decode_csv<R: Read>( - width: u32, parser: &mut EventReader<R>, -) -> Result<Vec<Vec<LayerTile>>, TiledError> { +) -> Result<Vec<LayerTile>, TiledError> { loop { match parser.next().map_err(TiledError::XmlDecodingError)? { XmlEvent::Characters(s) => { - let mut tiles_it = s - .split(&['\n', '\r', ','][0..]) - .filter(|v| v.trim() != "") - .map(|v| v.parse().unwrap()) + let tiles = s + .split(',') + .map(|v| v.trim().parse().unwrap()) .map(LayerTile::new) - .peekable(); - let mut rows = Vec::new(); - while tiles_it.peek().is_some() { - let row = tiles_it.by_ref().take(width as usize).collect(); - rows.push(row); - } - return Ok(rows); + .collect(); + return Ok(tiles); } XmlEvent::EndElement { name, .. } => { if name.local_name == "data" { @@ -254,20 +245,14 @@ pub(crate) fn decode_csv<R: Read>( } } -pub(crate) fn convert_to_tile(all: &Vec<u8>, width: u32) -> Vec<Vec<LayerTile>> { +pub(crate) fn convert_to_tiles(all: &Vec<u8>) -> Vec<LayerTile> { let mut data = Vec::new(); - for chunk in all.chunks((width * 4) as usize) { - let mut row = Vec::new(); - for i in 0..width { - let start: usize = i as usize * 4; - let n = ((chunk[start + 3] as u32) << 24) - + ((chunk[start + 2] as u32) << 16) - + ((chunk[start + 1] as u32) << 8) - + chunk[start] as u32; - let n = LayerTile::new(n); - row.push(n); - } - data.push(row); + for chunk in all.chunks_exact(4) { + let n = chunk[0] as u32 + + ((chunk[1] as u32) << 8) + + ((chunk[2] as u32) << 16) + + ((chunk[3] as u32) << 24); + data.push(LayerTile::new(n)); } data } diff --git a/tests/lib.rs b/tests/lib.rs index eb4aa6d7f5cb3381fbd695898d32e90b6013f4c0..061f4ed2a543d383dfce0f5c9659adb876ebc7ab 100644 --- a/tests/lib.rs +++ b/tests/lib.rs @@ -22,14 +22,12 @@ fn test_gzip_and_zlib_encoded_and_raw_are_the_same() { assert_eq!(z, zstd); if let LayerData::Finite(tiles) = &c.layers[0].tiles { - assert_eq!(tiles.len(), 100); - assert_eq!(tiles[0].len(), 100); - assert_eq!(tiles[99].len(), 100); - assert_eq!(tiles[0][0].gid, 35); - assert_eq!(tiles[1][0].gid, 17); - assert_eq!(tiles[2][0].gid, 0); - assert_eq!(tiles[2][1].gid, 17); - assert!(tiles[99].iter().map(|t| t.gid).all(|g| g == 0)); + assert_eq!(tiles.len(), 100 * 100); + assert_eq!(tiles[0].gid, 35); + assert_eq!(tiles[100].gid, 17); + assert_eq!(tiles[200].gid, 0); + assert_eq!(tiles[200 + 1].gid, 17); + assert!(tiles[9900..9999].iter().map(|t| t.gid).all(|g| g == 0)); } else { assert!(false, "It is wrongly recognised as an infinite map"); } @@ -163,10 +161,10 @@ fn test_flipped_gid() { let r = Map::parse_file("assets/tiled_flipped.tmx").unwrap(); if let LayerData::Finite(tiles) = &r.layers[0].tiles { - let t1 = tiles[0][0]; - let t2 = tiles[0][1]; - let t3 = tiles[1][0]; - let t4 = tiles[1][1]; + let t1 = tiles[0]; + let t2 = tiles[1]; + let t3 = tiles[2]; + let t4 = tiles[3]; assert_eq!(t1.gid, t2.gid); assert_eq!(t2.gid, t3.gid); assert_eq!(t3.gid, t4.gid); @@ -191,10 +189,9 @@ fn test_flipped_gid() { fn test_ldk_export() { let r = Map::parse_file("assets/ldk_tiled_export.tmx").unwrap(); if let LayerData::Finite(tiles) = &r.layers[0].tiles { - assert_eq!(tiles.len(), 8); - assert_eq!(tiles[0].len(), 8); - assert_eq!(tiles[0][0].gid, 0); - assert_eq!(tiles[1][0].gid, 1); + assert_eq!(tiles.len(), 8 * 8); + assert_eq!(tiles[0].gid, 0); + assert_eq!(tiles[8].gid, 1); } else { assert!(false, "It is wrongly recognised as an infinite map"); }