refactor progress
This commit is contained in:
807
fluxer_app/crates/libfluxcore/Cargo.lock
generated
807
fluxer_app/crates/libfluxcore/Cargo.lock
generated
@@ -2,12 +2,174 @@
|
||||
# It is not intended for manual editing.
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "adler2"
|
||||
version = "2.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"
|
||||
|
||||
[[package]]
|
||||
name = "aligned"
|
||||
version = "0.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ee4508988c62edf04abd8d92897fca0c2995d907ce1dfeaf369dac3716a40685"
|
||||
dependencies = [
|
||||
"as-slice",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "aligned-vec"
|
||||
version = "0.6.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dc890384c8602f339876ded803c97ad529f3842aba97f6392b3dba0dd171769b"
|
||||
dependencies = [
|
||||
"equator",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.100"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61"
|
||||
|
||||
[[package]]
|
||||
name = "arbitrary"
|
||||
version = "1.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c3d036a3c4ab069c7b410a2ce876bd74808d2d0888a82667669f8e783a898bf1"
|
||||
|
||||
[[package]]
|
||||
name = "arg_enum_proc_macro"
|
||||
version = "0.3.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0ae92a5119aa49cdbcf6b9f893fe4e1d98b04ccbf82ee0584ad948a44a734dea"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "arrayvec"
|
||||
version = "0.7.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50"
|
||||
|
||||
[[package]]
|
||||
name = "as-slice"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "516b6b4f0e40d50dcda9365d53964ec74560ad4284da2e7fc97122cd83174516"
|
||||
dependencies = [
|
||||
"stable_deref_trait",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
|
||||
|
||||
[[package]]
|
||||
name = "av-scenechange"
|
||||
version = "0.14.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0f321d77c20e19b92c39e7471cf986812cbb46659d2af674adc4331ef3f18394"
|
||||
dependencies = [
|
||||
"aligned",
|
||||
"anyhow",
|
||||
"arg_enum_proc_macro",
|
||||
"arrayvec",
|
||||
"log",
|
||||
"num-rational",
|
||||
"num-traits",
|
||||
"pastey",
|
||||
"rayon",
|
||||
"thiserror",
|
||||
"v_frame",
|
||||
"y4m",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "av1-grain"
|
||||
version = "0.2.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8cfddb07216410377231960af4fcab838eaa12e013417781b78bd95ee22077f8"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"arrayvec",
|
||||
"log",
|
||||
"nom",
|
||||
"num-rational",
|
||||
"v_frame",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "avif-serialize"
|
||||
version = "0.8.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "47c8fbc0f831f4519fe8b810b6a7a91410ec83031b8233f730a0480029f6a23f"
|
||||
dependencies = [
|
||||
"arrayvec",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "2.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3"
|
||||
|
||||
[[package]]
|
||||
name = "bitstream-io"
|
||||
version = "4.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "60d4bd9d1db2c6bdf285e223a7fa369d5ce98ec767dec949c6ca62863ce61757"
|
||||
dependencies = [
|
||||
"core2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "built"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f4ad8f11f288f48ca24471bbd51ac257aaeaaa07adae295591266b792902ae64"
|
||||
|
||||
[[package]]
|
||||
name = "bumpalo"
|
||||
version = "3.19.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43"
|
||||
|
||||
[[package]]
|
||||
name = "bytemuck"
|
||||
version = "1.24.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1fbdf580320f38b612e485521afda1ee26d10cc9884efaaa750d383e13e3c5f4"
|
||||
|
||||
[[package]]
|
||||
name = "byteorder-lite"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495"
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.2.52"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cd4932aefd12402b36c60956a4fe0035421f544799057659ff86f923657aada3"
|
||||
dependencies = [
|
||||
"find-msvc-tools",
|
||||
"jobserver",
|
||||
"libc",
|
||||
"shlex",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.4"
|
||||
@@ -20,6 +182,112 @@ version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b"
|
||||
|
||||
[[package]]
|
||||
name = "core2"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b49ba7ef1ad6107f8824dbe97de947cbaac53c44e7f9756a1fba0d37c1eec505"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crc32fast"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-deque"
|
||||
version = "0.8.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51"
|
||||
dependencies = [
|
||||
"crossbeam-epoch",
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-epoch"
|
||||
version = "0.9.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e"
|
||||
dependencies = [
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-utils"
|
||||
version = "0.8.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.15.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719"
|
||||
|
||||
[[package]]
|
||||
name = "equator"
|
||||
version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4711b213838dfee0117e3be6ac926007d7f433d7bbe33595975d4190cb07e6fc"
|
||||
dependencies = [
|
||||
"equator-macro",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "equator-macro"
|
||||
version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "44f23cf4b44bfce11a86ace86f8a73ffdec849c9fd00a386a53d278bd9e81fb3"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fdeflate"
|
||||
version = "0.3.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e6853b52649d4ac5c0bd02320cddc5ba956bdb407c4b75a2c6b75bf51500f8c"
|
||||
dependencies = [
|
||||
"simd-adler32",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "find-msvc-tools"
|
||||
version = "0.1.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f449e6c6c08c865631d4890cfacf252b3d396c9bcc83adb6623cdb02a8336c41"
|
||||
|
||||
[[package]]
|
||||
name = "flate2"
|
||||
version = "1.1.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b375d6465b98090a5f25b1c7703f3859783755aa9a80433b36e0379a3ec2f369"
|
||||
dependencies = [
|
||||
"crc32fast",
|
||||
"miniz_oxide",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.3.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"r-efi",
|
||||
"wasip2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gif"
|
||||
version = "0.13.3"
|
||||
@@ -30,21 +298,272 @@ dependencies = [
|
||||
"weezl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "image"
|
||||
version = "0.25.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e6506c6c10786659413faa717ceebcb8f70731c0a60cbae39795fdf114519c1a"
|
||||
dependencies = [
|
||||
"bytemuck",
|
||||
"byteorder-lite",
|
||||
"image-webp",
|
||||
"moxcms",
|
||||
"num-traits",
|
||||
"png 0.18.0",
|
||||
"ravif",
|
||||
"rgb",
|
||||
"zune-core",
|
||||
"zune-jpeg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "image-webp"
|
||||
version = "0.2.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "525e9ff3e1a4be2fbea1fdf0e98686a6d98b4d8f937e1bf7402245af1909e8c3"
|
||||
dependencies = [
|
||||
"byteorder-lite",
|
||||
"quick-error",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "imgref"
|
||||
version = "1.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e7c5cedc30da3a610cac6b4ba17597bdf7152cf974e8aab3afb3d54455e371c8"
|
||||
|
||||
[[package]]
|
||||
name = "interpolate_name"
|
||||
version = "0.2.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c34819042dc3d3971c46c2190835914dfbe0c3c13f61449b2997f4e9722dfa60"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "itertools"
|
||||
version = "0.14.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285"
|
||||
dependencies = [
|
||||
"either",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "jobserver"
|
||||
version = "0.1.34"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.180"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bcc35a38544a891a5f7c865aca548a982ccb3b8650a5b06d0fd33a10283c56fc"
|
||||
|
||||
[[package]]
|
||||
name = "libfluxcore"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"gif",
|
||||
"image",
|
||||
"png 0.17.16",
|
||||
"ruzstd",
|
||||
"serde",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "libfuzzer-sys"
|
||||
version = "0.4.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5037190e1f70cbeef565bd267599242926f724d3b8a9f510fd7e0b540cfa4404"
|
||||
dependencies = [
|
||||
"arbitrary",
|
||||
"cc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.29"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897"
|
||||
|
||||
[[package]]
|
||||
name = "loop9"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0fae87c125b03c1d2c0150c90365d7d6bcc53fb73a9acaef207d2d065860f062"
|
||||
dependencies = [
|
||||
"imgref",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "maybe-rayon"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8ea1f30cedd69f0a2954655f7188c6a834246d2bcf1e315e2ac40c4b24dc9519"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.7.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273"
|
||||
|
||||
[[package]]
|
||||
name = "miniz_oxide"
|
||||
version = "0.8.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316"
|
||||
dependencies = [
|
||||
"adler2",
|
||||
"simd-adler32",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "moxcms"
|
||||
version = "0.7.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac9557c559cd6fc9867e122e20d2cbefc9ca29d80d027a8e39310920ed2f0a97"
|
||||
dependencies = [
|
||||
"num-traits",
|
||||
"pxfm",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "new_debug_unreachable"
|
||||
version = "1.0.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086"
|
||||
|
||||
[[package]]
|
||||
name = "nom"
|
||||
version = "8.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "df9761775871bdef83bee530e60050f7e54b1105350d6884eb0fb4f46c2f9405"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "noop_proc_macro"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0676bb32a98c1a483ce53e500a81ad9c3d5b3f7c920c28c24e9cb0980d0b5bc8"
|
||||
|
||||
[[package]]
|
||||
name = "num-bigint"
|
||||
version = "0.4.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9"
|
||||
dependencies = [
|
||||
"num-integer",
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-derive"
|
||||
version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-integer"
|
||||
version = "0.1.46"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f"
|
||||
dependencies = [
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-rational"
|
||||
version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824"
|
||||
dependencies = [
|
||||
"num-bigint",
|
||||
"num-integer",
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-traits"
|
||||
version = "0.2.19"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.21.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
|
||||
|
||||
[[package]]
|
||||
name = "paste"
|
||||
version = "1.0.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a"
|
||||
|
||||
[[package]]
|
||||
name = "pastey"
|
||||
version = "0.1.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "35fb2e5f958ec131621fdd531e9fc186ed768cbe395337403ae56c17a74c68ec"
|
||||
|
||||
[[package]]
|
||||
name = "png"
|
||||
version = "0.17.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "82151a2fc869e011c153adc57cf2789ccb8d9906ce52c0b39a6b5697749d7526"
|
||||
dependencies = [
|
||||
"bitflags 1.3.2",
|
||||
"crc32fast",
|
||||
"fdeflate",
|
||||
"flate2",
|
||||
"miniz_oxide",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "png"
|
||||
version = "0.18.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "97baced388464909d42d89643fe4361939af9b7ce7a31ee32a168f832a70f2a0"
|
||||
dependencies = [
|
||||
"bitflags 2.10.0",
|
||||
"crc32fast",
|
||||
"fdeflate",
|
||||
"flate2",
|
||||
"miniz_oxide",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ppv-lite86"
|
||||
version = "0.2.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9"
|
||||
dependencies = [
|
||||
"zerocopy",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.103"
|
||||
@@ -54,6 +573,40 @@ dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "profiling"
|
||||
version = "1.0.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3eb8486b569e12e2c32ad3e204dbaba5e4b5b216e9367044f25f1dba42341773"
|
||||
dependencies = [
|
||||
"profiling-procmacros",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "profiling-procmacros"
|
||||
version = "1.0.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "52717f9a02b6965224f95ca2a81e2e0c5c43baacd28ca057577988930b6c3d5b"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pxfm"
|
||||
version = "0.1.27"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7186d3822593aa4393561d186d1393b3923e9d6163d3fbfd6e825e3e6cf3e6a8"
|
||||
dependencies = [
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quick-error"
|
||||
version = "2.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3"
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.42"
|
||||
@@ -63,6 +616,116 @@ dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "r-efi"
|
||||
version = "5.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f"
|
||||
|
||||
[[package]]
|
||||
name = "rand"
|
||||
version = "0.9.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1"
|
||||
dependencies = [
|
||||
"rand_chacha",
|
||||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_chacha"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb"
|
||||
dependencies = [
|
||||
"ppv-lite86",
|
||||
"rand_core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rand_core"
|
||||
version = "0.9.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rav1e"
|
||||
version = "0.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "43b6dd56e85d9483277cde964fd1bdb0428de4fec5ebba7540995639a21cb32b"
|
||||
dependencies = [
|
||||
"aligned-vec",
|
||||
"arbitrary",
|
||||
"arg_enum_proc_macro",
|
||||
"arrayvec",
|
||||
"av-scenechange",
|
||||
"av1-grain",
|
||||
"bitstream-io",
|
||||
"built",
|
||||
"cfg-if",
|
||||
"interpolate_name",
|
||||
"itertools",
|
||||
"libc",
|
||||
"libfuzzer-sys",
|
||||
"log",
|
||||
"maybe-rayon",
|
||||
"new_debug_unreachable",
|
||||
"noop_proc_macro",
|
||||
"num-derive",
|
||||
"num-traits",
|
||||
"paste",
|
||||
"profiling",
|
||||
"rand",
|
||||
"rand_chacha",
|
||||
"simd_helpers",
|
||||
"thiserror",
|
||||
"v_frame",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ravif"
|
||||
version = "0.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ef69c1990ceef18a116855938e74793a5f7496ee907562bd0857b6ac734ab285"
|
||||
dependencies = [
|
||||
"avif-serialize",
|
||||
"imgref",
|
||||
"loop9",
|
||||
"quick-error",
|
||||
"rav1e",
|
||||
"rgb",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rayon"
|
||||
version = "1.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "368f01d005bf8fd9b1206fb6fa653e6c4a81ceb1466406b81792d87c5677a58f"
|
||||
dependencies = [
|
||||
"either",
|
||||
"rayon-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rayon-core"
|
||||
version = "1.13.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91"
|
||||
dependencies = [
|
||||
"crossbeam-deque",
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rgb"
|
||||
version = "0.8.52"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0c6a884d2998352bb4daf0183589aec883f16a6da1f4dde84d8e2e9a5409a1ce"
|
||||
|
||||
[[package]]
|
||||
name = "rustversion"
|
||||
version = "1.0.22"
|
||||
@@ -75,6 +738,63 @@ version = "0.7.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fad02996bfc73da3e301efe90b1837be9ed8f4a462b6ed410aa35d00381de89f"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.228"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
|
||||
dependencies = [
|
||||
"serde_core",
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_core"
|
||||
version = "1.0.228"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.228"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "shlex"
|
||||
version = "1.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
|
||||
|
||||
[[package]]
|
||||
name = "simd-adler32"
|
||||
version = "0.3.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e320a6c5ad31d271ad523dcf3ad13e2767ad8b1cb8f047f75a8aeaf8da139da2"
|
||||
|
||||
[[package]]
|
||||
name = "simd_helpers"
|
||||
version = "0.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "95890f873bec569a0362c235787f3aca6e1e887302ba4840839bcc6459c42da6"
|
||||
dependencies = [
|
||||
"quote",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "stable_deref_trait"
|
||||
version = "1.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.111"
|
||||
@@ -86,12 +806,52 @@ dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "2.0.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "2.0.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.22"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5"
|
||||
|
||||
[[package]]
|
||||
name = "v_frame"
|
||||
version = "0.3.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "666b7727c8875d6ab5db9533418d7c764233ac9c0cff1d469aec8fa127597be2"
|
||||
dependencies = [
|
||||
"aligned-vec",
|
||||
"num-traits",
|
||||
"wasm-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasip2"
|
||||
version = "1.0.1+wasi-0.2.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7"
|
||||
dependencies = [
|
||||
"wit-bindgen",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen"
|
||||
version = "0.2.106"
|
||||
@@ -142,3 +902,50 @@ name = "weezl"
|
||||
version = "0.1.12"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a28ac98ddc8b9274cb41bb4d9d4d5c425b6020c50c46f25559911905610b4a88"
|
||||
|
||||
[[package]]
|
||||
name = "wit-bindgen"
|
||||
version = "0.46.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59"
|
||||
|
||||
[[package]]
|
||||
name = "y4m"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7a5a4b21e1a62b67a2970e6831bc091d7b87e119e7f9791aef9702e3bef04448"
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy"
|
||||
version = "0.8.33"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "668f5168d10b9ee831de31933dc111a459c97ec93225beb307aed970d1372dfd"
|
||||
dependencies = [
|
||||
"zerocopy-derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zerocopy-derive"
|
||||
version = "0.8.33"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2c7962b26b0a8685668b671ee4b54d007a67d4eaf05fda79ac0ecf41e32270f1"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "zune-core"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "111f7d9820f05fd715df3144e254d6fc02ee4088b0644c0ffd0efc9e6d9d2773"
|
||||
|
||||
[[package]]
|
||||
name = "zune-jpeg"
|
||||
version = "0.5.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e35aee689668bf9bd6f6f3a6c60bb29ba1244b3b43adfd50edd554a371da37d5"
|
||||
dependencies = [
|
||||
"zune-core",
|
||||
]
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
[package]
|
||||
name = "libfluxcore"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
edition = "2024"
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib"]
|
||||
|
||||
[dependencies]
|
||||
gif = "0.13"
|
||||
image = { version = "0.25.9", default-features = false, features = ["jpeg", "png", "webp", "avif"] }
|
||||
png = "0.17"
|
||||
ruzstd = { version = "0.7", default-features = false, features = ["std"] }
|
||||
wasm-bindgen = { version = "0.2", features = ["std"] }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
|
||||
159
fluxer_app/crates/libfluxcore/src/animation.rs
Normal file
159
fluxer_app/crates/libfluxcore/src/animation.rs
Normal file
@@ -0,0 +1,159 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Fluxer Contributors
|
||||
*
|
||||
* This file is part of Fluxer.
|
||||
*
|
||||
* Fluxer is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Fluxer is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
use gif::{ColorOutput, DecodeOptions};
|
||||
use std::io::Cursor;
|
||||
use wasm_bindgen::prelude::*;
|
||||
|
||||
const PNG_SIGNATURE: [u8; 8] = [137, 80, 78, 71, 13, 10, 26, 10];
|
||||
|
||||
enum ImageFormat {
|
||||
Gif,
|
||||
Png,
|
||||
Webp,
|
||||
Avif,
|
||||
Unknown,
|
||||
}
|
||||
|
||||
fn detect_format(input: &[u8]) -> ImageFormat {
|
||||
if input.len() >= 6 && &input[..6] == b"GIF89a" {
|
||||
return ImageFormat::Gif;
|
||||
}
|
||||
if input.len() >= 6 && &input[..6] == b"GIF87a" {
|
||||
return ImageFormat::Gif;
|
||||
}
|
||||
if input.len() >= PNG_SIGNATURE.len() && input[..PNG_SIGNATURE.len()] == PNG_SIGNATURE {
|
||||
return ImageFormat::Png;
|
||||
}
|
||||
if input.len() >= 12 && &input[..4] == b"RIFF" && &input[8..12] == b"WEBP" {
|
||||
return ImageFormat::Webp;
|
||||
}
|
||||
if is_avif_file(input) {
|
||||
return ImageFormat::Avif;
|
||||
}
|
||||
ImageFormat::Unknown
|
||||
}
|
||||
|
||||
fn is_animated_gif(input: &[u8]) -> bool {
|
||||
let mut options = DecodeOptions::new();
|
||||
options.set_color_output(ColorOutput::RGBA);
|
||||
let cursor = Cursor::new(input);
|
||||
let mut reader = match options.read_info(cursor) {
|
||||
Ok(reader) => reader,
|
||||
Err(_) => return false,
|
||||
};
|
||||
|
||||
let mut frame_count = 0;
|
||||
loop {
|
||||
match reader.read_next_frame() {
|
||||
Ok(Some(_frame)) => {
|
||||
frame_count += 1;
|
||||
if frame_count > 1 {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
Ok(None) => break,
|
||||
Err(_) => return false,
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
fn has_apng_actl(input: &[u8]) -> bool {
|
||||
if input.len() < PNG_SIGNATURE.len() || input[..PNG_SIGNATURE.len()] != PNG_SIGNATURE {
|
||||
return false;
|
||||
}
|
||||
|
||||
let mut offset = PNG_SIGNATURE.len();
|
||||
while offset + 12 <= input.len() {
|
||||
let length_bytes = &input[offset..offset + 4];
|
||||
let length = u32::from_be_bytes(length_bytes.try_into().unwrap()) as usize;
|
||||
let chunk_type = &input[offset + 4..offset + 8];
|
||||
if chunk_type == b"acTL" {
|
||||
return true;
|
||||
}
|
||||
offset = offset
|
||||
.saturating_add(8)
|
||||
.saturating_add(length)
|
||||
.saturating_add(4);
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
fn has_webp_anim(input: &[u8]) -> bool {
|
||||
if input.len() < 12 || &input[..4] != b"RIFF" || &input[8..12] != b"WEBP" {
|
||||
return false;
|
||||
}
|
||||
|
||||
let mut offset = 12;
|
||||
while offset + 8 <= input.len() {
|
||||
let chunk_id = &input[offset..offset + 4];
|
||||
let size_bytes = &input[offset + 4..offset + 8];
|
||||
let size = u32::from_le_bytes(size_bytes.try_into().unwrap()) as usize;
|
||||
|
||||
if chunk_id == b"ANIM" {
|
||||
return true;
|
||||
}
|
||||
|
||||
let advance = 8 + size + (size % 2);
|
||||
offset = offset.saturating_add(advance);
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
fn is_avif_file(input: &[u8]) -> bool {
|
||||
if input.len() < 12 {
|
||||
return false;
|
||||
}
|
||||
|
||||
let box_type = &input[4..8];
|
||||
|
||||
if box_type != b"ftyp" {
|
||||
return false;
|
||||
}
|
||||
|
||||
let brand = &input[8..12];
|
||||
brand == b"avif" || brand == b"avis"
|
||||
}
|
||||
|
||||
fn has_avif_anim(input: &[u8]) -> bool {
|
||||
if !is_avif_file(input) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if input.len() < 12 {
|
||||
return false;
|
||||
}
|
||||
|
||||
let brand = &input[8..12];
|
||||
brand == b"avis"
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub fn is_animated_image(input: &[u8]) -> bool {
|
||||
match detect_format(input) {
|
||||
ImageFormat::Gif => is_animated_gif(input),
|
||||
ImageFormat::Png => has_apng_actl(input),
|
||||
ImageFormat::Webp => has_webp_anim(input),
|
||||
ImageFormat::Avif => has_avif_anim(input),
|
||||
ImageFormat::Unknown => false,
|
||||
}
|
||||
}
|
||||
411
fluxer_app/crates/libfluxcore/src/apng.rs
Normal file
411
fluxer_app/crates/libfluxcore/src/apng.rs
Normal file
@@ -0,0 +1,411 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Fluxer Contributors
|
||||
*
|
||||
* This file is part of Fluxer.
|
||||
*
|
||||
* Fluxer is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Fluxer is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
use png::{BlendOp, DisposeOp};
|
||||
use std::io::Cursor;
|
||||
use wasm_bindgen::prelude::*;
|
||||
|
||||
#[wasm_bindgen]
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn crop_and_rotate_apng(
|
||||
input: &[u8],
|
||||
x: u32,
|
||||
y: u32,
|
||||
width: u32,
|
||||
height: u32,
|
||||
rotation_deg: u32,
|
||||
resize_width: Option<u32>,
|
||||
resize_height: Option<u32>,
|
||||
) -> Result<Box<[u8]>, JsValue> {
|
||||
process_apng(
|
||||
input,
|
||||
x,
|
||||
y,
|
||||
width,
|
||||
height,
|
||||
rotation_deg,
|
||||
resize_width,
|
||||
resize_height,
|
||||
)
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn process_apng(
|
||||
input: &[u8],
|
||||
x: u32,
|
||||
y: u32,
|
||||
width: u32,
|
||||
height: u32,
|
||||
rotation_deg: u32,
|
||||
resize_width: Option<u32>,
|
||||
resize_height: Option<u32>,
|
||||
) -> Result<Box<[u8]>, JsValue> {
|
||||
let cursor = Cursor::new(input);
|
||||
let mut decoder = png::Decoder::new(cursor);
|
||||
decoder.set_transformations(png::Transformations::EXPAND | png::Transformations::STRIP_16);
|
||||
|
||||
let mut reader = decoder
|
||||
.read_info()
|
||||
.map_err(|e| JsValue::from_str(&format!("png read_info: {e}")))?;
|
||||
|
||||
let info = reader.info();
|
||||
let animation_control = info
|
||||
.animation_control()
|
||||
.ok_or_else(|| JsValue::from_str("Not an animated PNG"))?;
|
||||
|
||||
let screen_width = info.width;
|
||||
let screen_height = info.height;
|
||||
|
||||
let crop_x = x.min(screen_width);
|
||||
let crop_y = y.min(screen_height);
|
||||
let crop_w = width.min(screen_width - crop_x);
|
||||
let crop_h = height.min(screen_height - crop_y);
|
||||
|
||||
if crop_w == 0 || crop_h == 0 {
|
||||
return Err(JsValue::from_str("Crop area is empty"));
|
||||
}
|
||||
|
||||
let rotation = rotation_deg.rem_euclid(360);
|
||||
|
||||
let (base_w, base_h) = match rotation {
|
||||
90 | 270 => (crop_h, crop_w),
|
||||
_ => (crop_w, crop_h),
|
||||
};
|
||||
|
||||
let (target_w, target_h) = match (
|
||||
resize_width.filter(|w| *w > 0),
|
||||
resize_height.filter(|h| *h > 0),
|
||||
) {
|
||||
(Some(w), Some(h)) => (w, h),
|
||||
_ => (base_w, base_h),
|
||||
};
|
||||
|
||||
if target_w == 0 || target_h == 0 {
|
||||
return Err(JsValue::from_str("Target dimensions are empty"));
|
||||
}
|
||||
|
||||
if crop_x == 0
|
||||
&& crop_y == 0
|
||||
&& crop_w == screen_width
|
||||
&& crop_h == screen_height
|
||||
&& rotation == 0
|
||||
&& target_w == screen_width
|
||||
&& target_h == screen_height
|
||||
{
|
||||
return Ok(input.to_vec().into_boxed_slice());
|
||||
}
|
||||
|
||||
let mut output = Cursor::new(Vec::new());
|
||||
let mut encoder = png::Encoder::new(&mut output, target_w, target_h);
|
||||
encoder.set_color(png::ColorType::Rgba);
|
||||
encoder.set_depth(png::BitDepth::Eight);
|
||||
encoder
|
||||
.set_animated(animation_control.num_frames, animation_control.num_plays)
|
||||
.map_err(|e| JsValue::from_str(&format!("png set_animated: {e}")))?;
|
||||
encoder.validate_sequence(true);
|
||||
let mut writer = encoder
|
||||
.write_header()
|
||||
.map_err(|e| JsValue::from_str(&format!("png write_header: {e}")))?;
|
||||
|
||||
let mut canvas = vec![0u8; (screen_width * screen_height * 4) as usize];
|
||||
let mut previous_canvas: Option<Vec<u8>> = None;
|
||||
|
||||
let mut processed_any = false;
|
||||
const MAX_TOTAL_PIXELS: u64 = 200_000_000;
|
||||
let mut processed_pixels: u64 = 0;
|
||||
|
||||
let mut frame_buffer = vec![0u8; (screen_width * screen_height * 4) as usize];
|
||||
|
||||
while let Ok(frame_info) = reader.next_frame_info() {
|
||||
processed_any = true;
|
||||
|
||||
let dispose_op = frame_info.dispose_op;
|
||||
let blend_op = frame_info.blend_op;
|
||||
let delay_num = frame_info.delay_num;
|
||||
let delay_den = frame_info.delay_den;
|
||||
let fx = frame_info.x_offset as usize;
|
||||
let fy = frame_info.y_offset as usize;
|
||||
let fw = frame_info.width as usize;
|
||||
let fh = frame_info.height as usize;
|
||||
let rect_x = frame_info.x_offset;
|
||||
let rect_y = frame_info.y_offset;
|
||||
let rect_w = frame_info.width;
|
||||
let rect_h = frame_info.height;
|
||||
|
||||
if dispose_op == DisposeOp::Previous {
|
||||
previous_canvas = Some(canvas.clone());
|
||||
}
|
||||
|
||||
reader
|
||||
.next_frame(&mut frame_buffer)
|
||||
.map_err(|e| JsValue::from_str(&format!("png next_frame: {e}")))?;
|
||||
|
||||
if blend_op == BlendOp::Source {
|
||||
draw_frame_on_canvas_source(&mut canvas, screen_width, fx, fy, fw, fh, &frame_buffer);
|
||||
} else {
|
||||
draw_frame_on_canvas_over(&mut canvas, screen_width, fx, fy, fw, fh, &frame_buffer);
|
||||
}
|
||||
|
||||
let (cw, ch) = (crop_w as usize, crop_h as usize);
|
||||
let cropped = crop_rgba(
|
||||
&canvas,
|
||||
screen_width as usize,
|
||||
screen_height as usize,
|
||||
crop_x as usize,
|
||||
crop_y as usize,
|
||||
cw,
|
||||
ch,
|
||||
)?;
|
||||
|
||||
let (rotated, rw, rh) = match rotation {
|
||||
90 => rotate_rgba_90(&cropped, cw, ch),
|
||||
180 => rotate_rgba_180(&cropped, cw, ch),
|
||||
270 => rotate_rgba_270(&cropped, cw, ch),
|
||||
_ => (cropped, cw, ch),
|
||||
};
|
||||
|
||||
let (final_rgba, _fw, _fh) = if target_w as usize != rw || target_h as usize != rh {
|
||||
let resized =
|
||||
resize_rgba_nearest(&rotated, rw, rh, target_w as usize, target_h as usize);
|
||||
(resized, target_w as usize, target_h as usize)
|
||||
} else {
|
||||
(rotated, rw, rh)
|
||||
};
|
||||
|
||||
processed_pixels += (final_rgba.len() / 4) as u64;
|
||||
if processed_pixels > MAX_TOTAL_PIXELS {
|
||||
return Err(JsValue::from_str(
|
||||
"Animated PNG is too large to crop. Try reducing its dimensions or number of frames.",
|
||||
));
|
||||
}
|
||||
|
||||
writer
|
||||
.set_frame_delay(delay_num, delay_den)
|
||||
.map_err(|e| JsValue::from_str(&format!("png set_frame_delay: {e}")))?;
|
||||
|
||||
writer
|
||||
.set_dispose_op(dispose_op)
|
||||
.map_err(|e| JsValue::from_str(&format!("png set_dispose_op: {e}")))?;
|
||||
writer
|
||||
.set_blend_op(blend_op)
|
||||
.map_err(|e| JsValue::from_str(&format!("png set_blend_op: {e}")))?;
|
||||
|
||||
writer
|
||||
.write_image_data(&final_rgba)
|
||||
.map_err(|e| JsValue::from_str(&format!("png write_image_data: {e}")))?;
|
||||
|
||||
match dispose_op {
|
||||
DisposeOp::Background => {
|
||||
clear_rect(&mut canvas, screen_width, rect_x, rect_y, rect_w, rect_h);
|
||||
}
|
||||
DisposeOp::Previous => {
|
||||
if let Some(prev) = previous_canvas.take() {
|
||||
canvas = prev;
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
if !processed_any {
|
||||
return Err(JsValue::from_str("APNG has no frames"));
|
||||
}
|
||||
|
||||
writer
|
||||
.finish()
|
||||
.map_err(|e| JsValue::from_str(&format!("png finish: {e}")))?;
|
||||
|
||||
Ok(output.into_inner().into_boxed_slice())
|
||||
}
|
||||
|
||||
fn draw_frame_on_canvas_source(
|
||||
canvas: &mut [u8],
|
||||
canvas_width: u32,
|
||||
fx: usize,
|
||||
fy: usize,
|
||||
fw: usize,
|
||||
fh: usize,
|
||||
buffer: &[u8],
|
||||
) {
|
||||
let cw = canvas_width as usize;
|
||||
|
||||
for row in 0..fh {
|
||||
let canvas_y = fy + row;
|
||||
let canvas_offset = (canvas_y * cw + fx) * 4;
|
||||
let frame_offset = row * fw * 4;
|
||||
|
||||
if canvas_offset + fw * 4 <= canvas.len() && frame_offset + fw * 4 <= buffer.len() {
|
||||
canvas[canvas_offset..canvas_offset + fw * 4]
|
||||
.copy_from_slice(&buffer[frame_offset..frame_offset + fw * 4]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn draw_frame_on_canvas_over(
|
||||
canvas: &mut [u8],
|
||||
canvas_width: u32,
|
||||
fx: usize,
|
||||
fy: usize,
|
||||
fw: usize,
|
||||
fh: usize,
|
||||
buffer: &[u8],
|
||||
) {
|
||||
let cw = canvas_width as usize;
|
||||
|
||||
for row in 0..fh {
|
||||
let canvas_y = fy + row;
|
||||
let canvas_offset = (canvas_y * cw + fx) * 4;
|
||||
let frame_offset = row * fw * 4;
|
||||
|
||||
if canvas_offset + fw * 4 <= canvas.len() && frame_offset + fw * 4 <= buffer.len() {
|
||||
let frame_row = &buffer[frame_offset..frame_offset + fw * 4];
|
||||
let canvas_row = &mut canvas[canvas_offset..canvas_offset + fw * 4];
|
||||
|
||||
for i in 0..fw {
|
||||
let pixel_idx = i * 4;
|
||||
let alpha = frame_row[pixel_idx + 3];
|
||||
if alpha > 0 {
|
||||
canvas_row[pixel_idx] = frame_row[pixel_idx];
|
||||
canvas_row[pixel_idx + 1] = frame_row[pixel_idx + 1];
|
||||
canvas_row[pixel_idx + 2] = frame_row[pixel_idx + 2];
|
||||
canvas_row[pixel_idx + 3] = frame_row[pixel_idx + 3];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn clear_rect(canvas: &mut [u8], canvas_width: u32, x: u32, y: u32, w: u32, h: u32) {
|
||||
let cw = canvas_width as usize;
|
||||
let x = x as usize;
|
||||
let y = y as usize;
|
||||
let w = w as usize;
|
||||
let h = h as usize;
|
||||
|
||||
for row in 0..h {
|
||||
let canvas_y = y + row;
|
||||
let offset = (canvas_y * cw + x) * 4;
|
||||
if offset + w * 4 <= canvas.len() {
|
||||
for i in 0..w {
|
||||
let idx = offset + i * 4;
|
||||
canvas[idx] = 0;
|
||||
canvas[idx + 1] = 0;
|
||||
canvas[idx + 2] = 0;
|
||||
canvas[idx + 3] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn crop_rgba(
|
||||
src: &[u8],
|
||||
src_w: usize,
|
||||
src_h: usize,
|
||||
x: usize,
|
||||
y: usize,
|
||||
w: usize,
|
||||
h: usize,
|
||||
) -> Result<Vec<u8>, JsValue> {
|
||||
if x + w > src_w || y + h > src_h {
|
||||
return Err(JsValue::from_str("Crop rect out of bounds"));
|
||||
}
|
||||
|
||||
let mut dst = vec![0u8; w * h * 4];
|
||||
|
||||
for row in 0..h {
|
||||
let src_y = y + row;
|
||||
let src_offset = (src_y * src_w + x) * 4;
|
||||
let dst_offset = row * w * 4;
|
||||
dst[dst_offset..dst_offset + w * 4].copy_from_slice(&src[src_offset..src_offset + w * 4]);
|
||||
}
|
||||
|
||||
Ok(dst)
|
||||
}
|
||||
|
||||
fn rotate_rgba_90(src: &[u8], src_w: usize, src_h: usize) -> (Vec<u8>, usize, usize) {
|
||||
let dst_w = src_h;
|
||||
let dst_h = src_w;
|
||||
let mut dst = vec![0u8; dst_w * dst_h * 4];
|
||||
|
||||
for y in 0..src_h {
|
||||
for x in 0..src_w {
|
||||
let src_idx = (y * src_w + x) * 4;
|
||||
let dst_x = src_h - 1 - y;
|
||||
let dst_y = x;
|
||||
let dst_idx = (dst_y * dst_w + dst_x) * 4;
|
||||
dst[dst_idx..dst_idx + 4].copy_from_slice(&src[src_idx..src_idx + 4]);
|
||||
}
|
||||
}
|
||||
|
||||
(dst, dst_w, dst_h)
|
||||
}
|
||||
|
||||
fn rotate_rgba_180(src: &[u8], src_w: usize, src_h: usize) -> (Vec<u8>, usize, usize) {
|
||||
let mut dst = vec![0u8; src.len()];
|
||||
for y in 0..src_h {
|
||||
for x in 0..src_w {
|
||||
let src_idx = (y * src_w + x) * 4;
|
||||
let dst_x = src_w - 1 - x;
|
||||
let dst_y = src_h - 1 - y;
|
||||
let dst_idx = (dst_y * src_w + dst_x) * 4;
|
||||
dst[dst_idx..dst_idx + 4].copy_from_slice(&src[src_idx..src_idx + 4]);
|
||||
}
|
||||
}
|
||||
(dst, src_w, src_h)
|
||||
}
|
||||
|
||||
fn rotate_rgba_270(src: &[u8], src_w: usize, src_h: usize) -> (Vec<u8>, usize, usize) {
|
||||
let dst_w = src_h;
|
||||
let dst_h = src_w;
|
||||
let mut dst = vec![0u8; dst_w * dst_h * 4];
|
||||
|
||||
for y in 0..src_h {
|
||||
for x in 0..src_w {
|
||||
let src_idx = (y * src_w + x) * 4;
|
||||
let dst_x = y;
|
||||
let dst_y = dst_h - 1 - x;
|
||||
let dst_idx = (dst_y * dst_w + dst_x) * 4;
|
||||
dst[dst_idx..dst_idx + 4].copy_from_slice(&src[src_idx..src_idx + 4]);
|
||||
}
|
||||
}
|
||||
|
||||
(dst, dst_w, dst_h)
|
||||
}
|
||||
|
||||
fn resize_rgba_nearest(
|
||||
src: &[u8],
|
||||
src_w: usize,
|
||||
src_h: usize,
|
||||
dst_w: usize,
|
||||
dst_h: usize,
|
||||
) -> Vec<u8> {
|
||||
let mut dst = vec![0u8; dst_w * dst_h * 4];
|
||||
|
||||
for dy in 0..dst_h {
|
||||
let sy = dy * src_h / dst_h;
|
||||
for dx in 0..dst_w {
|
||||
let sx = dx * src_w / dst_w;
|
||||
let src_idx = (sy * src_w + sx) * 4;
|
||||
let dst_idx = (dy * dst_w + dx) * 4;
|
||||
dst[dst_idx..dst_idx + 4].copy_from_slice(&src[src_idx..src_idx + 4]);
|
||||
}
|
||||
}
|
||||
|
||||
dst
|
||||
}
|
||||
@@ -35,6 +35,7 @@ impl From<JsValue> for EncodeError {
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn crop_and_rotate_gif(
|
||||
input: &[u8],
|
||||
x: u32,
|
||||
@@ -83,6 +84,7 @@ enum EncoderMode {
|
||||
Quantized,
|
||||
}
|
||||
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn process_gif(
|
||||
input: &[u8],
|
||||
x: u32,
|
||||
@@ -451,13 +453,15 @@ impl PaletteFrameEncoder {
|
||||
transparent_index,
|
||||
} = PaletteFrameData::from_rgba(&rgba)?;
|
||||
|
||||
let mut frame = Frame::default();
|
||||
frame.width = self.width;
|
||||
frame.height = self.height;
|
||||
frame.delay = delay;
|
||||
frame.buffer = Cow::Owned(indices);
|
||||
frame.palette = Some(palette);
|
||||
frame.transparent = transparent_index;
|
||||
let frame = Frame {
|
||||
width: self.width,
|
||||
height: self.height,
|
||||
delay,
|
||||
buffer: Cow::Owned(indices),
|
||||
palette: Some(palette),
|
||||
transparent: transparent_index,
|
||||
..Frame::default()
|
||||
};
|
||||
self.encoder.write_frame(&frame).map_err(map_encoding_error)
|
||||
}
|
||||
|
||||
|
||||
@@ -17,8 +17,14 @@
|
||||
* along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
pub mod gif;
|
||||
pub mod animation;
|
||||
pub mod apng;
|
||||
pub mod gateway;
|
||||
pub mod gif;
|
||||
pub mod static_image;
|
||||
|
||||
pub use gif::crop_and_rotate_gif;
|
||||
pub use animation::is_animated_image;
|
||||
pub use apng::crop_and_rotate_apng;
|
||||
pub use gateway::decompress_zstd_frame;
|
||||
pub use gif::crop_and_rotate_gif;
|
||||
pub use static_image::crop_and_rotate_image;
|
||||
|
||||
98
fluxer_app/crates/libfluxcore/src/static_image.rs
Normal file
98
fluxer_app/crates/libfluxcore/src/static_image.rs
Normal file
@@ -0,0 +1,98 @@
|
||||
/*
|
||||
* Copyright (C) 2026 Fluxer Contributors
|
||||
*
|
||||
* This file is part of Fluxer.
|
||||
*
|
||||
* Fluxer is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Affero General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Fluxer is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU Affero General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Affero General Public License
|
||||
* along with Fluxer. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
use image::imageops::FilterType;
|
||||
use image::{DynamicImage, GenericImageView, ImageFormat, RgbaImage, imageops};
|
||||
use std::io::Cursor;
|
||||
use wasm_bindgen::prelude::*;
|
||||
|
||||
fn normalize_rotation(rotation_deg: u32) -> u32 {
|
||||
rotation_deg % 360
|
||||
}
|
||||
|
||||
fn map_format_hint(hint: &str) -> Result<ImageFormat, JsValue> {
|
||||
match hint.trim().to_lowercase().as_str() {
|
||||
"png" | "apng" => Ok(ImageFormat::Png),
|
||||
"jpeg" | "jpg" => Ok(ImageFormat::Jpeg),
|
||||
"webp" => Ok(ImageFormat::WebP),
|
||||
"avif" => Ok(ImageFormat::Avif),
|
||||
"gif" => Ok(ImageFormat::Gif),
|
||||
_ => Err(JsValue::from_str("Unsupported static format")),
|
||||
}
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
pub fn crop_and_rotate_image(
|
||||
input: &[u8],
|
||||
format_hint: &str,
|
||||
x: u32,
|
||||
y: u32,
|
||||
width: u32,
|
||||
height: u32,
|
||||
rotation_deg: u32,
|
||||
resize_width: Option<u32>,
|
||||
resize_height: Option<u32>,
|
||||
) -> Result<Box<[u8]>, JsValue> {
|
||||
let format = map_format_hint(format_hint)?;
|
||||
let dynamic_image = image::load_from_memory_with_format(input, format)
|
||||
.map_err(|err| JsValue::from_str(&format!("Failed to decode {format_hint}: {err}")))?;
|
||||
|
||||
let (img_w, img_h) = dynamic_image.dimensions();
|
||||
let crop_x = x.min(img_w);
|
||||
let crop_y = y.min(img_h);
|
||||
let crop_w = width.min(img_w.saturating_sub(crop_x));
|
||||
let crop_h = height.min(img_h.saturating_sub(crop_y));
|
||||
|
||||
if crop_w == 0 || crop_h == 0 {
|
||||
return Err(JsValue::from_str("Crop area is empty"));
|
||||
}
|
||||
|
||||
let cropped: RgbaImage = dynamic_image
|
||||
.crop_imm(crop_x, crop_y, crop_w, crop_h)
|
||||
.to_rgba8();
|
||||
|
||||
let rotated = match normalize_rotation(rotation_deg) {
|
||||
90 => imageops::rotate90(&cropped),
|
||||
180 => imageops::rotate180(&cropped),
|
||||
270 => imageops::rotate270(&cropped),
|
||||
_ => cropped.clone(),
|
||||
};
|
||||
|
||||
let target_w = resize_width.filter(|w| *w > 0).unwrap_or(rotated.width());
|
||||
let target_h = resize_height.filter(|h| *h > 0).unwrap_or(rotated.height());
|
||||
|
||||
if target_w == 0 || target_h == 0 {
|
||||
return Err(JsValue::from_str("Target dimensions are empty"));
|
||||
}
|
||||
|
||||
let final_buffer = if target_w == rotated.width() && target_h == rotated.height() {
|
||||
rotated
|
||||
} else {
|
||||
imageops::resize(&rotated, target_w, target_h, FilterType::Lanczos3)
|
||||
};
|
||||
|
||||
let final_frame = DynamicImage::ImageRgba8(final_buffer);
|
||||
let mut output = Cursor::new(Vec::new());
|
||||
final_frame
|
||||
.write_to(&mut output, format)
|
||||
.map_err(|err| JsValue::from_str(&format!("Failed to encode {format_hint}: {err}")))?;
|
||||
|
||||
Ok(output.into_inner().into_boxed_slice())
|
||||
}
|
||||
Reference in New Issue
Block a user