mirror of
https://github.com/Cykooz/fast_image_resize.git
synced 2026-10-08 01:11:09 +00:00
- Added support of new type of pixels `PixelType::U8x2`.
- Added into ``MulDiv`` support of images with pixel type ``U8x2``.
This commit is contained in:
@@ -1,3 +1,10 @@
|
||||
## [Unreleased] - ReleaseDate
|
||||
|
||||
- Added support of new type of pixels ``PixelType::U8x2``.
|
||||
- Added into ``MulDiv`` support of images with pixel type ``U8x2``.
|
||||
- Added method ``Image::into_vec(self) -> Vec<u8>``
|
||||
([#7](https://github.com/Cykooz/fast_image_resize/pull/7)).
|
||||
|
||||
## [0.8.0] - 2022-03-23
|
||||
|
||||
- Added optimisation for convolution of U16x3 images with helps of ``SSE4.1``
|
||||
|
||||
Generated
+104
-105
@@ -33,9 +33,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.56"
|
||||
version = "1.0.57"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4361135be9122e0870de935d7c439aef945b9f9ddd4199a553b5270b49c82a27"
|
||||
checksum = "08f9b8508dccb7687a1d6c4ce66b2b0ecef467c94667de27d8d7fe1f8d2a9cdc"
|
||||
|
||||
[[package]]
|
||||
name = "argh"
|
||||
@@ -110,9 +110,9 @@ checksum = "a4a45a46ab1f2412e53d3a0ade76ffad2025804294569aae387231a0cd6e0899"
|
||||
|
||||
[[package]]
|
||||
name = "bytemuck"
|
||||
version = "1.8.0"
|
||||
version = "1.9.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0e851ca7c24871e7336801608a4797d7376545b6928a10d32d75685687141ead"
|
||||
checksum = "cdead85bdec19c194affaeeb670c0e41fe23de31459efd1c174d049269cf02cc"
|
||||
|
||||
[[package]]
|
||||
name = "byteorder"
|
||||
@@ -190,9 +190,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-channel"
|
||||
version = "0.5.2"
|
||||
version = "0.5.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e54ea8bc3fb1ee042f5aace6e3c6e025d3874866da222930f70ce62aceba0bfa"
|
||||
checksum = "5aaa7bd5fb665c6864b5f963dd9097905c54125909c7aa94c9e18507cdbe6c53"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"crossbeam-utils",
|
||||
@@ -211,10 +211,11 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-epoch"
|
||||
version = "0.9.7"
|
||||
version = "0.9.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c00d6d2ea26e8b151d99093005cb442fb9a37aeaca582a03ec70946f49ab5ed9"
|
||||
checksum = "1145cf131a2c6ba0615079ab6a638f7e1973ac9c2634fcbeaaad6114246efe8c"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"cfg-if",
|
||||
"crossbeam-utils",
|
||||
"lazy_static",
|
||||
@@ -224,9 +225,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-queue"
|
||||
version = "0.3.4"
|
||||
version = "0.3.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4dd435b205a4842da59efd07628f921c096bc1cc0a156835b4fa0bcb9a19bcce"
|
||||
checksum = "1f25d8400f4a7a5778f0e4e52384a48cbd9b5c495d110786187fc750075277a2"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"crossbeam-utils",
|
||||
@@ -234,9 +235,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-utils"
|
||||
version = "0.8.7"
|
||||
version = "0.8.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b5e5bed1f1c269533fa816a0a5492b3545209a205ca1a54842be180eb63a16a6"
|
||||
checksum = "0bf124c720b7686e3c2663cf54062ab0f68a88af2fb6a030e87e30bf721fcb38"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"lazy_static",
|
||||
@@ -348,9 +349,9 @@ checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457"
|
||||
|
||||
[[package]]
|
||||
name = "exr"
|
||||
version = "1.4.1"
|
||||
version = "1.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d4badb9489a465cb2c555af1f00f0bfd8cecd6fc12ac11da9d5b40c5dd5f0200"
|
||||
checksum = "14cc0e06fb5f67e5d6beadf3a382fec9baca1aa751c6d5368fdeee7e5932c215"
|
||||
dependencies = [
|
||||
"bit_field",
|
||||
"deflate",
|
||||
@@ -389,6 +390,7 @@ version = "0.8.0"
|
||||
dependencies = [
|
||||
"glassbench",
|
||||
"image",
|
||||
"nix",
|
||||
"num-traits",
|
||||
"png",
|
||||
"resize",
|
||||
@@ -407,14 +409,14 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "flate2"
|
||||
version = "1.0.22"
|
||||
version = "1.0.23"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e6988e897c1c9c485f43b47a529cef42fde0547f9d8d41a7062518f1d8fc53f"
|
||||
checksum = "b39522e96686d38f4bc984b9198e3a0613264abaebaff2c5c918bfa6b6da09af"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"crc32fast",
|
||||
"libc",
|
||||
"miniz_oxide 0.4.4",
|
||||
"miniz_oxide",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -454,9 +456,9 @@ checksum = "21163e139fa306126e6eedaf49ecdb4588f939600f0b1e770f4205ee4b7fa868"
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.2.5"
|
||||
version = "0.2.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d39cd93900197114fa1fcb7ae84ca742095eed9442088988ae74fa744e930e77"
|
||||
checksum = "9be70c98951c83b8d2f8f60d7065fa6d5146873094452a1008da8c2f1e4205ad"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"js-sys",
|
||||
@@ -575,16 +577,16 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "image"
|
||||
version = "0.24.1"
|
||||
version = "0.24.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "db207d030ae38f1eb6f240d5a1c1c88ff422aa005d10f8c6c6fc5e75286ab30e"
|
||||
checksum = "28edd9d7bc256be2502e325ac0628bde30b7001b9b52e0abe31a1a9dc2701212"
|
||||
dependencies = [
|
||||
"bytemuck",
|
||||
"byteorder",
|
||||
"color_quant",
|
||||
"exr",
|
||||
"gif",
|
||||
"jpeg-decoder 0.2.2",
|
||||
"jpeg-decoder",
|
||||
"num-iter",
|
||||
"num-rational",
|
||||
"num-traits",
|
||||
@@ -634,24 +636,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "jpeg-decoder"
|
||||
version = "0.1.22"
|
||||
version = "0.2.4"
|
||||
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"
|
||||
checksum = "744c24117572563a98a7e9168a5ac1ee4a1ca7f702211258797bbe0ed0346c3c"
|
||||
dependencies = [
|
||||
"rayon",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "js-sys"
|
||||
version = "0.3.56"
|
||||
version = "0.3.57"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a38fc24e30fd564ce974c02bf1d337caddff65be6cc4735a1f7eab22a7440f04"
|
||||
checksum = "671a26f820db17c2a2750743f1dd03bafd15b98c9f30c7c2628c024c05d73397"
|
||||
dependencies = [
|
||||
"wasm-bindgen",
|
||||
]
|
||||
@@ -670,9 +666,9 @@ checksum = "7efd1d698db0759e6ef11a7cd44407407399a910c774dd804c64c032da7826ff"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.119"
|
||||
version = "0.2.125"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1bf2e165bb3457c8e098ea76f3e3bc9db55f87aa90d52d0e6be741470916aaa4"
|
||||
checksum = "5916d2ae698f6de9bfb891ad7a8d65c09d232dc58cc4ac433c7da3b2fd84bc2b"
|
||||
|
||||
[[package]]
|
||||
name = "libgit2-sys"
|
||||
@@ -699,9 +695,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "libz-sys"
|
||||
version = "1.1.5"
|
||||
version = "1.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6f35facd4a5673cb5a48822be2be1d4236c1c99cb4113cab7061ac720d5bf859"
|
||||
checksum = "92e7e15d7610cce1d9752e137625f14e61a28cd45929b6e12e47b50fe154ee2e"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"libc",
|
||||
@@ -711,18 +707,19 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "lock_api"
|
||||
version = "0.4.6"
|
||||
version = "0.4.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "88943dd7ef4a2e5a4bfa2753aaab3013e34ce2533d1996fb18ef591e315e2b3b"
|
||||
checksum = "327fa5b6a6940e4699ec49a9beae1ea4845c6bab9314e4f84ac68742139d8c53"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"scopeguard",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.14"
|
||||
version = "0.4.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "51b9bbe6c47d51fc3e1a9b945965946b4c44142ab8792c50835a980d362c2710"
|
||||
checksum = "6389c490849ff5bc16be905ae24bc913a9c8892e19b2341dbc175e14c341c2b8"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
@@ -735,9 +732,9 @@ checksum = "a3e378b66a060d48947b590737b30a1be76706c8dd7b8ba0f2fe3989c68a853f"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.4.1"
|
||||
version = "2.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "308cc39be01b73d0d18f82a0e7b2a3df85245f84af96fdddc5d202d27e47b86a"
|
||||
checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
|
||||
|
||||
[[package]]
|
||||
name = "memoffset"
|
||||
@@ -757,16 +754,6 @@ dependencies = [
|
||||
"lazy_static",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "miniz_oxide"
|
||||
version = "0.4.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a92518e98c078586bc6c934028adcca4c92a53d6a958196de835170a01d84e4b"
|
||||
dependencies = [
|
||||
"adler",
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "miniz_oxide"
|
||||
version = "0.5.1"
|
||||
@@ -807,6 +794,18 @@ dependencies = [
|
||||
"getrandom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nix"
|
||||
version = "0.24.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8f17df307904acd05aa8e32e97bb20f2a0df1728bbc2d771ae8f9a90463441e9"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"memoffset",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ntapi"
|
||||
version = "0.3.7"
|
||||
@@ -818,9 +817,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "num-integer"
|
||||
version = "0.1.44"
|
||||
version = "0.1.45"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d2cc698a63b549a70bc047073d2949cce27cd1c7b0a4a862d08a8031bc2801db"
|
||||
checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"num-traits",
|
||||
@@ -828,9 +827,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "num-iter"
|
||||
version = "0.1.42"
|
||||
version = "0.1.43"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b2021c8337a54d21aca0d59a92577a029af9431cb59b909b03252b9c164fad59"
|
||||
checksum = "7d03e6c028c5dc5cac6e2dec0efda81fc887605bb3d884578bb6d6bf7514e252"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"num-integer",
|
||||
@@ -942,9 +941,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "pkg-config"
|
||||
version = "0.3.24"
|
||||
version = "0.3.25"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "58893f751c9b0412871a09abd62ecd2a00298c6c83befa223ef98c52aef40cbe"
|
||||
checksum = "1df8c4ec4b0627e53bdf214615ad287367e482558cf84b109250b37464dc03ae"
|
||||
|
||||
[[package]]
|
||||
name = "png"
|
||||
@@ -955,32 +954,32 @@ dependencies = [
|
||||
"bitflags",
|
||||
"crc32fast",
|
||||
"deflate",
|
||||
"miniz_oxide 0.5.1",
|
||||
"miniz_oxide",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.36"
|
||||
version = "1.0.37"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c7342d5883fbccae1cc37a2353b09c87c9b0f3afd73f5fb9bba687a1f733b029"
|
||||
checksum = "ec757218438d5fda206afc041538b2f6d889286160d649a86a24d37e1235afd1"
|
||||
dependencies = [
|
||||
"unicode-xid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.15"
|
||||
version = "1.0.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "864d3e96a899863136fc6e99f3d7cae289dafe43bf2c5ac19b70df7210c0a145"
|
||||
checksum = "a1feb54ed693b93a84e14094943b84b7c4eae204c512b7ccb95ab0c66d278ad1"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rayon"
|
||||
version = "1.5.1"
|
||||
version = "1.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c06aca804d41dbc8ba42dfd964f0d01334eceb64314b9ecf7c5fad5188a06d90"
|
||||
checksum = "fd249e82c21598a9a426a4e00dd7adc1d640b22445ec8545feef801d1a74c221"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"crossbeam-deque",
|
||||
@@ -990,34 +989,34 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rayon-core"
|
||||
version = "1.9.1"
|
||||
version = "1.9.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d78120e2c850279833f1dd3582f730c4ab53ed95aeaaaa862a2a5c71b1656d8e"
|
||||
checksum = "9f51245e1e62e1f1629cbfec37b5793bbabcaeb90f30e94d2ba03564687353e4"
|
||||
dependencies = [
|
||||
"crossbeam-channel",
|
||||
"crossbeam-deque",
|
||||
"crossbeam-utils",
|
||||
"lazy_static",
|
||||
"num_cpus",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_syscall"
|
||||
version = "0.2.11"
|
||||
version = "0.2.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8380fe0152551244f0747b1bf41737e0f8a74f97a14ccefd1148187271634f3c"
|
||||
checksum = "62f25bc4c7e55e0b0b7a1d43fb893f4fa1361d0abe38b9ce4f323c2adfe6ef42"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "redox_users"
|
||||
version = "0.4.0"
|
||||
version = "0.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "528532f3d801c87aec9def2add9ca802fe569e44a544afe633765267840abe64"
|
||||
checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b"
|
||||
dependencies = [
|
||||
"getrandom",
|
||||
"redox_syscall",
|
||||
"thiserror",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -1089,18 +1088,18 @@ checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.136"
|
||||
version = "1.0.137"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ce31e24b01e1e524df96f1c2fdd054405f8d7376249a5110886fb4b658484789"
|
||||
checksum = "61ea8d54c77f8315140a05f4c7237403bf38b72704d031543aa1d16abbf517d1"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.136"
|
||||
version = "1.0.137"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "08597e7152fcd306f41838ed3e37be9eaeed2b61c42e2117266a554fab4662f9"
|
||||
checksum = "1f26faba0c3959972377d3b2d306ee9f71faee9714294e41bb777f83f88578be"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -1109,9 +1108,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.79"
|
||||
version = "1.0.80"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e8d9fa5c3b304765ce1fd9c4c8a3de2c8db365a5b91be52f186efc675681d95"
|
||||
checksum = "f972498cf015f7c0746cac89ebe1d6ef10c293b94175a243a2d9442c163d9944"
|
||||
dependencies = [
|
||||
"itoa 1.0.1",
|
||||
"ryu",
|
||||
@@ -1146,9 +1145,9 @@ checksum = "f2dd574626839106c320a323308629dcb1acfc96e32a8cba364ddc61ac23ee83"
|
||||
|
||||
[[package]]
|
||||
name = "spin"
|
||||
version = "0.9.2"
|
||||
version = "0.9.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "511254be0c5bcf062b019a6c89c01a664aa359ded62f78aa72c6fc137c0590e5"
|
||||
checksum = "c530c2b0d0bf8b69304b39fe2001993e267461948b890cd037d8ad4293fa1a0d"
|
||||
dependencies = [
|
||||
"lock_api",
|
||||
]
|
||||
@@ -1161,9 +1160,9 @@ checksum = "3bdb25a4593d6656239319426f4025f7a658157e25e89f0e0319d7516d46042d"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.86"
|
||||
version = "1.0.92"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8a65b3f4ffa0092e9887669db0eae07941f023991ab58ea44da8fe8e2d511c6b"
|
||||
checksum = "7ff7c592601f11445996a06f8ad0c27f094a58857c2f89e97974ab9235b92c52"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -1200,18 +1199,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.30"
|
||||
version = "1.0.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "854babe52e4df1653706b98fcfc05843010039b406875930a70e4d9644e5c417"
|
||||
checksum = "bd829fe32373d27f76265620b5309d0340cb8550f523c1dda251d6298069069a"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "1.0.30"
|
||||
version = "1.0.31"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "aa32fd3f627f367fe16f893e2597ae3c05020f8bba2666a4e6ea73d377e5714b"
|
||||
checksum = "0396bc89e626244658bef819e22d0cc459e795a5ebe878e6ec336d1674a8d79a"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -1229,12 +1228,12 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tiff"
|
||||
version = "0.7.1"
|
||||
version = "0.7.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0247608e998cb6ce39dfc8f4a16c50361ce71e5b52e6d24ea1227ea8ea8ee0b2"
|
||||
checksum = "7cfada0986f446a770eca461e8c6566cb879682f7d687c8348aa0c857bd52286"
|
||||
dependencies = [
|
||||
"flate2",
|
||||
"jpeg-decoder 0.1.22",
|
||||
"jpeg-decoder",
|
||||
"weezl",
|
||||
]
|
||||
|
||||
@@ -1250,9 +1249,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tinyvec"
|
||||
version = "1.5.1"
|
||||
version = "1.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2c1c1d5a42b6245520c249549ec267180beaffcc0615401ac8e31853d4b6d8d2"
|
||||
checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50"
|
||||
dependencies = [
|
||||
"tinyvec_macros",
|
||||
]
|
||||
@@ -1265,9 +1264,9 @@ checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-bidi"
|
||||
version = "0.3.7"
|
||||
version = "0.3.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1a01404663e3db436ed2746d9fefef640d868edae3cceb81c3b8d5732fda678f"
|
||||
checksum = "099b7128301d285f79ddd55b9a83d5e6b9e97c92e0ea0daebee7263e932de992"
|
||||
|
||||
[[package]]
|
||||
name = "unicode-normalization"
|
||||
@@ -1328,9 +1327,9 @@ checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6"
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen"
|
||||
version = "0.2.79"
|
||||
version = "0.2.80"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "25f1af7423d8588a3d840681122e72e6a24ddbcb3f0ec385cac0d12d24256c06"
|
||||
checksum = "27370197c907c55e3f1a9fbe26f44e937fe6451368324e009cba39e139dc08ad"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"wasm-bindgen-macro",
|
||||
@@ -1338,9 +1337,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-backend"
|
||||
version = "0.2.79"
|
||||
version = "0.2.80"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8b21c0df030f5a177f3cba22e9bc4322695ec43e7257d865302900290bcdedca"
|
||||
checksum = "53e04185bfa3a779273da532f5025e33398409573f348985af9a1cbf3774d3f4"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"lazy_static",
|
||||
@@ -1353,9 +1352,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro"
|
||||
version = "0.2.79"
|
||||
version = "0.2.80"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2f4203d69e40a52ee523b2529a773d5ffc1dc0071801c87b3d270b471b80ed01"
|
||||
checksum = "17cae7ff784d7e83a2fe7611cfe766ecf034111b49deb850a3dc7699c08251f5"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"wasm-bindgen-macro-support",
|
||||
@@ -1363,9 +1362,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro-support"
|
||||
version = "0.2.79"
|
||||
version = "0.2.80"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bfa8a30d46208db204854cadbb5d4baf5fcf8071ba5bf48190c3e59937962ebc"
|
||||
checksum = "99ec0dc7a4756fffc231aab1b9f2f578d23cd391390ab27f952ae0c9b3ece20b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@@ -1376,15 +1375,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-shared"
|
||||
version = "0.2.79"
|
||||
version = "0.2.80"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3d958d035c4438e28c70e4321a2911302f10135ce78a9c7834c0cab4123d06a2"
|
||||
checksum = "d554b7f530dee5964d9a9468d95c1f8b8acae4f282807e7d27d4b03099a46744"
|
||||
|
||||
[[package]]
|
||||
name = "weezl"
|
||||
version = "0.1.5"
|
||||
version = "0.1.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d8b77fdfd5a253be4ab714e4ffa3c49caf146b4de743e97510c0656cf90f1e8e"
|
||||
checksum = "9c97e489d8f836838d497091de568cf16b117486d529ec5579233521065bd5e4"
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
|
||||
+7
-1
@@ -20,10 +20,11 @@ thiserror = "1.0.30"
|
||||
|
||||
[dev-dependencies]
|
||||
glassbench = "0.3.1"
|
||||
image = "0.24.1"
|
||||
image = "0.24.2"
|
||||
resize = "0.7.2"
|
||||
rgb = "0.8.32"
|
||||
png = "0.17.5"
|
||||
nix = "0.24.1"
|
||||
|
||||
|
||||
[[bench]]
|
||||
@@ -61,6 +62,11 @@ name = "bench_compare_u8"
|
||||
harness = false
|
||||
|
||||
|
||||
[[bench]]
|
||||
name = "bench_compare_la"
|
||||
harness = false
|
||||
|
||||
|
||||
[profile.dev.package.'*']
|
||||
opt-level = 3
|
||||
|
||||
|
||||
@@ -16,6 +16,10 @@ Supported pixel formats and available optimisations:
|
||||
- native Rust-code without forced SIMD
|
||||
- SSE4.1 (partial)
|
||||
- AVX2
|
||||
- `U8x2` - two `u8` components per pixel (e.g. LA):
|
||||
- native Rust-code without forced SIMD
|
||||
- SSE4.1 (partial)
|
||||
- AVX2 (partial)
|
||||
- `U8x3` - three `u8` components per pixel (e.g. RGB):
|
||||
- native Rust-code without forced SIMD
|
||||
- SSE4.1 (partial)
|
||||
@@ -37,11 +41,11 @@ Supported pixel formats and available optimisations:
|
||||
|
||||
Environment:
|
||||
|
||||
- CPU: Intel(R) Core(TM) i7-6700K CPU @ 4.00GHz
|
||||
- RAM: DDR4 3000 MHz
|
||||
- Ubuntu 20.04 (linux 5.13)
|
||||
- Rust 1.59.0
|
||||
- fast_image_resize = "0.8.0"
|
||||
- CPU: AMD Ryzen 9 5950X
|
||||
- RAM: DDR4 3800 MHz
|
||||
- Ubuntu 22.04 (linux 5.15.0)
|
||||
- Rust 1.60.0
|
||||
- fast_image_resize = "0.9.0"
|
||||
- glassbench = "0.3.1"
|
||||
- `rustflags = ["-C", "llvm-args=-x86-branches-within-32B-boundaries"]`
|
||||
|
||||
@@ -57,7 +61,7 @@ Resize algorithms:
|
||||
- Convolution with CatmullRom filter
|
||||
- Convolution with Lanczos3 filter
|
||||
|
||||
### Resize RGB image (U8x3) 4928x3279 => 852x567
|
||||
### Resize RGB8 image (U8x3) 4928x3279 => 852x567
|
||||
|
||||
Pipeline:
|
||||
|
||||
@@ -68,29 +72,29 @@ Pipeline:
|
||||
|
||||
| | Nearest | Bilinear | CatmullRom | Lanczos3 |
|
||||
|------------|:-------:|:--------:|:----------:|:--------:|
|
||||
| image | 53.21 | 116.37 | 191.14 | 271.65 |
|
||||
| resize | 15.30 | 65.72 | 120.28 | 175.05 |
|
||||
| fir rust | 0.49 | 54.24 | 90.68 | 131.56 |
|
||||
| fir sse4.1 | - | 37.70 | 43.88 | 61.05 |
|
||||
| fir avx2 | - | 10.92 | 14.15 | 19.81 |
|
||||
| image | 20.53 | 77.41 | 135.87 | 184.40 |
|
||||
| resize | - | 45.60 | 87.53 | 129.11 |
|
||||
| fir rust | 0.26 | 37.42 | 63.68 | 92.93 |
|
||||
| fir sse4.1 | 0.26 | 25.76 | 39.52 | 52.82 |
|
||||
| fir avx2 | 0.26 | 6.89 | 8.69 | 12.64 |
|
||||
|
||||
### Resize RGBA image (U8x4) 4928x3279 => 852x567
|
||||
### Resize RGBA8 image (U8x4) 4928x3279 => 852x567
|
||||
|
||||
Pipeline:
|
||||
|
||||
`src_image => multiply by alpha => resize => divide by alpha => dst_image`
|
||||
|
||||
- Source image
|
||||
- Source image
|
||||
[nasa-4928x3279-rgba.png](https://github.com/Cykooz/fast_image_resize/blob/main/data/nasa-4928x3279-rgba.png)
|
||||
- Numbers in table is mean duration of image resizing in milliseconds.
|
||||
|
||||
| | Nearest | Bilinear | CatmullRom | Lanczos3 |
|
||||
|------------|:-------:|:--------:|:----------:|:--------:|
|
||||
| image | 53.09 | 110.62 | 181.02 | 260.38 |
|
||||
| resize | 18.28 | 79.18 | 148.75 | 218.27 |
|
||||
| fir rust | 12.08 | 62.61 | 87.89 | 116.05 |
|
||||
| fir sse4.1 | 9.13 | 20.19 | 26.36 | 33.99 |
|
||||
| fir avx2 | 7.37 | 15.23 | 18.72 | 24.08 |
|
||||
| image | 20.71 | 77.57 | 131.22 | 183.61 |
|
||||
| resize | - | 53.38 | 101.44 | 150.50 |
|
||||
| fir rust | 0.17 | 33.06 | 47.36 | 68.15 |
|
||||
| fir sse4.1 | 0.17 | 12.00 | 15.64 | 20.46 |
|
||||
| fir avx2 | 0.17 | 9.13 | 11.38 | 15.12 |
|
||||
|
||||
### Resize grayscale image (U8) 4928x3279 => 852x567
|
||||
|
||||
@@ -104,11 +108,30 @@ Pipeline:
|
||||
|
||||
| | Nearest | Bilinear | CatmullRom | Lanczos3 |
|
||||
|------------|:-------:|:--------:|:----------:|:--------:|
|
||||
| image | 48.05 | 72.53 | 109.80 | 161.38 |
|
||||
| resize | 9.34 | 24.47 | 47.70 | 80.72 |
|
||||
| fir rust | 0.20 | 19.53 | 22.60 | 32.46 |
|
||||
| fir sse4.1 | - | 17.41 | 18.66 | 27.81 |
|
||||
| fir avx2 | - | 9.58 | 7.74 | 11.70 |
|
||||
| image | 17.09 | 47.71 | 75.01 | 102.30 |
|
||||
| resize | - | 16.23 | 32.66 | 55.36 |
|
||||
| fir rust | 0.14 | 13.00 | 14.74 | 22.12 |
|
||||
| fir sse4.1 | 0.14 | 11.06 | 11.03 | 16.75 |
|
||||
| fir avx2 | 0.13 | 5.90 | 4.39 | 7.12 |
|
||||
|
||||
### Resize grayscale image with alpha channel (U8x2) 4928x3279 => 852x567
|
||||
|
||||
Pipeline:
|
||||
|
||||
`src_image => multiply by alpha => resize => divide by alpha => dst_image`
|
||||
|
||||
- Source image
|
||||
[nasa-4928x3279-rgba.png](https://github.com/Cykooz/fast_image_resize/blob/main/data/nasa-4928x3279-rgba.png)
|
||||
has converted into grayscale image with alpha channel (two bytes per pixel).
|
||||
- Numbers in table is mean duration of image resizing in milliseconds.
|
||||
- The `resize` crate does not support this pixel format.
|
||||
|
||||
| | Nearest | Bilinear | CatmullRom | Lanczos3 |
|
||||
|------------|:-------:|:--------:|:----------:|:--------:|
|
||||
| image | 18.96 | 62.93 | 112.37 | 149.47 |
|
||||
| fir rust | 0.17 | 23.41 | 27.85 | 39.30 |
|
||||
| fir sse4.1 | 0.17 | 19.56 | 20.62 | 28.77 |
|
||||
| fir avx2 | 0.17 | 19.41 | 20.55 | 28.24 |
|
||||
|
||||
### Resize RGB16 image (U16x3) 4928x3279 => 852x567
|
||||
|
||||
@@ -122,11 +145,11 @@ Pipeline:
|
||||
|
||||
| | Nearest | Bilinear | CatmullRom | Lanczos3 |
|
||||
|------------|:-------:|:--------:|:----------:|:--------:|
|
||||
| image | 52.81 | 113.30 | 180.99 | 256.26 |
|
||||
| resize | 17.05 | 68.81 | 121.87 | 175.41 |
|
||||
| fir rust | 0.79 | 58.25 | 96.49 | 132.55 |
|
||||
| fir sse4.1 | - | 39.03 | 63.09 | 89.33 |
|
||||
| fir avx2 | - | 33.29 | 48.96 | 58.77 |
|
||||
| image | 20.31 | 72.79 | 122.71 | 171.64 |
|
||||
| resize | - | 47.23 | 90.10 | 132.22 |
|
||||
| fir rust | 0.30 | 42.43 | 78.20 | 115.72 |
|
||||
| fir sse4.1 | 0.31 | 22.08 | 35.60 | 50.43 |
|
||||
| fir avx2 | 0.31 | 19.07 | 28.24 | 34.08 |
|
||||
|
||||
## Examples
|
||||
|
||||
@@ -138,7 +161,7 @@ use std::num::NonZeroU32;
|
||||
|
||||
use image::codecs::png::PngEncoder;
|
||||
use image::io::Reader as ImageReader;
|
||||
use image::{ColorType, GenericImageView};
|
||||
use image::{ColorType, ImageEncoder};
|
||||
|
||||
use fast_image_resize as fr;
|
||||
|
||||
@@ -155,11 +178,13 @@ fn main() {
|
||||
height,
|
||||
img.to_rgba8().into_raw(),
|
||||
fr::PixelType::U8x4,
|
||||
).unwrap();
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
// Create MulDiv instance
|
||||
let alpha_mul_div = fr::MulDiv::default();
|
||||
// Multiple RGB channels of source image by alpha channel
|
||||
// Multiple RGB channels of source image by alpha channel
|
||||
// (not required for the Nearest algorithm)
|
||||
alpha_mul_div
|
||||
.multiply_alpha_inplace(&mut src_image.view_mut())
|
||||
.unwrap();
|
||||
@@ -179,7 +204,7 @@ fn main() {
|
||||
// Create Resizer instance and resize source image
|
||||
// into buffer of destination image
|
||||
let mut resizer = fr::Resizer::new(
|
||||
fr::ResizeAlg::Convolution(fr::FilterType::Lanczos3)
|
||||
fr::ResizeAlg::Convolution(fr::FilterType::Lanczos3),
|
||||
);
|
||||
resizer.resize(&src_image.view(), &mut dst_view).unwrap();
|
||||
|
||||
@@ -189,7 +214,7 @@ fn main() {
|
||||
// Write destination image as PNG-file
|
||||
let mut result_buf = BufWriter::new(Vec::new());
|
||||
PngEncoder::new(&mut result_buf)
|
||||
.encode(
|
||||
.write_image(
|
||||
dst_image.buffer(),
|
||||
dst_width.get(),
|
||||
dst_height.get(),
|
||||
|
||||
@@ -2,8 +2,9 @@ use std::num::NonZeroU32;
|
||||
|
||||
use glassbench::*;
|
||||
|
||||
use fast_image_resize::MulDiv;
|
||||
use fast_image_resize::PixelType;
|
||||
use fast_image_resize::{CpuExtensions, Image, MulDiv};
|
||||
use fast_image_resize::{CpuExtensions, Image};
|
||||
|
||||
const fn p(r: u8, g: u8, b: u8, a: u8) -> u32 {
|
||||
u32::from_le_bytes([r, g, b, a])
|
||||
@@ -150,8 +151,12 @@ fn divides_alpha_native(bench: &mut Bench) {
|
||||
}
|
||||
|
||||
pub fn main() {
|
||||
use glassbench::*;
|
||||
// Pin process to #0 CPU core
|
||||
let mut cpu_set = nix::sched::CpuSet::new();
|
||||
cpu_set.set(0).unwrap();
|
||||
nix::sched::sched_setaffinity(nix::unistd::Pid::from_raw(0), &cpu_set).unwrap();
|
||||
|
||||
use glassbench::*;
|
||||
let name = env!("CARGO_CRATE_NAME");
|
||||
let cmd = Command::read();
|
||||
if cmd.include_bench(name) {
|
||||
|
||||
@@ -0,0 +1,99 @@
|
||||
use std::num::NonZeroU32;
|
||||
|
||||
use glassbench::*;
|
||||
use image::imageops;
|
||||
|
||||
use fast_image_resize::{CpuExtensions, FilterType, Image, MulDiv, PixelType, ResizeAlg, Resizer};
|
||||
|
||||
mod utils;
|
||||
|
||||
pub fn bench_downscale_la(bench: &mut Bench) {
|
||||
let src_image = &utils::get_big_la_image();
|
||||
let new_width = NonZeroU32::new(852).unwrap();
|
||||
let new_height = NonZeroU32::new(567).unwrap();
|
||||
|
||||
let alg_names = ["Nearest", "Bilinear", "CatmullRom", "Lanczos3"];
|
||||
|
||||
// image crate
|
||||
// https://crates.io/crates/image
|
||||
for alg_name in alg_names {
|
||||
let filter = match alg_name {
|
||||
"Nearest" => imageops::Nearest,
|
||||
"Bilinear" => imageops::Triangle,
|
||||
"CatmullRom" => imageops::CatmullRom,
|
||||
"Lanczos3" => imageops::Lanczos3,
|
||||
_ => continue,
|
||||
};
|
||||
bench.task(format!("image - {}", alg_name), |task| {
|
||||
task.iter(|| {
|
||||
imageops::resize(src_image, new_width.get(), new_height.get(), filter);
|
||||
})
|
||||
});
|
||||
}
|
||||
|
||||
// fast_image_resize crate;
|
||||
let mut cpu_ext_and_name = vec![(CpuExtensions::None, "rust")];
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
{
|
||||
cpu_ext_and_name.push((CpuExtensions::Sse4_1, "sse4.1"));
|
||||
cpu_ext_and_name.push((CpuExtensions::Avx2, "avx2"));
|
||||
}
|
||||
for (cpu_ext, ext_name) in cpu_ext_and_name {
|
||||
for alg_name in alg_names {
|
||||
let resize_alg = match alg_name {
|
||||
"Nearest" => ResizeAlg::Nearest,
|
||||
"Bilinear" => ResizeAlg::Convolution(FilterType::Bilinear),
|
||||
"CatmullRom" => ResizeAlg::Convolution(FilterType::CatmullRom),
|
||||
"Lanczos3" => ResizeAlg::Convolution(FilterType::Lanczos3),
|
||||
_ => return,
|
||||
};
|
||||
let src_image_data = Image::from_vec_u8(
|
||||
NonZeroU32::new(src_image.width()).unwrap(),
|
||||
NonZeroU32::new(src_image.height()).unwrap(),
|
||||
src_image.as_raw().clone(),
|
||||
PixelType::U8x2,
|
||||
)
|
||||
.unwrap();
|
||||
let src_view = src_image_data.view();
|
||||
let mut premultiplied_src_image = Image::new(
|
||||
NonZeroU32::new(src_image.width()).unwrap(),
|
||||
NonZeroU32::new(src_image.height()).unwrap(),
|
||||
PixelType::U8x2,
|
||||
);
|
||||
let mut dst_image = Image::new(new_width, new_height, PixelType::U8x2);
|
||||
let mut dst_view = dst_image.view_mut();
|
||||
let mut mul_div = MulDiv::default();
|
||||
|
||||
let mut fast_resizer = Resizer::new(resize_alg);
|
||||
|
||||
unsafe {
|
||||
fast_resizer.reset_internal_buffers();
|
||||
fast_resizer.set_cpu_extensions(cpu_ext);
|
||||
mul_div.set_cpu_extensions(cpu_ext);
|
||||
}
|
||||
|
||||
bench.task(format!("fir {} - {}", ext_name, alg_name), |task| {
|
||||
task.iter(|| match resize_alg {
|
||||
ResizeAlg::Nearest => {
|
||||
fast_resizer
|
||||
.resize(&premultiplied_src_image.view(), &mut dst_view)
|
||||
.unwrap();
|
||||
}
|
||||
_ => {
|
||||
mul_div
|
||||
.multiply_alpha(&src_view, &mut premultiplied_src_image.view_mut())
|
||||
.unwrap();
|
||||
fast_resizer
|
||||
.resize(&premultiplied_src_image.view(), &mut dst_view)
|
||||
.unwrap();
|
||||
mul_div.divide_alpha_inplace(&mut dst_view).unwrap();
|
||||
}
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
utils::print_md_table(bench);
|
||||
}
|
||||
|
||||
bench_main!("Compare resize of LA image", bench_downscale_la,);
|
||||
@@ -112,4 +112,4 @@ pub fn bench_downscale_rgb(bench: &mut Bench) {
|
||||
utils::print_md_table(bench);
|
||||
}
|
||||
|
||||
glassbench!("Compare resize of RGB image", bench_downscale_rgb,);
|
||||
bench_main!("Compare resize of RGB image", bench_downscale_rgb,);
|
||||
|
||||
@@ -117,4 +117,4 @@ pub fn bench_downscale_rgb16(bench: &mut Bench) {
|
||||
utils::print_md_table(bench);
|
||||
}
|
||||
|
||||
glassbench!("Compare resize of RGB16 image", bench_downscale_rgb16,);
|
||||
bench_main!("Compare resize of RGB16 image", bench_downscale_rgb16,);
|
||||
|
||||
@@ -131,4 +131,4 @@ pub fn bench_downscale_rgba(bench: &mut Bench) {
|
||||
utils::print_md_table(bench);
|
||||
}
|
||||
|
||||
glassbench!("Compare resize of RGBA image", bench_downscale_rgba,);
|
||||
bench_main!("Compare resize of RGBA image", bench_downscale_rgba,);
|
||||
|
||||
@@ -60,4 +60,4 @@ pub fn bench_downscale_rgbx(bench: &mut Bench) {
|
||||
utils::print_md_table(bench);
|
||||
}
|
||||
|
||||
glassbench!("Compare resize of RGBx image", bench_downscale_rgbx,);
|
||||
bench_main!("Compare resize of RGBx image", bench_downscale_rgbx,);
|
||||
|
||||
@@ -113,4 +113,4 @@ pub fn bench_downscale_u8(bench: &mut Bench) {
|
||||
utils::print_md_table(bench);
|
||||
}
|
||||
|
||||
glassbench!("Compare resize of U8 image", bench_downscale_u8,);
|
||||
bench_main!("Compare resize of U8 image", bench_downscale_u8,);
|
||||
|
||||
@@ -279,6 +279,11 @@ fn u16x3_lanczos3_bench(bench: &mut Bench, cpu_extensions: CpuExtensions, name:
|
||||
}
|
||||
|
||||
pub fn main() {
|
||||
// Pin process to #0 CPU core
|
||||
let mut cpu_set = nix::sched::CpuSet::new();
|
||||
cpu_set.set(0).unwrap();
|
||||
nix::sched::sched_setaffinity(nix::unistd::Pid::from_raw(0), &cpu_set).unwrap();
|
||||
|
||||
use glassbench::*;
|
||||
let name = env!("CARGO_CRATE_NAME");
|
||||
let cmd = Command::read();
|
||||
|
||||
+51
-2
@@ -3,7 +3,7 @@ use std::env;
|
||||
|
||||
use glassbench::*;
|
||||
use image::io::Reader;
|
||||
use image::{GrayImage, ImageBuffer, Luma, Rgb, RgbImage, RgbaImage};
|
||||
use image::{GrayAlphaImage, GrayImage, ImageBuffer, Luma, Rgb, RgbImage, RgbaImage};
|
||||
|
||||
pub type Rgb16Image = ImageBuffer<Rgb<u16>, Vec<u16>>;
|
||||
|
||||
@@ -52,6 +52,15 @@ pub fn get_big_luma8_image() -> GrayImage {
|
||||
img.to_luma8()
|
||||
}
|
||||
|
||||
pub fn get_big_la_image() -> GrayAlphaImage {
|
||||
let cur_dir = env::current_dir().unwrap();
|
||||
let img = Reader::open(cur_dir.join("data/nasa-4928x3279-rgba.png"))
|
||||
.unwrap()
|
||||
.decode()
|
||||
.unwrap();
|
||||
img.to_luma_alpha8()
|
||||
}
|
||||
|
||||
pub fn get_small_rgba_image() -> RgbaImage {
|
||||
let cur_dir = env::current_dir().unwrap();
|
||||
let img = Reader::open(cur_dir.join("data/nasa-852x567.png"))
|
||||
@@ -84,7 +93,12 @@ pub fn print_md_table(bench: &Bench) {
|
||||
res_map.insert(crate_name.clone(), Vec::new());
|
||||
}
|
||||
if let Some(values) = res_map.get_mut(&crate_name) {
|
||||
values.push(format!("{:.2}", value));
|
||||
let s_value = format!("{:.2}", value);
|
||||
if s_value == "0.00" {
|
||||
values.push("-".to_string());
|
||||
} else {
|
||||
values.push(s_value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -132,3 +146,38 @@ fn print_header_underline(widths: &[usize]) {
|
||||
}
|
||||
println!("|");
|
||||
}
|
||||
|
||||
/// Generates a benchmark with a consistent id
|
||||
/// (using the benchmark file title), calling
|
||||
/// the benchmarking functions given in argument.
|
||||
///
|
||||
/// ```no-test
|
||||
/// bench_main!(
|
||||
/// "Sortings",
|
||||
/// bench_number_sorting,
|
||||
/// bench_alpha_sorting,
|
||||
/// );
|
||||
/// ```
|
||||
///
|
||||
/// This generates the whole main function.
|
||||
/// If you want to set the bench name yourself
|
||||
/// (not recommanded), or change the way the launch
|
||||
/// arguments are used, you can write the main
|
||||
/// yourself and call [create_bench] and [after_bench]
|
||||
/// instead of using this macro.
|
||||
#[macro_export]
|
||||
macro_rules! bench_main {
|
||||
(
|
||||
$title: literal,
|
||||
$( $fun: path, )+
|
||||
) => {
|
||||
pub fn main() {
|
||||
// Pin process to #0 CPU core
|
||||
let mut cpu_set = nix::sched::CpuSet::new();
|
||||
cpu_set.set(0).unwrap();
|
||||
nix::sched::sched_setaffinity(nix::unistd::Pid::from_raw(0), &cpu_set).unwrap();
|
||||
glassbench!($title, $($fun,)+);
|
||||
main();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,74 +0,0 @@
|
||||
use std::arch::x86_64::*;
|
||||
|
||||
use crate::alpha::sse4;
|
||||
use crate::image_view::{TypedImageView, TypedImageViewMut};
|
||||
use crate::pixels::U8x4;
|
||||
|
||||
#[target_feature(enable = "avx2")]
|
||||
pub(crate) unsafe fn divide_alpha_avx2(
|
||||
src_image: TypedImageView<U8x4>,
|
||||
mut dst_image: TypedImageViewMut<U8x4>,
|
||||
) {
|
||||
let src_rows = src_image.iter_rows(0);
|
||||
let dst_rows = dst_image.iter_rows_mut();
|
||||
|
||||
for (src_row, dst_row) in src_rows.zip(dst_rows) {
|
||||
divide_alpha_row_avx2(src_row, dst_row);
|
||||
}
|
||||
}
|
||||
|
||||
#[target_feature(enable = "avx2")]
|
||||
pub(crate) unsafe fn divide_alpha_inplace_avx2(mut image: TypedImageViewMut<U8x4>) {
|
||||
for dst_row in image.iter_rows_mut() {
|
||||
let src_row = std::slice::from_raw_parts(dst_row.as_ptr(), dst_row.len());
|
||||
divide_alpha_row_avx2(src_row, dst_row);
|
||||
}
|
||||
}
|
||||
|
||||
#[target_feature(enable = "avx2")]
|
||||
unsafe fn divide_alpha_row_avx2(src_row: &[U8x4], dst_row: &mut [U8x4]) {
|
||||
let zero = _mm256_setzero_si256();
|
||||
let alpha_mask = _mm256_set1_epi32(0xff000000u32 as i32);
|
||||
#[rustfmt::skip]
|
||||
let shuffle1 = _mm256_set_epi8(
|
||||
5, 4, 5, 4, 5, 4, 5, 4, 1, 0, 1, 0, 1, 0, 1, 0,
|
||||
5, 4, 5, 4, 5, 4, 5, 4, 1, 0, 1, 0, 1, 0, 1, 0,
|
||||
);
|
||||
#[rustfmt::skip]
|
||||
let shuffle2 = _mm256_set_epi8(
|
||||
13, 12, 13, 12, 13, 12, 13, 12, 9, 8, 9, 8, 9, 8, 9, 8,
|
||||
13, 12, 13, 12, 13, 12, 13, 12, 9, 8, 9, 8, 9, 8, 9, 8,
|
||||
);
|
||||
let alpha_scale = _mm256_set1_ps(255.0 * 256.0);
|
||||
|
||||
let src_chunks = src_row.chunks_exact(8);
|
||||
let src_remainder = src_chunks.remainder();
|
||||
let mut dst_chunks = dst_row.chunks_exact_mut(8);
|
||||
|
||||
for (src, dst) in src_chunks.zip(&mut dst_chunks) {
|
||||
let src_pixels = _mm256_loadu_si256(src.as_ptr() as *const __m256i);
|
||||
|
||||
let alpha_f32 = _mm256_cvtepi32_ps(_mm256_srli_epi32::<24>(src_pixels));
|
||||
let scaled_alpha_f32 = _mm256_div_ps(alpha_scale, alpha_f32);
|
||||
let scaled_alpha_i32 = _mm256_cvtps_epi32(scaled_alpha_f32);
|
||||
let mma0 = _mm256_shuffle_epi8(scaled_alpha_i32, shuffle1);
|
||||
let mma1 = _mm256_shuffle_epi8(scaled_alpha_i32, shuffle2);
|
||||
|
||||
let pix0 = _mm256_unpacklo_epi8(zero, src_pixels);
|
||||
let pix1 = _mm256_unpackhi_epi8(zero, src_pixels);
|
||||
|
||||
let pix0 = _mm256_mulhi_epu16(pix0, mma0);
|
||||
let pix1 = _mm256_mulhi_epu16(pix1, mma1);
|
||||
|
||||
let alpha = _mm256_and_si256(src_pixels, alpha_mask);
|
||||
let rgb = _mm256_packus_epi16(pix0, pix1);
|
||||
let dst_pixels = _mm256_blendv_epi8(rgb, alpha, alpha_mask);
|
||||
|
||||
_mm256_storeu_si256(dst.as_mut_ptr() as *mut __m256i, dst_pixels);
|
||||
}
|
||||
|
||||
if !src_remainder.is_empty() {
|
||||
let dst_reminder = dst_chunks.into_remainder();
|
||||
sse4::div::divide_alpha_row_sse4(src_remainder, dst_reminder);
|
||||
}
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
pub(crate) mod div;
|
||||
pub(crate) mod mul;
|
||||
@@ -1,75 +0,0 @@
|
||||
use std::arch::x86_64::*;
|
||||
|
||||
use crate::alpha::native;
|
||||
use crate::image_view::{TypedImageView, TypedImageViewMut};
|
||||
use crate::pixels::U8x4;
|
||||
use crate::simd_utils;
|
||||
|
||||
#[target_feature(enable = "avx2")]
|
||||
pub(crate) unsafe fn multiply_alpha_avx2(
|
||||
src_image: TypedImageView<U8x4>,
|
||||
mut dst_image: TypedImageViewMut<U8x4>,
|
||||
) {
|
||||
let width = src_image.width().get() as usize;
|
||||
let src_rows = src_image.iter_rows(0);
|
||||
let dst_rows = dst_image.iter_rows_mut();
|
||||
|
||||
for (src_row, dst_row) in src_rows.zip(dst_rows) {
|
||||
multiply_alpha_row_avx2(src_row, dst_row, width);
|
||||
}
|
||||
}
|
||||
|
||||
#[target_feature(enable = "avx2")]
|
||||
pub(crate) unsafe fn multiply_alpha_inplace_avx2(mut image: TypedImageViewMut<U8x4>) {
|
||||
let width = image.width().get() as usize;
|
||||
for dst_row in image.iter_rows_mut() {
|
||||
let src_row = std::slice::from_raw_parts(dst_row.as_ptr(), dst_row.len());
|
||||
multiply_alpha_row_avx2(src_row, dst_row, width);
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[target_feature(enable = "avx2")]
|
||||
unsafe fn multiply_alpha_row_avx2(src_row: &[U8x4], dst_row: &mut [U8x4], width: usize) {
|
||||
let zero = _mm256_setzero_si256();
|
||||
let half = _mm256_set1_epi16(128);
|
||||
|
||||
const MAX_A: i32 = 0xff000000u32 as i32;
|
||||
let max_alpha = _mm256_set1_epi32(MAX_A);
|
||||
#[rustfmt::skip]
|
||||
let factor_mask = _mm256_set_epi8(
|
||||
15, 15, 15, 15, 11, 11, 11, 11, 7, 7, 7, 7, 3, 3, 3, 3,
|
||||
15, 15, 15, 15, 11, 11, 11, 11, 7, 7, 7, 7, 3, 3, 3, 3,
|
||||
);
|
||||
|
||||
let mut x: usize = 0;
|
||||
while x < width.saturating_sub(7) {
|
||||
let src_pixels = simd_utils::loadu_si256(src_row, x);
|
||||
|
||||
let factor_pixels = _mm256_shuffle_epi8(src_pixels, factor_mask);
|
||||
let factor_pixels = _mm256_or_si256(factor_pixels, max_alpha);
|
||||
|
||||
let pix1 = _mm256_unpacklo_epi8(src_pixels, zero);
|
||||
let factors = _mm256_unpacklo_epi8(factor_pixels, zero);
|
||||
let pix1 = _mm256_add_epi16(_mm256_mullo_epi16(pix1, factors), half);
|
||||
let pix1 = _mm256_add_epi16(pix1, _mm256_srli_epi16::<8>(pix1));
|
||||
let pix1 = _mm256_srli_epi16::<8>(pix1);
|
||||
|
||||
let pix2 = _mm256_unpackhi_epi8(src_pixels, zero);
|
||||
let factors = _mm256_unpackhi_epi8(factor_pixels, zero);
|
||||
let pix2 = _mm256_add_epi16(_mm256_mullo_epi16(pix2, factors), half);
|
||||
let pix2 = _mm256_add_epi16(pix2, _mm256_srli_epi16::<8>(pix2));
|
||||
let pix2 = _mm256_srli_epi16::<8>(pix2);
|
||||
|
||||
let dst_pixels = _mm256_packus_epi16(pix1, pix2);
|
||||
|
||||
let dst_ptr = dst_row.get_unchecked_mut(x..).as_mut_ptr() as *mut __m256i;
|
||||
_mm256_storeu_si256(dst_ptr, dst_pixels);
|
||||
|
||||
x += 8;
|
||||
}
|
||||
|
||||
let src_tail = &src_row[x..];
|
||||
let dst_tail = &mut dst_row[x..];
|
||||
native::mul::multiply_alpha_row_native(src_tail, dst_tail);
|
||||
}
|
||||
@@ -1,43 +1,7 @@
|
||||
use crate::image_view::{TypedImageView, TypedImageViewMut};
|
||||
use crate::pixels::U8x4;
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn divide_alpha_native(
|
||||
src_image: TypedImageView<U8x4>,
|
||||
mut dst_image: TypedImageViewMut<U8x4>,
|
||||
) {
|
||||
let src_rows = src_image.iter_rows(0);
|
||||
let dst_rows = dst_image.iter_rows_mut();
|
||||
|
||||
for (src_row, dst_row) in src_rows.zip(dst_rows) {
|
||||
divide_alpha_row_native(src_row, dst_row);
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn divide_alpha_inplace_native(mut image: TypedImageViewMut<U8x4>) {
|
||||
for dst_row in image.iter_rows_mut() {
|
||||
let src_row = unsafe { std::slice::from_raw_parts(dst_row.as_ptr(), dst_row.len()) };
|
||||
divide_alpha_row_native(src_row, dst_row);
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub(crate) fn divide_alpha_row_native(src_row: &[U8x4], dst_row: &mut [U8x4]) {
|
||||
src_row
|
||||
.iter()
|
||||
.zip(dst_row)
|
||||
.for_each(|(src_pixel, dst_pixel)| {
|
||||
let components: [u8; 4] = src_pixel.0.to_le_bytes();
|
||||
let alpha = components[3];
|
||||
let recip_alpha = RECIP_ALPHA[alpha as usize];
|
||||
dst_pixel.0 = u32::from_le_bytes([
|
||||
div_and_clip(components[0], recip_alpha),
|
||||
div_and_clip(components[1], recip_alpha),
|
||||
div_and_clip(components[2], recip_alpha),
|
||||
alpha,
|
||||
]);
|
||||
});
|
||||
pub(crate) fn mul_div_255(a: u8, b: u8) -> u8 {
|
||||
let tmp = a as u32 * b as u32 + 128;
|
||||
(((tmp >> 8) + tmp) >> 8) as u8
|
||||
}
|
||||
|
||||
const fn recip_alpha_array(precision: u32) -> [u32; 256] {
|
||||
@@ -54,11 +18,11 @@ const fn recip_alpha_array(precision: u32) -> [u32; 256] {
|
||||
const PRECISION: u32 = 8;
|
||||
|
||||
#[inline(always)]
|
||||
fn div_and_clip(v: u8, recip_alpha: u32) -> u8 {
|
||||
pub(crate) fn div_and_clip(v: u8, recip_alpha: u32) -> u8 {
|
||||
((v as u32 * recip_alpha) >> PRECISION).min(255) as u8
|
||||
}
|
||||
|
||||
const RECIP_ALPHA: [u32; 256] = recip_alpha_array(PRECISION);
|
||||
pub(crate) const RECIP_ALPHA: [u32; 256] = recip_alpha_array(PRECISION);
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
+21
-141
@@ -1,157 +1,37 @@
|
||||
pub use errors::*;
|
||||
|
||||
use crate::image_view::{TypedImageView, TypedImageViewMut};
|
||||
use crate::pixels::U8x4;
|
||||
use crate::pixels::Pixel;
|
||||
use crate::CpuExtensions;
|
||||
use crate::{ImageView, ImageViewMut};
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
mod avx2;
|
||||
mod errors;
|
||||
mod native;
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
mod sse4;
|
||||
|
||||
/// Methods of this structure used to multiply or divide RGB-channels
|
||||
/// by alpha-channel.
|
||||
///
|
||||
/// By default, instance of `MulDiv` created with best CPU-extensions provided by your CPU.
|
||||
/// You can change this by use method [MulDiv::set_cpu_extensions].
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::num::NonZeroU32;
|
||||
/// use fast_image_resize::pixels::PixelType;
|
||||
/// use fast_image_resize::{Image, MulDiv};
|
||||
///
|
||||
/// let width = NonZeroU32::new(10).unwrap();
|
||||
/// let height = NonZeroU32::new(7).unwrap();
|
||||
/// let src_image = Image::new(width, height, PixelType::U8x4);
|
||||
/// let mut dst_image = Image::new(width, height, PixelType::U8x4);
|
||||
///
|
||||
/// let mul_div = MulDiv::default();
|
||||
/// mul_div.multiply_alpha(&src_image.view(), &mut dst_image.view_mut()).unwrap();
|
||||
/// ```
|
||||
#[derive(Default, Debug, Clone)]
|
||||
pub struct MulDiv {
|
||||
cpu_extensions: CpuExtensions,
|
||||
}
|
||||
|
||||
impl MulDiv {
|
||||
#[inline(always)]
|
||||
pub fn cpu_extensions(&self) -> CpuExtensions {
|
||||
self.cpu_extensions
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
/// This is unsafe because this method allows you to set a CPU-extensions
|
||||
/// that is not actually supported by your CPU.
|
||||
pub unsafe fn set_cpu_extensions(&mut self, extensions: CpuExtensions) {
|
||||
self.cpu_extensions = extensions;
|
||||
}
|
||||
mod common;
|
||||
pub(crate) mod errors;
|
||||
mod u8x2;
|
||||
mod u8x4;
|
||||
|
||||
pub(crate) trait AlphaMulDiv
|
||||
where
|
||||
Self: Pixel,
|
||||
{
|
||||
/// Multiplies RGB-channels of source image by alpha-channel and store
|
||||
/// result into destination image.
|
||||
pub fn multiply_alpha(
|
||||
&self,
|
||||
src_image: &ImageView,
|
||||
dst_image: &mut ImageViewMut,
|
||||
) -> Result<(), MulDivImagesError> {
|
||||
let (src_image_u8x4, dst_image_u8x4) = assert_images(src_image, dst_image)?;
|
||||
match self.cpu_extensions {
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
CpuExtensions::Avx2 => unsafe {
|
||||
avx2::mul::multiply_alpha_avx2(src_image_u8x4, dst_image_u8x4)
|
||||
},
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
CpuExtensions::Sse4_1 => unsafe {
|
||||
sse4::mul::multiply_alpha_sse4(src_image_u8x4, dst_image_u8x4)
|
||||
},
|
||||
_ => native::mul::multiply_alpha_native(src_image_u8x4, dst_image_u8x4),
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
fn multiply_alpha(
|
||||
src_image: TypedImageView<Self>,
|
||||
dst_image: TypedImageViewMut<Self>,
|
||||
cpu_extensions: CpuExtensions,
|
||||
);
|
||||
|
||||
/// Multiplies RGB-channels of image by alpha-channel inplace.
|
||||
pub fn multiply_alpha_inplace(&self, image: &mut ImageViewMut) -> Result<(), MulDivImageError> {
|
||||
let image_u8x4 = assert_image(image)?;
|
||||
match self.cpu_extensions {
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
CpuExtensions::Avx2 => unsafe { avx2::mul::multiply_alpha_inplace_avx2(image_u8x4) },
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
CpuExtensions::Sse4_1 => unsafe { sse4::mul::multiply_alpha_inplace_sse4(image_u8x4) },
|
||||
_ => native::mul::multiply_alpha_inplace_native(image_u8x4),
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
fn multiply_alpha_inplace(image: TypedImageViewMut<Self>, cpu_extensions: CpuExtensions);
|
||||
|
||||
/// Divides RGB-channels of source image by alpha-channel and store
|
||||
/// result into destination image.
|
||||
pub fn divide_alpha(
|
||||
&self,
|
||||
src_image: &ImageView,
|
||||
dst_image: &mut ImageViewMut,
|
||||
) -> Result<(), MulDivImagesError> {
|
||||
let (src_image_u8x4, dst_image_u8x4) = assert_images(src_image, dst_image)?;
|
||||
match self.cpu_extensions {
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
CpuExtensions::Avx2 => unsafe {
|
||||
avx2::div::divide_alpha_avx2(src_image_u8x4, dst_image_u8x4)
|
||||
},
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
CpuExtensions::Sse4_1 => unsafe {
|
||||
sse4::div::divide_alpha_sse4(src_image_u8x4, dst_image_u8x4)
|
||||
},
|
||||
_ => native::div::divide_alpha_native(src_image_u8x4, dst_image_u8x4),
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
fn divide_alpha(
|
||||
src_image: TypedImageView<Self>,
|
||||
dst_image: TypedImageViewMut<Self>,
|
||||
cpu_extensions: CpuExtensions,
|
||||
);
|
||||
|
||||
/// Divides RGB-channels of image by alpha-channel inplace.
|
||||
pub fn divide_alpha_inplace(&self, image: &mut ImageViewMut) -> Result<(), MulDivImageError> {
|
||||
let image_u8x4 = assert_image(image)?;
|
||||
match self.cpu_extensions {
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
CpuExtensions::Avx2 => unsafe { avx2::div::divide_alpha_inplace_avx2(image_u8x4) },
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
CpuExtensions::Sse4_1 => unsafe { sse4::div::divide_alpha_inplace_sse4(image_u8x4) },
|
||||
_ => native::div::divide_alpha_inplace_native(image_u8x4),
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn assert_images<'s, 'd, 'da>(
|
||||
src_image: &'s ImageView<'s>,
|
||||
dst_image: &'d mut ImageViewMut<'da>,
|
||||
) -> Result<
|
||||
(
|
||||
TypedImageView<'s, 's, U8x4>,
|
||||
TypedImageViewMut<'d, 'da, U8x4>,
|
||||
),
|
||||
MulDivImagesError,
|
||||
> {
|
||||
let src_image_u8x4 = src_image
|
||||
.u8x4_image()
|
||||
.ok_or(MulDivImagesError::UnsupportedPixelType)?;
|
||||
let dst_image_u8x4 = dst_image
|
||||
.u8x4_image()
|
||||
.ok_or(MulDivImagesError::UnsupportedPixelType)?;
|
||||
if src_image_u8x4.width() != dst_image_u8x4.width()
|
||||
|| src_image_u8x4.height() != dst_image_u8x4.height()
|
||||
{
|
||||
return Err(MulDivImagesError::SizeIsDifferent);
|
||||
}
|
||||
Ok((src_image_u8x4, dst_image_u8x4))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn assert_image<'a, 'b>(
|
||||
image: &'a mut ImageViewMut<'b>,
|
||||
) -> Result<TypedImageViewMut<'a, 'b, U8x4>, MulDivImageError> {
|
||||
image
|
||||
.u8x4_image()
|
||||
.ok_or(MulDivImageError::UnsupportedPixelType)
|
||||
fn divide_alpha_inplace(image: TypedImageViewMut<Self>, cpu_extensions: CpuExtensions);
|
||||
}
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
pub(crate) mod div;
|
||||
pub(crate) mod mul;
|
||||
@@ -1,41 +0,0 @@
|
||||
use crate::image_view::{TypedImageView, TypedImageViewMut};
|
||||
use crate::pixels::U8x4;
|
||||
|
||||
pub(crate) fn multiply_alpha_native(
|
||||
src_image: TypedImageView<U8x4>,
|
||||
mut dst_image: TypedImageViewMut<U8x4>,
|
||||
) {
|
||||
let src_rows = src_image.iter_rows(0);
|
||||
let dst_rows = dst_image.iter_rows_mut();
|
||||
|
||||
for (src_row, dst_row) in src_rows.zip(dst_rows) {
|
||||
multiply_alpha_row_native(src_row, dst_row);
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn multiply_alpha_inplace_native(mut image: TypedImageViewMut<U8x4>) {
|
||||
for dst_row in image.iter_rows_mut() {
|
||||
let src_row = unsafe { std::slice::from_raw_parts(dst_row.as_ptr(), dst_row.len()) };
|
||||
multiply_alpha_row_native(src_row, dst_row);
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub(crate) fn multiply_alpha_row_native(src_row: &[U8x4], dst_row: &mut [U8x4]) {
|
||||
for (src_pixel, dst_pixel) in src_row.iter().zip(dst_row) {
|
||||
let components: [u8; 4] = src_pixel.0.to_le_bytes();
|
||||
let alpha = components[3];
|
||||
dst_pixel.0 = u32::from_le_bytes([
|
||||
mul_div_255(components[0], alpha),
|
||||
mul_div_255(components[1], alpha),
|
||||
mul_div_255(components[2], alpha),
|
||||
alpha,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub(crate) fn mul_div_255(a: u8, b: u8) -> u8 {
|
||||
let tmp = a as u32 * b as u32 + 128;
|
||||
(((tmp >> 8) + tmp) >> 8) as u8
|
||||
}
|
||||
@@ -1,83 +0,0 @@
|
||||
use std::arch::x86_64::*;
|
||||
|
||||
use crate::image_view::{TypedImageView, TypedImageViewMut};
|
||||
use crate::pixels::U8x4;
|
||||
|
||||
#[target_feature(enable = "sse4.1")]
|
||||
pub(crate) unsafe fn divide_alpha_sse4(
|
||||
src_image: TypedImageView<U8x4>,
|
||||
mut dst_image: TypedImageViewMut<U8x4>,
|
||||
) {
|
||||
let src_rows = src_image.iter_rows(0);
|
||||
let dst_rows = dst_image.iter_rows_mut();
|
||||
|
||||
for (src_row, dst_row) in src_rows.zip(dst_rows) {
|
||||
divide_alpha_row_sse4(src_row, dst_row);
|
||||
}
|
||||
}
|
||||
|
||||
#[target_feature(enable = "sse4.1")]
|
||||
pub(crate) unsafe fn divide_alpha_inplace_sse4(mut image: TypedImageViewMut<U8x4>) {
|
||||
for dst_row in image.iter_rows_mut() {
|
||||
let src_row = std::slice::from_raw_parts(dst_row.as_ptr(), dst_row.len());
|
||||
divide_alpha_row_sse4(src_row, dst_row);
|
||||
}
|
||||
}
|
||||
|
||||
#[target_feature(enable = "sse4.1")]
|
||||
pub(crate) unsafe fn divide_alpha_row_sse4(src_row: &[U8x4], dst_row: &mut [U8x4]) {
|
||||
let src_chunks = src_row.chunks_exact(4);
|
||||
let src_remainder = src_chunks.remainder();
|
||||
let mut dst_chunks = dst_row.chunks_exact_mut(4);
|
||||
|
||||
for (src, dst) in src_chunks.zip(&mut dst_chunks) {
|
||||
divide_alpha(src.as_ptr(), dst.as_mut_ptr());
|
||||
}
|
||||
|
||||
if !src_remainder.is_empty() {
|
||||
let dst_reminder = dst_chunks.into_remainder();
|
||||
let mut src_pixels = [U8x4(0); 4];
|
||||
src_pixels
|
||||
.iter_mut()
|
||||
.zip(src_remainder)
|
||||
.for_each(|(d, s)| *d = *s);
|
||||
|
||||
let mut dst_pixels = [U8x4(0); 4];
|
||||
divide_alpha(src_pixels.as_ptr(), dst_pixels.as_mut_ptr());
|
||||
|
||||
dst_pixels
|
||||
.iter()
|
||||
.zip(dst_reminder)
|
||||
.for_each(|(s, d)| *d = *s);
|
||||
}
|
||||
}
|
||||
|
||||
#[target_feature(enable = "sse4.1")]
|
||||
unsafe fn divide_alpha(src: *const U8x4, dst: *mut U8x4) {
|
||||
let zero = _mm_setzero_si128();
|
||||
let alpha_mask = _mm_set1_epi32(0xff000000u32 as i32);
|
||||
let shuffle1 = _mm_set_epi8(5, 4, 5, 4, 5, 4, 5, 4, 1, 0, 1, 0, 1, 0, 1, 0);
|
||||
let shuffle2 = _mm_set_epi8(13, 12, 13, 12, 13, 12, 13, 12, 9, 8, 9, 8, 9, 8, 9, 8);
|
||||
let alpha_scale = _mm_set1_ps(255.0 * 256.0);
|
||||
|
||||
let src_pixels = _mm_loadu_si128(src as *const __m128i);
|
||||
|
||||
let alpha_f32 = _mm_cvtepi32_ps(_mm_srli_epi32::<24>(src_pixels));
|
||||
let scaled_alpha_f32 = _mm_div_ps(alpha_scale, alpha_f32);
|
||||
// let scaled_alpha_f32 = _mm_mul_ps(alpha_scale, _mm_rcp_ps(alpha_f32));
|
||||
let scaled_alpha_i32 = _mm_cvtps_epi32(scaled_alpha_f32);
|
||||
let mma0 = _mm_shuffle_epi8(scaled_alpha_i32, shuffle1);
|
||||
let mma1 = _mm_shuffle_epi8(scaled_alpha_i32, shuffle2);
|
||||
|
||||
let pix0 = _mm_unpacklo_epi8(zero, src_pixels);
|
||||
let pix1 = _mm_unpackhi_epi8(zero, src_pixels);
|
||||
|
||||
let pix0 = _mm_mulhi_epu16(pix0, mma0);
|
||||
let pix1 = _mm_mulhi_epu16(pix1, mma1);
|
||||
|
||||
let alpha = _mm_and_si128(src_pixels, alpha_mask);
|
||||
let rgb = _mm_packus_epi16(pix0, pix1);
|
||||
let dst_pixels = _mm_blendv_epi8(rgb, alpha, alpha_mask);
|
||||
|
||||
_mm_storeu_si128(dst as *mut __m128i, dst_pixels);
|
||||
}
|
||||
@@ -1,2 +0,0 @@
|
||||
pub(crate) mod div;
|
||||
pub(crate) mod mul;
|
||||
@@ -1,69 +0,0 @@
|
||||
use std::arch::x86_64::*;
|
||||
|
||||
use crate::alpha::native;
|
||||
use crate::image_view::{TypedImageView, TypedImageViewMut};
|
||||
use crate::pixels::U8x4;
|
||||
|
||||
#[target_feature(enable = "sse4.1")]
|
||||
pub(crate) unsafe fn multiply_alpha_sse4(
|
||||
src_image: TypedImageView<U8x4>,
|
||||
mut dst_image: TypedImageViewMut<U8x4>,
|
||||
) {
|
||||
let src_rows = src_image.iter_rows(0);
|
||||
let dst_rows = dst_image.iter_rows_mut();
|
||||
|
||||
for (src_row, dst_row) in src_rows.zip(dst_rows) {
|
||||
multiply_alpha_row_sse4(src_row, dst_row);
|
||||
}
|
||||
}
|
||||
|
||||
#[target_feature(enable = "sse4.1")]
|
||||
pub(crate) unsafe fn multiply_alpha_inplace_sse4(mut image: TypedImageViewMut<U8x4>) {
|
||||
for dst_row in image.iter_rows_mut() {
|
||||
let src_row = std::slice::from_raw_parts(dst_row.as_ptr(), dst_row.len());
|
||||
multiply_alpha_row_sse4(src_row, dst_row);
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[target_feature(enable = "sse4.1")]
|
||||
unsafe fn multiply_alpha_row_sse4(src_row: &[U8x4], dst_row: &mut [U8x4]) {
|
||||
let zero = _mm_setzero_si128();
|
||||
let half = _mm_set1_epi16(128);
|
||||
|
||||
const MAX_A: i32 = 0xff000000u32 as i32;
|
||||
let max_alpha = _mm_set1_epi32(MAX_A);
|
||||
let factor_mask = _mm_set_epi8(15, 15, 15, 15, 11, 11, 11, 11, 7, 7, 7, 7, 3, 3, 3, 3);
|
||||
|
||||
let src_chunks = src_row.chunks_exact(4);
|
||||
let src_remainder = src_chunks.remainder();
|
||||
let mut dst_chunks = dst_row.chunks_exact_mut(4);
|
||||
|
||||
for (src, dst) in src_chunks.zip(&mut dst_chunks) {
|
||||
let src_pixels = _mm_loadu_si128(src.as_ptr() as *const __m128i);
|
||||
|
||||
let factor_pixels = _mm_shuffle_epi8(src_pixels, factor_mask);
|
||||
let factor_pixels = _mm_or_si128(factor_pixels, max_alpha);
|
||||
|
||||
let pix1 = _mm_unpacklo_epi8(src_pixels, zero);
|
||||
let factors = _mm_unpacklo_epi8(factor_pixels, zero);
|
||||
let pix1 = _mm_add_epi16(_mm_mullo_epi16(pix1, factors), half);
|
||||
let pix1 = _mm_add_epi16(pix1, _mm_srli_epi16::<8>(pix1));
|
||||
let pix1 = _mm_srli_epi16::<8>(pix1);
|
||||
|
||||
let pix2 = _mm_unpackhi_epi8(src_pixels, zero);
|
||||
let factors = _mm_unpackhi_epi8(factor_pixels, zero);
|
||||
let pix2 = _mm_add_epi16(_mm_mullo_epi16(pix2, factors), half);
|
||||
let pix2 = _mm_add_epi16(pix2, _mm_srli_epi16::<8>(pix2));
|
||||
let pix2 = _mm_srli_epi16::<8>(pix2);
|
||||
|
||||
let dst_pixels = _mm_packus_epi16(pix1, pix2);
|
||||
|
||||
_mm_storeu_si128(dst.as_mut_ptr() as *mut __m128i, dst_pixels);
|
||||
}
|
||||
|
||||
if !src_remainder.is_empty() {
|
||||
let dst_reminder = dst_chunks.into_remainder();
|
||||
native::mul::multiply_alpha_row_native(src_remainder, dst_reminder);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
use crate::image_view::{TypedImageView, TypedImageViewMut};
|
||||
use crate::pixels::U8x2;
|
||||
use crate::CpuExtensions;
|
||||
|
||||
use super::AlphaMulDiv;
|
||||
|
||||
// #[cfg(target_arch = "x86_64")]
|
||||
// mod avx2;
|
||||
mod native;
|
||||
// #[cfg(target_arch = "x86_64")]
|
||||
// mod sse4;
|
||||
|
||||
impl AlphaMulDiv for U8x2 {
|
||||
fn multiply_alpha(
|
||||
src_image: TypedImageView<Self>,
|
||||
dst_image: TypedImageViewMut<Self>,
|
||||
cpu_extensions: CpuExtensions,
|
||||
) {
|
||||
match cpu_extensions {
|
||||
// #[cfg(target_arch = "x86_64")]
|
||||
// CpuExtensions::Avx2 => unsafe { avx2::multiply_alpha(src_image, dst_image) },
|
||||
// #[cfg(target_arch = "x86_64")]
|
||||
// CpuExtensions::Sse4_1 => unsafe { sse4::multiply_alpha(src_image, dst_image) },
|
||||
_ => native::multiply_alpha(src_image, dst_image),
|
||||
}
|
||||
}
|
||||
|
||||
fn multiply_alpha_inplace(image: TypedImageViewMut<Self>, cpu_extensions: CpuExtensions) {
|
||||
match cpu_extensions {
|
||||
// #[cfg(target_arch = "x86_64")]
|
||||
// CpuExtensions::Avx2 => unsafe { avx2::multiply_alpha_inplace(image) },
|
||||
// #[cfg(target_arch = "x86_64")]
|
||||
// CpuExtensions::Sse4_1 => unsafe { sse4::multiply_alpha_inplace(image) },
|
||||
_ => native::multiply_alpha_inplace(image),
|
||||
}
|
||||
}
|
||||
|
||||
fn divide_alpha(
|
||||
src_image: TypedImageView<Self>,
|
||||
dst_image: TypedImageViewMut<Self>,
|
||||
cpu_extensions: CpuExtensions,
|
||||
) {
|
||||
match cpu_extensions {
|
||||
// #[cfg(target_arch = "x86_64")]
|
||||
// CpuExtensions::Avx2 => unsafe { avx2::divide_alpha(src_image, dst_image) },
|
||||
// #[cfg(target_arch = "x86_64")]
|
||||
// CpuExtensions::Sse4_1 => unsafe { sse4::divide_alpha(src_image, dst_image) },
|
||||
_ => native::divide_alpha(src_image, dst_image),
|
||||
}
|
||||
}
|
||||
|
||||
fn divide_alpha_inplace(image: TypedImageViewMut<Self>, cpu_extensions: CpuExtensions) {
|
||||
match cpu_extensions {
|
||||
// #[cfg(target_arch = "x86_64")]
|
||||
// CpuExtensions::Avx2 => unsafe { avx2::divide_alpha_inplace(image) },
|
||||
// #[cfg(target_arch = "x86_64")]
|
||||
// CpuExtensions::Sse4_1 => unsafe { sse4::divide_alpha_inplace(image) },
|
||||
_ => native::divide_alpha_inplace(image),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
use crate::alpha::common::{div_and_clip, mul_div_255, RECIP_ALPHA};
|
||||
use crate::image_view::{TypedImageView, TypedImageViewMut};
|
||||
use crate::pixels::U8x2;
|
||||
|
||||
pub(crate) fn multiply_alpha(
|
||||
src_image: TypedImageView<U8x2>,
|
||||
mut dst_image: TypedImageViewMut<U8x2>,
|
||||
) {
|
||||
let src_rows = src_image.iter_rows(0);
|
||||
let dst_rows = dst_image.iter_rows_mut();
|
||||
|
||||
for (src_row, dst_row) in src_rows.zip(dst_rows) {
|
||||
multiply_alpha_row(src_row, dst_row);
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn multiply_alpha_inplace(mut image: TypedImageViewMut<U8x2>) {
|
||||
for dst_row in image.iter_rows_mut() {
|
||||
let src_row = unsafe { std::slice::from_raw_parts(dst_row.as_ptr(), dst_row.len()) };
|
||||
multiply_alpha_row(src_row, dst_row);
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub(crate) fn multiply_alpha_row(src_row: &[U8x2], dst_row: &mut [U8x2]) {
|
||||
for (src_pixel, dst_pixel) in src_row.iter().zip(dst_row) {
|
||||
let components: [u8; 2] = src_pixel.0.to_le_bytes();
|
||||
let alpha = components[1];
|
||||
dst_pixel.0 = u16::from_le_bytes([mul_div_255(components[0], alpha), alpha]);
|
||||
}
|
||||
}
|
||||
|
||||
// Divide
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn divide_alpha(
|
||||
src_image: TypedImageView<U8x2>,
|
||||
mut dst_image: TypedImageViewMut<U8x2>,
|
||||
) {
|
||||
let src_rows = src_image.iter_rows(0);
|
||||
let dst_rows = dst_image.iter_rows_mut();
|
||||
|
||||
for (src_row, dst_row) in src_rows.zip(dst_rows) {
|
||||
divide_alpha_row(src_row, dst_row);
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn divide_alpha_inplace(mut image: TypedImageViewMut<U8x2>) {
|
||||
for dst_row in image.iter_rows_mut() {
|
||||
let src_row = unsafe { std::slice::from_raw_parts(dst_row.as_ptr(), dst_row.len()) };
|
||||
divide_alpha_row(src_row, dst_row);
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub(crate) fn divide_alpha_row(src_row: &[U8x2], dst_row: &mut [U8x2]) {
|
||||
src_row
|
||||
.iter()
|
||||
.zip(dst_row)
|
||||
.for_each(|(src_pixel, dst_pixel)| {
|
||||
let components: [u8; 2] = src_pixel.0.to_le_bytes();
|
||||
let alpha = components[1];
|
||||
let recip_alpha = RECIP_ALPHA[alpha as usize];
|
||||
dst_pixel.0 = u16::from_le_bytes([div_and_clip(components[0], recip_alpha), alpha]);
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,146 @@
|
||||
use std::arch::x86_64::*;
|
||||
|
||||
use super::{native, sse4};
|
||||
use crate::image_view::{TypedImageView, TypedImageViewMut};
|
||||
use crate::pixels::U8x4;
|
||||
use crate::simd_utils;
|
||||
|
||||
#[target_feature(enable = "avx2")]
|
||||
pub(crate) unsafe fn multiply_alpha(
|
||||
src_image: TypedImageView<U8x4>,
|
||||
mut dst_image: TypedImageViewMut<U8x4>,
|
||||
) {
|
||||
let width = src_image.width().get() as usize;
|
||||
let src_rows = src_image.iter_rows(0);
|
||||
let dst_rows = dst_image.iter_rows_mut();
|
||||
|
||||
for (src_row, dst_row) in src_rows.zip(dst_rows) {
|
||||
multiply_alpha_row(src_row, dst_row, width);
|
||||
}
|
||||
}
|
||||
|
||||
#[target_feature(enable = "avx2")]
|
||||
pub(crate) unsafe fn multiply_alpha_inplace(mut image: TypedImageViewMut<U8x4>) {
|
||||
let width = image.width().get() as usize;
|
||||
for dst_row in image.iter_rows_mut() {
|
||||
let src_row = std::slice::from_raw_parts(dst_row.as_ptr(), dst_row.len());
|
||||
multiply_alpha_row(src_row, dst_row, width);
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[target_feature(enable = "avx2")]
|
||||
unsafe fn multiply_alpha_row(src_row: &[U8x4], dst_row: &mut [U8x4], width: usize) {
|
||||
let zero = _mm256_setzero_si256();
|
||||
let half = _mm256_set1_epi16(128);
|
||||
|
||||
const MAX_A: i32 = 0xff000000u32 as i32;
|
||||
let max_alpha = _mm256_set1_epi32(MAX_A);
|
||||
#[rustfmt::skip]
|
||||
let factor_mask = _mm256_set_epi8(
|
||||
15, 15, 15, 15, 11, 11, 11, 11, 7, 7, 7, 7, 3, 3, 3, 3,
|
||||
15, 15, 15, 15, 11, 11, 11, 11, 7, 7, 7, 7, 3, 3, 3, 3,
|
||||
);
|
||||
|
||||
let mut x: usize = 0;
|
||||
while x < width.saturating_sub(7) {
|
||||
let src_pixels = simd_utils::loadu_si256(src_row, x);
|
||||
|
||||
let factor_pixels = _mm256_shuffle_epi8(src_pixels, factor_mask);
|
||||
let factor_pixels = _mm256_or_si256(factor_pixels, max_alpha);
|
||||
|
||||
let pix1 = _mm256_unpacklo_epi8(src_pixels, zero);
|
||||
let factors = _mm256_unpacklo_epi8(factor_pixels, zero);
|
||||
let pix1 = _mm256_add_epi16(_mm256_mullo_epi16(pix1, factors), half);
|
||||
let pix1 = _mm256_add_epi16(pix1, _mm256_srli_epi16::<8>(pix1));
|
||||
let pix1 = _mm256_srli_epi16::<8>(pix1);
|
||||
|
||||
let pix2 = _mm256_unpackhi_epi8(src_pixels, zero);
|
||||
let factors = _mm256_unpackhi_epi8(factor_pixels, zero);
|
||||
let pix2 = _mm256_add_epi16(_mm256_mullo_epi16(pix2, factors), half);
|
||||
let pix2 = _mm256_add_epi16(pix2, _mm256_srli_epi16::<8>(pix2));
|
||||
let pix2 = _mm256_srli_epi16::<8>(pix2);
|
||||
|
||||
let dst_pixels = _mm256_packus_epi16(pix1, pix2);
|
||||
|
||||
let dst_ptr = dst_row.get_unchecked_mut(x..).as_mut_ptr() as *mut __m256i;
|
||||
_mm256_storeu_si256(dst_ptr, dst_pixels);
|
||||
|
||||
x += 8;
|
||||
}
|
||||
|
||||
let src_tail = &src_row[x..];
|
||||
let dst_tail = &mut dst_row[x..];
|
||||
native::multiply_alpha_row(src_tail, dst_tail);
|
||||
}
|
||||
|
||||
// Divide
|
||||
|
||||
#[target_feature(enable = "avx2")]
|
||||
pub(crate) unsafe fn divide_alpha(
|
||||
src_image: TypedImageView<U8x4>,
|
||||
mut dst_image: TypedImageViewMut<U8x4>,
|
||||
) {
|
||||
let src_rows = src_image.iter_rows(0);
|
||||
let dst_rows = dst_image.iter_rows_mut();
|
||||
|
||||
for (src_row, dst_row) in src_rows.zip(dst_rows) {
|
||||
divide_alpha_row(src_row, dst_row);
|
||||
}
|
||||
}
|
||||
|
||||
#[target_feature(enable = "avx2")]
|
||||
pub(crate) unsafe fn divide_alpha_inplace(mut image: TypedImageViewMut<U8x4>) {
|
||||
for dst_row in image.iter_rows_mut() {
|
||||
let src_row = std::slice::from_raw_parts(dst_row.as_ptr(), dst_row.len());
|
||||
divide_alpha_row(src_row, dst_row);
|
||||
}
|
||||
}
|
||||
|
||||
#[target_feature(enable = "avx2")]
|
||||
unsafe fn divide_alpha_row(src_row: &[U8x4], dst_row: &mut [U8x4]) {
|
||||
let zero = _mm256_setzero_si256();
|
||||
let alpha_mask = _mm256_set1_epi32(0xff000000u32 as i32);
|
||||
#[rustfmt::skip]
|
||||
let shuffle1 = _mm256_set_epi8(
|
||||
5, 4, 5, 4, 5, 4, 5, 4, 1, 0, 1, 0, 1, 0, 1, 0,
|
||||
5, 4, 5, 4, 5, 4, 5, 4, 1, 0, 1, 0, 1, 0, 1, 0,
|
||||
);
|
||||
#[rustfmt::skip]
|
||||
let shuffle2 = _mm256_set_epi8(
|
||||
13, 12, 13, 12, 13, 12, 13, 12, 9, 8, 9, 8, 9, 8, 9, 8,
|
||||
13, 12, 13, 12, 13, 12, 13, 12, 9, 8, 9, 8, 9, 8, 9, 8,
|
||||
);
|
||||
let alpha_scale = _mm256_set1_ps(255.0 * 256.0);
|
||||
|
||||
let src_chunks = src_row.chunks_exact(8);
|
||||
let src_remainder = src_chunks.remainder();
|
||||
let mut dst_chunks = dst_row.chunks_exact_mut(8);
|
||||
|
||||
for (src, dst) in src_chunks.zip(&mut dst_chunks) {
|
||||
let src_pixels = _mm256_loadu_si256(src.as_ptr() as *const __m256i);
|
||||
|
||||
let alpha_f32 = _mm256_cvtepi32_ps(_mm256_srli_epi32::<24>(src_pixels));
|
||||
let scaled_alpha_f32 = _mm256_div_ps(alpha_scale, alpha_f32);
|
||||
let scaled_alpha_i32 = _mm256_cvtps_epi32(scaled_alpha_f32);
|
||||
let mma0 = _mm256_shuffle_epi8(scaled_alpha_i32, shuffle1);
|
||||
let mma1 = _mm256_shuffle_epi8(scaled_alpha_i32, shuffle2);
|
||||
|
||||
let pix0 = _mm256_unpacklo_epi8(zero, src_pixels);
|
||||
let pix1 = _mm256_unpackhi_epi8(zero, src_pixels);
|
||||
|
||||
let pix0 = _mm256_mulhi_epu16(pix0, mma0);
|
||||
let pix1 = _mm256_mulhi_epu16(pix1, mma1);
|
||||
|
||||
let alpha = _mm256_and_si256(src_pixels, alpha_mask);
|
||||
let rgb = _mm256_packus_epi16(pix0, pix1);
|
||||
let dst_pixels = _mm256_blendv_epi8(rgb, alpha, alpha_mask);
|
||||
|
||||
_mm256_storeu_si256(dst.as_mut_ptr() as *mut __m256i, dst_pixels);
|
||||
}
|
||||
|
||||
if !src_remainder.is_empty() {
|
||||
let dst_reminder = dst_chunks.into_remainder();
|
||||
sse4::divide_alpha_row(src_remainder, dst_reminder);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,61 @@
|
||||
use crate::image_view::{TypedImageView, TypedImageViewMut};
|
||||
use crate::pixels::U8x4;
|
||||
use crate::CpuExtensions;
|
||||
|
||||
use super::AlphaMulDiv;
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
mod avx2;
|
||||
mod native;
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
mod sse4;
|
||||
|
||||
impl AlphaMulDiv for U8x4 {
|
||||
fn multiply_alpha(
|
||||
src_image: TypedImageView<Self>,
|
||||
dst_image: TypedImageViewMut<Self>,
|
||||
cpu_extensions: CpuExtensions,
|
||||
) {
|
||||
match cpu_extensions {
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
CpuExtensions::Avx2 => unsafe { avx2::multiply_alpha(src_image, dst_image) },
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
CpuExtensions::Sse4_1 => unsafe { sse4::multiply_alpha(src_image, dst_image) },
|
||||
_ => native::multiply_alpha(src_image, dst_image),
|
||||
}
|
||||
}
|
||||
|
||||
fn multiply_alpha_inplace(image: TypedImageViewMut<Self>, cpu_extensions: CpuExtensions) {
|
||||
match cpu_extensions {
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
CpuExtensions::Avx2 => unsafe { avx2::multiply_alpha_inplace(image) },
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
CpuExtensions::Sse4_1 => unsafe { sse4::multiply_alpha_inplace(image) },
|
||||
_ => native::multiply_alpha_inplace(image),
|
||||
}
|
||||
}
|
||||
|
||||
fn divide_alpha(
|
||||
src_image: TypedImageView<Self>,
|
||||
dst_image: TypedImageViewMut<Self>,
|
||||
cpu_extensions: CpuExtensions,
|
||||
) {
|
||||
match cpu_extensions {
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
CpuExtensions::Avx2 => unsafe { avx2::divide_alpha(src_image, dst_image) },
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
CpuExtensions::Sse4_1 => unsafe { sse4::divide_alpha(src_image, dst_image) },
|
||||
_ => native::divide_alpha(src_image, dst_image),
|
||||
}
|
||||
}
|
||||
|
||||
fn divide_alpha_inplace(image: TypedImageViewMut<Self>, cpu_extensions: CpuExtensions) {
|
||||
match cpu_extensions {
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
CpuExtensions::Avx2 => unsafe { avx2::divide_alpha_inplace(image) },
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
CpuExtensions::Sse4_1 => unsafe { sse4::divide_alpha_inplace(image) },
|
||||
_ => native::divide_alpha_inplace(image),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
use crate::alpha::common::{div_and_clip, mul_div_255, RECIP_ALPHA};
|
||||
use crate::image_view::{TypedImageView, TypedImageViewMut};
|
||||
use crate::pixels::U8x4;
|
||||
|
||||
pub(crate) fn multiply_alpha(
|
||||
src_image: TypedImageView<U8x4>,
|
||||
mut dst_image: TypedImageViewMut<U8x4>,
|
||||
) {
|
||||
let src_rows = src_image.iter_rows(0);
|
||||
let dst_rows = dst_image.iter_rows_mut();
|
||||
|
||||
for (src_row, dst_row) in src_rows.zip(dst_rows) {
|
||||
multiply_alpha_row(src_row, dst_row);
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn multiply_alpha_inplace(mut image: TypedImageViewMut<U8x4>) {
|
||||
for dst_row in image.iter_rows_mut() {
|
||||
let src_row = unsafe { std::slice::from_raw_parts(dst_row.as_ptr(), dst_row.len()) };
|
||||
multiply_alpha_row(src_row, dst_row);
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub(crate) fn multiply_alpha_row(src_row: &[U8x4], dst_row: &mut [U8x4]) {
|
||||
for (src_pixel, dst_pixel) in src_row.iter().zip(dst_row) {
|
||||
let components: [u8; 4] = src_pixel.0.to_le_bytes();
|
||||
let alpha = components[3];
|
||||
dst_pixel.0 = u32::from_le_bytes([
|
||||
mul_div_255(components[0], alpha),
|
||||
mul_div_255(components[1], alpha),
|
||||
mul_div_255(components[2], alpha),
|
||||
alpha,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
// Divide
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn divide_alpha(
|
||||
src_image: TypedImageView<U8x4>,
|
||||
mut dst_image: TypedImageViewMut<U8x4>,
|
||||
) {
|
||||
let src_rows = src_image.iter_rows(0);
|
||||
let dst_rows = dst_image.iter_rows_mut();
|
||||
|
||||
for (src_row, dst_row) in src_rows.zip(dst_rows) {
|
||||
divide_alpha_row(src_row, dst_row);
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn divide_alpha_inplace(mut image: TypedImageViewMut<U8x4>) {
|
||||
for dst_row in image.iter_rows_mut() {
|
||||
let src_row = unsafe { std::slice::from_raw_parts(dst_row.as_ptr(), dst_row.len()) };
|
||||
divide_alpha_row(src_row, dst_row);
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub(crate) fn divide_alpha_row(src_row: &[U8x4], dst_row: &mut [U8x4]) {
|
||||
src_row
|
||||
.iter()
|
||||
.zip(dst_row)
|
||||
.for_each(|(src_pixel, dst_pixel)| {
|
||||
let components: [u8; 4] = src_pixel.0.to_le_bytes();
|
||||
let alpha = components[3];
|
||||
let recip_alpha = RECIP_ALPHA[alpha as usize];
|
||||
dst_pixel.0 = u32::from_le_bytes([
|
||||
div_and_clip(components[0], recip_alpha),
|
||||
div_and_clip(components[1], recip_alpha),
|
||||
div_and_clip(components[2], recip_alpha),
|
||||
alpha,
|
||||
]);
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,151 @@
|
||||
use std::arch::x86_64::*;
|
||||
|
||||
use crate::image_view::{TypedImageView, TypedImageViewMut};
|
||||
use crate::pixels::U8x4;
|
||||
|
||||
use super::native;
|
||||
|
||||
#[target_feature(enable = "sse4.1")]
|
||||
pub(crate) unsafe fn multiply_alpha(
|
||||
src_image: TypedImageView<U8x4>,
|
||||
mut dst_image: TypedImageViewMut<U8x4>,
|
||||
) {
|
||||
let src_rows = src_image.iter_rows(0);
|
||||
let dst_rows = dst_image.iter_rows_mut();
|
||||
|
||||
for (src_row, dst_row) in src_rows.zip(dst_rows) {
|
||||
multiply_alpha_row(src_row, dst_row);
|
||||
}
|
||||
}
|
||||
|
||||
#[target_feature(enable = "sse4.1")]
|
||||
pub(crate) unsafe fn multiply_alpha_inplace(mut image: TypedImageViewMut<U8x4>) {
|
||||
for dst_row in image.iter_rows_mut() {
|
||||
let src_row = std::slice::from_raw_parts(dst_row.as_ptr(), dst_row.len());
|
||||
multiply_alpha_row(src_row, dst_row);
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
#[target_feature(enable = "sse4.1")]
|
||||
unsafe fn multiply_alpha_row(src_row: &[U8x4], dst_row: &mut [U8x4]) {
|
||||
let zero = _mm_setzero_si128();
|
||||
let half = _mm_set1_epi16(128);
|
||||
|
||||
const MAX_A: i32 = 0xff000000u32 as i32;
|
||||
let max_alpha = _mm_set1_epi32(MAX_A);
|
||||
let factor_mask = _mm_set_epi8(15, 15, 15, 15, 11, 11, 11, 11, 7, 7, 7, 7, 3, 3, 3, 3);
|
||||
|
||||
let src_chunks = src_row.chunks_exact(4);
|
||||
let src_remainder = src_chunks.remainder();
|
||||
let mut dst_chunks = dst_row.chunks_exact_mut(4);
|
||||
|
||||
for (src, dst) in src_chunks.zip(&mut dst_chunks) {
|
||||
let src_pixels = _mm_loadu_si128(src.as_ptr() as *const __m128i);
|
||||
|
||||
let factor_pixels = _mm_shuffle_epi8(src_pixels, factor_mask);
|
||||
let factor_pixels = _mm_or_si128(factor_pixels, max_alpha);
|
||||
|
||||
let pix1 = _mm_unpacklo_epi8(src_pixels, zero);
|
||||
let factors = _mm_unpacklo_epi8(factor_pixels, zero);
|
||||
let pix1 = _mm_add_epi16(_mm_mullo_epi16(pix1, factors), half);
|
||||
let pix1 = _mm_add_epi16(pix1, _mm_srli_epi16::<8>(pix1));
|
||||
let pix1 = _mm_srli_epi16::<8>(pix1);
|
||||
|
||||
let pix2 = _mm_unpackhi_epi8(src_pixels, zero);
|
||||
let factors = _mm_unpackhi_epi8(factor_pixels, zero);
|
||||
let pix2 = _mm_add_epi16(_mm_mullo_epi16(pix2, factors), half);
|
||||
let pix2 = _mm_add_epi16(pix2, _mm_srli_epi16::<8>(pix2));
|
||||
let pix2 = _mm_srli_epi16::<8>(pix2);
|
||||
|
||||
let dst_pixels = _mm_packus_epi16(pix1, pix2);
|
||||
|
||||
_mm_storeu_si128(dst.as_mut_ptr() as *mut __m128i, dst_pixels);
|
||||
}
|
||||
|
||||
if !src_remainder.is_empty() {
|
||||
let dst_reminder = dst_chunks.into_remainder();
|
||||
native::multiply_alpha_row(src_remainder, dst_reminder);
|
||||
}
|
||||
}
|
||||
|
||||
// Divide
|
||||
|
||||
#[target_feature(enable = "sse4.1")]
|
||||
pub(crate) unsafe fn divide_alpha(
|
||||
src_image: TypedImageView<U8x4>,
|
||||
mut dst_image: TypedImageViewMut<U8x4>,
|
||||
) {
|
||||
let src_rows = src_image.iter_rows(0);
|
||||
let dst_rows = dst_image.iter_rows_mut();
|
||||
|
||||
for (src_row, dst_row) in src_rows.zip(dst_rows) {
|
||||
divide_alpha_row(src_row, dst_row);
|
||||
}
|
||||
}
|
||||
|
||||
#[target_feature(enable = "sse4.1")]
|
||||
pub(crate) unsafe fn divide_alpha_inplace(mut image: TypedImageViewMut<U8x4>) {
|
||||
for dst_row in image.iter_rows_mut() {
|
||||
let src_row = std::slice::from_raw_parts(dst_row.as_ptr(), dst_row.len());
|
||||
divide_alpha_row(src_row, dst_row);
|
||||
}
|
||||
}
|
||||
|
||||
#[target_feature(enable = "sse4.1")]
|
||||
pub(crate) unsafe fn divide_alpha_row(src_row: &[U8x4], dst_row: &mut [U8x4]) {
|
||||
let src_chunks = src_row.chunks_exact(4);
|
||||
let src_remainder = src_chunks.remainder();
|
||||
let mut dst_chunks = dst_row.chunks_exact_mut(4);
|
||||
|
||||
for (src, dst) in src_chunks.zip(&mut dst_chunks) {
|
||||
divide_alpha_four_pixels(src.as_ptr(), dst.as_mut_ptr());
|
||||
}
|
||||
|
||||
if !src_remainder.is_empty() {
|
||||
let dst_reminder = dst_chunks.into_remainder();
|
||||
let mut src_pixels = [U8x4(0); 4];
|
||||
src_pixels
|
||||
.iter_mut()
|
||||
.zip(src_remainder)
|
||||
.for_each(|(d, s)| *d = *s);
|
||||
|
||||
let mut dst_pixels = [U8x4(0); 4];
|
||||
divide_alpha_four_pixels(src_pixels.as_ptr(), dst_pixels.as_mut_ptr());
|
||||
|
||||
dst_pixels
|
||||
.iter()
|
||||
.zip(dst_reminder)
|
||||
.for_each(|(s, d)| *d = *s);
|
||||
}
|
||||
}
|
||||
|
||||
#[target_feature(enable = "sse4.1")]
|
||||
unsafe fn divide_alpha_four_pixels(src: *const U8x4, dst: *mut U8x4) {
|
||||
let zero = _mm_setzero_si128();
|
||||
let alpha_mask = _mm_set1_epi32(0xff000000u32 as i32);
|
||||
let shuffle1 = _mm_set_epi8(5, 4, 5, 4, 5, 4, 5, 4, 1, 0, 1, 0, 1, 0, 1, 0);
|
||||
let shuffle2 = _mm_set_epi8(13, 12, 13, 12, 13, 12, 13, 12, 9, 8, 9, 8, 9, 8, 9, 8);
|
||||
let alpha_scale = _mm_set1_ps(255.0 * 256.0);
|
||||
|
||||
let src_pixels = _mm_loadu_si128(src as *const __m128i);
|
||||
|
||||
let alpha_f32 = _mm_cvtepi32_ps(_mm_srli_epi32::<24>(src_pixels));
|
||||
let scaled_alpha_f32 = _mm_div_ps(alpha_scale, alpha_f32);
|
||||
// let scaled_alpha_f32 = _mm_mul_ps(alpha_scale, _mm_rcp_ps(alpha_f32));
|
||||
let scaled_alpha_i32 = _mm_cvtps_epi32(scaled_alpha_f32);
|
||||
let mma0 = _mm_shuffle_epi8(scaled_alpha_i32, shuffle1);
|
||||
let mma1 = _mm_shuffle_epi8(scaled_alpha_i32, shuffle2);
|
||||
|
||||
let pix0 = _mm_unpacklo_epi8(zero, src_pixels);
|
||||
let pix1 = _mm_unpackhi_epi8(zero, src_pixels);
|
||||
|
||||
let pix0 = _mm_mulhi_epu16(pix0, mma0);
|
||||
let pix1 = _mm_mulhi_epu16(pix1, mma1);
|
||||
|
||||
let alpha = _mm_and_si128(src_pixels, alpha_mask);
|
||||
let rgb = _mm_packus_epi16(pix0, pix1);
|
||||
let dst_pixels = _mm_blendv_epi8(rgb, alpha, alpha_mask);
|
||||
|
||||
_mm_storeu_si128(dst as *mut __m128i, dst_pixels);
|
||||
}
|
||||
@@ -14,6 +14,7 @@ mod i32x1;
|
||||
mod optimisations;
|
||||
mod u16x3;
|
||||
mod u8x1;
|
||||
mod u8x2;
|
||||
mod u8x3;
|
||||
mod u8x4;
|
||||
mod vertical_u16;
|
||||
|
||||
@@ -0,0 +1,41 @@
|
||||
use crate::convolution::vertical_u8::vert_convolution_u8;
|
||||
use crate::image_view::{TypedImageView, TypedImageViewMut};
|
||||
use crate::pixels::U8x2;
|
||||
use crate::CpuExtensions;
|
||||
|
||||
use super::{Coefficients, Convolution};
|
||||
|
||||
// #[cfg(target_arch = "x86_64")]
|
||||
// mod avx2;
|
||||
mod native;
|
||||
// #[cfg(target_arch = "x86_64")]
|
||||
// mod sse4;
|
||||
|
||||
impl Convolution for U8x2 {
|
||||
fn horiz_convolution(
|
||||
src_image: TypedImageView<Self>,
|
||||
dst_image: TypedImageViewMut<Self>,
|
||||
offset: u32,
|
||||
coeffs: Coefficients,
|
||||
cpu_extensions: CpuExtensions,
|
||||
) {
|
||||
match cpu_extensions {
|
||||
// #[cfg(target_arch = "x86_64")]
|
||||
// CpuExtensions::Avx2 => avx2::horiz_convolution(src_image, dst_image, offset, coeffs),
|
||||
// #[cfg(target_arch = "x86_64")]
|
||||
// CpuExtensions::Sse4_1 => unsafe {
|
||||
// sse4::horiz_convolution(src_image, dst_image, offset, coeffs)
|
||||
// },
|
||||
_ => native::horiz_convolution(src_image, dst_image, offset, coeffs),
|
||||
}
|
||||
}
|
||||
|
||||
fn vert_convolution(
|
||||
src_image: TypedImageView<Self>,
|
||||
dst_image: TypedImageViewMut<Self>,
|
||||
coeffs: Coefficients,
|
||||
cpu_extensions: CpuExtensions,
|
||||
) {
|
||||
vert_convolution_u8(src_image, dst_image, coeffs, cpu_extensions);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
use crate::convolution::{optimisations, Coefficients};
|
||||
use crate::image_view::{TypedImageView, TypedImageViewMut};
|
||||
use crate::pixels::U8x2;
|
||||
|
||||
pub(crate) fn horiz_convolution(
|
||||
src_image: TypedImageView<U8x2>,
|
||||
mut dst_image: TypedImageViewMut<U8x2>,
|
||||
offset: u32,
|
||||
coeffs: Coefficients,
|
||||
) {
|
||||
let (values, window_size, bounds) = (coeffs.values, coeffs.window_size, coeffs.bounds);
|
||||
|
||||
let normalizer_guard = optimisations::NormalizerGuard16::new(values);
|
||||
let precision = normalizer_guard.precision();
|
||||
let coefficients_chunks = normalizer_guard.normalized_chunks(window_size, &bounds);
|
||||
let initial = 1 << (precision - 1);
|
||||
|
||||
let src_rows = src_image.iter_rows(offset);
|
||||
let dst_rows = dst_image.iter_rows_mut();
|
||||
for (dst_row, src_row) in dst_rows.zip(src_rows) {
|
||||
for (&coeffs_chunk, dst_pixel) in coefficients_chunks.iter().zip(dst_row.iter_mut()) {
|
||||
let first_x_src = coeffs_chunk.start as usize;
|
||||
let ks = coeffs_chunk.values;
|
||||
let mut ss = [initial; 2];
|
||||
let src_pixels = unsafe { src_row.get_unchecked(first_x_src..) };
|
||||
for (&k, &src_pixel) in ks.iter().zip(src_pixels) {
|
||||
let components: [u8; 2] = src_pixel.0.to_le_bytes();
|
||||
for (i, s) in ss.iter_mut().enumerate() {
|
||||
*s += components[i] as i32 * (k as i32);
|
||||
}
|
||||
}
|
||||
dst_pixel.0 = u16::from_le_bytes(ss.map(|v| unsafe { normalizer_guard.clip(v) }));
|
||||
}
|
||||
}
|
||||
}
|
||||
+28
-9
@@ -1,7 +1,7 @@
|
||||
use std::num::NonZeroU32;
|
||||
|
||||
use crate::image_view::{ImageRows, ImageRowsMut, TypedImageView, TypedImageViewMut};
|
||||
use crate::pixels::{Pixel, PixelType, U16x3, U8x3, U8x4, F32, I32, U8};
|
||||
use crate::pixels::{Pixel, PixelType, U16x3, U8x2, U8x3, U8x4, F32, I32, U8};
|
||||
use crate::{ImageBufferError, ImageView, ImageViewMut};
|
||||
|
||||
#[derive(Debug)]
|
||||
@@ -24,6 +24,7 @@ impl<'a> Image<'a> {
|
||||
pub fn new(width: NonZeroU32, height: NonZeroU32, pixel_type: PixelType) -> Self {
|
||||
let pixels_count = (width.get() * height.get()) as usize;
|
||||
let pixels = match pixel_type {
|
||||
PixelType::U8x2 => PixelsContainer::VecU8(vec![0; pixels_count * U8x2::size()]),
|
||||
PixelType::U8x3 => PixelsContainer::VecU8(vec![0; pixels_count * U8x3::size()]),
|
||||
PixelType::U16x3 => PixelsContainer::VecU8(vec![0; pixels_count * U16x3::size()]),
|
||||
PixelType::U8x4 => PixelsContainer::VecU8(vec![0; pixels_count * U8x4::size()]),
|
||||
@@ -105,14 +106,6 @@ impl<'a> Image<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn into_vec(self) -> Vec<u8> {
|
||||
match self.pixels {
|
||||
PixelsContainer::MutU8(p) => p.into(),
|
||||
PixelsContainer::VecU8(v) => v,
|
||||
}
|
||||
}
|
||||
|
||||
/// Mutable buffer with image pixels.
|
||||
#[inline(always)]
|
||||
fn buffer_mut(&mut self) -> &mut [u8] {
|
||||
@@ -122,11 +115,28 @@ impl<'a> Image<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn into_vec(self) -> Vec<u8> {
|
||||
match self.pixels {
|
||||
PixelsContainer::MutU8(p) => p.into(),
|
||||
PixelsContainer::VecU8(v) => v,
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub fn view(&self) -> ImageView {
|
||||
let buffer = self.buffer();
|
||||
let rows_count = self.height.get() as usize;
|
||||
let rows = match self.pixel_type {
|
||||
PixelType::U8x2 => {
|
||||
let pixels = unsafe { buffer.align_to::<U8x2>().1 };
|
||||
ImageRows::U8x2(
|
||||
pixels
|
||||
.chunks_exact(self.width.get() as usize)
|
||||
.take(rows_count)
|
||||
.collect(),
|
||||
)
|
||||
}
|
||||
PixelType::U8x3 => {
|
||||
let pixels = unsafe { buffer.align_to::<U8x3>().1 };
|
||||
ImageRows::U8x3(
|
||||
@@ -193,6 +203,15 @@ impl<'a> Image<'a> {
|
||||
let buffer = self.buffer_mut();
|
||||
let rows_count = height.get() as usize;
|
||||
let rows = match pixel_type {
|
||||
PixelType::U8x2 => {
|
||||
let pixels = unsafe { buffer.align_to_mut::<U8x2>().1 };
|
||||
ImageRowsMut::U8x2(
|
||||
pixels
|
||||
.chunks_exact_mut(width.get() as usize)
|
||||
.take(rows_count)
|
||||
.collect(),
|
||||
)
|
||||
}
|
||||
PixelType::U8x3 => {
|
||||
let pixels = unsafe { buffer.align_to_mut::<U8x3>().1 };
|
||||
ImageRowsMut::U8x3(
|
||||
|
||||
+73
-4
@@ -2,7 +2,7 @@ use std::num::NonZeroU32;
|
||||
use std::slice;
|
||||
|
||||
use crate::errors::{CropBoxError, ImageBufferError, ImageRowsError};
|
||||
use crate::pixels::{Pixel, PixelType, U16x3, U8x3, U8x4, F32, I32, U8};
|
||||
use crate::pixels::{Pixel, PixelType, U16x3, U8x2, U8x3, U8x4, F32, I32, U8};
|
||||
|
||||
pub(crate) type RowMut<'a, 'b, T> = &'a mut &'b mut [T];
|
||||
pub(crate) type TwoRows<'a, T> = (&'a [T], &'a [T]);
|
||||
@@ -25,13 +25,15 @@ pub struct CropBox {
|
||||
|
||||
/// An immutable rows of image.
|
||||
#[derive(Debug, Clone)]
|
||||
#[non_exhaustive]
|
||||
pub enum ImageRows<'a> {
|
||||
U8(Vec<&'a [U8]>),
|
||||
U8x2(Vec<&'a [U8x2]>),
|
||||
U8x3(Vec<&'a [U8x3]>),
|
||||
U8x4(Vec<&'a [U8x4]>),
|
||||
U16x3(Vec<&'a [U16x3]>),
|
||||
I32(Vec<&'a [I32]>),
|
||||
F32(Vec<&'a [F32]>),
|
||||
U8(Vec<&'a [U8]>),
|
||||
}
|
||||
|
||||
impl<'a> ImageRows<'a> {
|
||||
@@ -41,30 +43,52 @@ impl<'a> ImageRows<'a> {
|
||||
height: NonZeroU32,
|
||||
) -> Result<(), ImageRowsError> {
|
||||
match self {
|
||||
ImageRows::U8(rows) => check_rows_count_and_size(width, height, rows),
|
||||
ImageRows::U8x2(rows) => check_rows_count_and_size(width, height, rows),
|
||||
ImageRows::U8x3(rows) => check_rows_count_and_size(width, height, rows),
|
||||
ImageRows::U8x4(rows) => check_rows_count_and_size(width, height, rows),
|
||||
ImageRows::U16x3(rows) => check_rows_count_and_size(width, height, rows),
|
||||
ImageRows::I32(rows) => check_rows_count_and_size(width, height, rows),
|
||||
ImageRows::F32(rows) => check_rows_count_and_size(width, height, rows),
|
||||
ImageRows::U8(rows) => check_rows_count_and_size(width, height, rows),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn pixel_type(&self) -> PixelType {
|
||||
match self {
|
||||
Self::U8(_) => PixelType::U8,
|
||||
Self::U8x2(_) => PixelType::U8x2,
|
||||
Self::U8x3(_) => PixelType::U8x3,
|
||||
Self::U8x4(_) => PixelType::U8x4,
|
||||
Self::U16x3(_) => PixelType::U16x3,
|
||||
Self::I32(_) => PixelType::I32,
|
||||
Self::F32(_) => PixelType::F32,
|
||||
Self::U8(_) => PixelType::U8,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! image_rows_from {
|
||||
($pixel_type:tt, $enum_type:expr) => {
|
||||
impl<'a> From<Vec<&'a [$pixel_type]>> for ImageRows<'a> {
|
||||
fn from(rows: Vec<&'a [$pixel_type]>) -> Self {
|
||||
$enum_type(rows)
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
image_rows_from!(U8, ImageRows::U8);
|
||||
image_rows_from!(U8x2, ImageRows::U8x2);
|
||||
image_rows_from!(U8x3, ImageRows::U8x3);
|
||||
image_rows_from!(U8x4, ImageRows::U8x4);
|
||||
image_rows_from!(U16x3, ImageRows::U16x3);
|
||||
image_rows_from!(I32, ImageRows::I32);
|
||||
image_rows_from!(F32, ImageRows::F32);
|
||||
|
||||
/// A mutable rows of image.
|
||||
#[derive(Debug)]
|
||||
#[non_exhaustive]
|
||||
pub enum ImageRowsMut<'a> {
|
||||
U8x2(Vec<&'a mut [U8x2]>),
|
||||
U8x3(Vec<&'a mut [U8x3]>),
|
||||
U8x4(Vec<&'a mut [U8x4]>),
|
||||
U16x3(Vec<&'a mut [U16x3]>),
|
||||
@@ -80,6 +104,7 @@ impl<'a> ImageRowsMut<'a> {
|
||||
height: NonZeroU32,
|
||||
) -> Result<(), ImageRowsError> {
|
||||
match self {
|
||||
Self::U8x2(rows) => check_rows_count_and_size(width, height, rows),
|
||||
Self::U8x3(rows) => check_rows_count_and_size(width, height, rows),
|
||||
Self::U8x4(rows) => check_rows_count_and_size(width, height, rows),
|
||||
Self::U16x3(rows) => check_rows_count_and_size(width, height, rows),
|
||||
@@ -91,6 +116,7 @@ impl<'a> ImageRowsMut<'a> {
|
||||
|
||||
pub fn pixel_type(&self) -> PixelType {
|
||||
match self {
|
||||
Self::U8x2(_) => PixelType::U8x2,
|
||||
Self::U8x3(_) => PixelType::U8x3,
|
||||
Self::U8x4(_) => PixelType::U8x4,
|
||||
Self::U16x3(_) => PixelType::U16x3,
|
||||
@@ -142,6 +168,15 @@ impl<'a> ImageView<'a> {
|
||||
}
|
||||
let rows_count = height.get() as usize;
|
||||
let rows = match pixel_type {
|
||||
PixelType::U8x2 => {
|
||||
let pixels = align_buffer_to(buffer)?;
|
||||
ImageRows::U8x2(
|
||||
pixels
|
||||
.chunks_exact(width.get() as usize)
|
||||
.take(rows_count)
|
||||
.collect(),
|
||||
)
|
||||
}
|
||||
PixelType::U8x3 => {
|
||||
let pixels = align_buffer_to(buffer)?;
|
||||
ImageRows::U8x3(
|
||||
@@ -305,6 +340,19 @@ impl<'a> ImageView<'a> {
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
pub(crate) fn u8x2_image(&self) -> Option<TypedImageView<U8x2>> {
|
||||
if let ImageRows::U8x2(ref rows) = self.rows {
|
||||
Some(TypedImageView {
|
||||
width: self.width,
|
||||
height: self.height,
|
||||
crop_box: self.crop_box,
|
||||
rows,
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn u8x3_image(&self) -> Option<TypedImageView<U8x3>> {
|
||||
if let ImageRows::U8x3(ref rows) = self.rows {
|
||||
Some(TypedImageView {
|
||||
@@ -522,6 +570,15 @@ impl<'a> ImageViewMut<'a> {
|
||||
}
|
||||
let rows_count = height.get() as usize;
|
||||
let rows = match pixel_type {
|
||||
PixelType::U8x2 => {
|
||||
let pixels = align_buffer_to_mut(buffer)?;
|
||||
ImageRowsMut::U8x2(
|
||||
pixels
|
||||
.chunks_exact_mut(width.get() as usize)
|
||||
.take(rows_count)
|
||||
.collect(),
|
||||
)
|
||||
}
|
||||
PixelType::U8x3 => {
|
||||
let pixels = align_buffer_to_mut(buffer)?;
|
||||
ImageRowsMut::U8x3(
|
||||
@@ -599,6 +656,18 @@ impl<'a> ImageViewMut<'a> {
|
||||
self.height
|
||||
}
|
||||
|
||||
pub(crate) fn u8x2_image<'s>(&'s mut self) -> Option<TypedImageViewMut<'s, 'a, U8x2>> {
|
||||
if let ImageRowsMut::U8x2(rows) = &mut self.rows {
|
||||
Some(TypedImageViewMut {
|
||||
width: self.width,
|
||||
height: self.height,
|
||||
rows,
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn u8x3_image<'s>(&'s mut self) -> Option<TypedImageViewMut<'s, 'a, U8x3>> {
|
||||
if let ImageRowsMut::U8x3(rows) = &mut self.rows {
|
||||
Some(TypedImageViewMut {
|
||||
|
||||
+3
-1
@@ -1,9 +1,10 @@
|
||||
#![doc = include_str!("../README.md")]
|
||||
|
||||
pub use alpha::{MulDiv, MulDivImageError, MulDivImagesError};
|
||||
pub use alpha::errors::*;
|
||||
pub use convolution::FilterType;
|
||||
pub use errors::*;
|
||||
pub use image_view::{CropBox, ImageRows, ImageRowsMut, ImageView, ImageViewMut};
|
||||
pub use mul_div::MulDiv;
|
||||
pub use pixels::PixelType;
|
||||
pub use resizer::{CpuExtensions, ResizeAlg, Resizer};
|
||||
|
||||
@@ -14,6 +15,7 @@ mod convolution;
|
||||
mod errors;
|
||||
mod image;
|
||||
mod image_view;
|
||||
mod mul_div;
|
||||
pub mod pixels;
|
||||
mod resizer;
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
|
||||
+226
@@ -0,0 +1,226 @@
|
||||
use crate::alpha::AlphaMulDiv;
|
||||
use crate::image_view::{TypedImageView, TypedImageViewMut};
|
||||
use crate::pixels::{U8x2, U8x4};
|
||||
use crate::{
|
||||
CpuExtensions, ImageView, ImageViewMut, MulDivImageError, MulDivImagesError, PixelType,
|
||||
};
|
||||
|
||||
/// Methods of this structure used to multiply or divide color-channels (RGB or Luma)
|
||||
/// by alpha-channel. Supported pixel types: U8x2, U8x4.
|
||||
///
|
||||
/// By default, instance of `MulDiv` created with best CPU-extensions provided by your CPU.
|
||||
/// You can change this by use method [MulDiv::set_cpu_extensions].
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```
|
||||
/// use std::num::NonZeroU32;
|
||||
/// use fast_image_resize::pixels::PixelType;
|
||||
/// use fast_image_resize::{Image, MulDiv};
|
||||
///
|
||||
/// let width = NonZeroU32::new(10).unwrap();
|
||||
/// let height = NonZeroU32::new(7).unwrap();
|
||||
/// let src_image = Image::new(width, height, PixelType::U8x4);
|
||||
/// let mut dst_image = Image::new(width, height, PixelType::U8x4);
|
||||
///
|
||||
/// let mul_div = MulDiv::default();
|
||||
/// mul_div.multiply_alpha(&src_image.view(), &mut dst_image.view_mut()).unwrap();
|
||||
/// ```
|
||||
#[derive(Default, Debug, Clone)]
|
||||
pub struct MulDiv {
|
||||
cpu_extensions: CpuExtensions,
|
||||
}
|
||||
|
||||
impl MulDiv {
|
||||
#[inline(always)]
|
||||
pub fn cpu_extensions(&self) -> CpuExtensions {
|
||||
self.cpu_extensions
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
/// This is unsafe because this method allows you to set a CPU-extensions
|
||||
/// that is not actually supported by your CPU.
|
||||
pub unsafe fn set_cpu_extensions(&mut self, extensions: CpuExtensions) {
|
||||
self.cpu_extensions = extensions;
|
||||
}
|
||||
|
||||
/// Multiplies color-channels (RGB or Luma) of source image by alpha-channel and store
|
||||
/// result into destination image.
|
||||
pub fn multiply_alpha(
|
||||
&self,
|
||||
src_image: &ImageView,
|
||||
dst_image: &mut ImageViewMut,
|
||||
) -> Result<(), MulDivImagesError> {
|
||||
match src_image.pixel_type() {
|
||||
PixelType::U8x2 => {
|
||||
let (typed_src_image, typed_dst_image) = assert_images_u8x2(src_image, dst_image)?;
|
||||
multiply_alpha(typed_src_image, typed_dst_image, self.cpu_extensions);
|
||||
Ok(())
|
||||
}
|
||||
PixelType::U8x4 => {
|
||||
let (typed_src_image, typed_dst_image) = assert_images_u8x4(src_image, dst_image)?;
|
||||
multiply_alpha(typed_src_image, typed_dst_image, self.cpu_extensions);
|
||||
Ok(())
|
||||
}
|
||||
_ => Err(MulDivImagesError::UnsupportedPixelType),
|
||||
}
|
||||
}
|
||||
|
||||
/// Multiplies color-channels (RGB or Luma) of image by alpha-channel inplace.
|
||||
pub fn multiply_alpha_inplace(&self, image: &mut ImageViewMut) -> Result<(), MulDivImageError> {
|
||||
match image.pixel_type() {
|
||||
PixelType::U8x2 => {
|
||||
let typed_image = assert_image_u8x2(image)?;
|
||||
multiply_alpha_inplace(typed_image, self.cpu_extensions);
|
||||
Ok(())
|
||||
}
|
||||
PixelType::U8x4 => {
|
||||
let typed_image = assert_image_u8x4(image)?;
|
||||
multiply_alpha_inplace(typed_image, self.cpu_extensions);
|
||||
Ok(())
|
||||
}
|
||||
_ => Err(MulDivImageError::UnsupportedPixelType),
|
||||
}
|
||||
}
|
||||
|
||||
/// Divides color-channels (RGB or Luma) of source image by alpha-channel and store
|
||||
/// result into destination image.
|
||||
pub fn divide_alpha(
|
||||
&self,
|
||||
src_image: &ImageView,
|
||||
dst_image: &mut ImageViewMut,
|
||||
) -> Result<(), MulDivImagesError> {
|
||||
match src_image.pixel_type() {
|
||||
PixelType::U8x2 => {
|
||||
let (typed_src_image, typed_dst_image) = assert_images_u8x2(src_image, dst_image)?;
|
||||
divide_alpha(typed_src_image, typed_dst_image, self.cpu_extensions);
|
||||
Ok(())
|
||||
}
|
||||
PixelType::U8x4 => {
|
||||
let (typed_src_image, typed_dst_image) = assert_images_u8x4(src_image, dst_image)?;
|
||||
divide_alpha(typed_src_image, typed_dst_image, self.cpu_extensions);
|
||||
Ok(())
|
||||
}
|
||||
_ => Err(MulDivImagesError::UnsupportedPixelType),
|
||||
}
|
||||
}
|
||||
|
||||
/// Divides color-channels (RGB or Luma) of image by alpha-channel inplace.
|
||||
pub fn divide_alpha_inplace(&self, image: &mut ImageViewMut) -> Result<(), MulDivImageError> {
|
||||
match image.pixel_type() {
|
||||
PixelType::U8x2 => {
|
||||
let typed_image = assert_image_u8x2(image)?;
|
||||
divide_alpha_inplace(typed_image, self.cpu_extensions);
|
||||
Ok(())
|
||||
}
|
||||
PixelType::U8x4 => {
|
||||
let typed_image = assert_image_u8x4(image)?;
|
||||
divide_alpha_inplace(typed_image, self.cpu_extensions);
|
||||
Ok(())
|
||||
}
|
||||
_ => Err(MulDivImageError::UnsupportedPixelType),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn assert_images_u8x2<'s, 'd, 'da>(
|
||||
src_image: &'s ImageView<'s>,
|
||||
dst_image: &'d mut ImageViewMut<'da>,
|
||||
) -> Result<
|
||||
(
|
||||
TypedImageView<'s, 's, U8x2>,
|
||||
TypedImageViewMut<'d, 'da, U8x2>,
|
||||
),
|
||||
MulDivImagesError,
|
||||
> {
|
||||
let src_image_u8x2 = src_image
|
||||
.u8x2_image()
|
||||
.ok_or(MulDivImagesError::UnsupportedPixelType)?;
|
||||
let dst_image_u8x2 = dst_image
|
||||
.u8x2_image()
|
||||
.ok_or(MulDivImagesError::UnsupportedPixelType)?;
|
||||
if src_image_u8x2.width() != dst_image_u8x2.width()
|
||||
|| src_image_u8x2.height() != dst_image_u8x2.height()
|
||||
{
|
||||
return Err(MulDivImagesError::SizeIsDifferent);
|
||||
}
|
||||
Ok((src_image_u8x2, dst_image_u8x2))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn assert_image_u8x2<'a, 'b>(
|
||||
image: &'a mut ImageViewMut<'b>,
|
||||
) -> Result<TypedImageViewMut<'a, 'b, U8x2>, MulDivImageError> {
|
||||
image
|
||||
.u8x2_image()
|
||||
.ok_or(MulDivImageError::UnsupportedPixelType)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn assert_images_u8x4<'s, 'd, 'da>(
|
||||
src_image: &'s ImageView<'s>,
|
||||
dst_image: &'d mut ImageViewMut<'da>,
|
||||
) -> Result<
|
||||
(
|
||||
TypedImageView<'s, 's, U8x4>,
|
||||
TypedImageViewMut<'d, 'da, U8x4>,
|
||||
),
|
||||
MulDivImagesError,
|
||||
> {
|
||||
let src_image_u8x4 = src_image
|
||||
.u8x4_image()
|
||||
.ok_or(MulDivImagesError::UnsupportedPixelType)?;
|
||||
let dst_image_u8x4 = dst_image
|
||||
.u8x4_image()
|
||||
.ok_or(MulDivImagesError::UnsupportedPixelType)?;
|
||||
if src_image_u8x4.width() != dst_image_u8x4.width()
|
||||
|| src_image_u8x4.height() != dst_image_u8x4.height()
|
||||
{
|
||||
return Err(MulDivImagesError::SizeIsDifferent);
|
||||
}
|
||||
Ok((src_image_u8x4, dst_image_u8x4))
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn assert_image_u8x4<'a, 'b>(
|
||||
image: &'a mut ImageViewMut<'b>,
|
||||
) -> Result<TypedImageViewMut<'a, 'b, U8x4>, MulDivImageError> {
|
||||
image
|
||||
.u8x4_image()
|
||||
.ok_or(MulDivImageError::UnsupportedPixelType)
|
||||
}
|
||||
|
||||
fn multiply_alpha<P>(
|
||||
src_image: TypedImageView<P>,
|
||||
dst_image: TypedImageViewMut<P>,
|
||||
cpu_extensions: CpuExtensions,
|
||||
) where
|
||||
P: AlphaMulDiv,
|
||||
{
|
||||
P::multiply_alpha(src_image, dst_image, cpu_extensions)
|
||||
}
|
||||
|
||||
fn multiply_alpha_inplace<P>(image: TypedImageViewMut<P>, cpu_extensions: CpuExtensions)
|
||||
where
|
||||
P: AlphaMulDiv,
|
||||
{
|
||||
P::multiply_alpha_inplace(image, cpu_extensions)
|
||||
}
|
||||
|
||||
fn divide_alpha<P>(
|
||||
src_image: TypedImageView<P>,
|
||||
dst_image: TypedImageViewMut<P>,
|
||||
cpu_extensions: CpuExtensions,
|
||||
) where
|
||||
P: AlphaMulDiv,
|
||||
{
|
||||
P::divide_alpha(src_image, dst_image, cpu_extensions)
|
||||
}
|
||||
|
||||
fn divide_alpha_inplace<P>(image: TypedImageViewMut<P>, cpu_extensions: CpuExtensions)
|
||||
where
|
||||
P: AlphaMulDiv,
|
||||
{
|
||||
P::divide_alpha_inplace(image, cpu_extensions)
|
||||
}
|
||||
+15
-3
@@ -5,6 +5,7 @@ use std::slice;
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
#[non_exhaustive]
|
||||
pub enum PixelType {
|
||||
U8x2,
|
||||
U8x3,
|
||||
U8x4,
|
||||
U16x3,
|
||||
@@ -16,9 +17,10 @@ pub enum PixelType {
|
||||
impl PixelType {
|
||||
pub(crate) fn size(&self) -> usize {
|
||||
match self {
|
||||
Self::U8 => 1,
|
||||
Self::U8x2 => 2,
|
||||
Self::U8x3 => 3,
|
||||
Self::U16x3 => 6,
|
||||
Self::U8 => 1,
|
||||
_ => 4,
|
||||
}
|
||||
}
|
||||
@@ -26,12 +28,13 @@ impl PixelType {
|
||||
/// Returns `true` if given buffer is aligned by the alignment of pixel.
|
||||
pub(crate) fn is_aligned(&self, buffer: &[u8]) -> bool {
|
||||
match self {
|
||||
Self::U8 => true,
|
||||
Self::U8x2 => unsafe { buffer.align_to::<U8x2>().0.is_empty() },
|
||||
Self::U8x3 => unsafe { buffer.align_to::<U8x3>().0.is_empty() },
|
||||
Self::U8x4 => unsafe { buffer.align_to::<U8x4>().0.is_empty() },
|
||||
Self::U16x3 => unsafe { buffer.align_to::<U16x3>().0.is_empty() },
|
||||
Self::I32 => unsafe { buffer.align_to::<I32>().0.is_empty() },
|
||||
Self::F32 => unsafe { buffer.align_to::<F32>().0.is_empty() },
|
||||
Self::U8 => true,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -53,8 +56,9 @@ where
|
||||
///
|
||||
/// Example:
|
||||
/// ```
|
||||
/// # use fast_image_resize::pixels::{U8x3, U8, Pixel};
|
||||
/// # use fast_image_resize::pixels::{U8x2, U8x3, U8, Pixel};
|
||||
/// assert_eq!(U8x3::size(), 3);
|
||||
/// assert_eq!(U8x2::size(), 2);
|
||||
/// assert_eq!(U8::size(), 1);
|
||||
/// ```
|
||||
fn size() -> usize {
|
||||
@@ -98,6 +102,14 @@ macro_rules! pixel_struct {
|
||||
}
|
||||
|
||||
pixel_struct!(U8, u8, u8, 1, PixelType::U8, "One byte per pixel");
|
||||
pixel_struct!(
|
||||
U8x2,
|
||||
u16,
|
||||
u8,
|
||||
2,
|
||||
PixelType::U8x2,
|
||||
"Two bytes per pixel (e.g. LA)"
|
||||
);
|
||||
pixel_struct!(
|
||||
U8x3,
|
||||
[u8; 3],
|
||||
|
||||
@@ -83,6 +83,13 @@ impl Resizer {
|
||||
return Err(DifferentTypesOfPixelsError);
|
||||
}
|
||||
match src_image.pixel_type() {
|
||||
PixelType::U8x2 => {
|
||||
if let Some(src_rows) = src_image.u8x2_image() {
|
||||
if let Some(dst_rows) = dst_image.u8x2_image() {
|
||||
self.resize_inner(src_rows, dst_rows);
|
||||
}
|
||||
}
|
||||
}
|
||||
PixelType::U8x3 => {
|
||||
if let Some(src_rows) = src_image.u8x3_image() {
|
||||
if let Some(dst_rows) = dst_image.u8x3_image() {
|
||||
|
||||
+234
-111
@@ -1,6 +1,6 @@
|
||||
use std::num::NonZeroU32;
|
||||
|
||||
use fast_image_resize::pixels::U8x4;
|
||||
use fast_image_resize::pixels::{Pixel, U8x2, U8x4};
|
||||
use fast_image_resize::{
|
||||
CpuExtensions, Image, ImageRows, ImageRowsMut, ImageView, ImageViewMut, MulDiv, PixelType,
|
||||
};
|
||||
@@ -8,37 +8,78 @@ use utils::{cpu_ext_into_str, image_checksum};
|
||||
|
||||
mod utils;
|
||||
|
||||
const fn p(r: u8, g: u8, b: u8, a: u8) -> U8x4 {
|
||||
trait IntoImageRows
|
||||
where
|
||||
Self: Pixel,
|
||||
{
|
||||
fn into_image_rows(rows: Vec<&[Self]>) -> ImageRows;
|
||||
|
||||
fn into_image_rows_mut(rows: Vec<&mut [Self]>) -> ImageRowsMut;
|
||||
}
|
||||
|
||||
impl IntoImageRows for U8x2 {
|
||||
fn into_image_rows(rows: Vec<&[Self]>) -> ImageRows {
|
||||
ImageRows::U8x2(rows)
|
||||
}
|
||||
|
||||
fn into_image_rows_mut(rows: Vec<&mut [Self]>) -> ImageRowsMut {
|
||||
ImageRowsMut::U8x2(rows)
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoImageRows for U8x4 {
|
||||
fn into_image_rows(rows: Vec<&[Self]>) -> ImageRows {
|
||||
ImageRows::U8x4(rows)
|
||||
}
|
||||
|
||||
fn into_image_rows_mut(rows: Vec<&mut [Self]>) -> ImageRowsMut {
|
||||
ImageRowsMut::U8x4(rows)
|
||||
}
|
||||
}
|
||||
|
||||
const fn p2(l: u8, a: u8) -> U8x2 {
|
||||
U8x2(u16::from_le_bytes([l, a]))
|
||||
}
|
||||
|
||||
const fn p4(r: u8, g: u8, b: u8, a: u8) -> U8x4 {
|
||||
U8x4(u32::from_le_bytes([r, g, b, a]))
|
||||
}
|
||||
|
||||
enum Oper {
|
||||
Mul,
|
||||
Div,
|
||||
}
|
||||
|
||||
// Multiplies by alpha
|
||||
|
||||
fn multiply_alpha_test(cpu_extensions: CpuExtensions) {
|
||||
fn mul_div_alpha_test<P>(oper: Oper, src_pixel: P, result_pixel: P, cpu_extensions: CpuExtensions)
|
||||
where
|
||||
P: Pixel + IntoImageRows,
|
||||
{
|
||||
let width: u32 = 8 + 8 + 7;
|
||||
let height: u32 = 3;
|
||||
|
||||
let src_pixels = [p(255, 128, 0, 128), p(255, 128, 0, 255), p(255, 128, 0, 0)];
|
||||
let res_pixels = [p(128, 64, 0, 128), p(255, 128, 0, 255), p(0, 0, 0, 0)];
|
||||
let src_size = width as usize * height as usize;
|
||||
let mut src_pixels: Vec<P> = vec![src_pixel; src_size];
|
||||
let res_pixels: Vec<P> = vec![result_pixel; src_size];
|
||||
let res_buffer = unsafe { res_pixels.align_to::<u8>().1 };
|
||||
|
||||
let mut src_rows: [Vec<U8x4>; 3] = [
|
||||
vec![src_pixels[0]; width as usize],
|
||||
vec![src_pixels[1]; width as usize],
|
||||
vec![src_pixels[2]; width as usize],
|
||||
];
|
||||
let rows: Vec<&[P]> = src_pixels
|
||||
.chunks_exact(width as usize)
|
||||
.map(|r| r.as_ref())
|
||||
.collect();
|
||||
|
||||
let rows: Vec<&[U8x4]> = src_rows.iter().map(|r| r.as_ref()).collect();
|
||||
let src_image_view = ImageView::new(
|
||||
NonZeroU32::new(width).unwrap(),
|
||||
NonZeroU32::new(height).unwrap(),
|
||||
ImageRows::U8x4(rows),
|
||||
P::into_image_rows(rows),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let mut dst_image = Image::new(
|
||||
NonZeroU32::new(width).unwrap(),
|
||||
NonZeroU32::new(height).unwrap(),
|
||||
PixelType::U8x4,
|
||||
P::pixel_type(),
|
||||
);
|
||||
let mut dst_image_view = dst_image.view_mut();
|
||||
|
||||
@@ -47,133 +88,215 @@ fn multiply_alpha_test(cpu_extensions: CpuExtensions) {
|
||||
alpha_mul_div.set_cpu_extensions(cpu_extensions);
|
||||
}
|
||||
|
||||
alpha_mul_div
|
||||
.multiply_alpha(&src_image_view, &mut dst_image_view)
|
||||
.unwrap();
|
||||
|
||||
let dst_pixels = unsafe { dst_image.buffer().align_to::<u32>().1 };
|
||||
let dst_rows = dst_pixels.chunks_exact(width as usize);
|
||||
for (row, &valid_pixel) in dst_rows.zip(res_pixels.iter()) {
|
||||
for &pixel in row.iter() {
|
||||
assert_eq!(pixel, valid_pixel.0);
|
||||
}
|
||||
match oper {
|
||||
Oper::Mul => alpha_mul_div
|
||||
.multiply_alpha(&src_image_view, &mut dst_image_view)
|
||||
.unwrap(),
|
||||
Oper::Div => alpha_mul_div
|
||||
.divide_alpha(&src_image_view, &mut dst_image_view)
|
||||
.unwrap(),
|
||||
}
|
||||
|
||||
let dst_buffer = dst_image.buffer();
|
||||
assert!(dst_buffer.iter().zip(res_buffer).all(|(&d, &r)| d == r));
|
||||
|
||||
// Inplace
|
||||
let rows: Vec<&mut [U8x4]> = src_rows.iter_mut().map(|r| r.as_mut()).collect();
|
||||
let rows: Vec<&mut [P]> = src_pixels
|
||||
.chunks_exact_mut(width as usize)
|
||||
.map(|r| r.as_mut())
|
||||
.collect();
|
||||
let mut image_view = ImageViewMut::new(
|
||||
NonZeroU32::new(width).unwrap(),
|
||||
NonZeroU32::new(height).unwrap(),
|
||||
ImageRowsMut::U8x4(rows),
|
||||
P::into_image_rows_mut(rows),
|
||||
)
|
||||
.unwrap();
|
||||
alpha_mul_div
|
||||
.multiply_alpha_inplace(&mut image_view)
|
||||
.unwrap();
|
||||
|
||||
for (row, &valid_pixel) in src_rows.iter().zip(res_pixels.iter()) {
|
||||
for &pixel in row.iter() {
|
||||
assert_eq!(pixel, valid_pixel);
|
||||
match oper {
|
||||
Oper::Mul => alpha_mul_div
|
||||
.multiply_alpha_inplace(&mut image_view)
|
||||
.unwrap(),
|
||||
Oper::Div => alpha_mul_div.divide_alpha_inplace(&mut image_view).unwrap(),
|
||||
}
|
||||
|
||||
let src_buffer = unsafe { src_pixels.align_to::<u8>().1 };
|
||||
assert!(src_buffer.iter().zip(res_buffer).all(|(&s, &r)| s == r));
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod multiply_alpha_u8x4 {
|
||||
use super::*;
|
||||
|
||||
const SRC_PIXELS: [U8x4; 3] = [
|
||||
p4(255, 128, 0, 128),
|
||||
p4(255, 128, 0, 255),
|
||||
p4(255, 128, 0, 0),
|
||||
];
|
||||
const RES_PIXELS: [U8x4; 3] = [p4(128, 64, 0, 128), p4(255, 128, 0, 255), p4(0, 0, 0, 0)];
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
#[test]
|
||||
fn avx2_test() {
|
||||
for (s, r) in SRC_PIXELS.into_iter().zip(RES_PIXELS) {
|
||||
mul_div_alpha_test(Oper::Mul, s, r, CpuExtensions::Avx2);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
#[test]
|
||||
fn sse4_test() {
|
||||
for (s, r) in SRC_PIXELS.into_iter().zip(RES_PIXELS) {
|
||||
mul_div_alpha_test(Oper::Mul, s, r, CpuExtensions::Sse4_1);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn native_test() {
|
||||
for (s, r) in SRC_PIXELS.into_iter().zip(RES_PIXELS) {
|
||||
mul_div_alpha_test(Oper::Mul, s, r, CpuExtensions::None);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
#[test]
|
||||
fn multiply_alpha_avx2_test() {
|
||||
multiply_alpha_test(CpuExtensions::Avx2);
|
||||
}
|
||||
#[cfg(test)]
|
||||
mod multiply_alpha_u8x2 {
|
||||
use super::*;
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
#[test]
|
||||
fn multiply_alpha_sse4_test() {
|
||||
multiply_alpha_test(CpuExtensions::Sse4_1);
|
||||
}
|
||||
const SRC_PIXELS: [U8x2; 9] = [
|
||||
p2(255, 128),
|
||||
p2(128, 128),
|
||||
p2(0, 128),
|
||||
p2(255, 255),
|
||||
p2(128, 255),
|
||||
p2(0, 255),
|
||||
p2(255, 0),
|
||||
p2(128, 0),
|
||||
p2(0, 0),
|
||||
];
|
||||
const RES_PIXELS: [U8x2; 9] = [
|
||||
p2(128, 128),
|
||||
p2(64, 128),
|
||||
p2(0, 128),
|
||||
p2(255, 255),
|
||||
p2(128, 255),
|
||||
p2(0, 255),
|
||||
p2(0, 0),
|
||||
p2(0, 0),
|
||||
p2(0, 0),
|
||||
];
|
||||
|
||||
#[test]
|
||||
fn multiply_alpha_native_test() {
|
||||
multiply_alpha_test(CpuExtensions::None);
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
#[test]
|
||||
fn avx2_test() {
|
||||
for (s, r) in SRC_PIXELS.into_iter().zip(RES_PIXELS) {
|
||||
mul_div_alpha_test(Oper::Mul, s, r, CpuExtensions::Avx2);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
#[test]
|
||||
fn sse4_test() {
|
||||
for (s, r) in SRC_PIXELS.into_iter().zip(RES_PIXELS) {
|
||||
mul_div_alpha_test(Oper::Mul, s, r, CpuExtensions::Sse4_1);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn native_test() {
|
||||
for (s, r) in SRC_PIXELS.into_iter().zip(RES_PIXELS) {
|
||||
mul_div_alpha_test(Oper::Mul, s, r, CpuExtensions::None);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Divides by alpha
|
||||
|
||||
fn divide_alpha_test(cpu_extensions: CpuExtensions) {
|
||||
let width: u32 = 8 + 8 + 7;
|
||||
let height: u32 = 3;
|
||||
#[cfg(test)]
|
||||
mod divide_alpha_u8x4 {
|
||||
use super::*;
|
||||
|
||||
let src_pixels = [p(128, 64, 0, 128), p(255, 128, 0, 255), p(255, 128, 0, 0)];
|
||||
let res_pixels = [p(255, 127, 0, 128), p(255, 128, 0, 255), p(0, 0, 0, 0)];
|
||||
const OPER: Oper = Oper::Div;
|
||||
const SRC_PIXELS: [U8x4; 3] = [
|
||||
p4(128, 64, 0, 128),
|
||||
p4(255, 128, 0, 255),
|
||||
p4(255, 128, 0, 0),
|
||||
];
|
||||
const RES_PIXELS: [U8x4; 3] = [p4(255, 127, 0, 128), p4(255, 128, 0, 255), p4(0, 0, 0, 0)];
|
||||
|
||||
let mut src_rows: [Vec<U8x4>; 3] = [
|
||||
vec![src_pixels[0]; width as usize],
|
||||
vec![src_pixels[1]; width as usize],
|
||||
vec![src_pixels[2]; width as usize],
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
#[test]
|
||||
fn avx2_test() {
|
||||
for (s, r) in SRC_PIXELS.into_iter().zip(RES_PIXELS) {
|
||||
mul_div_alpha_test(OPER, s, r, CpuExtensions::Avx2);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
#[test]
|
||||
fn sse4_test() {
|
||||
for (s, r) in SRC_PIXELS.into_iter().zip(RES_PIXELS) {
|
||||
mul_div_alpha_test(OPER, s, r, CpuExtensions::Sse4_1);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn native_test() {
|
||||
for (s, r) in SRC_PIXELS.into_iter().zip(RES_PIXELS) {
|
||||
mul_div_alpha_test(OPER, s, r, CpuExtensions::None);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod divide_alpha_u8x2 {
|
||||
use super::*;
|
||||
|
||||
const OPER: Oper = Oper::Div;
|
||||
const SRC_PIXELS: [U8x2; 9] = [
|
||||
p2(128, 128),
|
||||
p2(64, 128),
|
||||
p2(0, 128),
|
||||
p2(255, 255),
|
||||
p2(128, 255),
|
||||
p2(0, 255),
|
||||
p2(255, 0),
|
||||
p2(128, 0),
|
||||
p2(0, 0),
|
||||
];
|
||||
const RES_PIXELS: [U8x2; 9] = [
|
||||
p2(255, 128),
|
||||
p2(127, 128),
|
||||
p2(0, 128),
|
||||
p2(255, 255),
|
||||
p2(128, 255),
|
||||
p2(0, 255),
|
||||
p2(0, 0),
|
||||
p2(0, 0),
|
||||
p2(0, 0),
|
||||
];
|
||||
|
||||
let rows: Vec<&[U8x4]> = src_rows.iter().map(|r| r.as_ref()).collect();
|
||||
let src_image_view = ImageView::new(
|
||||
NonZeroU32::new(width).unwrap(),
|
||||
NonZeroU32::new(height).unwrap(),
|
||||
ImageRows::U8x4(rows),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let mut dst_image = Image::new(
|
||||
NonZeroU32::new(width).unwrap(),
|
||||
NonZeroU32::new(height).unwrap(),
|
||||
PixelType::U8x4,
|
||||
);
|
||||
let mut dst_image_view = dst_image.view_mut();
|
||||
|
||||
let mut alpha_mul_div: MulDiv = Default::default();
|
||||
unsafe {
|
||||
alpha_mul_div.set_cpu_extensions(cpu_extensions);
|
||||
}
|
||||
|
||||
alpha_mul_div
|
||||
.divide_alpha(&src_image_view, &mut dst_image_view)
|
||||
.unwrap();
|
||||
|
||||
let dst_pixels = unsafe { dst_image.buffer().align_to::<u32>().1 };
|
||||
let dst_rows = dst_pixels.chunks_exact(width as usize);
|
||||
for (row, &valid_pixel) in dst_rows.zip(res_pixels.iter()) {
|
||||
for &pixel in row.iter() {
|
||||
assert_eq!(pixel, valid_pixel.0);
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
#[test]
|
||||
fn avx2_test() {
|
||||
for (s, r) in SRC_PIXELS.into_iter().zip(RES_PIXELS) {
|
||||
mul_div_alpha_test(OPER, s, r, CpuExtensions::Avx2);
|
||||
}
|
||||
}
|
||||
|
||||
// Inplace
|
||||
let rows: Vec<&mut [U8x4]> = src_rows.iter_mut().map(|r| r.as_mut()).collect();
|
||||
let mut image_view = ImageViewMut::new(
|
||||
NonZeroU32::new(width).unwrap(),
|
||||
NonZeroU32::new(height).unwrap(),
|
||||
ImageRowsMut::U8x4(rows),
|
||||
)
|
||||
.unwrap();
|
||||
alpha_mul_div.divide_alpha_inplace(&mut image_view).unwrap();
|
||||
|
||||
for (row, &valid_pixel) in src_rows.iter().zip(res_pixels.iter()) {
|
||||
for &pixel in row.iter() {
|
||||
assert_eq!(pixel, valid_pixel);
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
#[test]
|
||||
fn sse4_test() {
|
||||
for (s, r) in SRC_PIXELS.into_iter().zip(RES_PIXELS) {
|
||||
mul_div_alpha_test(OPER, s, r, CpuExtensions::Sse4_1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
#[test]
|
||||
fn divide_alpha_avx2_test() {
|
||||
divide_alpha_test(CpuExtensions::Avx2);
|
||||
}
|
||||
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
#[test]
|
||||
fn divide_alpha_sse4_test() {
|
||||
divide_alpha_test(CpuExtensions::Sse4_1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn divide_alpha_native_test() {
|
||||
divide_alpha_test(CpuExtensions::None);
|
||||
#[test]
|
||||
fn native_test() {
|
||||
for (s, r) in SRC_PIXELS.into_iter().zip(RES_PIXELS) {
|
||||
mul_div_alpha_test(OPER, s, r, CpuExtensions::None);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
+94
-50
@@ -155,6 +155,50 @@ fn upscale_u8() {
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn downscale_u8x2() {
|
||||
type P = U8x2;
|
||||
let buffer = downscale_test::<P>(ResizeAlg::Nearest, CpuExtensions::None);
|
||||
assert_eq!(utils::image_checksum::<2>(&buffer), [2920348, 11054250]);
|
||||
|
||||
let mut cpu_extensions_vec = vec![CpuExtensions::None];
|
||||
// #[cfg(target_arch = "x86_64")]
|
||||
// {
|
||||
// cpu_extensions_vec.push(CpuExtensions::Sse4_1);
|
||||
// cpu_extensions_vec.push(CpuExtensions::Avx2);
|
||||
// }
|
||||
for cpu_extensions in cpu_extensions_vec {
|
||||
let buffer =
|
||||
downscale_test::<P>(ResizeAlg::Convolution(FilterType::Lanczos3), cpu_extensions);
|
||||
assert_eq!(utils::image_checksum::<2>(&buffer), [2923557, 11054250]);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn upscale_u8x2() {
|
||||
type P = U8x2;
|
||||
let buffer = upscale_test::<P>(ResizeAlg::Nearest, CpuExtensions::None);
|
||||
assert_eq!(
|
||||
utils::image_checksum::<2>(&buffer),
|
||||
[1148754010, 4269569040]
|
||||
);
|
||||
|
||||
let mut cpu_extensions_vec = vec![CpuExtensions::None];
|
||||
// #[cfg(target_arch = "x86_64")]
|
||||
// {
|
||||
// cpu_extensions_vec.push(CpuExtensions::Sse4_1);
|
||||
// cpu_extensions_vec.push(CpuExtensions::Avx2);
|
||||
// }
|
||||
for cpu_extensions in cpu_extensions_vec {
|
||||
let buffer =
|
||||
upscale_test::<P>(ResizeAlg::Convolution(FilterType::Lanczos3), cpu_extensions);
|
||||
assert_eq!(
|
||||
utils::image_checksum::<2>(&buffer),
|
||||
[1148811406, 4269569040]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn downscale_u8x3() {
|
||||
type P = U8x3;
|
||||
@@ -205,56 +249,6 @@ fn upscale_u8x3() {
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn downscale_u16x3() {
|
||||
type P = U16x3;
|
||||
let buffer = downscale_test::<P>(ResizeAlg::Nearest, CpuExtensions::None);
|
||||
assert_eq!(
|
||||
utils::image_u16_checksum::<3>(&buffer),
|
||||
[755050580, 756962660, 740848503]
|
||||
);
|
||||
|
||||
let mut cpu_extensions_vec = vec![CpuExtensions::None];
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
{
|
||||
cpu_extensions_vec.push(CpuExtensions::Sse4_1);
|
||||
cpu_extensions_vec.push(CpuExtensions::Avx2);
|
||||
}
|
||||
for cpu_extensions in cpu_extensions_vec {
|
||||
let buffer =
|
||||
downscale_test::<P>(ResizeAlg::Convolution(FilterType::Lanczos3), cpu_extensions);
|
||||
assert_eq!(
|
||||
utils::image_u16_checksum::<3>(&buffer),
|
||||
[756269847, 757632467, 741478612]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn upscale_u16x3() {
|
||||
type P = U16x3;
|
||||
let buffer = upscale_test::<P>(ResizeAlg::Nearest, CpuExtensions::None);
|
||||
assert_eq!(
|
||||
utils::image_u16_checksum::<3>(&buffer),
|
||||
[297094122820, 297713401842, 291717497780]
|
||||
);
|
||||
|
||||
let mut cpu_extensions_vec = vec![CpuExtensions::None];
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
{
|
||||
cpu_extensions_vec.push(CpuExtensions::Sse4_1);
|
||||
cpu_extensions_vec.push(CpuExtensions::Avx2);
|
||||
}
|
||||
for cpu_extensions in cpu_extensions_vec {
|
||||
let buffer =
|
||||
upscale_test::<P>(ResizeAlg::Convolution(FilterType::Lanczos3), cpu_extensions);
|
||||
assert_eq!(
|
||||
utils::image_u16_checksum::<3>(&buffer),
|
||||
[297122154090, 297723994984, 291725294637]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn downscale_u8x4() {
|
||||
type P = U8x4;
|
||||
@@ -310,6 +304,56 @@ fn upscale_u8x4() {
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn downscale_u16x3() {
|
||||
type P = U16x3;
|
||||
let buffer = downscale_test::<P>(ResizeAlg::Nearest, CpuExtensions::None);
|
||||
assert_eq!(
|
||||
utils::image_u16_checksum::<3>(&buffer),
|
||||
[755050580, 756962660, 740848503]
|
||||
);
|
||||
|
||||
let mut cpu_extensions_vec = vec![CpuExtensions::None];
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
{
|
||||
cpu_extensions_vec.push(CpuExtensions::Sse4_1);
|
||||
cpu_extensions_vec.push(CpuExtensions::Avx2);
|
||||
}
|
||||
for cpu_extensions in cpu_extensions_vec {
|
||||
let buffer =
|
||||
downscale_test::<P>(ResizeAlg::Convolution(FilterType::Lanczos3), cpu_extensions);
|
||||
assert_eq!(
|
||||
utils::image_u16_checksum::<3>(&buffer),
|
||||
[756269847, 757632467, 741478612]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn upscale_u16x3() {
|
||||
type P = U16x3;
|
||||
let buffer = upscale_test::<P>(ResizeAlg::Nearest, CpuExtensions::None);
|
||||
assert_eq!(
|
||||
utils::image_u16_checksum::<3>(&buffer),
|
||||
[297094122820, 297713401842, 291717497780]
|
||||
);
|
||||
|
||||
let mut cpu_extensions_vec = vec![CpuExtensions::None];
|
||||
#[cfg(target_arch = "x86_64")]
|
||||
{
|
||||
cpu_extensions_vec.push(CpuExtensions::Sse4_1);
|
||||
cpu_extensions_vec.push(CpuExtensions::Avx2);
|
||||
}
|
||||
for cpu_extensions in cpu_extensions_vec {
|
||||
let buffer =
|
||||
upscale_test::<P>(ResizeAlg::Convolution(FilterType::Lanczos3), cpu_extensions);
|
||||
assert_eq!(
|
||||
utils::image_u16_checksum::<3>(&buffer),
|
||||
[297122154090, 297723994984, 291725294637]
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// #[test]
|
||||
fn _resize_i32() {
|
||||
type P = I32;
|
||||
|
||||
@@ -27,6 +27,7 @@ pub trait PixelExt: Pixel {
|
||||
fn pixel_type_str() -> &'static str {
|
||||
match Self::pixel_type() {
|
||||
PixelType::U8 => "u8",
|
||||
PixelType::U8x2 => "u8x2",
|
||||
PixelType::U8x3 => "u8x3",
|
||||
PixelType::U8x4 => "u8x4",
|
||||
PixelType::U16x3 => "u16x3",
|
||||
@@ -87,6 +88,12 @@ impl PixelExt for U8 {
|
||||
}
|
||||
}
|
||||
|
||||
impl PixelExt for U8x2 {
|
||||
fn img_into_bytes(img: DynamicImage) -> Vec<u8> {
|
||||
img.to_luma_alpha8().into_raw()
|
||||
}
|
||||
}
|
||||
|
||||
impl PixelExt for U8x3 {
|
||||
fn img_into_bytes(img: DynamicImage) -> Vec<u8> {
|
||||
img.to_rgb8().into_raw()
|
||||
@@ -138,6 +145,7 @@ pub fn save_result(image: &Image, name: &str) {
|
||||
std::fs::create_dir_all("./data/result").unwrap();
|
||||
let path = format!("./data/result/{}.png", name);
|
||||
let color_type = match image.pixel_type() {
|
||||
PixelType::U8x2 => ColorType::La8,
|
||||
PixelType::U8x3 => ColorType::Rgb8,
|
||||
PixelType::U8x4 => ColorType::Rgba8,
|
||||
PixelType::U16x3 => ColorType::Rgb16,
|
||||
|
||||
Reference in New Issue
Block a user