Lots of changes!!!

Note: Still in alpha because we are using git as dependency

Change Log:
1) Added support for webp
2) Added result ext to show error and warning dialogs
3) Image save extension is stored as enum now
4) Mime of image is also stored with the name of image
5) Different mime type images are being loaded with own loaders
This commit is contained in:
Piyush मिश्रः 2022-03-24 20:28:29 +05:30
parent a21f638432
commit c0b31a9b31
11 changed files with 513 additions and 237 deletions

367
Cargo.lock generated
View File

@ -29,6 +29,15 @@ dependencies = [
"memchr",
]
[[package]]
name = "approx"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cab112f0a86d568ea0e627cc1d6be74a1e9cd55214684db5561995f6dad897c6"
dependencies = [
"num-traits",
]
[[package]]
name = "atty"
version = "0.2.14"
@ -46,6 +55,12 @@ version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cdb031dd78e28731d87d56cc8ffef4a8f36ca26c38fe2de700543e627f8a464a"
[[package]]
name = "bit_field"
version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dcb6dd1c2376d2e096796e234a70e17e94cc2d5d54ff8ce42b28cef1d0d359a4"
[[package]]
name = "bitflags"
version = "1.3.2"
@ -75,6 +90,19 @@ name = "cc"
version = "1.0.72"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "22a9137b95ea06864e018375b72adfb7db6e6f68cfc8df5a04d00288050485ee"
dependencies = [
"jobserver",
]
[[package]]
name = "cfb"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "74f89d248799e3f15f91b70917f65381062a01bb8e222700ea0e5a7ff9785f9c"
dependencies = [
"byteorder",
"uuid",
]
[[package]]
name = "cfg-if"
@ -228,12 +256,11 @@ checksum = "ef8ae57c4978a2acd8b869ce6b9ca1dfe817bff704c220209fdef2c0b75a01b9"
[[package]]
name = "deflate"
version = "0.8.6"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "73770f8e1fe7d64df17ca66ad28994a0a623ea497fa69486e14984e715c5d174"
checksum = "c86f7e25f518f4b81808a2cf1c50996a61f5c2eb394b2393bd87f2a4780a432f"
dependencies = [
"adler32",
"byteorder",
]
[[package]]
@ -262,6 +289,34 @@ version = "1.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457"
[[package]]
name = "exr"
version = "1.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d4badb9489a465cb2c555af1f00f0bfd8cecd6fc12ac11da9d5b40c5dd5f0200"
dependencies = [
"bit_field",
"deflate",
"flume",
"half",
"inflate",
"lebe",
"smallvec",
"threadpool",
]
[[package]]
name = "flate2"
version = "1.0.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e6988e897c1c9c485f43b47a529cef42fde0547f9d8d41a7062518f1d8fc53f"
dependencies = [
"cfg-if",
"crc32fast",
"libc",
"miniz_oxide 0.4.4",
]
[[package]]
name = "fltk"
version = "1.2.26"
@ -295,6 +350,31 @@ dependencies = [
"lazy_static",
]
[[package]]
name = "flume"
version = "0.10.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b279436a715a9de95dcd26b151db590a71961cc06e54918b24fe0dd5b7d3fc4"
dependencies = [
"futures-core",
"futures-sink",
"nanorand",
"pin-project",
"spin",
]
[[package]]
name = "futures-core"
version = "0.3.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c09fd04b7e4073ac7156a9539b57a484a8ea920f79c7c675d05d289ab6110d3"
[[package]]
name = "futures-sink"
version = "0.3.21"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "21163e139fa306126e6eedaf49ecdb4588f939600f0b1e770f4205ee4b7fa868"
[[package]]
name = "getrandom"
version = "0.1.16"
@ -313,8 +393,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753"
dependencies = [
"cfg-if",
"js-sys",
"libc",
"wasi 0.10.2+wasi-snapshot-preview1",
"wasm-bindgen",
]
[[package]]
@ -327,6 +409,12 @@ dependencies = [
"weezl",
]
[[package]]
name = "half"
version = "1.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eabb4a44450da02c90444cf74558da904edde8fb4e9035a9a6a4e15445af0bd7"
[[package]]
name = "hashbrown"
version = "0.11.2"
@ -350,15 +438,16 @@ dependencies = [
[[package]]
name = "image"
version = "0.23.14"
version = "0.24.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24ffcb7e7244a9bf19d35bf2883b9c080c4ced3c07a9895572178cdb8f13f6a1"
checksum = "db207d030ae38f1eb6f240d5a1c1c88ff422aa005d10f8c6c6fc5e75286ab30e"
dependencies = [
"bytemuck",
"byteorder",
"color_quant",
"exr",
"gif",
"jpeg-decoder",
"jpeg-decoder 0.2.2",
"num-iter",
"num-rational",
"num-traits",
@ -370,17 +459,17 @@ dependencies = [
[[package]]
name = "imageproc"
version = "0.22.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7923654f3ce7cb6849d5dc9e544aaeab49c508a90b56c721b046e7234c74ab53"
source = "git+https://github.com/image-rs/imageproc#b7942657b1a370fc485507693ed4df1f8a116cb7"
dependencies = [
"approx",
"conv",
"image",
"itertools",
"num 0.3.1",
"nalgebra",
"num",
"rand",
"rand_distr",
"rayon",
"rulinalg",
"rusttype",
]
@ -395,10 +484,28 @@ dependencies = [
]
[[package]]
name = "itertools"
version = "0.9.0"
name = "infer"
version = "0.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "284f18f85651fe11e8a991b2adb42cb078325c996ed026d994719efcfca1d54b"
checksum = "20b2b533137b9cad970793453d4f921c2e91312a6d88b1085c07bc15fc51bb3b"
dependencies = [
"cfb",
]
[[package]]
name = "inflate"
version = "0.4.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1cdb29978cc5797bd8dcc8e5bf7de604891df2a8dc576973d71a281e916db2ff"
dependencies = [
"adler32",
]
[[package]]
name = "itertools"
version = "0.10.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a9a9d19fa1e79b6215ff29b9d6880b706147f16e9b1dbb1e4e5947b5b02bc5e3"
dependencies = [
"either",
]
@ -409,11 +516,26 @@ version = "1.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1aab8fc367588b89dcee83ab0fd66b72b50b72fa1904d7095045ace2b0c81c35"
[[package]]
name = "jobserver"
version = "0.1.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "af25a77299a7f711a01975c35a6a424eb6862092cc2d6c72c4ed6cbc56dfc1fa"
dependencies = [
"libc",
]
[[package]]
name = "jpeg-decoder"
version = "0.1.22"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "229d53d58899083193af11e15917b5640cd40b29ff475a1fe4ef725deb02d0f2"
[[package]]
name = "jpeg-decoder"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "105fb082d64e2100074587f59a74231f771750c664af903f1f9f76c9dedfc6f1"
dependencies = [
"rayon",
]
@ -433,12 +555,36 @@ version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
[[package]]
name = "lebe"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7efd1d698db0759e6ef11a7cd44407407399a910c774dd804c64c032da7826ff"
[[package]]
name = "libc"
version = "0.2.112"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b03d17f364a3a042d5e5d46b053bbbf82c92c9430c592dd4c064dc6ee997125"
[[package]]
name = "libwebp-sys"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "439fd1885aa28937e7edcd68d2e793cb4a22f8733460d2519fbafd2b215672bf"
dependencies = [
"cc",
]
[[package]]
name = "lock_api"
version = "0.4.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "88943dd7ef4a2e5a4bfa2753aaab3013e34ce2533d1996fb18ef591e315e2b3b"
dependencies = [
"scopeguard",
]
[[package]]
name = "log"
version = "0.4.14"
@ -450,9 +596,9 @@ dependencies = [
[[package]]
name = "matrixmultiply"
version = "0.1.15"
version = "0.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dcad67dcec2d58ff56f6292582377e6921afdf3bfbd533e26fb8900ae575e002"
checksum = "add85d4dd35074e6fedc608f8c8f513a3548619a9024b751949ef0e8e45a4d84"
dependencies = [
"rawpointer",
]
@ -472,15 +618,6 @@ dependencies = [
"autocfg",
]
[[package]]
name = "miniz_oxide"
version = "0.3.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "791daaae1ed6889560f8c4359194f56648355540573244a5448a83ba1ecc7435"
dependencies = [
"adler32",
]
[[package]]
name = "miniz_oxide"
version = "0.4.4"
@ -492,21 +629,43 @@ dependencies = [
]
[[package]]
name = "num"
version = "0.1.42"
name = "miniz_oxide"
version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4703ad64153382334aa8db57c637364c322d3372e097840c72000dabdcf6156e"
checksum = "d2b29bd4bc3f33391105ebee3589c19197c4271e3e5a9ec9bfe8127eeff8f082"
dependencies = [
"num-integer",
"num-iter",
"adler",
]
[[package]]
name = "nalgebra"
version = "0.30.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4fb2d0de08694bed883320212c18ee3008576bfe8c306f4c3c4a58b4876998be"
dependencies = [
"approx",
"matrixmultiply",
"num-complex",
"num-rational",
"num-traits",
"simba",
"typenum",
]
[[package]]
name = "nanorand"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "729eb334247daa1803e0a094d0a5c55711b85571179f5ec6e53eccfdf7008958"
dependencies = [
"getrandom 0.2.3",
]
[[package]]
name = "num"
version = "0.3.1"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b7a8e9be5e039e2ff869df49155f1c06bd01ade2117ec783e56ab0932b67a8f"
checksum = "43db66d1170d347f9a065114077f7dccb00c1b9478c89384490a3425279a4606"
dependencies = [
"num-bigint",
"num-complex",
@ -518,9 +677,9 @@ dependencies = [
[[package]]
name = "num-bigint"
version = "0.3.3"
version = "0.4.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5f6f7833f2cbf2360a6cfd58cd41a53aa7a90bd4c202f5b1c7dd2ed73c57b2c3"
checksum = "f93ab6289c7b344a8a9f60f88d80aa20032336fe78da341afc91c8a2341fc75f"
dependencies = [
"autocfg",
"num-integer",
@ -529,9 +688,9 @@ dependencies = [
[[package]]
name = "num-complex"
version = "0.3.1"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "747d632c0c558b87dbabbe6a82f3b4ae03720d0646ac5b7b4dae89394be5f2c5"
checksum = "26873667bbbb7c5182d4a37c1add32cdf09f841af72da53318fdb81543c15085"
dependencies = [
"num-traits",
]
@ -559,9 +718,9 @@ dependencies = [
[[package]]
name = "num-rational"
version = "0.3.2"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "12ac428b1cb17fce6f731001d307d351ec70a6d202fc2e60f7d4c5e42d8f4f07"
checksum = "d41702bd167c2df5520b384281bc111a4b5efcf7fbc4c9c222c815b07e0a6a6a"
dependencies = [
"autocfg",
"num-bigint",
@ -613,20 +772,40 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0744126afe1a6dd7f394cb50a716dbe086cb06e255e53d8d0185d82828358fb5"
[[package]]
name = "png"
version = "0.16.8"
name = "pin-project"
version = "1.0.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3c3287920cb847dee3de33d301c463fba14dda99db24214ddf93f83d3021f4c6"
checksum = "58ad3879ad3baf4e44784bc6a718a8698867bb991f8ce24d1bcbe2cfb4c3a75e"
dependencies = [
"pin-project-internal",
]
[[package]]
name = "pin-project-internal"
version = "1.0.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "744b6f092ba29c3650faf274db506afd39944f48420f6c86b17cfe0ee1cb36bb"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "png"
version = "0.17.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dc38c0ad57efb786dd57b9864e5b18bae478c00c824dc55a38bbc9da95dde3ba"
dependencies = [
"bitflags",
"crc32fast",
"deflate",
"miniz_oxide 0.3.7",
"miniz_oxide 0.5.1",
]
[[package]]
name = "post_maker"
version = "0.3.0"
version = "0.4.0-alpha.1"
dependencies = [
"clap",
"dirs",
@ -634,6 +813,7 @@ dependencies = [
"fltk-theme",
"image",
"imageproc",
"infer",
"lazy_static",
"log",
"rusttype",
@ -642,6 +822,7 @@ dependencies = [
"simplelog",
"textwrap",
"webbrowser",
"webp",
]
[[package]]
@ -744,9 +925,9 @@ dependencies = [
[[package]]
name = "rawpointer"
version = "0.1.0"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ebac11a9d2e11f2af219b8b8d833b76b1ea0e054aa0e8d8e9e4cbde353bdf019"
checksum = "60a357793950651c4ed0f3f52338f53b2f809f32d83a07f72909fa13e4c6c1e3"
[[package]]
name = "rayon"
@ -809,16 +990,6 @@ version = "0.6.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f497285884f3fcff424ffc933e56d7cbca511def0c9831a7f9b5f6153e3cc89b"
[[package]]
name = "rulinalg"
version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "04ada202c9685e1d72a7420c578e92b358dbf807d3dfabb676a3dab9cc3bb12f"
dependencies = [
"matrixmultiply",
"num 0.1.42",
]
[[package]]
name = "rusttype"
version = "0.9.2"
@ -835,6 +1006,15 @@ version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "73b4b750c782965c211b42f022f59af1fbceabdd026623714f104152f1ec149f"
[[package]]
name = "safe_arch"
version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "794821e4ccb0d9f979512f9c1973480123f9bd62a90d74ab0f9426fcf8f4a529"
dependencies = [
"bytemuck",
]
[[package]]
name = "scoped_threadpool"
version = "0.1.9"
@ -878,6 +1058,19 @@ dependencies = [
"serde",
]
[[package]]
name = "simba"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13a2609e876d4f77f6ab7ff5254fc39b4f1927ba8e6db3d18be7c32534d3725e"
dependencies = [
"approx",
"num-complex",
"num-traits",
"paste",
"wide",
]
[[package]]
name = "simplelog"
version = "0.11.2"
@ -889,12 +1082,27 @@ dependencies = [
"termcolor",
]
[[package]]
name = "smallvec"
version = "1.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f2dd574626839106c320a323308629dcb1acfc96e32a8cba364ddc61ac23ee83"
[[package]]
name = "smawk"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f67ad224767faa3c7d8b6d91985b78e70a1324408abcb1cfcc2be4c06bc06043"
[[package]]
name = "spin"
version = "0.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "511254be0c5bcf062b019a6c89c01a664aa359ded62f78aa72c6fc137c0590e5"
dependencies = [
"lock_api",
]
[[package]]
name = "strsim"
version = "0.10.0"
@ -933,13 +1141,22 @@ dependencies = [
]
[[package]]
name = "tiff"
version = "0.6.1"
name = "threadpool"
version = "1.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a53f4706d65497df0c4349241deddf35f84cee19c87ed86ea8ca590f4464437"
checksum = "d050e60b33d41c19108b32cea32164033a9013fe3b46cbd4457559bfbf77afaa"
dependencies = [
"jpeg-decoder",
"miniz_oxide 0.4.4",
"num_cpus",
]
[[package]]
name = "tiff"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0247608e998cb6ce39dfc8f4a16c50361ce71e5b52e6d24ea1227ea8ea8ee0b2"
dependencies = [
"flate2",
"jpeg-decoder 0.1.22",
"weezl",
]
@ -959,6 +1176,12 @@ version = "0.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3e5d7cd7ab3e47dda6e56542f4bbf3824c15234958c6e1bd6aaa347e93499fdc"
[[package]]
name = "typenum"
version = "1.15.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dcf81ac59edc17cc8697ff311e8f5ef2d99fcbd9817b34cec66f90b6c3dfd987"
[[package]]
name = "unicode-linebreak"
version = "0.1.2"
@ -980,6 +1203,12 @@ version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ccb82d61f80a663efe1f787a51b16b5a51e3314d6ac365b08639f52387b33f3"
[[package]]
name = "uuid"
version = "0.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7"
[[package]]
name = "version_check"
version = "0.9.4"
@ -1073,12 +1302,32 @@ dependencies = [
"winapi",
]
[[package]]
name = "webp"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cf022f821f166079a407d000ab57e84de020e66ffbbf4edde999bc7d6e371cae"
dependencies = [
"image",
"libwebp-sys",
]
[[package]]
name = "weezl"
version = "0.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d8b77fdfd5a253be4ab714e4ffa3c49caf146b4de743e97510c0656cf90f1e8e"
[[package]]
name = "wide"
version = "0.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b3aba2d1dac31ac7cae82847ac5b8be822aee8f99a4e100f279605016b185c5f"
dependencies = [
"bytemuck",
"safe_arch",
]
[[package]]
name = "widestring"
version = "0.4.3"

View File

@ -1,6 +1,6 @@
[package]
name = "post_maker"
version = "0.3.0"
version = "0.4.0-alpha.1"
edition = "2021"
description = "Post Maker helps you to make post for Instagram and other Social Media apps easily."
authors = ["PiyushXCoder <https://piyushxcoder.in>"]
@ -16,12 +16,14 @@ log = "0.4"
simplelog = "0.11"
fltk = "1.2"
fltk-theme = "0.4"
image = "0.23"
imageproc = "0.22"
image = "0.24.1"
imageproc = { git = "https://github.com/image-rs/imageproc"}
webp = "0.2"
rusttype = "0.9"
serde_json = "1.0"
serde = { version = "1.0", features = ["derive"] }
lazy_static = "1.4"
dirs = "4.0"
infer = "0.7.0"
textwrap = "0.14"
webbrowser = "0.5"

View File

@ -13,11 +13,10 @@
*/
//! About Window
use crate::{config, globals};
use crate::{config, globals, result_ext::ResultExt};
use fltk::{
app,
button::Button,
dialog,
enums::{self, Align, Event},
frame::Frame,
group::Flex,
@ -152,10 +151,7 @@ impl About {
// Repository Link
self.repo_link.handle(|_, ev| {
if ev == Event::Push {
if let Err(e) = webbrowser::open(env!("CARGO_PKG_REPOSITORY")) {
dialog::alert_default("Failed to open the link!");
warn!("Failed to open the link!\n{:?}", e);
}
webbrowser::open(env!("CARGO_PKG_REPOSITORY")).warn_log("Failed to open the link!");
}
true
});
@ -163,10 +159,7 @@ impl About {
// Developer's Link
self.dev_link.handle(|_, ev| {
if ev == Event::Push {
if let Err(e) = webbrowser::open("https://piyushxcoder.in") {
dialog::alert_default("Failed to open the link!");
warn!("Failed to open the link!\n{:?}", e);
}
webbrowser::open("https://piyushxcoder.in").warn_log("Failed to open the link!");
}
true
});
@ -174,10 +167,7 @@ impl About {
// License Link
self.license_link.handle(|_, ev| {
if ev == Event::Push {
if let Err(e) = webbrowser::open("https://www.gnu.org/licenses/gpl-3.0.html") {
dialog::alert_default("Failed to open the link!");
warn!("Failed to open the link!\n{:?}", e);
}
webbrowser::open("https://www.gnu.org/licenses/gpl-3.0.html").warn_log("Failed to open the link!");
}
true
});

View File

@ -13,7 +13,7 @@
*/
//! load, save configuration and parse cli args
use crate::{config_picker::ConfigPicker, globals};
use crate::{config_picker::ConfigPicker, globals, result_ext::ResultExt, utils::ImageType};
use clap::{ArgEnum, Parser};
use fltk::dialog;
use fltk_theme::ThemeType;
@ -128,7 +128,7 @@ pub(crate) struct ConfigFile {
pub(crate) tag2_position_ratio: f64,
pub(crate) image_ratio: (f64, f64),
pub(crate) color_layer: [u8; 4],
pub(crate) image_format: String,
pub(crate) image_format: ImageType,
}
impl Default for ConfigFile {
@ -151,7 +151,7 @@ impl Default for ConfigFile {
tag2_position_ratio: 0.95,
image_ratio: (4.0, 5.0),
color_layer: [20, 22, 25, 197],
image_format: "png".to_owned(),
image_format: ImageType::Png,
}
}
}
@ -207,28 +207,10 @@ pub(crate) fn get_configs() -> Option<HashMap<String, ConfigFile>> {
/// Save configs
pub(crate) fn save_configs(configs: HashMap<String, ConfigFile>) {
if let Err(e) = std::fs::write(&*CONFIG_FILE, serde_json::to_string(&configs).unwrap()) {
dialog::alert_default("Can't write config!");
error!("Can't write config!\n{:?}", e);
panic!("Can't write config!\n{:?}", e);
}
std::fs::write(&*CONFIG_FILE, serde_json::to_string(&configs).unwrap()).expect_log("Can't write config!");
}
pub(crate) fn log_file() -> File {
// match File::open(&*LOG_FILE) {
// Ok(mut file) => {
// if is_file_30_days_old(&file) {
// match File::create(&*LOG_FILE) {
// Ok(f) => file = f,
// Err(e) => {
// dialog::alert_default("Can't open log file!");
// panic!("{:?}", e);
// }
// }
// }
// file
// }
// Err(_) =>
match File::create(&*LOG_FILE) {
Ok(f) => f,
Err(e) => {
@ -236,18 +218,4 @@ pub(crate) fn log_file() -> File {
panic!("{:?}", e);
}
}
// }
}
// pub(crate) fn is_file_30_days_old(file: &File) -> bool {
// if let Ok(meta) = file.metadata() {
// if let Ok(time) = meta.created() {
// if let Ok(dur) = SystemTime::now().duration_since(time) {
// if dur > Duration::from_secs(60 * 60 * 24 * 30) {
// return true;
// }
// }
// }
// }
// false
// }

View File

@ -33,7 +33,7 @@ use fltk::{
use crate::{
config::{self, ConfigFile},
globals, utils,
globals, utils::{self, ImageType},
};
pub(crate) struct ConfigWindow {
@ -505,12 +505,12 @@ impl ConfigWindow {
self.translucent_layer_alpha
.set_value(config.color_layer[3] as f64);
match config.image_format.as_str() {
"png" => {
match config.image_format {
utils::ImageType::Png => {
self.png_format.set_value(true);
self.jpeg_format.set_value(false)
}
"jpg" => {
utils::ImageType::Jpeg => {
self.png_format.set_value(false);
self.jpeg_format.set_value(true)
}
@ -1063,7 +1063,7 @@ impl ConfigWindow {
.borrow_mut()
.get_mut(&browse.selected_text().unwrap())
{
conf.image_format = "png".to_owned();
conf.image_format = ImageType::Png;
}
});
@ -1075,7 +1075,7 @@ impl ConfigWindow {
.borrow_mut()
.get_mut(&browse.selected_text().unwrap())
{
conf.image_format = "jpg".to_owned();
conf.image_format = ImageType::Jpeg;
}
});

View File

@ -15,7 +15,7 @@
//! Window to change Crop properties of image
use crate::{
globals,
utils::{self, Coord, ImageContainer, ImageProperties},
utils::{self, Coord, ImageContainer, ImageProperties, ImageInfo},
};
use fltk::{
app, button::Button, draw, enums::Event, frame::Frame, group::Flex, image::SvgImage,
@ -24,7 +24,6 @@ use fltk::{
use image::GenericImageView;
use std::{
cell::RefCell,
path::PathBuf,
rc::Rc,
sync::{Arc, RwLock},
};
@ -108,7 +107,7 @@ impl CropWindow {
/// Call it to show window to crop image
pub(crate) fn load_to_crop(
&mut self,
path: &PathBuf,
path: &ImageInfo,
crop_pos: Option<(f64, f64)>,
) -> Option<(f64, f64)> {
let mut container =

View File

@ -14,7 +14,8 @@
//! Thread to manage drawing in background
use crate::utils::{ImageContainer, ImageProperties};
use crate::result_ext::ResultExt;
use crate::utils::{ImageContainer, ImageProperties, ImageInfo};
use crate::{
main_window::{MainWindow, Page},
utils::{self, ImagePropertiesFile},
@ -33,7 +34,7 @@ use fltk::{
};
use std::{
fs,
path::{Path, PathBuf},
path::{Path},
sync::{mpsc, Arc, RwLock},
};
@ -85,7 +86,7 @@ pub(crate) fn spawn_image_thread(
let mut status = main_win.status.clone();
let mut count = main_win.count.clone();
let mut dimension = main_win.dimension.clone();
let images_path = Arc::clone(&main_win.images_path);
let images_path = Arc::clone(&main_win.images_list);
let mut _container: Option<ImageContainer> = None;
std::thread::spawn(move || loop {
@ -179,13 +180,13 @@ pub(crate) fn spawn_image_thread(
if let Some(cont) = &mut _container {
status.set_label("Cloning...");
win.deactivate();
if let Some(path) = cont.clone_img() {
if let Some(image_info) = cont.clone_img() {
let idx = file_choice.value();
let mut imgs = images_path.write().unwrap();
imgs.insert(idx as usize, path.clone());
imgs.insert(idx as usize, image_info.clone());
file_choice.insert(
idx,
path.file_name().unwrap().to_str().unwrap(),
image_info.path.file_name().unwrap().to_str().unwrap(),
enums::Shortcut::None,
menu::MenuFlag::Normal,
|a| a.do_callback(),
@ -225,7 +226,7 @@ pub(crate) fn spawn_image_thread(
/// Loads the selected image in file_choice to ImageContainer to edit
fn load_image(
file_choice: &mut menu::Choice,
images_path: Arc<RwLock<Vec<PathBuf>>>,
images_list: Arc<RwLock<Vec<ImageInfo>>>,
crop: Option<(f64, f64)>,
quote: &mut MultilineInput,
subquote: &mut MultilineInput,
@ -251,19 +252,19 @@ fn load_image(
properties: Arc<RwLock<ImageProperties>>,
container: &mut Option<ImageContainer>,
) {
let imgs = images_path.read().unwrap();
let imgs = images_list.read().unwrap();
if imgs.len() == 0 {
*container = None;
flush_buffer(app_sender, container);
return;
}
count.set_label(&format!("[{}/{}]", file_choice.value() + 1, imgs.len()));
let file = imgs.get(file_choice.value() as usize).unwrap();
let image_info = imgs.get(file_choice.value() as usize).unwrap();
*container = Some(ImageContainer::new(&file, Arc::clone(&properties)));
*container = Some(ImageContainer::new(&image_info, Arc::clone(&properties)));
if let Some(cont) = container {
let file = Path::new(&file);
let file = Path::new(&image_info.path);
let properties_file = file.with_extension("prop");
let read = fs::read_to_string(&properties_file).unwrap_or("{}".to_owned());
@ -273,10 +274,7 @@ fn load_image(
warn!("Config is corrupt\n{:?}", e);
match dialog::choice_default("Config is corrupt, fix??", "yes", "no", "") {
1 => {
if let Err(e) = fs::remove_file(&properties_file) {
dialog::alert_default("Failed to delete image properties file!");
warn!("Failed to delete image properties file!\n{:?}", e);
}
fs::remove_file(&properties_file).warn_log("Failed to delete image properties file!");
ImagePropertiesFile::default()
}
_ => return,

View File

@ -26,6 +26,7 @@ mod draw_thread;
mod globals;
mod main_window;
mod utils;
mod result_ext;
use fltk::{
app::{channel, App},

View File

@ -16,7 +16,10 @@
use crate::about_window::About;
use crate::crop_window::CropWindow;
use crate::draw_thread::*;
use crate::result_ext::ResultExt;
use crate::utils;
use crate::utils::ImageInfo;
use crate::utils::ImageType;
use crate::utils::ImageProperties;
use crate::{config_window::ConfigWindow, globals};
use fltk::{
@ -38,7 +41,7 @@ use fltk::{
};
use std::path::PathBuf;
use std::sync::{mpsc, RwLock};
use std::{ffi::OsStr, fs, sync::Arc};
use std::{ fs, sync::Arc};
pub(crate) struct MainWindow {
pub(crate) win: Window,
@ -81,7 +84,7 @@ pub(crate) struct MainWindow {
pub(crate) count: Frame,
pub(crate) dimension: Frame,
pub(crate) page: Page,
pub(crate) images_path: Arc<RwLock<Vec<PathBuf>>>,
pub(crate) images_list: Arc<RwLock<Vec<ImageInfo>>>,
pub(crate) draw_buff: Arc<RwLock<Option<Vec<u8>>>>,
pub(crate) properties: Arc<RwLock<ImageProperties>>,
pub(crate) sender: mpsc::Sender<DrawMessage>,
@ -380,7 +383,7 @@ impl MainWindow {
status,
count,
dimension,
images_path: Arc::new(RwLock::new(vec![])),
images_list: Arc::new(RwLock::new(vec![])),
draw_buff,
properties: Arc::clone(&properties),
page: Page {
@ -401,7 +404,7 @@ impl MainWindow {
fn menu(&mut self) {
let mut file_choice = self.file_choice.clone();
let sender = self.sender.clone();
let imgs = Arc::clone(&self.images_path);
let imgs = Arc::clone(&self.images_list);
self.menubar.add(
"&File/Open Folder...\t",
Shortcut::Ctrl | 'o',
@ -418,8 +421,7 @@ impl MainWindow {
let expost_dir = path.join("export");
if !expost_dir.exists() {
if let Err(e) = fs::create_dir(expost_dir) {
fltk::dialog::alert_default("Failed to create export folder!");
warn!("Failed to create export folder!\n{:?}", e);
Result::<(), _>::Err(e).warn_log("Failed to create export folder!");
return;
}
}
@ -492,10 +494,10 @@ impl MainWindow {
// Resest Button for FileChoice
let mut file_choice = self.file_choice.clone();
let sender = self.sender.clone();
let imgs = Arc::clone(&self.images_path);
let imgs = Arc::clone(&self.images_list);
self.reset_file_choice.set_callback(move |_| {
let path = match imgs.read().unwrap().first() {
Some(path) => path.parent().unwrap().to_path_buf(),
Some(image_info) => image_info.path.parent().unwrap().to_path_buf(),
None => return,
};
load_dir(&path, Arc::clone(&imgs), &mut file_choice, &sender);
@ -662,8 +664,8 @@ impl MainWindow {
let sender = self.sender.clone();
self.crop_btn.set_callback(move |_| {
let mut prop = properties.write().unwrap();
if let Some(path) = &prop.path {
if let Some((x, y)) = crop_win.load_to_crop(path, prop.crop_position) {
if let Some(image_info) = &prop.image_info {
if let Some((x, y)) = crop_win.load_to_crop(&image_info, prop.crop_position) {
sender.send(DrawMessage::ChangeCrop((x, y))).unwrap();
prop.is_saved = false;
}
@ -1004,7 +1006,7 @@ impl MainWindow {
/// Load all iamges in a directory
fn load_dir(
path: &PathBuf,
imgs: Arc<RwLock<Vec<PathBuf>>>,
imgs: Arc<RwLock<Vec<ImageInfo>>>,
file_choice: &mut menu::Choice,
sender: &mpsc::Sender<DrawMessage>,
) {
@ -1018,11 +1020,15 @@ fn load_dir(
*imgs_b = vec![];
for file in files {
let path = file.path();
if path.extension() == Some(OsStr::new("jpg"))
|| path.extension() == Some(OsStr::new("png"))
{
if let Ok(Some(ty)) = infer::get_from_path(&path) {
let mime = ty.mime_type();
match ImageType::from_mime(mime) {
ImageType::None => (),
_ => {
text = format!("{}|{}", text, path.file_name().unwrap().to_str().unwrap());
imgs_b.push(path);
imgs_b.push(ImageInfo { path, image_type: ImageType::from_mime(mime) });
}
}
}
}
if text.len() == 0 {

39
src/result_ext.rs Normal file
View File

@ -0,0 +1,39 @@
use std::fmt::Debug;
use std::panic::Location;
use fltk::dialog;
pub trait ResultExt<T, E> {
fn expect_log(self, msg: &str) -> T;
fn error_log(&self, msg: &str);
fn warn_log(&self, msg: &str);
}
impl<T, E: Debug> ResultExt<T, E> for Result<T, E> {
#[track_caller]
fn expect_log(self, msg: &str) -> T {
match self {
Ok(v) => v,
Err(e) => {
dialog::alert_default(msg);
error!("{}\n{:?}\n{}", msg, e, Location::caller());
std::process::exit(1);
}
}
}
#[track_caller]
fn error_log(&self, msg: &str) {
if let Err(e) = self {
dialog::alert_default(msg);
error!("{}\n{:?}\n{}", msg, e, Location::caller());
}
}
#[track_caller]
fn warn_log(&self, msg: &str) {
if let Err(e) = self {
dialog::alert_default(msg);
warn!("{}\n{:?}", msg, e);
}
}
}

View File

@ -15,13 +15,14 @@
use std::{
fs::{self, File},
path::{Path, PathBuf},
sync::{Arc, RwLock},
sync::{Arc, RwLock}, io::Read
};
use fltk::{button::Button, dialog, enums, prelude::*};
use fltk::{button::Button, enums, prelude::*};
use image::{DynamicImage, GenericImageView, ImageBuffer, ImageEncoder};
use serde::{Deserialize, Serialize};
use crate::result_ext::ResultExt;
use crate::globals;
/// helps cast tupels to f64
@ -63,6 +64,40 @@ impl Into<(i32, i32)> for Coord {
}
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub(crate) struct ImageInfo {
pub(crate) path: PathBuf,
pub(crate) image_type: ImageType
}
#[derive(Serialize, Deserialize, Debug, Clone)]
pub(crate) enum ImageType {
Jpeg,
Png,
Webp,
None
}
impl ImageType {
pub(crate) fn from_mime(v: &str) -> Self {
match v {
"image/jpeg" | "image/jpg" => Self::Jpeg,
"image/png" => Self::Png,
"image/webp" => Self::Webp,
_ => Self::None
}
}
pub(crate) fn as_extension(&self) -> String {
match self {
Self::Jpeg => "jpg",
Self::Png => "png",
Self::Webp => "webp",
Self::None => "none"
}.to_owned()
}
}
/// Contains Image and its buffer(edited image)
#[derive(Debug, Clone)]
pub(crate) struct ImageContainer {
@ -72,22 +107,13 @@ pub(crate) struct ImageContainer {
}
impl ImageContainer {
pub(crate) fn new(path: &PathBuf, properties: Arc<RwLock<ImageProperties>>) -> Self {
let img = match image::open(path) {
Ok(i) => i,
Err(e) => {
dialog::alert_default("Failed to open image!");
error!("Failed to open image\n{:?}", e);
panic!("Failed to open image\n{:?}", e);
}
};
let img = DynamicImage::ImageRgb8(img.into_rgb8());
pub(crate) fn new(image_info: &ImageInfo, properties: Arc<RwLock<ImageProperties>>) -> Self {
let img = load_image(&image_info);
let (width, height): (f64, f64) = Coord::from(img.dimensions()).into();
let config = globals::CONFIG.read().unwrap();
let mut prop = properties.write().unwrap();
prop.path = Some(path.to_owned());
prop.image_info = Some(image_info.to_owned());
prop.original_dimension = (width, height);
prop.quote_position = height * config.quote_position_ratio;
prop.subquote_position = height * config.subquote_position_ratio;
@ -185,16 +211,16 @@ impl ImageContainer {
pub(crate) fn save(&self) {
let prop = self.properties.read().unwrap();
let path_original = match &prop.path {
Some(p) => Path::new(p),
let (path_original, mut original_image) = match &prop.image_info {
Some(p) => (Path::new(&p.path), load_image(p)),
None => return,
};
let path_properties = path_original.with_extension("prop");
let config = globals::CONFIG.read().unwrap();
let export_format = config.image_format.as_str();
let export_format = &config.image_format;
let export = path_original.parent().unwrap().join("export").join(
path_original
.with_extension(export_format)
.with_extension(export_format.as_extension())
.file_name()
.unwrap()
.to_str()
@ -202,20 +228,13 @@ impl ImageContainer {
);
let mut prop = prop.clone();
prop.path = None;
if let Err(e) = fs::write(
&path_properties,
serde_json::to_string(&ImagePropertiesFile::from(&prop)).unwrap(),
) {
dialog::alert_default("Failed to save properties!");
warn!("Failed to save properties!\n{:?}", e);
}
prop.image_info = None;
fs::write(&path_properties, serde_json::to_string(&ImagePropertiesFile::from(&prop)).unwrap()).warn_log("Failed to save properties!");
let mut img = image::open(&path_original).unwrap();
let (width, height): (f64, f64) = Coord::from(img.dimensions()).into();
let (width, height): (f64, f64) = Coord::from(original_image.dimensions()).into();
let (crop_x, crop_y) = prop.crop_position.unwrap();
let (crop_width, crop_height) = croped_ratio(width, height);
let mut img = img.crop(
let mut img = original_image.crop(
crop_x as u32,
crop_y as u32,
crop_width as u32,
@ -241,75 +260,62 @@ impl ImageContainer {
let mut output = match File::create(&export) {
Ok(a) => a,
Err(e) => {
dialog::alert_default("Failed to write to disk!");
warn!("Failed to write to disk!\n{:?}", e);
Result::<(), _>::Err(e).warn_log("Failed to write to disk!");
return;
}
};
match export_format {
"png" => {
ImageType::Png => {
let encoder = image::codecs::png::PngEncoder::new_with_quality(
&mut output,
image::png::CompressionType::Default,
image::png::FilterType::NoFilter,
image::codecs::png::CompressionType::Best,
image::codecs::png::FilterType::Sub
);
let (w, h) = img.dimensions();
if let Err(e) =
encoder.write_image(&img.into_rgba8(), w, h, image::ColorType::Rgba8)
{
dialog::alert_default("Failed to export Image!");
warn!("Failed to export Image!\n{:?}", e);
encoder.write_image(&img.into_rgba8(), w, h, image::ColorType::Rgba8).warn_log("Failed to export Image!");
}
}
"jpg" => {
ImageType::Jpeg => {
let mut encoder =
image::codecs::jpeg::JpegEncoder::new_with_quality(&mut output, 100);
encoder.set_pixel_density(image::codecs::jpeg::PixelDensity::dpi(300));
if let Err(e) = encoder.encode_image(&img) {
dialog::alert_default("Failed to export Image!");
warn!("Failed to export Image!\n{:?}", e);
}
encoder.encode_image(&img).warn_log("Failed to export Image!");
}
_ => (),
}
}
pub(crate) fn clone_img(&self) -> Option<PathBuf> {
pub(crate) fn clone_img(&self) -> Option<ImageInfo> {
let prop = self.properties.read().unwrap();
match &prop.path {
Some(path) => {
let name = path.file_stem().unwrap().to_string_lossy();
let ext = path.extension().unwrap().to_string_lossy();
match &prop.image_info {
Some(image_info) => {
let name = image_info.path.file_stem().unwrap().to_string_lossy();
let ext = image_info.path.extension().unwrap().to_string_lossy();
let mut i = 1;
let mut new_path = path.clone();
let mut new_path = image_info.path.clone();
while new_path.exists() {
let new_file = format!("{}{}.{}", name, "-copy".repeat(i), ext);
new_path = path.with_file_name(&new_file);
new_path = image_info.path.with_file_name(&new_file);
i += 1;
}
let path_properties = path.with_extension("prop");
let path_properties = image_info.path.with_extension("prop");
let path_properties_new = new_path.with_extension("prop");
if path.exists() {
if let Err(e) = fs::copy(path, &new_path) {
dialog::alert_default("Failed to clone image!");
warn!("Failed to clone image!\n{:?}", e);
return None;
}
if image_info.path.exists() {
fs::copy(&image_info.path, &new_path).warn_log("Failed to clone image!");
}
if path_properties.exists() {
if let Err(e) = fs::copy(path_properties, &path_properties_new) {
dialog::alert_default("Failed to clone image properties!");
warn!("Failed to clone image properties!\n{:?}", e);
fs::copy(path_properties, &path_properties_new).warn_log("Failed to clone image properties!");
}
}
Some(new_path)
Some(ImageInfo {
path: new_path,
image_type: image_info.image_type.clone()
})
}
None => None,
}
@ -318,10 +324,10 @@ impl ImageContainer {
pub(crate) fn delete(&self) {
let prop = self.properties.read().unwrap();
let config = globals::CONFIG.read().unwrap();
let export_format = config.image_format.as_str();
let export_format = config.image_format.as_extension();
let path_original = match &prop.path {
Some(p) => Path::new(p),
let path_original = match &prop.image_info {
Some(p) => Path::new(&p.path),
None => return,
};
let path_properties = path_original.with_extension("prop");
@ -335,24 +341,15 @@ impl ImageContainer {
);
if path_original.exists() {
if let Err(e) = fs::remove_file(path_original) {
dialog::alert_default("Failed to delete image!");
warn!("Failed to delete image!\n{:?}", e);
}
fs::remove_file(path_original).warn_log("Failed to delete image!");
}
if path_properties.exists() {
if let Err(e) = fs::remove_file(path_properties) {
dialog::alert_default("Failed to delete image properties!");
warn!("Failed to delete image properties!\n{:?}", e);
}
fs::remove_file(path_properties).warn_log("Failed to delete image properties!");
}
if export.exists() {
if let Err(e) = fs::remove_file(export) {
dialog::alert_default("Failed to delete exported image!");
warn!("Failed to delete exported image!\n{:?}", e);
}
fs::remove_file(export).warn_log("Failed to delete exported image!");
}
}
}
@ -415,7 +412,7 @@ impl From<&ImageProperties> for ImagePropertiesFile {
/// Properties of loaded image
#[derive(Serialize, Deserialize, Debug, Clone)]
pub(crate) struct ImageProperties {
pub(crate) path: Option<PathBuf>,
pub(crate) image_info: Option<ImageInfo>,
pub(crate) dimension: (f64, f64),
pub(crate) original_dimension: (f64, f64),
pub(crate) crop_position: Option<(f64, f64)>,
@ -436,7 +433,7 @@ pub(crate) struct ImageProperties {
impl Default for ImageProperties {
fn default() -> Self {
Self {
path: None,
image_info: None,
dimension: (0.0, 0.0),
original_dimension: (0.0, 0.0),
crop_position: None,
@ -480,6 +477,33 @@ impl ImageProperties {
}
}
/// Load image as Dynamic Image
fn load_image(image_info: &ImageInfo) -> DynamicImage {
let img = match image_info.image_type {
ImageType::Webp => {
let mut f = File::open(&image_info.path).expect_log("Failed to open image!");
let mut buf = vec![];
f.read_to_end(&mut buf).unwrap();
let a = webp::Decoder::new(&buf).decode().unwrap();
a.to_image()
}
ImageType::Jpeg => {
let dec = image::codecs::jpeg::JpegDecoder::new(File::open(&image_info.path).expect_log("Failed to open image!")).expect_log("Failed to decode image!");
DynamicImage::from_decoder(dec).expect_log("Failed to open image!")
}
ImageType::Png => {
let dec = image::codecs::png::PngDecoder::new(File::open(&image_info.path).expect_log("Failed to open image!")).expect_log("Failed to decode image!");
DynamicImage::from_decoder(dec).expect_log("Failed to open image!")
}
ImageType::None => {
Result::<(), _>::Err("Failed to open image!").expect_log("");
std::process::exit(1);
}
};
DynamicImage::ImageRgb8(img.into_rgb8())
}
/// Draw text and stuffs on image
fn draw_layer_and_text(
tmp: &mut DynamicImage,
@ -552,8 +576,8 @@ fn draw_layer_and_text(
imageproc::drawing::draw_text_mut(
tmp,
image::Rgba([255, 255, 255, 255]),
(width * 0.99 - text_width) as u32,
((tag_position * height) / original_height + index as f64 * (text_height * 1.2)) as u32,
(width * 0.99 - text_width) as i32,
((tag_position * height) / original_height + index as f64 * (text_height * 1.2)) as i32,
rusttype::Scale::uniform(size as f32),
&globals::FONT_TAG,
line,
@ -578,8 +602,8 @@ pub(crate) fn draw_multiline_mid_string(
imageproc::drawing::draw_text_mut(
tmp,
image::Rgba([255, 255, 255, 255]),
((width - text_width) / 2.0) as u32,
((position * height) / original_height + index as f64 * (text_height * 1.15)) as u32,
((width - text_width) / 2.0) as i32,
((position * height) / original_height + index as f64 * (text_height * 1.15)) as i32,
rusttype::Scale::uniform(size as f32),
font,
line,