diff --git a/Cargo.lock b/Cargo.lock index 3ce77aac..ebab8efd 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -44,7 +44,7 @@ name = "approx" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -90,7 +90,7 @@ dependencies = [ [[package]] name = "autocfg" -version = "0.1.2" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -98,7 +98,7 @@ name = "backtrace" version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "autocfg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "autocfg 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", "backtrace-sys 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)", "cfg-if 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.54 (registry+https://github.com/rust-lang/crates.io-index)", @@ -142,6 +142,11 @@ name = "block" version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "build_const" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "byteorder" version = "1.3.1" @@ -251,7 +256,7 @@ version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "num-integer 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", "time 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -366,6 +371,14 @@ dependencies = [ "libc 0.2.54 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "crc" +version = "1.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "build_const 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "crc32fast" version = "1.2.0" @@ -545,7 +558,7 @@ version = "0.19.8" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "euclid_macros 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -815,6 +828,11 @@ dependencies = [ "gl_generator 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "half" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "harfbuzz" version = "0.3.1" @@ -855,6 +873,11 @@ dependencies = [ "serde 1.0.91 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "hex" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "humantime" version = "1.2.0" @@ -875,7 +898,7 @@ dependencies = [ "num-derive 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", "num-iter 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)", "num-rational 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", "png 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)", "scoped_threadpool 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -889,7 +912,7 @@ dependencies = [ "lzw 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", "num-iter 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)", "num-rational 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", "png 0.14.1 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1017,7 +1040,7 @@ name = "line_drawing" version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1059,7 +1082,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "arrayvec 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)", "euclid 0.19.8 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1070,6 +1093,16 @@ dependencies = [ "lyon_geom 0.12.4 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "lzma-rs" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "crc 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "lzw" version = "0.10.0" @@ -1158,6 +1191,15 @@ name = "nodrop" version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "nom" +version = "4.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "memchr 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "version_check 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "num" version = "0.1.42" @@ -1165,7 +1207,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "num-integer 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)", "num-iter 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1183,7 +1225,7 @@ name = "num-integer" version = "0.1.39" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1192,7 +1234,7 @@ version = "0.1.37" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "num-integer 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1201,7 +1243,7 @@ version = "0.1.42" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "num-integer 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1210,13 +1252,16 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "num-integer 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "num-traits" -version = "0.2.6" +version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "autocfg 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", +] [[package]] name = "num_cpus" @@ -1271,7 +1316,7 @@ name = "ordered-float" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1371,6 +1416,18 @@ dependencies = [ "usvg 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "pathfinder_flash" +version = "0.1.0" +dependencies = [ + "pathfinder_geometry 0.3.0", + "pathfinder_gl 0.1.0", + "pathfinder_gpu 0.1.0", + "pathfinder_renderer 0.1.0", + "swf-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "swf-tree 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "pathfinder_geometry" version = "0.3.0" @@ -1610,7 +1667,7 @@ name = "rand" version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "autocfg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "autocfg 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", "libc 0.2.54 (registry+https://github.com/rust-lang/crates.io-index)", "rand_chacha 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1628,7 +1685,7 @@ name = "rand_chacha" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "autocfg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "autocfg 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", "rand_core 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1689,7 +1746,7 @@ name = "rand_pcg" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "autocfg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", + "autocfg 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", "rand_core 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2029,6 +2086,58 @@ dependencies = [ "phf 0.7.24 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "swf-fixed" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "serde 1.0.91 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "swf-parser" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "half 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "inflate 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "lzma-rs 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", + "memchr 2.2.0 (registry+https://github.com/rust-lang/crates.io-index)", + "nom 4.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 1.1.6 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.39 (registry+https://github.com/rust-lang/crates.io-index)", + "swf-fixed 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "swf-tree 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "swf-tree" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "hex 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.91 (registry+https://github.com/rust-lang/crates.io-index)", + "swf-fixed 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "swf_basic" +version = "0.1.0" +dependencies = [ + "gl 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)", + "pathfinder_flash 0.1.0", + "pathfinder_geometry 0.3.0", + "pathfinder_gl 0.1.0", + "pathfinder_gpu 0.1.0", + "pathfinder_renderer 0.1.0", + "sdl2 0.32.2 (registry+https://github.com/rust-lang/crates.io-index)", + "sdl2-sys 0.32.6 (registry+https://github.com/rust-lang/crates.io-index)", + "swf-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "swf-tree 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "syn" version = "0.15.34" @@ -2388,13 +2497,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum arrayvec 0.4.10 (registry+https://github.com/rust-lang/crates.io-index)" = "92c7fb76bc8826a8b33b4ee5bb07a247a81e76764ab4d55e8f73e3a4d8808c71" "checksum ascii 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a5fc969a8ce2c9c0c4b0429bb8431544f6658283c8326ba5ff8c762b75369335" "checksum atty 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "9a7d5b8723950951411ee34d271d99dddcc2035a16ab25310ea2c8cfd4369652" -"checksum autocfg 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a6d640bee2da49f60a4068a7fae53acde8982514ab7bae8b8cea9e88cbcfd799" +"checksum autocfg 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "0e49efa51329a5fd37e7c79db4621af617cd4e3e5bc224939808d076077077bf" "checksum backtrace 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)" = "f106c02a3604afcdc0df5d36cc47b44b55917dbaf3d808f71c163a0ddba64637" "checksum backtrace-sys 0.1.28 (registry+https://github.com/rust-lang/crates.io-index)" = "797c830ac25ccc92a7f8a7b9862bde440715531514594a6154e3d4a54dd769b6" "checksum base64 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0b25d992356d2eb0ed82172f5248873db5560c4721f564b13cb5193bda5e668e" "checksum bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "228047a76f468627ca71776ecdebd732a3423081fcf5125585bcd7c49886ce12" "checksum blake2-rfc 0.2.18 (registry+https://github.com/rust-lang/crates.io-index)" = "5d6d530bdd2d52966a6d03b7a964add7ae1a288d25214066fd4b600f0f796400" "checksum block 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" +"checksum build_const 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "39092a32794787acd8525ee150305ff051b0aa6cc2abaf193924f5ab05425f39" "checksum byteorder 1.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a019b10a2a7cdeb292db131fc8113e57ea2a908f6e7894b0c3c671893b65dbeb" "checksum cc 1.0.36 (registry+https://github.com/rust-lang/crates.io-index)" = "a0c56216487bb80eec9c4516337b2588a4f2a2290d72a1416d930e4dcdb0c90d" "checksum cesu8 1.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" @@ -2413,6 +2523,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum core-foundation-sys 0.6.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e7ca8a5221364ef15ce201e8ed2f609fc312682a8f4e0e3d4aa5879764e0fa3b" "checksum core-graphics 0.17.3 (registry+https://github.com/rust-lang/crates.io-index)" = "56790968ab1c8a1202a102e6de05fc6e1ec87da99e4e93e9a7d13efbfc1e95a9" "checksum core-text 13.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d95a72b5e50e549969dd88eff3047495fe5b8c6f028635442c2b708be707e669" +"checksum crc 1.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d663548de7f5cca343f1e0a48d14dcfb0e9eb4e079ec58883b7251539fa10aeb" "checksum crc32fast 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ba125de2af0df55319f41944744ad91c71113bf74a4646efff39afe1f6842db1" "checksum crossbeam-channel 0.3.8 (registry+https://github.com/rust-lang/crates.io-index)" = "0f0ed1a4de2235cabda8558ff5840bffb97fcb64c97827f354a451307df5f72b" "checksum crossbeam-deque 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f739f8c5363aca78cfb059edf753d8f0d36908c348f3d8d1503f03d8b75d9cf3" @@ -2458,10 +2569,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum glutin_gles2_sys 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "89996c30857ae1b4de4b5189abf1ea822a20a9fe9e1c93e5e7b862ff0bdd5cdf" "checksum glutin_glx_sys 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "1290a5ca5e46fcfa7f66f949cc9d9194b2cb6f2ed61892c8c2b82343631dba57" "checksum glutin_wgl_sys 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "f801bbc91efc22dd1c4818a47814fc72bf74d024510451b119381579bfa39021" +"checksum half 1.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9353c2a89d550b58fa0061d8ed8d002a7d8cdf2494eb0e432859bd3a9e543836" "checksum harfbuzz 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "46f7426266a5ece3e49eae6f48e602c0f8c39917354a847eac9c06437dcde8da" "checksum harfbuzz-sys 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "e1042ab0b3e7bc1ff64f7f5935778b644ff2194a1cae5ec52167127d3fd23961" "checksum harfbuzz_rs 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "534c8e9b15d8db6e69654b07dad955f4132757194e7d2bba620d38cf08996088" "checksum hashbrown 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "3bae29b6653b3412c2e71e9d486db9f9df5d701941d86683005efb9f2d28e3da" +"checksum hex 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "805026a5d0141ffc30abb3be3173848ad46a1b1664fe632428479619a3644d77" "checksum humantime 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "3ca7e5f2e110db35f93b837c81797f3714500b81d517bf20c431b16d3ca4f114" "checksum image 0.19.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ebdff791af04e30089bde8ad2a632b86af433b40c04db8d70ad4b21487db7a6a" "checksum image 0.21.1 (registry+https://github.com/rust-lang/crates.io-index)" = "293e54ce142a936a39da748ba8178ae6aa1914b82d846a4278f11590c89bf116" @@ -2487,6 +2600,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum log 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "c84ec4b527950aa83a329754b01dbe3f58361d1c5efacd1f6d68c494d08a17c6" "checksum lyon_geom 0.12.4 (registry+https://github.com/rust-lang/crates.io-index)" = "0ea0ba5f8d2d91d6d895aca54d1ec0d84ddfa4826f33fbfe8abb39f08f9e4153" "checksum lyon_path 0.12.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e9dc8e0746b7cca11960b602f7fe037bb067746a01eab4aa502fed1494544843" +"checksum lzma-rs 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9550ba35a4d6bb6be7f273bce93af3a3141c517bf7d7298763a7149e1bdb9af5" "checksum lzw 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7d947cbb889ed21c2a84be6ffbaebf5b4e0f4340638cba0444907e38b56be084" "checksum malloc_buf 0.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb" "checksum matches 0.1.8 (registry+https://github.com/rust-lang/crates.io-index)" = "7ffc5c5338469d4d3ea17d269fa8ea3512ad247247c30bd2df69e68309ed0a08" @@ -2498,13 +2612,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum nfd 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8e752e3c216bc8a491c5b59fa46da10f1379ae450b19ac688e07f4bb55042e98" "checksum nix 0.13.0 (registry+https://github.com/rust-lang/crates.io-index)" = "46f0f3210768d796e8fa79ec70ee6af172dacbe7147f5e69be5240a47778302b" "checksum nodrop 0.1.13 (registry+https://github.com/rust-lang/crates.io-index)" = "2f9667ddcc6cc8a43afc9b7917599d7216aa09c463919ea32c59ed6cac8bc945" +"checksum nom 4.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "2ad2a91a8e869eeb30b9cb3119ae87773a8f4ae617f41b1eb9c154b2905f7bd6" "checksum num 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "4703ad64153382334aa8db57c637364c322d3372e097840c72000dabdcf6156e" "checksum num-derive 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "eafd0b45c5537c3ba526f79d3e75120036502bebacbb3f3220914067ce39dbf2" "checksum num-integer 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)" = "e83d528d2677f0518c570baf2b7abdcf0cd2d248860b68507bdcb3e91d4c0cea" "checksum num-iter 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)" = "af3fdbbc3291a5464dc57b03860ec37ca6bf915ed6ee385e7c6c052c422b2124" "checksum num-rational 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "ee314c74bd753fc86b4780aa9475da469155f3848473a261d2d18e35245a784e" "checksum num-rational 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "4e96f040177bb3da242b5b1ecf3f54b5d5af3efbbfb18608977a5d2767b22f10" -"checksum num-traits 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0b3a5d7cc97d6d30d8b9bc8fa19bf45349ffe46241e8816f50f62f6d6aaabee1" +"checksum num-traits 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "6ba9a427cfca2be13aa6f6403b0b7e7368fe982bfa16fccc450ce74c46cd9b32" "checksum num_cpus 1.10.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1a23f0ed30a54abaa0c7e83b1d2d87ada7c3c23078d1d87815af3e3b6385fbba" "checksum numtoa 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b8f8bdf33df195859076e54ab11ee78a1b208382d3a26ec40d142ffc1ecc49ef" "checksum objc 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "31d20fd2b37e07cf5125be68357b588672e8cefe9a96f8c17a9d46053b3e590d" @@ -2576,6 +2691,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum strsim 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "8ea5119cdb4c55b55d432abb513a0429384878c15dde60cc77b1c99de1a95a6a" "checksum svgdom 0.17.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9ddce601e49ed213b0126ff4172cd9f5f8dba5f1df2277ecbe0e298f9865baba" "checksum svgtypes 0.4.4 (registry+https://github.com/rust-lang/crates.io-index)" = "444c882c28925ae0585df228a90f9951569588646ceca4753560de93cdd02258" +"checksum swf-fixed 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "bf3c575807f18641c5e079568b53630a46baf441da57caccd9ced9a9ee70f941" +"checksum swf-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "5b6349830c1ed5d26b97d801e3fb8664556e12a01560e3479e3961e8f5675a05" +"checksum swf-tree 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c075cea0328cc5f6ee694bcd1c43534d1186a730d79258b10735f8f08e2f1baa" "checksum syn 0.15.34 (registry+https://github.com/rust-lang/crates.io-index)" = "a1393e4a97a19c01e900df2aec855a29f71cf02c402e2f443b8d2747c25c5dbe" "checksum synstructure 0.10.1 (registry+https://github.com/rust-lang/crates.io-index)" = "73687139bf99285483c96ac0add482c3776528beac1d97d444f6e91f203a2015" "checksum term 0.5.2 (registry+https://github.com/rust-lang/crates.io-index)" = "edd106a334b7657c10b7c540a0106114feadeb4dc314513e97df481d5d966f42" diff --git a/Cargo.toml b/Cargo.toml index 83f67f54..305b8133 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -12,9 +12,11 @@ members = [ "examples/canvas_moire", "examples/canvas_text", "examples/lottie_basic", + "examples/swf_basic", "geometry", "gl", "gpu", + "flash", "lottie", "metal", "renderer", diff --git a/examples/swf_basic/Cargo.toml b/examples/swf_basic/Cargo.toml new file mode 100644 index 00000000..eb64fe17 --- /dev/null +++ b/examples/swf_basic/Cargo.toml @@ -0,0 +1,28 @@ +[package] +name = "swf_basic" +version = "0.1.0" +authors = ["Jon Hardie "] +edition = "2018" + +[dependencies] +gl = "0.6" +sdl2 = "0.32" +sdl2-sys = "0.32" + +swf-parser = "0.7.0" +swf-tree = "0.7.0" + +[dependencies.pathfinder_flash] +path = "../../flash" + +[dependencies.pathfinder_geometry] +path = "../../geometry" + +[dependencies.pathfinder_gl] +path = "../../gl" + +[dependencies.pathfinder_gpu] +path = "../../gpu" + +[dependencies.pathfinder_renderer] +path = "../../renderer" diff --git a/examples/swf_basic/src/main.rs b/examples/swf_basic/src/main.rs new file mode 100644 index 00000000..ed5e9f9d --- /dev/null +++ b/examples/swf_basic/src/main.rs @@ -0,0 +1,135 @@ +// pathfinder/examples/swf_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. + +use pathfinder_geometry::basic::vector::{Vector2F, Vector2I}; +use pathfinder_geometry::basic::rect::RectF; +use pathfinder_gl::{GLDevice, GLVersion}; +use pathfinder_gpu::resources::FilesystemResourceLoader; +use pathfinder_renderer::concurrent::rayon::RayonExecutor; +use pathfinder_renderer::concurrent::scene_proxy::SceneProxy; +use pathfinder_renderer::gpu::renderer::Renderer; +use pathfinder_renderer::gpu::options::{DestFramebuffer, RendererOptions}; +use pathfinder_renderer::options::{RenderTransform, BuildOptions}; +use sdl2::event::Event; +use sdl2::keyboard::Keycode; +use sdl2::video::GLProfile; +use pathfinder_renderer::scene::Scene; +use pathfinder_flash::{draw_paths_into_scene, process_swf_tags}; +use std::env; +use std::fs::read; +use pathfinder_geometry::basic::transform2d::Transform2DF; + +fn main() { + let swf_bytes; + if let Some(path) = env::args().skip(1).next() { + match read(path) { + Ok(bytes) => { + swf_bytes = bytes; + }, + Err(e) => panic!(e) + } + } else { + // NOTE(jon): This is a version of the ghostscript tiger graphic flattened to a single + // layer with no overlapping shapes. This is how artwork is 'natively' created in the Flash + // authoring tool when an artist just draws directly onto the canvas (without 'object' mode + // turned on, which is the default). + // Subsequent shapes with different fills will knock out existing fills where they overlap. + // A downside of this in current pathfinder is that cracks are visible between shape fills - + // especially obvious if you set the context clear color to #ff00ff or similar. + + // Common speculation as to why the swf format stores vector graphics in this way says that + // it is to save on file-size bytes, however in the case of our tiger, it results in a + // larger file than the layered version, since the overlapping shapes and strokes create + // a lot more geometry. I think a more likely explanation for the choice is that it was + // done to reduce overdraw in the software rasterizer running on late 90's era hardware? + // Indeed, this mode gives pathfinders' occlusion culling pass nothing to do! + //let default_tiger = include_bytes!("../swf/tiger-flat.swf"); + + // NOTE(jon): This is a version of the same graphic cut and pasted into the Flash authoring + // tool from the SVG version loaded in Illustrator. When layered graphics are pasted + // into Flash, by default they retain their layering, expressed as groups. + // They are still presented as being on a single timeline layer. + // They will be drawn back to front in much the same way as the SVG version. + + let default_tiger = include_bytes!("../swf/tiger.swf"); + swf_bytes = Vec::from(&default_tiger[..]); + } + + let (_, movie): (_, swf_tree::Movie) = swf_parser::parsers::movie::parse_movie(&swf_bytes[..]).unwrap(); + + // Set up SDL2. + let sdl_context = sdl2::init().unwrap(); + let video = sdl_context.video().unwrap(); + + // Make sure we have at least a GL 3.0 context. Pathfinder requires this. + let gl_attributes = video.gl_attr(); + gl_attributes.set_context_profile(GLProfile::Core); + gl_attributes.set_context_version(3, 3); + + // process swf scene + // TODO(jon): Since swf is a streaming format, this really wants to be a lazy iterator over + // swf frames eventually. + let (library, stage) = process_swf_tags(&movie); + + // Open a window. + let window_size = Vector2I::new(stage.width(), stage.height()); + let window = video.window("Minimal example", window_size.x() as u32, window_size.y() as u32) + .opengl() + .allow_highdpi() + .build() + .unwrap(); + + let pixel_size = Vector2I::new( + window.drawable_size().0 as i32, + window.drawable_size().1 as i32 + ); + let device_pixel_ratio = pixel_size.x() as f32 / window_size.x() as f32; + + // Create the GL context, and make it current. + let gl_context = window.gl_create_context().unwrap(); + gl::load_with(|name| video.gl_get_proc_address(name) as *const _); + window.gl_make_current(&gl_context).unwrap(); + + // Create a Pathfinder renderer. + let mut renderer = Renderer::new( + GLDevice::new(GLVersion::GL3, 0), + &FilesystemResourceLoader::locate(), + DestFramebuffer::full_window(pixel_size), + RendererOptions { background_color: Some(stage.background_color()) } + ); + // Clear to swf stage background color. + let mut scene = Scene::new(); + scene.set_view_box(RectF::new( + Vector2F::default(), + Vector2F::new( + stage.width() as f32 * device_pixel_ratio, + stage.height() as f32 * device_pixel_ratio) + )); + draw_paths_into_scene(&library, &mut scene); + + // Render the canvas to screen. + let scene = SceneProxy::from_scene(scene, RayonExecutor); + let mut build_options = BuildOptions::default(); + let scale_transform = Transform2DF::from_scale( + Vector2F::new(device_pixel_ratio, device_pixel_ratio) + ); + build_options.transform = RenderTransform::Transform2D(scale_transform); + scene.build_and_render(&mut renderer, build_options); + + window.gl_swap_window(); + // Wait for a keypress. + let mut event_pump = sdl_context.event_pump().unwrap(); + loop { + match event_pump.wait_event() { + Event::Quit {..} | Event::KeyDown { keycode: Some(Keycode::Escape), .. } => return, + _ => {} + } + } +} diff --git a/examples/swf_basic/swf/tiger-flat.swf b/examples/swf_basic/swf/tiger-flat.swf new file mode 100644 index 00000000..70127188 Binary files /dev/null and b/examples/swf_basic/swf/tiger-flat.swf differ diff --git a/examples/swf_basic/swf/tiger.swf b/examples/swf_basic/swf/tiger.swf new file mode 100644 index 00000000..9bb17c65 Binary files /dev/null and b/examples/swf_basic/swf/tiger.swf differ diff --git a/flash/Cargo.toml b/flash/Cargo.toml new file mode 100644 index 00000000..2dff909f --- /dev/null +++ b/flash/Cargo.toml @@ -0,0 +1,21 @@ +[package] +name = "pathfinder_flash" +version = "0.1.0" +authors = ["Jon Hardie "] +edition = "2018" + +[dependencies] +swf-parser = "0.7.0" +swf-tree = "0.7.0" + +[dependencies.pathfinder_geometry] +path = "../geometry" + +[dependencies.pathfinder_renderer] +path = "../renderer" + +[dependencies.pathfinder_gl] +path = "../gl" + +[dependencies.pathfinder_gpu] +path = "../gpu" diff --git a/flash/src/lib.rs b/flash/src/lib.rs new file mode 100644 index 00000000..34bf5518 --- /dev/null +++ b/flash/src/lib.rs @@ -0,0 +1,206 @@ +// pathfinder/flash/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. + +use std::ops::Add; +use pathfinder_geometry::color::{ColorU, ColorF}; +use pathfinder_geometry::outline::{Outline, Contour}; +use pathfinder_geometry::basic::vector::Vector2F; +use pathfinder_geometry::stroke::{OutlineStrokeToFill, StrokeStyle}; +use pathfinder_renderer::scene::{PathObject, Scene}; + +use swf_tree; +use swf_tree::tags::SetBackgroundColor; +use swf_tree::{Tag, SRgb8, Movie}; + +use crate::shapes::{GraphicLayers, PaintOrLine}; + +mod shapes; + +type SymbolId = u16; + +// In swf, most values are specified in a fixed point format known as "twips" or twentieths of +// a pixel. We store twips in their integer form, as if we were to convert them to floating point +// at the beginning of the pipeline it's easy to start running into precision errors when we add +// coordinate deltas and then try and compare coords for equality. + +#[derive(Copy, Clone, Debug, PartialEq)] +struct Twips(i32); + +impl Twips { + // Divide twips by 20 to get the f32 value, just to be used once all processing + // of the swf coords is completed and we want to output. + fn as_f32(&self) -> f32 { + self.0 as f32 / 20.0 + } +} + +impl Add for Twips { + type Output = Twips; + fn add(self, rhs: Twips) -> Self { + Twips(self.0 + rhs.0) + } +} + +#[derive(Copy, Clone, Debug, PartialEq)] +struct Point2 { + x: T, + y: T +} + +impl Point2 { + fn as_f32(self: Point2) -> Point2 { + Point2 { + x: self.x.as_f32(), + y: self.y.as_f32(), + } + } +} + +impl Add for Point2 { + type Output = Self; + fn add(self, rhs: Self) -> Self { + Point2 { x: self.x + rhs.x, y: self.y + rhs.y } + } +} + +enum Symbol { + Graphic(GraphicLayers), + // Timeline, // TODO(jon) +} + +pub struct Stage { + // TODO(jon): Support some kind of lazy frames iterator. + // frames: Timeline, + background_color: SRgb8, + width: i32, + height: i32, +} + +impl Stage { + pub fn width(&self) -> i32 { + self.width + } + + pub fn height(&self) -> i32 { + self.height + } + + pub fn background_color(&self) -> ColorF { + ColorU { + r: self.background_color.r, + g: self.background_color.g, + b: self.background_color.b, + a: 255, + }.to_f32() + } +} + + +pub struct SymbolLibrary(Vec); + +impl SymbolLibrary { + fn add_symbol(&mut self, symbol: Symbol) { + self.0.push(symbol); + } + + fn symbols(&self) -> &Vec { + &self.0 + } +} + +pub fn process_swf_tags(movie: &Movie) -> (SymbolLibrary, Stage) { + let mut symbol_library = SymbolLibrary(Vec::new()); + let stage_width = Twips(movie.header.frame_size.x_max); + let stage_height = Twips(movie.header.frame_size.y_max); + // let num_frames = movie.header.frame_count; + + let mut stage = Stage { + // frames: Timeline(Vec::new()), // TODO(jon) + background_color: SRgb8 { + r: 255, + g: 255, + b: 255 + }, + width: stage_width.as_f32() as i32, + height: stage_height.as_f32() as i32, + }; + + for tag in &movie.tags { + match tag { + Tag::SetBackgroundColor(SetBackgroundColor { color }) => { + stage.background_color = *color; + }, + Tag::DefineShape(shape) => { + symbol_library.add_symbol(Symbol::Graphic(shapes::decode_shape(&shape))); + // We will assume that symbol ids just go up, and are 1 based. + let symbol_id: SymbolId = shape.id; + debug_assert!(symbol_id as usize == symbol_library.0.len()); + } + _ => () + } + } + (symbol_library, stage) +} + +#[allow(irrefutable_let_patterns)] +pub fn draw_paths_into_scene(library: &SymbolLibrary, scene: &mut Scene) { + for symbol in library.symbols() { + // NOTE: Right now symbols only contain graphics. + if let Symbol::Graphic(graphic) = symbol { + for style_layer in graphic.layers() { + let mut path = Outline::new(); + let paint_id = scene.push_paint(&style_layer.fill()); + + for shape in style_layer.shapes() { + let mut contour = Contour::new(); + let Point2 { x, y } = shape.outline.first().unwrap().from.as_f32(); + contour.push_endpoint(Vector2F::new(x, y)); + for segment in &shape.outline { + let Point2 { x, y } = segment.to.as_f32(); + match segment.ctrl { + Some(ctrl) => { + let Point2 { x: ctrl_x, y: ctrl_y } = ctrl.as_f32(); + contour.push_quadratic( + Vector2F::new(ctrl_x, ctrl_y), + Vector2F::new(x, y) + ); + } + None => { + contour.push_endpoint(Vector2F::new(x, y)); + }, + } + } + if shape.is_closed() { + // NOTE: I'm not sure if this really does anything in this context, + // since all our closed shapes already have coincident start and end points. + contour.close(); + } + path.push_contour(contour); + } + + if let PaintOrLine::Line(line) = style_layer.kind() { + let mut stroke_to_fill = OutlineStrokeToFill::new(&path, StrokeStyle { + line_width: line.width.as_f32(), + line_cap: line.cap, + line_join: line.join, + }); + stroke_to_fill.offset(); + path = stroke_to_fill.into_outline(); + } + + scene.push_path(PathObject::new( + path, + paint_id, + String::new() + )); + } + } + } +} diff --git a/flash/src/shapes.rs b/flash/src/shapes.rs new file mode 100644 index 00000000..73d55603 --- /dev/null +++ b/flash/src/shapes.rs @@ -0,0 +1,615 @@ +use pathfinder_renderer::paint::Paint; +use pathfinder_geometry::stroke::{LineJoin, LineCap}; +use crate::{Twips, Point2}; +use std::mem; +use std::cmp::Ordering; +use swf_tree::{ + FillStyle, + StraightSRgba8, + LineStyle, + fill_styles, + JoinStyle, + CapStyle, + join_styles, + ShapeRecord, + shape_records, + Vector2D +}; +use pathfinder_geometry::color::ColorU; +use swf_tree::tags::DefineShape; + +#[derive(Clone, Copy, Debug)] +pub(crate) struct LineSegment { + pub(crate) from: Point2, + pub(crate) to: Point2, + pub(crate) ctrl: Option>, +} + +impl LineSegment { + fn reverse(&mut self) { + let tmp = self.from; + self.from = self.to; + self.to = tmp; + } +} + +#[derive(Copy, Clone, PartialEq, Debug)] +pub(crate) enum LineDirection { + Left, + Right, +} + +impl LineDirection { + fn reverse(&mut self) { + *self = match self { + LineDirection::Right => LineDirection::Left, + LineDirection::Left => LineDirection::Right + }; + } +} + +#[derive(Clone, Debug)] +pub(crate) struct Shape { + pub(crate) outline: Vec, // Could be Vec<(start, end)> + direction: LineDirection, + reversed: bool, +} + +impl Shape { + pub fn new_with_direction(direction: LineDirection) -> Shape { + Shape { + direction, + outline: Vec::new(), + reversed: false, + } + } + + fn prepend_shape(&mut self, shape: &mut Shape) { + shape.append_shape(&self); + mem::swap(&mut self.outline, &mut shape.outline); + } + + fn append_shape(&mut self, shape: &Shape) { + self.outline.extend_from_slice(&shape.outline); + } + + fn add_line_segment(&mut self, segment: LineSegment) { + self.outline.push(segment); + } + + #[inline] + fn len(&self) -> usize { + self.outline.len() + } + + #[inline] + fn first(&self) -> &LineSegment { + &self.outline.first().unwrap() + } + + #[inline] + fn last(&self) -> &LineSegment { + &self.outline.last().unwrap() + } + + #[inline] + fn comes_before(&self, other: &Shape) -> bool { + self.last().to == other.first().from + } + + #[inline] + fn comes_after(&self, other: &Shape) -> bool { + self.first().from == other.last().to + } + + #[inline] + pub(crate) fn is_closed(&self) -> bool { + self.len() > 1 && self.comes_after(self) + } + + fn reverse(&mut self) { + self.reversed = !self.reversed; + self.direction.reverse(); + for segment in &mut self.outline { + segment.reverse(); + } + self.outline.reverse(); + } +} + +pub(crate) struct SwfLineStyle { + color: Paint, + pub(crate) width: Twips, + pub(crate) join: LineJoin, + pub(crate) cap: LineCap, +} + +pub(crate) enum PaintOrLine { + Paint(Paint), + Line(SwfLineStyle), +} + +pub(crate) struct StyleLayer { + fill: PaintOrLine, + // TODO(jon): Maybe shapes are actually slices into a single buffer, then we don't + // need to realloc anything, we're just shuffling shapes around? + shapes: Vec, +} + +impl StyleLayer { + pub(crate) fn kind(&self) -> &PaintOrLine { + &self.fill + } + + fn is_fill(&self) -> bool { + match &self.fill { + PaintOrLine::Paint(_) => true, + PaintOrLine::Line(_) => false, + } + } + + pub(crate) fn fill(&self) -> Paint { + match &self.fill { + PaintOrLine::Paint(paint) => *paint, + PaintOrLine::Line(line) => line.color, + } + } + + fn push_new_shape(&mut self, direction: LineDirection) { + if let Some(prev_shape) = self.shapes.last_mut() { + // Check that the previous shape was actually used, otherwise reuse it. + if prev_shape.len() != 0 { + self.shapes.push(Shape::new_with_direction(direction)) + } else { + prev_shape.direction = direction; + } + } else { + self.shapes.push(Shape::new_with_direction(direction)) + } + } + + pub(crate) fn shapes(&self) -> &Vec { + &self.shapes + } + + fn shapes_mut(&mut self) -> &mut Vec { + &mut self.shapes + } + + fn current_shape_mut(&mut self) -> &mut Shape { + self.shapes.last_mut().unwrap() + } + + fn consolidate_edges(&mut self) { + // Reverse left fill shape fragments in place. + { + self.shapes + .iter_mut() + .filter(|frag| frag.direction == LineDirection::Left) + .for_each(|frag| frag.reverse()); + } + + // Sort shapes into [closed...open] + if self.is_fill() { + // I think sorting is only necessary when we want to have closed shapes, + // lines don't really need this? + self.shapes.sort_unstable_by(|a, b| { + match (a.is_closed(), b.is_closed()) { + (true, true) | (false, false) => Ordering::Equal, + (true, false) => Ordering::Less, + (false, true) => Ordering::Greater, + } + }); + } + + // A cursor at the index of the first unclosed shape, if any. + let first_open_index = self.shapes + .iter() + .position(|frag| !frag.is_closed()); + + if let Some(first_open_index) = first_open_index { + if self.shapes.len() - first_open_index >= 2 { + // TODO(jon): This might be sped up by doing it in a way that we don't have + // to allocate more vecs? + // Also, maybe avoid path reversal, and just flag the path as reversed and iterate it + // backwards. + let unmatched_pieces = find_matches(first_open_index, &mut self.shapes, false); + if let Some(mut unmatched_pieces) = unmatched_pieces { + if self.is_fill() { + // If they didn't match before, they're probably parts of inner shapes + // and should be reversed again so they have correct winding + let unclosed = find_matches(0, &mut unmatched_pieces, true); + // If it's a shape we should always be able to close it. + debug_assert!(unclosed.is_none()); + } + for dropped in &mut unmatched_pieces { + dropped.reverse(); + } + self.shapes.extend_from_slice(&unmatched_pieces); + } + // FIXME(jon): Sometimes we don't get the correct winding of internal closed shapes, + // need to figure out why this happens. + } + } + } +} + + +fn get_new_styles<'a>( + fills: &'a Vec, + lines: &'a Vec +) -> impl Iterator + 'a { + // This enforces the order that fills and line groupings are added in. + // Fills always come first. + fills.iter().filter_map(|fill_style| { + match fill_style { + FillStyle::Solid( + fill_styles::Solid { + color: StraightSRgba8 { + r, + g, + b, + a + } + } + ) => { + Some(PaintOrLine::Paint(Paint { + color: ColorU { + r: *r, + g: *g, + b: *b, + a: *a + } + })) + }, + _ => unimplemented!("Unimplemented fill style") + } + }).chain( + lines.iter().filter_map(|LineStyle { + width, + fill, + join, + start_cap, + end_cap: _, + /* + TODO(jon): Handle these cases? + pub no_h_scale: bool, + pub no_v_scale: bool, + pub no_close: bool, + pub pixel_hinting: bool, + */ + .. + }| { + if let FillStyle::Solid(fill_styles::Solid { + color: StraightSRgba8 { + r, + g, + b, + a + } + }) = fill { + // NOTE: PathFinder doesn't support different cap styles for start and end of + // strokes, so lets assume that they're always the same for the inputs we care about. + // Alternately, we split a line in two with a diff cap style for each. + // assert_eq!(start_cap, end_cap); + Some(PaintOrLine::Line(SwfLineStyle { + width: Twips(*width as i32), + color: Paint { color: ColorU { r: *r, g: *g, b: *b, a: *a } }, + join: match join { + JoinStyle::Bevel => LineJoin::Bevel, + JoinStyle::Round => LineJoin::Round, + JoinStyle::Miter(join_styles::Miter { limit }) => { + LineJoin::Miter(*limit as f32) + }, + }, + cap: match start_cap { + CapStyle::None => LineCap::Butt, + CapStyle::Square => LineCap::Square, + CapStyle::Round => LineCap::Round, + }, + })) + } else { + unimplemented!("unimplemented line fill style"); + } + }) + ) +} + +pub(crate) fn decode_shape(shape: &DefineShape) -> GraphicLayers { + let DefineShape { + shape, + // id, + // has_fill_winding, NOTE(jon): Could be important for some inputs? + // has_non_scaling_strokes, + // has_scaling_strokes, + .. + } = shape; + let mut graphic = GraphicLayers::new(); + let mut current_line_style = None; + let mut current_left_fill = None; + let mut current_right_fill = None; + let mut prev_pos = None; + + let mut some_fill_set = false; + let mut both_fills_set; + let mut both_fills_same = false; + let mut both_fills_set_and_same = false; + + // Create style groups for initially specified fills and lines. + for fills_or_line in get_new_styles(&shape.initial_styles.fill, &shape.initial_styles.line) { + match fills_or_line { + PaintOrLine::Paint(fill) => graphic.begin_fill_style(fill), + PaintOrLine::Line(line) => graphic.begin_line_style(line), + } + } + + for record in &shape.records { + match record { + ShapeRecord::StyleChange( + shape_records::StyleChange { + move_to, + new_styles, + line_style, + left_fill, + right_fill, + } + ) => { + // Start a whole new style grouping. + if let Some(new_style) = new_styles { + // Consolidate current style grouping and begin a new one. + graphic.end_style_group(); + graphic.begin_style_group(); + for fills_or_line in get_new_styles(&new_style.fill, &new_style.line) { + match fills_or_line { + PaintOrLine::Paint(fill) => graphic.begin_fill_style(fill), + PaintOrLine::Line(line) => graphic.begin_line_style(line), + } + } + } + + // If there's a change in right fill + if let Some(fill_id) = right_fill { + if *fill_id == 0 { + current_right_fill = None; + } else { + current_right_fill = Some(*fill_id); + graphic + .with_fill_style_mut(*fill_id) + .unwrap() + .push_new_shape(LineDirection::Right); + } + } + // If there's a change in left fill + if let Some(fill_id) = left_fill { + if *fill_id == 0 { + current_left_fill = None; + } else { + current_left_fill = Some(*fill_id); + graphic + .with_fill_style_mut(*fill_id) + .unwrap() + .push_new_shape(LineDirection::Left); + } + } + + some_fill_set = current_left_fill.is_some() || current_right_fill.is_some(); + both_fills_set = current_left_fill.is_some() && current_right_fill.is_some(); + both_fills_same = current_left_fill == current_right_fill; + both_fills_set_and_same = both_fills_set && both_fills_same; + + // If there's a change in line style + if let Some(style_id) = line_style { + if *style_id == 0 { + current_line_style = None; + } else { + current_line_style = Some(*style_id); + graphic + .with_line_style_mut(*style_id) + .unwrap() + .push_new_shape(LineDirection::Right); + } + } + + // Move to, start new shape fragments with the current styles. + if let Some(Vector2D { x, y }) = move_to { + let to: Point2 = Point2 { x: Twips(*x), y: Twips(*y) }; + prev_pos = Some(to); + + // If we didn't start a new shape for the current fill due to a fill + // style change earlier, we definitely want to start a new shape now, + // since each move_to command indicates a new shape fragment. + if let Some(current_right_fill) = current_right_fill { + graphic + .with_fill_style_mut(current_right_fill) + .unwrap() + .push_new_shape(LineDirection::Right); + } + if let Some(current_left_fill) = current_left_fill { + graphic + .with_fill_style_mut(current_left_fill) + .unwrap() + .push_new_shape(LineDirection::Left); + } + if let Some(current_line_style) = current_line_style { + // TODO(jon): Does the direction of this line depend on the current + // fill directions? + graphic + .with_line_style_mut(current_line_style) + .unwrap() + .push_new_shape(LineDirection::Right); + } + } + }, + ShapeRecord::Edge( + shape_records::Edge { + delta, + control_delta, + } + ) => { + let from = prev_pos.unwrap(); + let to = Point2 { + x: from.x + Twips(delta.x), + y: from.y + Twips(delta.y) + }; + prev_pos = Some(to); + let new_segment = LineSegment { + from, + to, + ctrl: control_delta.map(|Vector2D { x, y }| { + Point2 { + x: from.x + Twips(x), + y: from.y + Twips(y), + } + }), + }; + if some_fill_set && !both_fills_same { + for fill_id in [ + current_right_fill, + current_left_fill + ].iter() { + if let Some(fill_id) = fill_id { + graphic + .with_fill_style_mut(*fill_id) + .unwrap() + .current_shape_mut() + .add_line_segment(new_segment); + } + } + } else if both_fills_set_and_same { + for (fill_id, direction) in [ + (current_right_fill, LineDirection::Right), + (current_left_fill, LineDirection::Left) + ].iter() { + // NOTE: If both left and right fill are set the same, + // then we don't record the edge as part of the current shape; + // it's will just be an internal stroke inside an otherwise solid + // shape, and recording these edges as part of the shape means that + // we can't determine the closed shape outline later. + if let Some(fill_id) = fill_id { + graphic + .with_fill_style_mut(*fill_id) + .unwrap() + .push_new_shape(*direction); + } + } + } + if let Some(current_line_style) = current_line_style { + graphic + .with_line_style_mut(current_line_style) + .unwrap() + .current_shape_mut() + .add_line_segment(new_segment); + } + } + } + } + // NOTE: Consolidate current group of styles, joining edges of shapes/strokes where + // possible and forming closed shapes. In swf, all filled shapes should always be closed, + // so there will always be a solution for joining shape line segments together so that + // the start point and end point are coincident. + graphic.end_style_group(); + graphic +} + +fn find_matches( + mut first_open_index: usize, + shapes: &mut Vec, + reverse: bool +) -> Option> { + let mut dropped_pieces = None; + while first_open_index < shapes.len() { + // Take the last unclosed value, and try to join it onto + // one of the other unclosed values. + let mut last = shapes.pop().unwrap(); + if reverse { + last.reverse(); + } + let mut found_match = false; + for i in first_open_index..shapes.len() { + let fragment = &mut shapes[i]; + if last.comes_after(fragment) { + // NOTE(jon): We do realloc quite a bit here, I wonder if it's worth trying + // to avoid that? Could do it with another level of indirection, where an outline + // is a list of fragments. + + // println!("app ({}, {})", last.reversed, fragment.reversed); + fragment.append_shape(&last); + found_match = true; + } else if last.comes_before(fragment) { + // println!("pre ({}, {})", last.reversed, fragment.reversed); + fragment.prepend_shape(&mut last); + found_match = true; + } + if found_match { + if fragment.is_closed() { + // Move the shape that was just closed to the left side of the current slice, + // and advance the cursor. + shapes.swap(first_open_index, i); + first_open_index += 1; + } + break; + } + } + if !found_match { + // Have we tried matching a reversed version of this segment? + // move last back onto the array, it will never be closed, presumably because + // it's a set of line segments rather than a shape that needs to be closed. + let dropped_pieces: &mut Vec = dropped_pieces.get_or_insert(Vec::new()); + dropped_pieces.push(last); + } + } + dropped_pieces +} + +pub(crate) struct GraphicLayers { + style_layers: Vec, + base_layer_offset: usize, + stroke_layer_offset: Option, +} + +impl GraphicLayers { + fn new() -> GraphicLayers { + GraphicLayers { style_layers: Vec::new(), stroke_layer_offset: None, base_layer_offset: 0 } + } + + fn begin_style_group(&mut self) { + self.stroke_layer_offset = None; + self.base_layer_offset = self.style_layers.len(); + } + + fn begin_fill_style(&mut self, fill: Paint) { + self.style_layers.push(StyleLayer { fill: PaintOrLine::Paint(fill), shapes: Vec::new() }) + } + + fn begin_line_style(&mut self, line: SwfLineStyle) { + if self.stroke_layer_offset.is_none() { + self.stroke_layer_offset = Some(self.style_layers.len()); + } + self.style_layers.push(StyleLayer { fill: PaintOrLine::Line(line), shapes: Vec::new() }) + } + + fn with_fill_style_mut(&mut self, fill_id: usize) -> Option<&mut StyleLayer> { + self.style_layers.get_mut(self.base_layer_offset + fill_id - 1) + } + + fn with_line_style_mut(&mut self, line_id: usize) -> Option<&mut StyleLayer> { + self.style_layers.get_mut((self.stroke_layer_offset.unwrap() + line_id) - 1) + } + + pub(crate) fn layers(&self) -> &Vec { + &self.style_layers + } + + fn end_style_group(&mut self) { + for style_layer in &mut self.style_layers[self.base_layer_offset..] { + // There can be an unused style group at the end of each layer, which we should remove. + if let Some(last) = style_layer.shapes().last() { + if last.len() == 0 { + style_layer.shapes_mut().pop(); + } + } + style_layer.consolidate_edges(); + } + } +} + diff --git a/flash/src/timeline.rs b/flash/src/timeline.rs new file mode 100644 index 00000000..662dba00 --- /dev/null +++ b/flash/src/timeline.rs @@ -0,0 +1,32 @@ +struct PlacementInfo { + symbol_id: u32, + translate_x: Twips, + translate_y: Twips, +} + +struct Timeline(Vec); + +impl Timeline { + fn first(&self) -> &Frame { + &self.0[0] + } + + fn last(&self) -> &Frame { + &self.0[self.0.len() - 1] + } + + fn first_mut(&mut self) -> &mut Frame { + &mut self.0[0] + } + + fn last_mut(&mut self) -> &mut Frame { + let last = self.0.len() - 1; + &mut self.0[last] + } +} + +struct Frame { + duration_frames_initial: u16, + duration_remaining_frames: u16, + placements: Vec +}