diff --git a/Cargo.lock b/Cargo.lock index 04c6960e..3a3ce277 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -957,6 +957,13 @@ dependencies = [ "cfg-if 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "lottie_basic" +version = "0.1.0" +dependencies = [ + "pathfinder_lottie 0.1.0", +] + [[package]] name = "lyon_geom" version = "0.12.4" @@ -1256,6 +1263,14 @@ dependencies = [ "rustache 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "pathfinder_lottie" +version = "0.1.0" +dependencies = [ + "serde 1.0.91 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "pathfinder_magicleap_demo" version = "0.1.0" @@ -1753,6 +1768,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" name = "serde" version = "1.0.91" source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "serde_derive 1.0.91 (registry+https://github.com/rust-lang/crates.io-index)", +] [[package]] name = "serde_derive" diff --git a/Cargo.toml b/Cargo.toml index 506e2089..3163c514 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,9 +8,11 @@ members = [ "examples/canvas_minimal", "examples/canvas_moire", "examples/canvas_text", + "examples/lottie_basic", "geometry", "gl", "gpu", + "lottie", "renderer", "simd", "svg", diff --git a/examples/lottie_basic/Cargo.toml b/examples/lottie_basic/Cargo.toml new file mode 100644 index 00000000..82bc984a --- /dev/null +++ b/examples/lottie_basic/Cargo.toml @@ -0,0 +1,10 @@ +[package] +name = "lottie_basic" +version = "0.1.0" +authors = ["Patrick Walton "] +edition = "2018" + +[dependencies] + +[dependencies.pathfinder_lottie] +path = "../../lottie" diff --git a/examples/lottie_basic/src/main.rs b/examples/lottie_basic/src/main.rs new file mode 100644 index 00000000..58eb2a18 --- /dev/null +++ b/examples/lottie_basic/src/main.rs @@ -0,0 +1,23 @@ +// pathfinder/examples/lottie_basic/src/main.rs +// +// Copyright © 2019 The Pathfinder Project Developers. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Experimental example for reading Lottie animations. This is very incomplete. + +use pathfinder_lottie::Lottie; +use std::env; +use std::fs::File; +use std::io::BufReader; + +fn main() { + let path = env::args().skip(1).next().unwrap(); + let file = BufReader::new(File::open(path).unwrap()); + let lottie = Lottie::from_reader(file).unwrap(); + println!("{:#?}", lottie); +} diff --git a/lottie/Cargo.toml b/lottie/Cargo.toml new file mode 100644 index 00000000..a7b6b262 --- /dev/null +++ b/lottie/Cargo.toml @@ -0,0 +1,12 @@ +[package] +name = "pathfinder_lottie" +version = "0.1.0" +authors = ["Patrick Walton "] +edition = "2018" + +[dependencies] +serde_json = "1.0" + +[dependencies.serde] +version = "1.0" +features = ["derive"] diff --git a/lottie/src/lib.rs b/lottie/src/lib.rs new file mode 100644 index 00000000..4b062cdb --- /dev/null +++ b/lottie/src/lib.rs @@ -0,0 +1,313 @@ +// pathfinder/lottie/src/lib.rs +// +// Copyright © 2019 The Pathfinder Project Developers. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Experimental support for Lottie. This is very incomplete. + +use serde::{Deserialize, Serialize}; +use serde_json::Error as JSONError; +use std::io::Read; + +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct Lottie { + #[serde(rename = "v")] + pub version: String, + #[serde(rename = "fr")] + pub frame_rate: i64, + #[serde(rename = "ip")] + pub in_point: i64, + #[serde(rename = "op")] + pub out_point: i64, + #[serde(rename = "w")] + pub width: f64, + #[serde(rename = "h")] + pub height: f64, + #[serde(rename = "ddd")] + pub three_d: i64, + pub assets: Vec, + pub layers: Vec, +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct Asset {} + +// FIXME(pcwalton): Using an untagged enum is a botch here. There actually is a tag: it's just an +// integer, which `serde_json` doesn't support natively. +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(untagged)] +pub enum Layer { + Shape { + #[serde(rename = "ddd")] + three_d: i64, + #[serde(rename = "ind")] + index: i64, + #[serde(rename = "nm")] + name: String, + #[serde(rename = "ks")] + transform: Transform, + #[serde(rename = "ao")] + auto_orient: i64, + #[serde(rename = "ip")] + in_point: i64, + #[serde(rename = "op")] + out_point: i64, + #[serde(rename = "st")] + start_time: i64, + #[serde(rename = "bm")] + blend_mode: i64, + #[serde(rename = "sr")] + stretch: i64, + #[serde(rename = "ln")] + #[serde(default)] + layer_id: Option, + shapes: Vec, + }, +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct Transform { + #[serde(rename = "p")] + pub position: MultidimensionalPropertyValue, + #[serde(rename = "a")] + pub anchor_point: MultidimensionalPropertyValue, + #[serde(rename = "s")] + pub scale: MultidimensionalPropertyValue, + #[serde(rename = "r")] + pub rotation: PropertyValue, + #[serde(rename = "o")] + #[serde(default)] + pub opacity: Option, + #[serde(rename = "px")] + #[serde(default)] + pub position_x: Option, + #[serde(rename = "py")] + #[serde(default)] + pub position_y: Option, + #[serde(rename = "pz")] + #[serde(default)] + pub position_z: Option, + #[serde(rename = "sk")] + #[serde(default)] + pub skew: Option, + #[serde(rename = "sa")] + #[serde(default)] + pub skew_axis: Option, +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(untagged)] +pub enum PropertyValue { + Value { + #[serde(rename = "k")] + value: f32, + #[serde(rename = "x")] + #[serde(default)] + expression: Option, + #[serde(rename = "ix")] + #[serde(default)] + index: Option, + }, + KeyframedValue { + #[serde(rename = "k")] + keyframes: Vec, + #[serde(rename = "x")] + #[serde(default)] + expression: Option, + #[serde(rename = "ix")] + #[serde(default)] + index: Option, + }, +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct KeyframeValue { + #[serde(rename = "s")] + #[serde(default)] + pub start: Option>, + #[serde(rename = "t")] + pub time: i64, + #[serde(rename = "i")] + #[serde(default)] + pub interpolation: Option, +} + +#[derive(Clone, Copy, Debug, Serialize, Deserialize)] +pub struct Interpolation { + pub x: f32, + pub y: f32, +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct OffsetInterpolation { + pub x: Vec, + pub y: Vec, +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct OffsetKeyframe { + #[serde(rename = "s")] + #[serde(default)] + pub start: Option>, + #[serde(rename = "t")] + pub time: i64, + #[serde(rename = "i")] + #[serde(default)] + pub in_value: Option, + #[serde(rename = "o")] + #[serde(default)] + pub out_value: Option, +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(untagged)] +pub enum MultidimensionalPropertyValue { + Value { + #[serde(rename = "k")] + value: Vec, + #[serde(rename = "x")] + #[serde(default)] + expression: Option, + #[serde(rename = "ix")] + #[serde(default)] + index: Option, + }, + KeyframedValue { + #[serde(rename = "k")] + keyframes: Vec, + #[serde(rename = "x")] + #[serde(default)] + expression: Option, + #[serde(rename = "ix")] + #[serde(default)] + index: Option, + #[serde(rename = "ti")] + #[serde(default)] + in_tangent: Option, + #[serde(rename = "to")] + #[serde(default)] + out_tangent: Option, + }, +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(tag = "ty")] +pub enum Shape { + #[serde(rename = "gr")] + Group { + #[serde(rename = "it")] + items: Vec, + #[serde(rename = "nm")] + name: String, + }, + #[serde(rename = "sh")] + Shape { + #[serde(rename = "ks")] + vertices: ShapeVertices, + #[serde(rename = "d")] + #[serde(default)] + direction: Option, + }, + #[serde(rename = "fl")] + Fill { + #[serde(rename = "nm")] + #[serde(default)] + name: Option, + #[serde(rename = "o")] + #[serde(default)] + opacity: Option, + #[serde(rename = "c")] + color: MultidimensionalPropertyValue, + }, + #[serde(rename = "tr")] + Transform { + #[serde(rename = "r")] + rotation: PropertyValue, + #[serde(rename = "sk")] + skew: PropertyValue, + #[serde(rename = "sa")] + skew_axis: PropertyValue, + #[serde(rename = "p")] + position: MultidimensionalPropertyValue, + #[serde(rename = "a")] + anchor_point: MultidimensionalPropertyValue, + #[serde(rename = "s")] + scale: MultidimensionalPropertyValue, + }, + #[serde(other)] + Unimplemented, +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +#[serde(untagged)] +pub enum ShapeVertices { + Shape { + #[serde(rename = "k")] + value: ShapeProperty, + #[serde(rename = "x")] + #[serde(default)] + expression: Option, + #[serde(rename = "ix")] + #[serde(default)] + index: Option, + #[serde(rename = "a")] + animated: i64, + }, + ShapeKeyframed { + #[serde(rename = "k")] + value: Vec, + #[serde(rename = "x")] + #[serde(default)] + expression: Option, + #[serde(rename = "ix")] + #[serde(default)] + index: Option, + #[serde(rename = "a")] + animated: i64, + #[serde(rename = "ti")] + #[serde(default)] + in_tangent: Vec, + #[serde(rename = "to")] + #[serde(default)] + out_tangent: Vec, + }, +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct ShapeProperty { + #[serde(rename = "c")] + pub closed: bool, + #[serde(rename = "i")] + pub in_points: Vec<[f32; 2]>, + #[serde(rename = "o")] + pub out_points: Vec<[f32; 2]>, + #[serde(rename = "v")] + pub vertices: Vec<[f32; 2]>, +} + +#[derive(Clone, Debug, Serialize, Deserialize)] +pub struct ShapeKeyframeProperty { + #[serde(rename = "s")] + #[serde(default)] + pub start: Vec>, + #[serde(rename = "t")] + pub time: i64, + #[serde(rename = "i")] + #[serde(default)] + pub in_value: Option, + #[serde(rename = "o")] + #[serde(default)] + pub out_value: Option, +} + +impl Lottie { + #[inline] + pub fn from_reader(reader: R) -> Result where R: Read { + serde_json::from_reader(reader) + } +}