···
48
48
]
49
49
50
50
[[package]]
51
51
+
name = "anstream"
52
52
+
version = "0.6.15"
53
53
+
source = "registry+https://github.com/rust-lang/crates.io-index"
54
54
+
checksum = "64e15c1ab1f89faffbf04a634d5e1962e9074f2741eef6d97f3c4e322426d526"
55
55
+
dependencies = [
56
56
+
"anstyle",
57
57
+
"anstyle-parse",
58
58
+
"anstyle-query",
59
59
+
"anstyle-wincon",
60
60
+
"colorchoice",
61
61
+
"is_terminal_polyfill",
62
62
+
"utf8parse",
63
63
+
]
64
64
+
65
65
+
[[package]]
66
66
+
name = "anstyle"
67
67
+
version = "1.0.10"
68
68
+
source = "registry+https://github.com/rust-lang/crates.io-index"
69
69
+
checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9"
70
70
+
71
71
+
[[package]]
72
72
+
name = "anstyle-parse"
73
73
+
version = "0.2.6"
74
74
+
source = "registry+https://github.com/rust-lang/crates.io-index"
75
75
+
checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9"
76
76
+
dependencies = [
77
77
+
"utf8parse",
78
78
+
]
79
79
+
80
80
+
[[package]]
81
81
+
name = "anstyle-query"
82
82
+
version = "1.1.1"
83
83
+
source = "registry+https://github.com/rust-lang/crates.io-index"
84
84
+
checksum = "6d36fc52c7f6c869915e99412912f22093507da8d9e942ceaf66fe4b7c14422a"
85
85
+
dependencies = [
86
86
+
"windows-sys",
87
87
+
]
88
88
+
89
89
+
[[package]]
90
90
+
name = "anstyle-wincon"
91
91
+
version = "3.0.4"
92
92
+
source = "registry+https://github.com/rust-lang/crates.io-index"
93
93
+
checksum = "5bf74e1b6e971609db8ca7a9ce79fd5768ab6ae46441c572e46cf596f59e57f8"
94
94
+
dependencies = [
95
95
+
"anstyle",
96
96
+
"windows-sys",
97
97
+
]
98
98
+
99
99
+
[[package]]
51
100
name = "anyhow"
52
101
version = "1.0.82"
53
102
source = "registry+https://github.com/rust-lang/crates.io-index"
···
58
107
version = "1.0.1"
59
108
source = "registry+https://github.com/rust-lang/crates.io-index"
60
109
checksum = "170433209e817da6aae2c51aa0dd443009a613425dd041ebfb2492d1c4c11a25"
110
110
+
111
111
+
[[package]]
112
112
+
name = "arrayref"
113
113
+
version = "0.3.9"
114
114
+
source = "registry+https://github.com/rust-lang/crates.io-index"
115
115
+
checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb"
116
116
+
117
117
+
[[package]]
118
118
+
name = "arrayvec"
119
119
+
version = "0.7.6"
120
120
+
source = "registry+https://github.com/rust-lang/crates.io-index"
121
121
+
checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50"
61
122
62
123
[[package]]
63
124
name = "ascii"
···
152
213
checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c"
153
214
154
215
[[package]]
216
216
+
name = "bytemuck"
217
217
+
version = "1.21.0"
218
218
+
source = "registry+https://github.com/rust-lang/crates.io-index"
219
219
+
checksum = "ef657dfab802224e671f5818e9a4935f9b1957ed18e58292690cc39e7a4092a3"
220
220
+
221
221
+
[[package]]
222
222
+
name = "byteorder-lite"
223
223
+
version = "0.1.0"
224
224
+
source = "registry+https://github.com/rust-lang/crates.io-index"
225
225
+
checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495"
226
226
+
227
227
+
[[package]]
155
228
name = "bytes"
156
229
version = "1.10.0"
157
230
source = "registry+https://github.com/rust-lang/crates.io-index"
···
239
312
source = "git+https://github.com/robbert-vdh/clap-sys.git?branch=feature%2Fcstr-macro#523a5f8a8dd021ec99e7d6e0c0ebe7741a3da9d4"
240
313
241
314
[[package]]
315
315
+
name = "color_quant"
316
316
+
version = "1.1.0"
317
317
+
source = "registry+https://github.com/rust-lang/crates.io-index"
318
318
+
checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b"
319
319
+
320
320
+
[[package]]
321
321
+
name = "colorchoice"
322
322
+
version = "1.0.3"
323
323
+
source = "registry+https://github.com/rust-lang/crates.io-index"
324
324
+
checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990"
325
325
+
326
326
+
[[package]]
242
327
name = "console"
243
328
version = "0.15.8"
244
329
source = "registry+https://github.com/rust-lang/crates.io-index"
···
266
351
version = "0.8.6"
267
352
source = "registry+https://github.com/rust-lang/crates.io-index"
268
353
checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f"
354
354
+
355
355
+
[[package]]
356
356
+
name = "core_maths"
357
357
+
version = "0.1.1"
358
358
+
source = "registry+https://github.com/rust-lang/crates.io-index"
359
359
+
checksum = "77745e017f5edba1a9c1d854f6f3a52dac8a12dd5af5d2f54aecf61e43d80d30"
360
360
+
dependencies = [
361
361
+
"libm",
362
362
+
]
269
363
270
364
[[package]]
271
365
name = "cpufeatures"
···
352
446
]
353
447
354
448
[[package]]
449
449
+
name = "data-url"
450
450
+
version = "0.3.1"
451
451
+
source = "registry+https://github.com/rust-lang/crates.io-index"
452
452
+
checksum = "5c297a1c74b71ae29df00c3e22dd9534821d60eb9af5a0192823fa2acea70c2a"
453
453
+
454
454
+
[[package]]
355
455
name = "deranged"
356
456
version = "0.3.11"
357
457
source = "registry+https://github.com/rust-lang/crates.io-index"
···
401
501
checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f"
402
502
403
503
[[package]]
504
504
+
name = "env_filter"
505
505
+
version = "0.1.3"
506
506
+
source = "registry+https://github.com/rust-lang/crates.io-index"
507
507
+
checksum = "186e05a59d4c50738528153b83b0b0194d3a29507dfec16eccd4b342903397d0"
508
508
+
dependencies = [
509
509
+
"log",
510
510
+
"regex",
511
511
+
]
512
512
+
513
513
+
[[package]]
514
514
+
name = "env_logger"
515
515
+
version = "0.11.6"
516
516
+
source = "registry+https://github.com/rust-lang/crates.io-index"
517
517
+
checksum = "dcaee3d8e3cfc3fd92428d477bc97fc29ec8716d180c0d74c643bb26166660e0"
518
518
+
dependencies = [
519
519
+
"anstream",
520
520
+
"anstyle",
521
521
+
"env_filter",
522
522
+
"humantime",
523
523
+
"log",
524
524
+
]
525
525
+
526
526
+
[[package]]
404
527
name = "equivalent"
405
528
version = "1.0.2"
406
529
source = "registry+https://github.com/rust-lang/crates.io-index"
407
530
checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
408
531
409
532
[[package]]
533
533
+
name = "fdeflate"
534
534
+
version = "0.3.7"
535
535
+
source = "registry+https://github.com/rust-lang/crates.io-index"
536
536
+
checksum = "1e6853b52649d4ac5c0bd02320cddc5ba956bdb407c4b75a2c6b75bf51500f8c"
537
537
+
dependencies = [
538
538
+
"simd-adler32",
539
539
+
]
540
540
+
541
541
+
[[package]]
410
542
name = "flate2"
411
543
version = "1.0.35"
412
544
source = "registry+https://github.com/rust-lang/crates.io-index"
···
415
547
"crc32fast",
416
548
"miniz_oxide 0.8.4",
417
549
]
550
550
+
551
551
+
[[package]]
552
552
+
name = "float-cmp"
553
553
+
version = "0.9.0"
554
554
+
source = "registry+https://github.com/rust-lang/crates.io-index"
555
555
+
checksum = "98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4"
418
556
419
557
[[package]]
420
558
name = "fnv"
···
423
561
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
424
562
425
563
[[package]]
564
564
+
name = "fontconfig-parser"
565
565
+
version = "0.5.7"
566
566
+
source = "registry+https://github.com/rust-lang/crates.io-index"
567
567
+
checksum = "c1fcfcd44ca6e90c921fee9fa665d530b21ef1327a4c1a6c5250ea44b776ada7"
568
568
+
dependencies = [
569
569
+
"roxmltree 0.20.0",
570
570
+
]
571
571
+
572
572
+
[[package]]
573
573
+
name = "fontdb"
574
574
+
version = "0.22.0"
575
575
+
source = "registry+https://github.com/rust-lang/crates.io-index"
576
576
+
checksum = "a3a6f9af55fb97ad673fb7a69533eb2f967648a06fa21f8c9bb2cd6d33975716"
577
577
+
dependencies = [
578
578
+
"fontconfig-parser",
579
579
+
"log",
580
580
+
"memmap2",
581
581
+
"slotmap",
582
582
+
"tinyvec",
583
583
+
"ttf-parser",
584
584
+
]
585
585
+
586
586
+
[[package]]
426
587
name = "generic-array"
427
588
version = "0.14.7"
428
589
source = "registry+https://github.com/rust-lang/crates.io-index"
···
446
607
]
447
608
448
609
[[package]]
610
610
+
name = "gif"
611
611
+
version = "0.13.1"
612
612
+
source = "registry+https://github.com/rust-lang/crates.io-index"
613
613
+
checksum = "3fb2d69b19215e18bb912fa30f7ce15846e301408695e44e0ef719f1da9e19f2"
614
614
+
dependencies = [
615
615
+
"color_quant",
616
616
+
"weezl",
617
617
+
]
618
618
+
619
619
+
[[package]]
449
620
name = "gimli"
450
621
version = "0.28.1"
451
622
source = "registry+https://github.com/rust-lang/crates.io-index"
···
533
704
checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9"
534
705
535
706
[[package]]
707
707
+
name = "humantime"
708
708
+
version = "2.1.0"
709
709
+
source = "registry+https://github.com/rust-lang/crates.io-index"
710
710
+
checksum = "9a3a5bfb195931eeb336b2a7b4d761daec841b97f947d34394601737a7bba5e4"
711
711
+
712
712
+
[[package]]
536
713
name = "iana-time-zone"
537
714
version = "0.1.60"
538
715
source = "registry+https://github.com/rust-lang/crates.io-index"
···
556
733
]
557
734
558
735
[[package]]
736
736
+
name = "image-webp"
737
737
+
version = "0.1.3"
738
738
+
source = "registry+https://github.com/rust-lang/crates.io-index"
739
739
+
checksum = "f79afb8cbee2ef20f59ccd477a218c12a93943d075b492015ecb1bb81f8ee904"
740
740
+
dependencies = [
741
741
+
"byteorder-lite",
742
742
+
"quick-error",
743
743
+
]
744
744
+
745
745
+
[[package]]
746
746
+
name = "imagesize"
747
747
+
version = "0.13.0"
748
748
+
source = "registry+https://github.com/rust-lang/crates.io-index"
749
749
+
checksum = "edcd27d72f2f071c64249075f42e205ff93c9a4c5f6c6da53e79ed9f9832c285"
750
750
+
751
751
+
[[package]]
559
752
name = "indexmap"
560
753
version = "2.7.1"
561
754
source = "registry+https://github.com/rust-lang/crates.io-index"
···
588
781
]
589
782
590
783
[[package]]
784
784
+
name = "is_terminal_polyfill"
785
785
+
version = "1.70.1"
786
786
+
source = "registry+https://github.com/rust-lang/crates.io-index"
787
787
+
checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf"
788
788
+
789
789
+
[[package]]
591
790
name = "itertools"
592
791
version = "0.12.1"
593
792
source = "registry+https://github.com/rust-lang/crates.io-index"
···
612
811
]
613
812
614
813
[[package]]
814
814
+
name = "kurbo"
815
815
+
version = "0.11.1"
816
816
+
source = "registry+https://github.com/rust-lang/crates.io-index"
817
817
+
checksum = "89234b2cc610a7dd927ebde6b41dd1a5d4214cffaef4cf1fb2195d592f92518f"
818
818
+
dependencies = [
819
819
+
"arrayvec",
820
820
+
"smallvec",
821
821
+
]
822
822
+
823
823
+
[[package]]
615
824
name = "lazy_static"
616
825
version = "1.4.0"
617
826
source = "registry+https://github.com/rust-lang/crates.io-index"
···
624
833
checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a"
625
834
626
835
[[package]]
836
836
+
name = "libm"
837
837
+
version = "0.2.11"
838
838
+
source = "registry+https://github.com/rust-lang/crates.io-index"
839
839
+
checksum = "8355be11b20d696c8f18f6cc018c4e372165b1fa8126cef092399c9951984ffa"
840
840
+
841
841
+
[[package]]
627
842
name = "lock_api"
628
843
version = "0.4.12"
629
844
source = "registry+https://github.com/rust-lang/crates.io-index"
···
635
850
636
851
[[package]]
637
852
name = "log"
638
638
-
version = "0.4.25"
853
853
+
version = "0.4.26"
639
854
source = "registry+https://github.com/rust-lang/crates.io-index"
640
640
-
checksum = "04cbf5b083de1c7e0222a7a51dbfdba1cbe1c6ab0b15e29fff3f6c077fd9cd9f"
855
855
+
checksum = "30bde2b3dc3671ae49d8e2e9f044c7c005836e7a023ee57cffa25ab82764bb9e"
641
856
642
857
[[package]]
643
858
name = "malloc_buf"
···
649
864
]
650
865
651
866
[[package]]
867
867
+
name = "measure_time"
868
868
+
version = "0.9.0"
869
869
+
source = "registry+https://github.com/rust-lang/crates.io-index"
870
870
+
checksum = "51c55d61e72fc3ab704396c5fa16f4c184db37978ae4e94ca8959693a235fc0e"
871
871
+
dependencies = [
872
872
+
"log",
873
873
+
]
874
874
+
875
875
+
[[package]]
652
876
name = "memchr"
653
877
version = "2.7.2"
654
878
source = "registry+https://github.com/rust-lang/crates.io-index"
655
879
checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d"
880
880
+
881
881
+
[[package]]
882
882
+
name = "memmap2"
883
883
+
version = "0.9.5"
884
884
+
source = "registry+https://github.com/rust-lang/crates.io-index"
885
885
+
checksum = "fd3f7eed9d3848f8b98834af67102b720745c4ec028fcd0aa0239277e7de374f"
886
886
+
dependencies = [
887
887
+
"libc",
888
888
+
]
656
889
657
890
[[package]]
658
891
name = "midi-consts"
···
685
918
checksum = "b3b1c9bd4fe1f0f8b387f6eb9eb3b4a1aa26185e5750efb9140301703f62cd1b"
686
919
dependencies = [
687
920
"adler2",
921
921
+
"simd-adler32",
688
922
]
689
923
690
924
[[package]]
···
893
1127
]
894
1128
895
1129
[[package]]
1130
1130
+
name = "pico-args"
1131
1131
+
version = "0.5.0"
1132
1132
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1133
1133
+
checksum = "5be167a7af36ee22fe3115051bc51f6e6c7054c9348e28deb4f49bd6f705a315"
1134
1134
+
1135
1135
+
[[package]]
896
1136
name = "plain"
897
1137
version = "0.2.3"
898
1138
source = "registry+https://github.com/rust-lang/crates.io-index"
899
1139
checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6"
1140
1140
+
1141
1141
+
[[package]]
1142
1142
+
name = "png"
1143
1143
+
version = "0.17.16"
1144
1144
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1145
1145
+
checksum = "82151a2fc869e011c153adc57cf2789ccb8d9906ce52c0b39a6b5697749d7526"
1146
1146
+
dependencies = [
1147
1147
+
"bitflags 1.3.2",
1148
1148
+
"crc32fast",
1149
1149
+
"fdeflate",
1150
1150
+
"flate2",
1151
1151
+
"miniz_oxide 0.8.4",
1152
1152
+
]
900
1153
901
1154
[[package]]
902
1155
name = "portable-atomic"
···
926
1179
]
927
1180
928
1181
[[package]]
1182
1182
+
name = "quick-error"
1183
1183
+
version = "2.0.1"
1184
1184
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1185
1185
+
checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3"
1186
1186
+
1187
1187
+
[[package]]
929
1188
name = "quote"
930
1189
version = "1.0.36"
931
1190
source = "registry+https://github.com/rust-lang/crates.io-index"
···
1038
1297
checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56"
1039
1298
1040
1299
[[package]]
1300
1300
+
name = "resvg"
1301
1301
+
version = "0.44.0"
1302
1302
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1303
1303
+
checksum = "4a325d5e8d1cebddd070b13f44cec8071594ab67d1012797c121f27a669b7958"
1304
1304
+
dependencies = [
1305
1305
+
"gif",
1306
1306
+
"image-webp",
1307
1307
+
"log",
1308
1308
+
"pico-args",
1309
1309
+
"rgb",
1310
1310
+
"svgtypes",
1311
1311
+
"tiny-skia",
1312
1312
+
"usvg",
1313
1313
+
"zune-jpeg",
1314
1314
+
]
1315
1315
+
1316
1316
+
[[package]]
1317
1317
+
name = "rgb"
1318
1318
+
version = "0.8.50"
1319
1319
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1320
1320
+
checksum = "57397d16646700483b67d2dd6511d79318f9d057fdbd21a4066aeac8b41d310a"
1321
1321
+
dependencies = [
1322
1322
+
"bytemuck",
1323
1323
+
]
1324
1324
+
1325
1325
+
[[package]]
1041
1326
name = "ring"
1042
1327
version = "0.17.9"
1043
1328
source = "registry+https://github.com/rust-lang/crates.io-index"
···
1058
1343
checksum = "3cd14fd5e3b777a7422cca79358c57a8f6e3a703d9ac187448d0daf220c2407f"
1059
1344
1060
1345
[[package]]
1346
1346
+
name = "roxmltree"
1347
1347
+
version = "0.20.0"
1348
1348
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1349
1349
+
checksum = "6c20b6793b5c2fa6553b250154b78d6d0db37e72700ae35fad9387a46f487c97"
1350
1350
+
1351
1351
+
[[package]]
1061
1352
name = "rust-analyzer"
1062
1353
version = "0.0.1"
1063
1354
source = "registry+https://github.com/rust-lang/crates.io-index"
···
1117
1408
checksum = "80af6f9131f277a45a3fba6ce8e2258037bb0477a67e610d3c1fe046ab31de47"
1118
1409
1119
1410
[[package]]
1411
1411
+
name = "rustybuzz"
1412
1412
+
version = "0.18.0"
1413
1413
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1414
1414
+
checksum = "c85d1ccd519e61834798eb52c4e886e8c2d7d698dd3d6ce0b1b47eb8557f1181"
1415
1415
+
dependencies = [
1416
1416
+
"bitflags 2.8.0",
1417
1417
+
"bytemuck",
1418
1418
+
"core_maths",
1419
1419
+
"log",
1420
1420
+
"smallvec",
1421
1421
+
"ttf-parser",
1422
1422
+
"unicode-bidi-mirroring",
1423
1423
+
"unicode-ccc",
1424
1424
+
"unicode-properties",
1425
1425
+
"unicode-script",
1426
1426
+
]
1427
1427
+
1428
1428
+
[[package]]
1120
1429
name = "ryu"
1121
1430
version = "1.0.17"
1122
1431
source = "registry+https://github.com/rust-lang/crates.io-index"
···
1228
1537
"chrono-human-duration",
1229
1538
"console",
1230
1539
"docopt",
1540
1540
+
"env_logger",
1231
1541
"getrandom",
1232
1542
"handlebars",
1233
1543
"hound",
1234
1544
"indicatif",
1235
1545
"itertools",
1546
1546
+
"log",
1547
1547
+
"measure_time",
1236
1548
"midly",
1237
1549
"nanoid",
1238
1550
"nih_plug",
1239
1551
"once_cell",
1240
1552
"rand",
1241
1241
-
"roxmltree",
1553
1553
+
"resvg",
1554
1554
+
"roxmltree 0.19.0",
1242
1555
"rust-analyzer",
1243
1556
"serde",
1244
1557
"serde_cbor",
···
1247
1560
"strum",
1248
1561
"strum_macros",
1249
1562
"svg",
1563
1563
+
"tiny-skia",
1250
1564
"tiny_http",
1251
1565
"ureq",
1252
1566
"wasm-bindgen",
···
1260
1574
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
1261
1575
1262
1576
[[package]]
1577
1577
+
name = "simd-adler32"
1578
1578
+
version = "0.3.7"
1579
1579
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1580
1580
+
checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe"
1581
1581
+
1582
1582
+
[[package]]
1583
1583
+
name = "simplecss"
1584
1584
+
version = "0.2.2"
1585
1585
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1586
1586
+
checksum = "7a9c6883ca9c3c7c90e888de77b7a5c849c779d25d74a1269b0218b14e8b136c"
1587
1587
+
dependencies = [
1588
1588
+
"log",
1589
1589
+
]
1590
1590
+
1591
1591
+
[[package]]
1592
1592
+
name = "siphasher"
1593
1593
+
version = "1.0.1"
1594
1594
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1595
1595
+
checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d"
1596
1596
+
1597
1597
+
[[package]]
1598
1598
+
name = "slotmap"
1599
1599
+
version = "1.0.7"
1600
1600
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1601
1601
+
checksum = "dbff4acf519f630b3a3ddcfaea6c06b42174d9a44bc70c620e9ed1649d58b82a"
1602
1602
+
dependencies = [
1603
1603
+
"version_check",
1604
1604
+
]
1605
1605
+
1606
1606
+
[[package]]
1263
1607
name = "slug"
1264
1608
version = "0.1.5"
1265
1609
source = "registry+https://github.com/rust-lang/crates.io-index"
···
1276
1620
checksum = "7fcf8323ef1faaee30a44a340193b1ac6814fd9b7b4e88e9d4519a3e4abe1cfd"
1277
1621
1278
1622
[[package]]
1623
1623
+
name = "strict-num"
1624
1624
+
version = "0.1.1"
1625
1625
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1626
1626
+
checksum = "6637bab7722d379c8b41ba849228d680cc12d0a45ba1fa2b48f2a30577a06731"
1627
1627
+
dependencies = [
1628
1628
+
"float-cmp",
1629
1629
+
]
1630
1630
+
1631
1631
+
[[package]]
1279
1632
name = "strsim"
1280
1633
version = "0.10.0"
1281
1634
source = "registry+https://github.com/rust-lang/crates.io-index"
···
1314
1667
version = "0.17.0"
1315
1668
source = "registry+https://github.com/rust-lang/crates.io-index"
1316
1669
checksum = "700efb40f3f559c23c18b446e8ed62b08b56b2bb3197b36d57e0470b4102779e"
1670
1670
+
1671
1671
+
[[package]]
1672
1672
+
name = "svgtypes"
1673
1673
+
version = "0.15.3"
1674
1674
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1675
1675
+
checksum = "68c7541fff44b35860c1a7a47a7cadf3e4a304c457b58f9870d9706ece028afc"
1676
1676
+
dependencies = [
1677
1677
+
"kurbo",
1678
1678
+
"siphasher",
1679
1679
+
]
1317
1680
1318
1681
[[package]]
1319
1682
name = "syn"
···
1400
1763
]
1401
1764
1402
1765
[[package]]
1766
1766
+
name = "tiny-skia"
1767
1767
+
version = "0.11.4"
1768
1768
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1769
1769
+
checksum = "83d13394d44dae3207b52a326c0c85a8bf87f1541f23b0d143811088497b09ab"
1770
1770
+
dependencies = [
1771
1771
+
"arrayref",
1772
1772
+
"arrayvec",
1773
1773
+
"bytemuck",
1774
1774
+
"cfg-if",
1775
1775
+
"log",
1776
1776
+
"png",
1777
1777
+
"tiny-skia-path",
1778
1778
+
]
1779
1779
+
1780
1780
+
[[package]]
1781
1781
+
name = "tiny-skia-path"
1782
1782
+
version = "0.11.4"
1783
1783
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1784
1784
+
checksum = "9c9e7fc0c2e86a30b117d0462aa261b72b7a99b7ebd7deb3a14ceda95c5bdc93"
1785
1785
+
dependencies = [
1786
1786
+
"arrayref",
1787
1787
+
"bytemuck",
1788
1788
+
"strict-num",
1789
1789
+
]
1790
1790
+
1791
1791
+
[[package]]
1403
1792
name = "tiny_http"
1404
1793
version = "0.12.0"
1405
1794
source = "registry+https://github.com/rust-lang/crates.io-index"
···
1412
1801
]
1413
1802
1414
1803
[[package]]
1804
1804
+
name = "tinyvec"
1805
1805
+
version = "1.8.1"
1806
1806
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1807
1807
+
checksum = "022db8904dfa342efe721985167e9fcd16c29b226db4397ed752a761cfce81e8"
1808
1808
+
dependencies = [
1809
1809
+
"tinyvec_macros",
1810
1810
+
]
1811
1811
+
1812
1812
+
[[package]]
1813
1813
+
name = "tinyvec_macros"
1814
1814
+
version = "0.1.1"
1815
1815
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1816
1816
+
checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
1817
1817
+
1818
1818
+
[[package]]
1415
1819
name = "toml"
1416
1820
version = "0.7.8"
1417
1821
source = "registry+https://github.com/rust-lang/crates.io-index"
···
1443
1847
"serde_spanned",
1444
1848
"toml_datetime",
1445
1849
"winnow",
1850
1850
+
]
1851
1851
+
1852
1852
+
[[package]]
1853
1853
+
name = "ttf-parser"
1854
1854
+
version = "0.24.1"
1855
1855
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1856
1856
+
checksum = "5be21190ff5d38e8b4a2d3b6a3ae57f612cc39c96e83cedeaf7abc338a8bac4a"
1857
1857
+
dependencies = [
1858
1858
+
"core_maths",
1446
1859
]
1447
1860
1448
1861
[[package]]
···
1458
1871
checksum = "ed646292ffc8188ef8ea4d1e0e0150fb15a5c2e12ad9b8fc191ae7a8a7f3c4b9"
1459
1872
1460
1873
[[package]]
1874
1874
+
name = "unicode-bidi"
1875
1875
+
version = "0.3.18"
1876
1876
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1877
1877
+
checksum = "5c1cb5db39152898a79168971543b1cb5020dff7fe43c8dc468b0885f5e29df5"
1878
1878
+
1879
1879
+
[[package]]
1880
1880
+
name = "unicode-bidi-mirroring"
1881
1881
+
version = "0.3.0"
1882
1882
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1883
1883
+
checksum = "64af057ad7466495ca113126be61838d8af947f41d93a949980b2389a118082f"
1884
1884
+
1885
1885
+
[[package]]
1886
1886
+
name = "unicode-ccc"
1887
1887
+
version = "0.3.0"
1888
1888
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1889
1889
+
checksum = "260bc6647b3893a9a90668360803a15f96b85a5257b1c3a0c3daf6ae2496de42"
1890
1890
+
1891
1891
+
[[package]]
1461
1892
name = "unicode-ident"
1462
1893
version = "1.0.12"
1463
1894
source = "registry+https://github.com/rust-lang/crates.io-index"
1464
1895
checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
1465
1896
1466
1897
[[package]]
1898
1898
+
name = "unicode-properties"
1899
1899
+
version = "0.1.3"
1900
1900
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1901
1901
+
checksum = "e70f2a8b45122e719eb623c01822704c4e0907e7e426a05927e1a1cfff5b75d0"
1902
1902
+
1903
1903
+
[[package]]
1904
1904
+
name = "unicode-script"
1905
1905
+
version = "0.5.7"
1906
1906
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1907
1907
+
checksum = "9fb421b350c9aff471779e262955939f565ec18b86c15364e6bdf0d662ca7c1f"
1908
1908
+
1909
1909
+
[[package]]
1910
1910
+
name = "unicode-vo"
1911
1911
+
version = "0.1.0"
1912
1912
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1913
1913
+
checksum = "b1d386ff53b415b7fe27b50bb44679e2cc4660272694b7b6f3326d8480823a94"
1914
1914
+
1915
1915
+
[[package]]
1467
1916
name = "unicode-width"
1468
1917
version = "0.1.12"
1469
1918
source = "registry+https://github.com/rust-lang/crates.io-index"
···
1506
1955
]
1507
1956
1508
1957
[[package]]
1958
1958
+
name = "usvg"
1959
1959
+
version = "0.44.0"
1960
1960
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1961
1961
+
checksum = "7447e703d7223b067607655e625e0dbca80822880248937da65966194c4864e6"
1962
1962
+
dependencies = [
1963
1963
+
"base64",
1964
1964
+
"data-url",
1965
1965
+
"flate2",
1966
1966
+
"fontdb",
1967
1967
+
"imagesize",
1968
1968
+
"kurbo",
1969
1969
+
"log",
1970
1970
+
"pico-args",
1971
1971
+
"roxmltree 0.20.0",
1972
1972
+
"rustybuzz",
1973
1973
+
"simplecss",
1974
1974
+
"siphasher",
1975
1975
+
"strict-num",
1976
1976
+
"svgtypes",
1977
1977
+
"tiny-skia-path",
1978
1978
+
"unicode-bidi",
1979
1979
+
"unicode-script",
1980
1980
+
"unicode-vo",
1981
1981
+
"xmlwriter",
1982
1982
+
]
1983
1983
+
1984
1984
+
[[package]]
1509
1985
name = "utf-8"
1510
1986
version = "0.7.6"
1511
1987
source = "registry+https://github.com/rust-lang/crates.io-index"
1512
1988
checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9"
1513
1989
1514
1990
[[package]]
1991
1991
+
name = "utf8parse"
1992
1992
+
version = "0.2.2"
1993
1993
+
source = "registry+https://github.com/rust-lang/crates.io-index"
1994
1994
+
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
1995
1995
+
1996
1996
+
[[package]]
1515
1997
name = "version_check"
1516
1998
version = "0.9.4"
1517
1999
source = "registry+https://github.com/rust-lang/crates.io-index"
···
1632
2114
dependencies = [
1633
2115
"rustls-pki-types",
1634
2116
]
2117
2117
+
2118
2118
+
[[package]]
2119
2119
+
name = "weezl"
2120
2120
+
version = "0.1.8"
2121
2121
+
source = "registry+https://github.com/rust-lang/crates.io-index"
2122
2122
+
checksum = "53a85b86a771b1c87058196170769dd264f66c0782acf1ae6cc51bfd64b39082"
1635
2123
1636
2124
[[package]]
1637
2125
name = "widestring"
···
1828
2316
]
1829
2317
1830
2318
[[package]]
2319
2319
+
name = "xmlwriter"
2320
2320
+
version = "0.1.0"
2321
2321
+
source = "registry+https://github.com/rust-lang/crates.io-index"
2322
2322
+
checksum = "ec7a2a501ed189703dba8b08142f057e887dfc4b2cc4db2d343ac6376ba3e0b9"
2323
2323
+
2324
2324
+
[[package]]
1831
2325
name = "xtask"
1832
2326
version = "0.1.0"
1833
2327
dependencies = [
···
1839
2333
version = "1.8.1"
1840
2334
source = "registry+https://github.com/rust-lang/crates.io-index"
1841
2335
checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde"
2336
2336
+
2337
2337
+
[[package]]
2338
2338
+
name = "zune-core"
2339
2339
+
version = "0.4.12"
2340
2340
+
source = "registry+https://github.com/rust-lang/crates.io-index"
2341
2341
+
checksum = "3f423a2c17029964870cfaabb1f13dfab7d092a62a29a89264f4d36990ca414a"
2342
2342
+
2343
2343
+
[[package]]
2344
2344
+
name = "zune-jpeg"
2345
2345
+
version = "0.4.14"
2346
2346
+
source = "registry+https://github.com/rust-lang/crates.io-index"
2347
2347
+
checksum = "99a5bab8d7dedf81405c4bb1f2b83ea057643d9cb28778cea9eecddeedd2e028"
2348
2348
+
dependencies = [
2349
2349
+
"zune-core",
2350
2350
+
]
···
56
56
strum = { version = "0.26.2", features = ["strum_macros"] }
57
57
strum_macros = "0.26.2"
58
58
ureq = "3.0.6"
59
59
+
tiny-skia = "0.11.4"
60
60
+
resvg = "0.44.0"
61
61
+
measure_time = "0.9.0"
62
62
+
env_logger = "0.11.6"
63
63
+
log = "0.4.26"
59
64
60
65
61
66
[dev-dependencies]
···
21
21
cp shapemaker ~/.local/bin/
22
22
23
23
example-video out="out.mp4" args='':
24
24
-
./shapemaker video --colors colorschemes/palenight.css {{out}} --sync-with fixtures/schedule-hell.midi --audio fixtures/schedule-hell.flac --grid-size 16x10 --resolution 1920 {{args}}
24
24
+
./shapemaker video --colors colorschemes/palenight.css {{out}} --sync-with fixtures/schedule-hell.midi --audio fixtures/schedule-hell.flac --grid-size 16x10 --resolution 480 {{args}}
25
25
26
26
example-image out="out.png" args='':
27
27
-
./shapemaker image --colors colorschemes/palenight.css --resolution 3000 {{out}} {{args}}
27
27
+
./shapemaker image --colors colorschemes/palenight.css --resolution 1400 {{out}} {{args}}
···
1
1
-
/**
2
2
-
* @typedef {object} GlobalData
3
3
-
* @property {Map<number, HTMLDivElement>} [frames]
4
4
-
*/
5
5
-
6
6
-
/**
7
7
-
* @typedef {Window & GlobalData} WindowWithData
8
8
-
*/
9
9
-
10
10
-
function displayFrame() {
11
11
-
const ms = millisecondsSinceStart(window.videoStartedAt)
12
12
-
console.debug("displayFrame", ms)
13
13
-
14
14
-
if (window.previouslyRenderedFrame) {
15
15
-
let f = window.frames.get(window.previouslyRenderedFrame)
16
16
-
if (f) {
17
17
-
f.style.display = "none"
18
18
-
f.classList.toggle("shown")
19
19
-
}
20
20
-
}
21
21
-
22
22
-
let frame = closestFrame(ms)
23
23
-
f = window.frames.get(frame)
24
24
-
f.style.display = "block"
25
25
-
f.classList.toggle("shown")
26
26
-
27
27
-
console.debug("framestLeftCount", framesLeftCount())
28
28
-
if (framesLeftCount() < window.FRAMES_BUFFER_SIZE) {
29
29
-
console.warn(
30
30
-
window.updatingBuffer ? "already updating buffer" : "update buffer now"
31
31
-
)
32
32
-
33
33
-
if (
34
34
-
!window.updatingBuffer &&
35
35
-
window.previouslyRenderedFrame - window.lastBufferUpdateWasOn > 2000
36
36
-
) {
37
37
-
console.info(
38
38
-
`Updating buffer: ${framesLeftCount()} < ${
39
39
-
window.FRAMES_BUFFER_SIZE
40
40
-
} remaining and last update was ${
41
41
-
window.previouslyRenderedFrame - window.lastBufferUpdateWasOn
42
42
-
} > 2000 ms ago`
43
43
-
)
44
44
-
updateBuffer()
45
45
-
}
46
46
-
}
47
47
-
48
48
-
window.previouslyRenderedFrame = frame
49
49
-
}
50
50
-
51
51
-
/**
52
52
-
*
53
53
-
* @returns number
54
54
-
*/
55
55
-
function framesLeftCount() {
56
56
-
return [...window.frames.keys()].filter(
57
57
-
(key) => key > window.previouslyRenderedFrame
58
58
-
).length
59
59
-
}
60
60
-
61
61
-
/**
62
62
-
*
63
63
-
* @param {number} ms
64
64
-
* @returns {number}
65
65
-
*/
66
66
-
function closestFrame(ms) {
67
67
-
const closest = [...window.frames.keys()].reduce((a, b) =>
68
68
-
Math.abs(b - ms) < Math.abs(a - ms) ? b : a
69
69
-
)
70
70
-
console.debug("closestFrame", ms, "is", closest)
71
71
-
return closest
72
72
-
}
73
73
-
74
74
-
/**
75
75
-
*
76
76
-
* @param {number} start
77
77
-
* @returns
78
78
-
*/
79
79
-
function millisecondsSinceStart(start) {
80
80
-
return new Date().getTime() - start
81
81
-
}
82
82
-
83
83
-
/**
84
84
-
*
85
85
-
* @returns {number}
86
86
-
*/
87
87
-
function lastLoadedFrame() {
88
88
-
return Math.max(...[...window.frames.keys()])
89
89
-
}
90
90
-
91
91
-
async function updateBuffer() {
92
92
-
console.time("fetchFrames")
93
93
-
console.log("set updatingBuffer to true")
94
94
-
window.updatingBuffer = true
95
95
-
console.info(
96
96
-
"updateBuffer",
97
97
-
4 * window.FRAMES_BUFFER_SIZE,
98
98
-
window.previouslyRenderedFrame
99
99
-
)
100
100
-
// request half the buffer size for the next frames
101
101
-
await fetch(
102
102
-
new URL(
103
103
-
"/frames?" +
104
104
-
new URLSearchParams({
105
105
-
next: 4 * window.FRAMES_BUFFER_SIZE,
106
106
-
from: window.previouslyRenderedFrame,
107
107
-
}),
108
108
-
window.SERVER_ORIGIN
109
109
-
)
110
110
-
).then((response) => {
111
111
-
if (!response.ok) {
112
112
-
console.error("Failed to fetch frames", response)
113
113
-
return
114
114
-
}
115
115
-
console.timeEnd("fetchFrames")
116
116
-
117
117
-
console.time("insertFramesToDOM")
118
118
-
response.text().then((frames) => {
119
119
-
document.body.insertAdjacentHTML("beforeend", frames)
120
120
-
})
121
121
-
console.timeEnd("insertFramesToDOM")
122
122
-
})
123
123
-
124
124
-
console.time("pruneFramesFromDOM")
125
125
-
// remove frames that are not needed anymore
126
126
-
;[...window.frames.keys()].forEach((key) => {
127
127
-
if (key < window.previouslyRenderedFrame) {
128
128
-
window.frames.get(key).remove()
129
129
-
}
130
130
-
})
131
131
-
console.timeEnd("pruneFramesFromDOM")
132
132
-
133
133
-
loadFramesFromDOM()
134
134
-
console.log("set updatingBuffer to false")
135
135
-
window.updatingBuffer = false
136
136
-
window.lastBufferUpdateWasOn = window.previouslyRenderedFrame
137
137
-
}
138
138
-
139
139
-
window.addEventListener("keypress", (e) => {
140
140
-
if (e.key === " ") {
141
141
-
if (window.intervalID) {
142
142
-
stopVideo()
143
143
-
} else {
144
144
-
startVideo()
145
145
-
}
146
146
-
}
147
147
-
})
148
148
-
149
149
-
function loadFramesFromDOM() {
150
150
-
console.time("loadFramesFromDOM")
151
151
-
window.frames = new Map(
152
152
-
[...document.querySelectorAll("[id^=frame-]")].map((el) => [
153
153
-
parseInt(el.id.replace("frame-", "")),
154
154
-
el,
155
155
-
])
156
156
-
)
157
157
-
console.timeEnd("loadFramesFromDOM")
158
158
-
}
159
159
-
160
160
-
window.startVideo = () => {
161
161
-
loadFramesFromDOM()
162
162
-
window.refreshRate = 50
163
163
-
window.videoStartedAt = new Date().getTime()
164
164
-
window.previouslyRenderedFrame = null
165
165
-
window.lastBufferUpdateWasOn = null
166
166
-
window.updatingBuffer = false
167
167
-
168
168
-
displayFrame()
169
169
-
window.intervalID = setInterval(displayFrame, window.refreshRate)
170
170
-
}
171
171
-
172
172
-
window.stopVideo = () => {
173
173
-
console.info("stopVideo", window.currentFrame)
174
174
-
clearInterval(window.intervalID)
175
175
-
}
···
1
1
-
<!DOCTYPE html>
2
2
-
<html lang="en">
3
3
-
4
4
-
<head>
5
5
-
<meta charset="UTF-8">
6
6
-
<meta name="viewport" content="width=device-width, initial-scale=1.0">
7
7
-
<title>Shapemaker Preview</title>
8
8
-
<script>
9
9
-
window.SERVER_ORIGIN = "{{ serverorigin }}";
10
10
-
window.FRAMES_BUFFER_SIZE = {{ framesbuffersize }};
11
11
-
{{{ enginesource }}}
12
12
-
</script>
13
13
-
{{!-- <script src="preview/engine.js"></script> --}}
14
14
-
<style>
15
15
-
.frame.shown {
16
16
-
position: fixed;
17
17
-
top: 0;
18
18
-
left: 0;
19
19
-
right: 0;
20
20
-
bottom: 0;
21
21
-
}
22
22
-
23
23
-
.frame.shown>svg {
24
24
-
width: 100%;
25
25
-
height: 100%;
26
26
-
}
27
27
-
28
28
-
audio {
29
29
-
position: fixed;
30
30
-
z-index: 100;
31
31
-
bottom: 0;
32
32
-
left: 0;
33
33
-
}
34
34
-
</style>
35
35
-
</head>
36
36
-
37
37
-
<body style="background-color: {{background}};">
38
38
-
<audio src="{{ audiopath }}" controls onplay="startVideo()" onpause="stopVideo()"></audio>
39
39
-
{{#each frames}}
40
40
-
<div style="display: none;" id="frame-{{@key}}" class="frame">
41
41
-
{{{ this }}}
42
42
-
</div>
43
43
-
{{/each}}
44
44
-
</body>
45
45
-
46
46
-
</html>
···
1
1
use core::panic;
2
2
-
use std::{collections::HashMap, io::Write as _, ops::Range};
2
2
+
use std::{collections::HashMap, ops::Range};
3
3
4
4
-
use anyhow::Result;
5
4
use itertools::Itertools as _;
5
5
+
use measure_time::info_time;
6
6
use rand::Rng;
7
7
8
8
use crate::{
···
24
24
pub background: Option<Color>,
25
25
26
26
pub world_region: Region,
27
27
+
28
28
+
/// Render cache for the SVG string. Prevents having to re-calculate a pixmap when the SVG hasn't changed.
29
29
+
png_render_cache: Option<String>,
27
30
}
28
31
29
32
impl Canvas {
···
187
190
layers: vec![],
188
191
world_region: Region::new(0, 0, 3, 3).unwrap(),
189
192
background: None,
193
193
+
png_render_cache: None,
190
194
}
191
195
}
192
196
···
414
418
self.remove_background()
415
419
}
416
420
417
417
-
pub fn save_as(
421
421
+
// previous_frame_at gives path to the previously rendered frame, which allows to copy on cache hits instead of having to re-write bytes again
422
422
+
pub fn render_to_png(
423
423
+
&mut self,
418
424
at: &str,
419
419
-
aspect_ratio: f32,
420
420
-
resolution: usize,
421
421
-
rendered: String,
422
422
-
) -> Result<(), String> {
425
425
+
resolution: u32,
426
426
+
previous_frame_at: Option<&str>,
427
427
+
) -> anyhow::Result<()> {
428
428
+
info_time!("render_to_png");
429
429
+
let aspect_ratio = self.aspect_ratio();
423
430
let (height, width) = if aspect_ratio > 1.0 {
424
431
// landscape: resolution is width
425
425
-
(resolution, (resolution as f32 * aspect_ratio) as usize)
432
432
+
(resolution, (resolution as f32 * aspect_ratio) as u32)
426
433
} else {
427
434
// portrait: resolution is height
428
428
-
((resolution as f32 / aspect_ratio) as usize, resolution)
435
435
+
((resolution as f32 / aspect_ratio) as u32, resolution)
429
436
};
430
437
431
431
-
let mut spawned = std::process::Command::new("resvg")
432
432
-
.args(["--background", "transparent"])
433
433
-
.args(["--width", &format!("{width}")])
434
434
-
.args(["--height", &format!("{height}")])
435
435
-
.args(["--use-font-file", "Inconsolata-Bold.ttf"])
436
436
-
.args(["--resources-dir", "."])
437
437
-
.arg("-")
438
438
-
.arg(at)
439
439
-
.stdin(std::process::Stdio::piped())
440
440
-
.spawn()
441
441
-
.unwrap();
438
438
+
if let Some(previous_frame_at) = previous_frame_at {
439
439
+
match self.render_to_pixmap(width, height)? {
440
440
+
None => {
441
441
+
std::fs::copy(previous_frame_at, at)?;
442
442
+
}
443
443
+
Some(pixmap) => {
444
444
+
pixmap_to_png_data(pixmap).and_then(|data| write_png_data(data, at))?
445
445
+
}
446
446
+
}
447
447
+
return Ok(());
448
448
+
}
449
449
+
450
450
+
Ok(self
451
451
+
.render_to_pixmap_no_cache(width, height)
452
452
+
.and_then(|pixmap| {
453
453
+
pixmap_to_png_data(pixmap).and_then(|data| write_png_data(data, at))
454
454
+
})?)
455
455
+
}
456
456
+
}
442
457
443
443
-
let stdin = spawned.stdin.as_mut().unwrap();
444
444
-
stdin.write_all(rendered.as_bytes()).unwrap();
458
458
+
fn pixmap_to_png_data(pixmap: tiny_skia::Pixmap) -> anyhow::Result<Vec<u8>> {
459
459
+
info_time!("\tpixmap_to_png_data");
460
460
+
Ok(pixmap.encode_png()?)
461
461
+
}
445
462
446
446
-
match spawned.wait_with_output() {
447
447
-
Ok(_) => Ok(()),
448
448
-
Err(e) => Err(format!("Failed to execute convert: {}", e)),
449
449
-
}
450
450
-
}
463
463
+
fn write_png_data(data: Vec<u8>, at: &str) -> anyhow::Result<()> {
464
464
+
info_time!("\twrite_png_data");
465
465
+
std::fs::write(at, data)?;
466
466
+
Ok(())
451
467
}
452
468
453
469
impl Canvas {
···
514
530
)
515
531
}
516
532
517
517
-
pub fn render(&mut self, render_background: bool) -> Result<String> {
533
533
+
pub fn render_to_svg(&mut self) -> anyhow::Result<String> {
534
534
+
info_time!("render_to_svg");
518
535
let background_color = self.background.unwrap_or_default();
519
536
let mut svg = svg::Document::new();
520
520
-
if render_background {
521
521
-
svg = svg.add(
522
522
-
svg::node::element::Rectangle::new()
523
523
-
.set("x", -(self.canvas_outter_padding as i32))
524
524
-
.set("y", -(self.canvas_outter_padding as i32))
525
525
-
.set("width", self.width())
526
526
-
.set("height", self.height())
527
527
-
.set("fill", background_color.render(&self.colormap)),
528
528
-
);
529
529
-
}
537
537
+
svg = svg.add(
538
538
+
svg::node::element::Rectangle::new()
539
539
+
.set("x", -(self.canvas_outter_padding as i32))
540
540
+
.set("y", -(self.canvas_outter_padding as i32))
541
541
+
.set("width", self.width())
542
542
+
.set("height", self.height())
543
543
+
.set("fill", background_color.render(&self.colormap)),
544
544
+
);
545
545
+
530
546
for layer in self.layers.iter_mut().filter(|layer| !layer.hidden).rev() {
531
547
svg = svg.add(layer.render(self.colormap.clone(), self.cell_size, layer.object_sizes));
532
548
}
···
559
575
560
576
Ok(rendered)
561
577
}
578
578
+
579
579
+
pub fn render_to_pixmap_no_cache(
580
580
+
&mut self,
581
581
+
width: u32,
582
582
+
height: u32,
583
583
+
) -> anyhow::Result<tiny_skia::Pixmap> {
584
584
+
info_time!("render_to_pixmap_no_cache");
585
585
+
let mut pixmap = self.create_pixmap(width, height);
586
586
+
587
587
+
let parsed_svg = &svg_to_usvg_tree(&self.render_to_svg()?)?;
588
588
+
589
589
+
self.usvg_tree_to_pixmap(width, height, pixmap.as_mut(), parsed_svg);
590
590
+
591
591
+
Ok(pixmap)
592
592
+
}
593
593
+
594
594
+
// Returns None if we had a render cache hit -- pixmap is in self.png_render_cache in that case
595
595
+
pub fn render_to_pixmap(
596
596
+
&mut self,
597
597
+
width: u32,
598
598
+
height: u32,
599
599
+
) -> anyhow::Result<Option<tiny_skia::Pixmap>> {
600
600
+
info_time!("render_to_pixmap");
601
601
+
602
602
+
let new_svg_contents = self.render_to_svg()?;
603
603
+
if let Some(cached_svg) = &self.png_render_cache {
604
604
+
if *cached_svg == new_svg_contents {
605
605
+
// TODO find a way to avoid .cloneing the pixmap
606
606
+
return Ok(None);
607
607
+
}
608
608
+
}
609
609
+
610
610
+
let mut pixmap = self.create_pixmap(width, height);
611
611
+
612
612
+
let parsed_svg = &svg_to_usvg_tree(&new_svg_contents)?;
613
613
+
614
614
+
self.usvg_tree_to_pixmap(width, height, pixmap.as_mut(), parsed_svg);
615
615
+
616
616
+
self.png_render_cache = Some(new_svg_contents);
617
617
+
618
618
+
Ok(Some(pixmap))
619
619
+
}
620
620
+
621
621
+
fn usvg_tree_to_pixmap(
622
622
+
&mut self,
623
623
+
width: u32,
624
624
+
height: u32,
625
625
+
mut pixmap_mut: tiny_skia::PixmapMut<'_>,
626
626
+
parsed_svg: &resvg::usvg::Tree,
627
627
+
) {
628
628
+
info_time!("usvg_tree_to_pixmap");
629
629
+
resvg::render(
630
630
+
parsed_svg,
631
631
+
tiny_skia::Transform::from_scale(
632
632
+
width as f32 / self.width() as f32,
633
633
+
height as f32 / self.height() as f32,
634
634
+
),
635
635
+
&mut pixmap_mut,
636
636
+
);
637
637
+
}
638
638
+
639
639
+
fn create_pixmap(&self, width: u32, height: u32) -> tiny_skia::Pixmap {
640
640
+
info_time!("create_pixmap");
641
641
+
tiny_skia::Pixmap::new(width, height).expect("Failed to create pixmap")
642
642
+
}
643
643
+
}
644
644
+
645
645
+
fn svg_to_usvg_tree(svg: &str) -> anyhow::Result<resvg::usvg::Tree> {
646
646
+
info_time!("svg_to_usvg_tree");
647
647
+
Ok(resvg::usvg::Tree::from_str(
648
648
+
svg,
649
649
+
&resvg::usvg::Options::default(),
650
650
+
)?)
562
651
}
···
1
1
use docopt::Docopt;
2
2
+
use measure_time::info_time;
2
3
use serde::Deserialize;
3
4
use crate::{Canvas, ColorMapping};
4
5
···
38
39
--audio <file> Audio file to use for the video
39
40
--duration <seconds> Number of seconds to render. If not set, the video will be as long as the audio file.
40
41
--start <seconds> Start the video at this time in seconds. [default: 0]
41
41
-
--preview Only create preview.html, not the output video. Preview.html will be created in the same directory as <file>, but <file> will not be created.
42
42
--sync-with <directory> Directory containing the audio files to sync to.
43
43
The directory must contain:
44
44
- stems/(instrument name).wav — stems
···
63
63
}
64
64
65
65
pub fn canvas_from_cli(args: &Args) -> Canvas {
66
66
+
info_time!("canvas_from_cli");
66
67
let mut canvas = Canvas::new(vec![]);
67
68
canvas.colormap = load_colormap(args);
68
69
set_canvas_settings_from_args(args, &mut canvas);
···
90
91
pub flag_fps: Option<usize>,
91
92
pub flag_sync_with: Option<String>,
92
93
pub flag_audio: Option<String>,
93
93
-
pub flag_resolution: Option<usize>,
94
94
+
pub flag_resolution: Option<u32>,
94
95
pub flag_workers: Option<usize>,
95
96
pub flag_duration: Option<usize>,
96
97
pub flag_start: Option<usize>,
97
97
-
pub flag_preview: bool,
98
98
}
99
99
100
100
fn set_canvas_settings_from_args(args: &Args, canvas: &mut Canvas) {
···
12
12
pub mod midi;
13
13
pub mod objects;
14
14
pub mod point;
15
15
-
pub mod preview;
16
15
pub mod region;
17
16
pub mod sync;
18
17
pub mod transform;
···
1
1
-
use std::env;
2
2
-
3
1
use anyhow::Result;
4
4
-
use itertools::Itertools;
5
5
-
use rand::Rng;
2
2
+
use measure_time::info_time;
6
3
use shapemaker::{
7
4
cli::{canvas_from_cli, cli_args},
8
5
*,
9
6
};
10
7
8
8
+
#[macro_use]
9
9
+
extern crate log;
10
10
+
11
11
pub fn main() -> Result<()> {
12
12
+
env_logger::init();
12
13
run(cli_args())
13
14
}
14
15
15
16
pub fn run(args: cli::Args) -> Result<()> {
17
17
+
info_time!("run");
16
18
let mut canvas = canvas_from_cli(&args);
17
19
18
20
if args.cmd_image && !args.cmd_video {
19
21
canvas = examples::title();
20
22
21
21
-
let rendered = canvas.render(true)?;
22
23
if args.arg_file.ends_with(".svg") {
23
23
-
std::fs::write(args.arg_file, rendered).unwrap();
24
24
+
std::fs::write(args.arg_file, canvas.render_to_svg()?).unwrap();
24
25
} else {
25
25
-
match Canvas::save_as(
26
26
-
&args.arg_file,
27
27
-
canvas.aspect_ratio(),
28
28
-
args.flag_resolution.unwrap_or(1000),
29
29
-
rendered,
30
30
-
) {
26
26
+
match canvas.render_to_png(&args.arg_file, args.flag_resolution.unwrap_or(1000), None) {
31
27
Ok(_) => println!("Image saved to {}", args.arg_file),
32
28
Err(e) => println!("Error saving image: {}", e),
33
29
}
···
39
35
video.duration_override = args.flag_duration.map(|seconds| seconds * 1000);
40
36
video.start_rendering_at = args.flag_start.unwrap_or_default() * 1000;
41
37
video.fps = args.flag_fps.unwrap_or(30);
42
42
-
43
43
-
if args.flag_preview {
44
44
-
video.preview_on(8888)
45
45
-
} else {
46
46
-
video.render_to(args.arg_file, args.flag_workers.unwrap_or(8), false)
47
47
-
}
38
38
+
video.audiofile = args
39
39
+
.flag_audio
40
40
+
.expect("Provide audio with --audio to render a video")
41
41
+
.into();
42
42
+
video
43
43
+
.sync_audio_with(
44
44
+
&args
45
45
+
.flag_sync_with
46
46
+
.expect("Provide MIDI sync file with --sync-with to render a video"),
47
47
+
)
48
48
+
.each_beat(&|canvas, ctx| {
49
49
+
canvas.background = Some(if ctx.beat % 2 == 0 {
50
50
+
Color::Black
51
51
+
} else {
52
52
+
Color::White
53
53
+
});
54
54
+
Ok(())
55
55
+
})
56
56
+
.render(args.arg_file)
48
57
}
···
1
1
-
use std::{collections::HashMap, fs, path::PathBuf};
2
2
-
3
3
-
use anyhow::Result;
4
4
-
use handlebars::Handlebars;
5
5
-
use itertools::Itertools;
6
6
-
use serde_json::json;
7
7
-
8
8
-
use crate::Canvas;
9
9
-
10
10
-
const FRAMES_BUFFER_SIZE: usize = 500;
11
11
-
12
12
-
pub fn render_template(
13
13
-
frames: &HashMap<usize, String>,
14
14
-
canvas: &Canvas,
15
15
-
path_to_audio_file: PathBuf,
16
16
-
port: usize,
17
17
-
) -> String {
18
18
-
let template = String::from_utf8_lossy(include_bytes!("../preview/index.html.hbs"));
19
19
-
let engine_js_source = String::from_utf8_lossy(include_bytes!("../preview/engine.js"));
20
20
-
21
21
-
let hbs = Handlebars::new();
22
22
-
hbs.render_template(
23
23
-
&template,
24
24
-
&json!({
25
25
-
"frames":frames,
26
26
-
"audiopath": path_to_audio_file,
27
27
-
"enginesource": engine_js_source,
28
28
-
"background": canvas.background.map_or("black".to_string(), |color| color.render(&canvas.colormap)),
29
29
-
"serverorigin": format!("http://localhost:{}", port),
30
30
-
"framesbuffersize": FRAMES_BUFFER_SIZE,
31
31
-
}),
32
32
-
)
33
33
-
.unwrap()
34
34
-
}
35
35
-
36
36
-
// rendered_svg_frames should map ms timestamps to SVG strings
37
37
-
pub fn output_preview(
38
38
-
canvas: &Canvas,
39
39
-
rendered_svg_frames: &HashMap<usize, String>,
40
40
-
server_port: usize,
41
41
-
output_file: PathBuf,
42
42
-
audio_file: PathBuf,
43
43
-
) -> Result<()> {
44
44
-
let first_frames = rendered_svg_frames
45
45
-
.iter()
46
46
-
// over 3000 loaded frames get really heavy on the browser (too much DOM nodes)
47
47
-
.sorted_by_key(|(ms, _)| *ms)
48
48
-
.take((2 * FRAMES_BUFFER_SIZE).min(10_000))
49
49
-
.map(|(ms, svg)| (*ms, svg.clone()))
50
50
-
.collect::<HashMap<usize, String>>();
51
51
-
52
52
-
let contents = render_template(&first_frames, canvas, audio_file, server_port);
53
53
-
fs::write(output_file, contents)?;
54
54
-
Ok(())
55
55
-
}
56
56
-
57
57
-
pub fn start_preview_server(port: usize, frames: HashMap<usize, String>) -> Result<()> {
58
58
-
let server = tiny_http::Server::http(format!("0.0.0.0:{}", port)).unwrap();
59
59
-
println!("Preview server running on port {}", port);
60
60
-
let sorted_frames: Vec<(&usize, &String)> =
61
61
-
frames.iter().sorted_by_key(|(ms, _)| *ms).collect();
62
62
-
println!("{} frames available", sorted_frames.len());
63
63
-
64
64
-
for request in server.incoming_requests() {
65
65
-
let (frame_start_ms, requested_frames_count) = get_request_params(request.url());
66
66
-
67
67
-
println!(
68
68
-
"Request for {} frames @ {}ms",
69
69
-
requested_frames_count, frame_start_ms,
70
70
-
);
71
71
-
72
72
-
let contents = sorted_frames
73
73
-
.iter()
74
74
-
.filter(|(ms, _)| **ms >= frame_start_ms)
75
75
-
.take(requested_frames_count)
76
76
-
.map(|(ms, svg_string)| {
77
77
-
format!(
78
78
-
r#"<div style="display: none;" id="frame-{}" class="frame">{}</div>"#,
79
79
-
ms, svg_string
80
80
-
)
81
81
-
})
82
82
-
.join("\n");
83
83
-
84
84
-
request.respond(tiny_http::Response::from_string(contents).with_header(
85
85
-
tiny_http::Header {
86
86
-
field: "Access-Control-Allow-Origin".parse().unwrap(),
87
87
-
value: "*".parse().unwrap(),
88
88
-
},
89
89
-
))?;
90
90
-
}
91
91
-
Ok(())
92
92
-
}
93
93
-
94
94
-
// returns (ms timestamp of first frame to send, number of frames to send)
95
95
-
fn get_request_params(url: &str) -> (usize, usize) {
96
96
-
let mut first_frame_ms = 0;
97
97
-
let mut num_frames = 1;
98
98
-
99
99
-
let (_, querystring) = url.split_once('?').unwrap_or(("", ""));
100
100
-
for (key, value) in querystring
101
101
-
.split('&')
102
102
-
.map(|pair| pair.split_once('=').unwrap_or(("", "")))
103
103
-
{
104
104
-
match key {
105
105
-
"from" => first_frame_ms = value.parse().unwrap_or(0),
106
106
-
"next" => num_frames = value.parse().unwrap_or(1),
107
107
-
_ => (),
108
108
-
}
109
109
-
}
110
110
-
111
111
-
(first_frame_ms, num_frames)
112
112
-
}
···
1
1
use std::process;
2
2
use std::{
3
3
-
cmp::min,
4
4
-
collections::HashMap,
5
3
fmt::Formatter,
6
4
fs::{create_dir, create_dir_all, remove_dir_all},
7
5
panic,
8
6
path::{Path, PathBuf},
9
9
-
sync::Arc,
10
7
};
11
8
12
12
-
use std::thread;
13
13
-
14
9
use anyhow::Result;
15
10
use chrono::{DateTime, NaiveDateTime};
16
11
use indicatif::{ProgressBar, ProgressIterator};
12
12
+
use measure_time::info_time;
17
13
18
14
use crate::{
19
19
-
preview,
20
15
sync::SyncData,
21
16
ui::{self, format_log_msg, setup_progress_bar, Log as _},
22
17
Canvas, ColoredObject, Context, LayerAnimationUpdateFunction, MidiSynchronizer,
···
49
44
pub frames_output_directory: &'static str,
50
45
pub syncdata: SyncData,
51
46
pub audiofile: PathBuf,
52
52
-
pub resolution: usize,
47
47
+
pub resolution: u32,
53
48
pub duration_override: Option<usize>,
54
49
pub start_rendering_at: usize,
55
50
pub progress_bar: indicatif::ProgressBar,
56
51
}
52
52
+
57
53
pub struct Hook<C> {
58
54
pub when: Box<HookCondition<C>>,
59
55
pub render_function: Box<RenderFunction<C>>,
···
122
118
}
123
119
124
120
pub fn sync_audio_with(self, sync_data_path: &str) -> Self {
121
121
+
info_time!("sync_audio_with");
125
122
if sync_data_path.ends_with(".mid") || sync_data_path.ends_with(".midi") {
126
123
let loader = MidiSynchronizer::new(sync_data_path);
127
124
let syncdata = loader.load(Some(&self.progress_bar));
···
192
189
}
193
190
}
194
191
195
195
-
fn build_frame(
196
196
-
svg_string: String,
197
197
-
frame_no: usize,
198
198
-
total_frames: usize,
199
199
-
frames_output_directory: &str,
200
200
-
aspect_ratio: f32,
201
201
-
resolution: usize,
202
202
-
) -> Result<(), String> {
203
203
-
Canvas::save_as(
204
204
-
&format!(
205
205
-
"{}/{:0width$}.png",
206
206
-
frames_output_directory,
207
207
-
frame_no,
208
208
-
width = total_frames.to_string().len()
209
209
-
),
210
210
-
aspect_ratio,
211
211
-
resolution,
212
212
-
svg_string,
192
192
+
fn frame_output_path(&self, frame_no: usize) -> String {
193
193
+
format!(
194
194
+
"{}/{:0width$}.png",
195
195
+
self.frames_output_directory,
196
196
+
frame_no,
197
197
+
width = self.total_frames().to_string().len()
213
198
)
214
199
}
215
200
···
515
500
.expect("No audio sync data provided. Use .sync_audio_with() to load a MIDI file, or provide a duration override.")
516
501
}
517
502
518
518
-
pub fn preview_on(&self, port: usize) -> Result<()> {
519
519
-
let mut rendered_frames: HashMap<usize, String> = HashMap::new();
520
520
-
let progress_bar = self.setup_progress_bar();
521
521
-
522
522
-
for (frame, _, ms) in self.render_frames(&progress_bar, true)? {
523
523
-
rendered_frames.insert(ms, frame);
524
524
-
}
525
525
-
526
526
-
progress_bar.finish_and_clear();
527
527
-
528
528
-
preview::output_preview(
529
529
-
&self.initial_canvas,
530
530
-
&rendered_frames,
531
531
-
port,
532
532
-
PathBuf::from(".").join("preview.html"),
533
533
-
self.audiofile.clone(),
534
534
-
)?;
535
535
-
536
536
-
preview::start_preview_server(port, rendered_frames)
537
537
-
}
538
538
-
539
539
-
pub fn render_to(
540
540
-
&self,
541
541
-
output_file: String,
542
542
-
workers_count: usize,
543
543
-
preview_only: bool,
544
544
-
) -> Result<()> {
545
545
-
self.render(output_file, true, workers_count, preview_only)
546
546
-
}
547
547
-
548
548
-
pub fn render_layers_in(&self, output_directory: String, workers_count: usize) -> Result<()> {
503
503
+
pub fn render_layers_in(&self, output_directory: String) -> Result<()> {
549
504
for composition in self
550
505
.initial_canvas
551
506
.layers
552
507
.iter()
553
508
.map(|l| vec![l.name.as_str()])
554
509
{
555
555
-
self.render(
556
556
-
format!("{}/{}.mov", output_directory, composition.join("+")),
557
557
-
false,
558
558
-
workers_count,
559
559
-
false,
560
560
-
)?;
510
510
+
self.render(format!(
511
511
+
"{}/{}.mov",
512
512
+
output_directory,
513
513
+
composition.join("+")
514
514
+
))?;
561
515
}
562
516
Ok(())
563
517
}
564
518
565
565
-
// Returns a triple of (SVG content, frame number, millisecond at frame)
566
566
-
pub fn render_frames(
567
567
-
&self,
568
568
-
progress_bar: &ProgressBar,
569
569
-
render_background: bool,
570
570
-
) -> Result<Vec<(String, usize, usize)>> {
519
519
+
// Saves PNG frames to disk. Returns number of frames written.
520
520
+
pub fn render_frames(&self, progress_bar: &ProgressBar) -> Result<usize> {
521
521
+
let mut written_frames_count: usize = 0;
571
522
let mut context = Context {
572
523
frame: 0,
573
524
beat: 0,
···
586
537
587
538
let mut previous_rendered_beat = 0;
588
539
let mut previous_rendered_frame = 0;
589
589
-
let mut frames_to_write: Vec<(String, usize, usize)> = vec![];
590
540
591
541
let render_ms_range = 0..self.duration_ms() + self.start_rendering_at;
592
542
···
661
611
}
662
612
663
613
if context.frame != previous_rendered_frame {
664
664
-
let rendered = canvas.render(render_background)?;
614
614
+
canvas.render_to_png(
615
615
+
&self.frame_output_path(context.frame),
616
616
+
self.resolution,
617
617
+
Some(&self.frame_output_path(previous_rendered_frame)),
618
618
+
)?;
619
619
+
written_frames_count += 1;
665
620
666
621
previous_rendered_beat = context.beat;
667
622
previous_rendered_frame = context.frame;
668
668
-
669
669
-
frames_to_write.push((rendered, context.frame, context.ms))
670
623
}
671
624
}
672
625
673
673
-
Ok(frames_to_write)
626
626
+
Ok(written_frames_count)
674
627
}
675
628
676
629
pub fn setup_progress_bar(&self) -> ProgressBar {
677
630
ui::setup_progress_bar(self.total_frames() as u64, "Rendering")
678
631
}
679
632
680
680
-
pub fn render(
681
681
-
&self,
682
682
-
output_file: String,
683
683
-
render_background: bool,
684
684
-
workers_count: usize,
685
685
-
_preview_only: bool,
686
686
-
) -> Result<()> {
687
687
-
// Ensure resvg is installed
688
688
-
if !is_binary_installed("resvg") {
689
689
-
panic!("resvg is not installed. Please install it by running `cargo install resvg`.");
690
690
-
}
633
633
+
pub fn render(&self, output_file: String) -> Result<()> {
634
634
+
info_time!("render");
691
635
// Ensure ffmpeg is installed
692
636
if !is_binary_installed("ffmpeg") {
693
637
panic!("ffmpeg is not installed. Please install it.");
694
638
}
695
639
696
696
-
let mut frame_writer_threads = vec![];
697
697
-
let mut frames_to_write: Vec<(String, usize, usize)> = vec![];
698
698
-
699
640
create_dir_all(self.frames_output_directory)?;
700
641
remove_dir_all(self.frames_output_directory)?;
701
642
create_dir(self.frames_output_directory)?;
702
643
create_dir_all(Path::new(&output_file).parent().unwrap())?;
703
703
-
704
704
-
let total_frames = self.total_frames();
705
705
-
let aspect_ratio =
706
706
-
self.initial_canvas.grid_size.0 as f32 / self.initial_canvas.grid_size.1 as f32;
707
707
-
let resolution = self.resolution;
708
644
709
645
self.progress_bar.set_position(0);
710
646
self.progress_bar.set_prefix("Rendering");
711
647
self.progress_bar.set_message("");
712
648
713
713
-
for (frame, no, ms) in self.render_frames(&self.progress_bar, render_background)? {
714
714
-
frames_to_write.push((frame, no, ms));
715
715
-
}
649
649
+
let frames_written = self.render_frames(&self.progress_bar)?;
716
650
717
717
-
self.progress_bar.log(
718
718
-
"Rendered",
719
719
-
&format!("{} frames to SVG", frames_to_write.len()),
720
720
-
);
721
721
-
722
722
-
frames_to_write.retain(|(_, _, ms)| *ms >= self.start_rendering_at);
723
723
-
724
724
-
self.progress_bar.set_prefix("Converting");
725
651
self.progress_bar
726
726
-
.set_message("converting SVG frames to PNG");
727
727
-
self.progress_bar.set_position(0);
728
728
-
self.progress_bar.set_length(frames_to_write.len() as u64);
729
729
-
730
730
-
for (frame, no, _) in &frames_to_write {
731
731
-
std::fs::write(
732
732
-
format!("{}/{}.svg", self.frames_output_directory, no),
733
733
-
frame,
734
734
-
)?;
735
735
-
}
736
736
-
737
737
-
let chunk_size = (frames_to_write.len() as f32 / workers_count as f32).ceil() as usize;
738
738
-
let frames_to_write = Arc::new(frames_to_write);
739
739
-
let frames_output_directory = self.frames_output_directory;
740
740
-
for i in 0..workers_count {
741
741
-
let frames_to_write = Arc::clone(&frames_to_write);
742
742
-
let progress_bar = self.progress_bar.clone();
743
743
-
frame_writer_threads.push(
744
744
-
thread::Builder::new()
745
745
-
.name(format!("worker-{}", i))
746
746
-
.spawn(move || {
747
747
-
for (frame_svg, frame_no, _) in &frames_to_write
748
748
-
[i * chunk_size..min((i + 1) * chunk_size, frames_to_write.len())]
749
749
-
{
750
750
-
Video::<AdditionalContext>::build_frame(
751
751
-
frame_svg.clone(),
752
752
-
*frame_no,
753
753
-
total_frames,
754
754
-
frames_output_directory,
755
755
-
aspect_ratio,
756
756
-
resolution,
757
757
-
)
758
758
-
.unwrap();
759
759
-
progress_bar.inc(1);
760
760
-
}
761
761
-
})
762
762
-
.unwrap(),
763
763
-
);
764
764
-
}
765
765
-
766
766
-
for handle in frame_writer_threads {
767
767
-
handle.join().unwrap();
768
768
-
}
769
769
-
770
770
-
self.progress_bar.log(
771
771
-
"Converted",
772
772
-
&format!("{} SVG frames to PNG", self.progress_bar.position()),
773
773
-
);
774
774
-
self.progress_bar.finish_and_clear();
652
652
+
.log("Rendered", &format!("{} frames to SVG", frames_written));
775
653
776
654
let spinner = ui::Spinner::start("Building", "video");
777
655
let result = self.build_video(&output_file);
···
14
14
WEB_CANVAS.lock().unwrap()
15
15
}
16
16
17
17
-
18
17
// Can't bind Color.name directly, see https://github.com/rustwasm/wasm-bindgen/issues/1715
19
18
#[wasm_bindgen]
20
19
pub fn color_name(c: Color) -> String {
···
60
59
61
60
#[wasm_bindgen]
62
61
pub fn render_canvas_into(selector: String) {
63
63
-
let svgstring = canvas().render(false).unwrap_throw();
62
62
+
let svgstring = canvas().render_to_svg().unwrap_throw();
64
63
append_new_div_inside(svgstring, selector)
65
64
}
66
65
67
66
#[wasm_bindgen]
68
67
pub fn render_canvas_at(selector: String) {
69
69
-
let svgstring = canvas().render(false).unwrap_throw();
68
68
+
let svgstring = canvas().render_to_svg().unwrap_throw();
70
69
replace_content_with(svgstring, selector)
71
70
}
72
71
···
130
129
}
131
130
132
131
#[wasm_bindgen]
133
133
-
pub fn render_canvas(render_background: Option<bool>) {
134
134
-
canvas()
135
135
-
.render(render_background.unwrap_or(false))
136
136
-
.unwrap_throw();
132
132
+
pub fn render_canvas() {
133
133
+
canvas().render_to_svg().unwrap_throw();
137
134
}
138
135
139
136
#[wasm_bindgen]
···
203
200
#[wasm_bindgen]
204
201
impl LayerWeb {
205
202
pub fn render(&self) -> String {
206
206
-
canvas().render(false).unwrap_throw()
203
203
+
canvas().render_to_svg().unwrap_throw()
207
204
}
208
205
209
206
pub fn render_into(&self, selector: String) {
···
229
226
}
230
227
}
231
228
232
232
-
pub fn new_line(
233
233
-
&self,
234
234
-
name: &str,
235
235
-
start: Point,
236
236
-
end: Point,
237
237
-
thickness: f32,
238
238
-
color: Color,
239
239
-
) {
229
229
+
pub fn new_line(&self, name: &str, start: Point, end: Point, thickness: f32, color: Color) {
240
230
canvas().layer(name).add_object(
241
231
name,
242
232
(
···
287
277
.layer(name)
288
278
.add_object(name, Object::BigCircle(center).color(Fill::Solid(color)))
289
279
}
290
290
-
pub fn new_text(
291
291
-
&self,
292
292
-
name: &str,
293
293
-
anchor: Point,
294
294
-
text: String,
295
295
-
font_size: f32,
296
296
-
color: Color,
297
297
-
) {
280
280
+
pub fn new_text(&self, name: &str, anchor: Point, text: String, font_size: f32, color: Color) {
298
281
canvas().layer(name).add_object(
299
282
name,
300
283
Object::Text(anchor, text, font_size).color(Fill::Solid(color)),
301
284
)
302
285
}
303
303
-
pub fn new_rectangle(
304
304
-
&self,
305
305
-
name: &str,
306
306
-
topleft: Point,
307
307
-
bottomright: Point,
308
308
-
color: Color,
309
309
-
) {
286
286
+
pub fn new_rectangle(&self, name: &str, topleft: Point, bottomright: Point, color: Color) {
310
287
canvas().layer(name).add_object(
311
288
name,
312
289
Object::Rectangle(topleft, bottomright).color(Fill::Solid(color)),