fast image operations
| -rw-r--r-- | Cargo.toml | 11 | ||||
| -rw-r--r-- | README.md | 9 | ||||
| -rw-r--r-- | benches/3_8x8.imgbuf (renamed from benches/3_4x4.imgbuf) | 0 | ||||
| -rw-r--r-- | benches/3x3_at_out.imgbuf | 1 | ||||
| -rw-r--r-- | benches/4_160x160.imgbuf (renamed from test_data/4_180x180.imgbuf) | bin | 102400 -> 102400 bytes | |||
| -rw-r--r-- | benches/4_8x8.imgbuf (renamed from benches/4_4x4.imgbuf) | 0 | ||||
| -rw-r--r-- | benches/4x3_at_out.imgbuf | 1 | ||||
| -rw-r--r-- | benches/4x4_at_out.imgbuf | 1 | ||||
| -rw-r--r-- | benches/affine_transformations.rs | 23 | ||||
| -rw-r--r-- | benches/overlays.rs | 83 | ||||
| -rw-r--r-- | src/affine.rs | 149 | ||||
| -rw-r--r-- | src/builder.rs | 98 | ||||
| -rw-r--r-- | src/cat.png | bin | 0 -> 2533832 bytes | |||
| -rw-r--r-- | src/drawing/box.rs | 65 | ||||
| -rw-r--r-- | src/drawing/line.rs | 174 | ||||
| -rw-r--r-- | src/drawing/mod.rs | 4 | ||||
| -rw-r--r-- | src/drawing/tri.rs | 45 | ||||
| -rw-r--r-- | src/lib.rs | 246 | ||||
| -rw-r--r-- | src/overlay.rs | 82 | ||||
| -rw-r--r-- | src/scale.rs | 41 | ||||
| -rw-r--r-- | src/small_cat.png | bin | 0 -> 77497 bytes |
21 files changed, 802 insertions, 231 deletions
@@ -1,24 +1,27 @@ [package] name = "fimg" -version = "0.1.0" +version = "0.4.1" authors = ["bend-n <[email protected]>"] license = "MIT" edition = "2021" description = "fast image operations" repository = "https://github.com/bend-n/fimg" - -# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html +exclude = ["src/cat.png", "src/small_cat.png", "benches/"] [dependencies] png = { version = "0.17", features = ["unstable"], optional = true } [dev-dependencies] -criterion = "0.5" +iai = { version = "0.1.1", features = [], git = "https://github.com/bend-n/iai/" } [[bench]] name = "overlays" harness = false +[[bench]] +name = "affine_transformations" +harness = false + [features] save = ["png"] default = ["save"] @@ -6,4 +6,11 @@ quick simple image operations - [x] overlay - [x] rotation -- [x] flipping
\ No newline at end of file +- [x] flipping +- [x] nearest neighbor scaling +- [x] triangle drawing +- [x] simple line drawing +- [x] box drawing +- [ ] thick box drawing +- [ ] thick line drawing +- [ ] polygon drawing diff --git a/benches/3_4x4.imgbuf b/benches/3_8x8.imgbuf index f3a8624..f3a8624 100644 --- a/benches/3_4x4.imgbuf +++ b/benches/3_8x8.imgbuf diff --git a/benches/3x3_at_out.imgbuf b/benches/3x3_at_out.imgbuf deleted file mode 100644 index 21d0141..0000000 --- a/benches/3x3_at_out.imgbuf +++ /dev/null @@ -1 +0,0 @@ -Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ��ˤ�ˤӮ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�Ӯ�
\ No newline at end of file diff --git a/test_data/4_180x180.imgbuf b/benches/4_160x160.imgbuf Binary files differindex 58aabd0..58aabd0 100644 --- a/test_data/4_180x180.imgbuf +++ b/benches/4_160x160.imgbuf diff --git a/benches/4_4x4.imgbuf b/benches/4_8x8.imgbuf index bc6cdf8..bc6cdf8 100644 --- a/benches/4_4x4.imgbuf +++ b/benches/4_8x8.imgbuf diff --git a/benches/4x3_at_out.imgbuf b/benches/4x3_at_out.imgbuf deleted file mode 100644 index d5d8851..0000000 --- a/benches/4x3_at_out.imgbuf +++ /dev/null @@ -1 +0,0 @@ -������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������
\ No newline at end of file diff --git a/benches/4x4_at_out.imgbuf b/benches/4x4_at_out.imgbuf deleted file mode 100644 index 4dedb62..0000000 --- a/benches/4x4_at_out.imgbuf +++ /dev/null @@ -1 +0,0 @@ -����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������
\ No newline at end of file diff --git a/benches/affine_transformations.rs b/benches/affine_transformations.rs new file mode 100644 index 0000000..b74d9ba --- /dev/null +++ b/benches/affine_transformations.rs @@ -0,0 +1,23 @@ +use fimg::*; + +macro_rules! bench { + (fn $name: ident() { run $fn: ident() }) => { + fn $name() { + let mut img: Image<_, 4> = + Image::build(160, 160).buf(include_bytes!("4_160x160.imgbuf").to_vec()); + for _ in 0..256 { + #[allow(unused_unsafe)] + unsafe { + img.$fn() + }; + } + } + }; +} + +bench!(fn flip_h() { run flip_h() }); +bench!(fn flip_v() { run flip_v() }); +bench!(fn rotate_90() { run rot_90() }); +bench!(fn rotate_180() { run rot_180() }); +bench!(fn rotate_270() { run rot_270() }); +iai::main!(flip_h, flip_v, rotate_90, rotate_180, rotate_270); diff --git a/benches/overlays.rs b/benches/overlays.rs index 2236905..46e10f6 100644 --- a/benches/overlays.rs +++ b/benches/overlays.rs @@ -1,65 +1,32 @@ -use criterion::{criterion_group, criterion_main, Criterion}; use fimg::*; -pub fn criterion_benchmark(bench: &mut Criterion) { - let mut group = bench.benchmark_group("overlays"); - { - let mut a: Image<_, 3> = Image::alloc(64, 64); - let b = Image::<&[u8], 3>::new( - 4.try_into().unwrap(), - 4.try_into().unwrap(), - *&include_bytes!("3_4x4.imgbuf"), - ); - group.bench_function("overlay 3x3 offset", |bench| { - bench.iter(|| unsafe { - for x in 0..16 { - for y in 0..16 { - a.as_mut().overlay_at(&b, x * 4, y * 4); - } - } - }); - }); - assert_eq!(a.as_ref().buffer, include_bytes!("3x3_at_out.imgbuf")); +fn overlay_3on3at() { + let mut a: Image<_, 3> = Image::alloc(128, 128); + let b: Image<&[u8], 3> = Image::build(8, 8).buf(include_bytes!("3_8x8.imgbuf")); + for x in 0..16 { + for y in 0..16 { + unsafe { a.as_mut().overlay_at(&b, x * 8, y * 8) }; + } } - { - let mut a: Image<_, 3> = Image::alloc(64, 64); - let b = Image::<&[u8], 4>::new( - 4.try_into().unwrap(), - 4.try_into().unwrap(), - *&include_bytes!("4_4x4.imgbuf"), - ); - group.bench_function("overlay 4x3 offset", |bench| { - bench.iter(|| unsafe { - for x in 0..16 { - for y in 0..16 { - a.as_mut().overlay_at(&b, x * 4, y * 4); - } - } - }); - }); +} - assert_eq!(a.as_ref().buffer, include_bytes!("4x3_at_out.imgbuf")); - } - { - let mut a: Image<_, 4> = Image::alloc(64, 64); - let b = Image::<&[u8], 4>::new( - 4.try_into().unwrap(), - 4.try_into().unwrap(), - *&include_bytes!("4_4x4.imgbuf"), - ); - group.bench_function("overlay 4x4 offset", |bench| { - bench.iter(|| unsafe { - for x in 0..16 { - for y in 0..16 { - a.as_mut().overlay_at(&b, x * 4, y * 4); - } - } - }); - }); - assert_eq!(a.as_ref().buffer, include_bytes!("4x4_at_out.imgbuf")); +fn overlay_4on3at() { + let mut a: Image<_, 3> = Image::alloc(128, 128); + let b: Image<&[u8], 4> = Image::build(8, 8).buf(include_bytes!("4_8x8.imgbuf")); + for x in 0..16 { + for y in 0..16 { + unsafe { a.as_mut().overlay_at(&b, x * 8, y * 8) }; + } } - group.finish(); } -criterion_group!(benches, criterion_benchmark); -criterion_main!(benches); +fn overlay_4on4at() { + let mut a: Image<_, 4> = Image::alloc(128, 128); + let b: Image<&[u8], 4> = Image::build(8, 8).buf(include_bytes!("4_8x8.imgbuf")); + for x in 0..16 { + for y in 0..16 { + unsafe { a.as_mut().overlay_at(&b, x * 8, y * 8) }; + } + } +} +iai::main!(overlay_3on3at, overlay_4on3at, overlay_4on4at); diff --git a/src/affine.rs b/src/affine.rs index 393b571..362a746 100644 --- a/src/affine.rs +++ b/src/affine.rs @@ -1,87 +1,92 @@ -use crate::{FromRefMut, Image}; - -pub trait Rotations { - /// Rotate a image 180 degrees clockwise. - fn rot_180(&mut self); - /// Rotate a image 90 degrees clockwise. - /// # Safety - /// - /// UB if the image is not square - unsafe fn rot_90(&mut self); - /// Rotate a image 270 degrees clockwise, or 90 degrees anti clockwise. - /// # Safety - /// - /// UB if the image is not square - unsafe fn rot_270(&mut self); -} - -pub trait Flips { - /// Flip a image vertically. - fn flip_v(&mut self); +//! Manages the affine image transformations. +use crate::Image; +impl<const CHANNELS: usize> Image<Vec<u8>, CHANNELS> { /// Flip a image horizontally. - fn flip_h(&mut self); -} - -impl<const CHANNELS: usize> Flips for Image<Vec<u8>, CHANNELS> { - fn flip_h(&mut self) { + pub fn flip_h(&mut self) { self.as_mut().flip_h(); } - fn flip_v(&mut self) { + /// Flip a image vertically. + pub fn flip_v(&mut self) { self.as_mut().flip_v(); } } -impl<const CHANNELS: usize> Flips for Image<&mut [u8], CHANNELS> { - fn flip_v(&mut self) { +impl<const CHANNELS: usize> Image<&mut [u8], CHANNELS> { + /// Flip a image vertically. + pub fn flip_v(&mut self) { for y in 0..self.height() / 2 { for x in 0..self.width() { let y2 = self.height() - y - 1; + #[allow(clippy::multiple_unsafe_ops_per_block)] // SAFETY: within bounds - let p2 = unsafe { self.pixel(x, y2) }; - let p = unsafe { self.pixel(x, y) }; - unsafe { self.set_pixel(x, y2, p) }; - unsafe { self.set_pixel(x, y, p2) }; + unsafe { + let p2 = self.pixel(x, y2); + let p = self.pixel(x, y); + self.set_pixel(x, y2, p); + self.set_pixel(x, y, p2); + } } } } - fn flip_h(&mut self) { + /// Flip a image horizontally. + pub fn flip_h(&mut self) { for y in 0..self.height() { for x in 0..self.width() / 2 { let x2 = self.width() - x - 1; - let p2 = unsafe { self.pixel(x2, y) }; - let p = unsafe { self.pixel(x, y) }; - unsafe { self.set_pixel(x2, y, p) }; - unsafe { self.set_pixel(x, y, p2) }; + #[allow(clippy::multiple_unsafe_ops_per_block)] + // SAFETY: bounded + unsafe { + let p2 = self.pixel(x2, y); + let p = self.pixel(x, y); + self.set_pixel(x2, y, p); + self.set_pixel(x, y, p2); + } } } } } -impl<const CHANNELS: usize> Rotations for Image<Vec<u8>, CHANNELS> { - fn rot_180(&mut self) { +impl<const CHANNELS: usize> Image<Vec<u8>, CHANNELS> { + /// Rotate a image 180 degrees clockwise. + pub fn rot_180(&mut self) { self.as_mut().rot_180(); } - unsafe fn rot_90(&mut self) { + /// Rotate a image 90 degrees clockwise. + /// # Safety + /// + /// UB if the image is not square + pub unsafe fn rot_90(&mut self) { + // SAFETY: make sure to keep the safety docs linked unsafe { self.as_mut().rot_90() } } - unsafe fn rot_270(&mut self) { + /// Rotate a image 270 degrees clockwise, or 90 degrees anti clockwise. + /// # Safety + /// + /// UB if the image is not square + pub unsafe fn rot_270(&mut self) { + // SAFETY: idk this is just a convenience impl unsafe { self.as_mut().rot_270() } } } -impl<const CHANNELS: usize> Rotations for Image<&mut [u8], CHANNELS> { - fn rot_180(&mut self) { +impl<const CHANNELS: usize> Image<&mut [u8], CHANNELS> { + /// Rotate a image 180 degrees clockwise. + pub fn rot_180(&mut self) { for y in 0..self.height() / 2 { for x in 0..self.width() { + // SAFETY: x, y come from the loop, must be ok let p = unsafe { self.pixel(x, y) }; let x2 = self.width() - x - 1; let y2 = self.height() - y - 1; + // SAFETY: values are good let p2 = unsafe { self.pixel(x2, y2) }; + // SAFETY: swapping would be cool, alas. unsafe { self.set_pixel(x, y, p2) }; + // SAFETY: although maybe i can cast it to a `[[u8; CHANNELS]]` and swap that 🤔 unsafe { self.set_pixel(x2, y2, p) }; } } @@ -90,26 +95,38 @@ impl<const CHANNELS: usize> Rotations for Image<&mut [u8], CHANNELS> { let middle = self.height() / 2; for x in 0..self.width() / 2 { - let p = unsafe { self.pixel(x, middle) }; let x2 = self.width() - x - 1; - let p2 = unsafe { self.pixel(x2, middle) }; - unsafe { self.set_pixel(x, middle, p2) }; - unsafe { self.set_pixel(x2, middle, p) }; + #[allow(clippy::multiple_unsafe_ops_per_block)] + // SAFETY: its just doing the swappy + unsafe { + let p = self.pixel(x, middle); + let p2 = self.pixel(x2, middle); + self.set_pixel(x, middle, p2); + self.set_pixel(x2, middle, p); + } } } } + /// Rotate a image 90 degrees clockwise. + /// # Safety + /// + /// UB if the image is not square #[inline] - unsafe fn rot_90(&mut self) { + pub unsafe fn rot_90(&mut self) { // This is done by first flipping self.flip_v(); - // Then transposing the image, to save allocations. + // Then transposing the image, as to not allocate. // SAFETY: caller ensures square unsafe { transpose(self) }; } + /// Rotate a image 270 degrees clockwise, or 90 degrees anti clockwise. + /// # Safety + /// + /// UB if the image is not square #[inline] - unsafe fn rot_270(&mut self) { + pub unsafe fn rot_270(&mut self) { self.flip_h(); // SAFETY: caller ensures squareness unsafe { transpose(self) }; @@ -219,35 +236,3 @@ mod tests { ); } } - -#[cfg(test)] -mod bench { - use super::*; - extern crate test; - use crate::Image; - use test::Bencher; - - macro_rules! bench { - (fn $name: ident() { run $fn: ident() }) => { - #[bench] - fn $name(b: &mut Bencher) { - let mut img: Image<_, 4> = Image::new( - 64.try_into().unwrap(), - 64.try_into().unwrap(), - include_bytes!("../test_data/4_180x180.imgbuf").to_vec(), - ); - b.iter(|| { - for _ in 0..256 { - img.flip_h(); - } - }); - } - }; - } - - bench!(fn flip_h() { run flip_h() }); - bench!(fn flip_v() { run flip_v() }); - bench!(fn rotate_90() { run rot_90() }); - bench!(fn rotate_180() { run rot_180() }); - bench!(fn rotate_270() { run rot_270() }); -} diff --git a/src/builder.rs b/src/builder.rs new file mode 100644 index 0000000..be5a837 --- /dev/null +++ b/src/builder.rs @@ -0,0 +1,98 @@ +//! safe builder for the image +//! +//! does not let you do funny things +use std::marker::PhantomData; + +use crate::Image; + +impl<B: buf::Buffer, const C: usize> Image<B, C> { + /// creates a builder + pub const fn build(w: u32, h: u32) -> Builder<B, C> { + Builder::new(w, h) + } +} + +/// Safe [Image] builder. +pub struct Builder<B, const C: usize> { + /// the width in a zeroable type. zeroable so as to make the check in [`buf`] easier. + width: u32, + /// the height in a zeroable type. + height: u32, + #[allow(clippy::missing_docs_in_private_items)] + _buffer: PhantomData<B>, +} +impl<B: buf::Buffer, const C: usize> Builder<B, C> { + /// create new builder + pub const fn new(w: u32, h: u32) -> Self { + Self { + width: w, + height: h, + _buffer: PhantomData, + } + } + + /// apply a buffer, and build + #[track_caller] + pub fn buf(self, buffer: B) -> Image<B, C> { + let len = C as u32 * self.width * self.height; + assert!( + buffer.len() as u32 == len, + "invalid buffer size (expected {len}, got {})", + buffer.len() + ); + Image { + buffer, + width: self.width.try_into().expect("passed zero width to builder"), + height: self + .height + .try_into() + .expect("passed zero height to builder"), + } + } +} + +impl<const C: usize> Builder<Vec<u8>, C> { + /// allocate this image + pub fn alloc(self) -> Image<Vec<u8>, C> { + Image::alloc(self.width, self.height) + } +} + +/// seals the [`Buffer`] trait +mod buf { + /// A valid buffer for use in the builder + pub trait Buffer { + #[doc(hidden)] + fn len(&self) -> usize; + } + impl<T> Buffer for Vec<T> { + fn len(&self) -> usize { + self.len() + } + } + impl<T> Buffer for &[T] { + fn len(&self) -> usize { + <[T]>::len(self) + } + } + impl<T> Buffer for &mut [T] { + fn len(&self) -> usize { + <[T]>::len(self) + } + } + impl<T, const N: usize> Buffer for [T; N] { + fn len(&self) -> usize { + N + } + } + impl<T, const N: usize> Buffer for &[T; N] { + fn len(&self) -> usize { + N + } + } + impl<T, const N: usize> Buffer for &mut [T; N] { + fn len(&self) -> usize { + N + } + } +} diff --git a/src/cat.png b/src/cat.png Binary files differnew file mode 100644 index 0000000..361804d --- /dev/null +++ b/src/cat.png diff --git a/src/drawing/box.rs b/src/drawing/box.rs new file mode 100644 index 0000000..15f183f --- /dev/null +++ b/src/drawing/box.rs @@ -0,0 +1,65 @@ +//! `Box<cat>` +use crate::Image; + +impl<const CHANNELS: usize> Image<&mut [u8], CHANNELS> { + /// Draw a bordered box + /// + /// # Safety + /// + /// UB if the box is out of bounds + /// ``` + /// # use fimg::Image; + /// let mut b = Image::alloc(10, 9); + /// unsafe { b.as_mut().r#box((1, 1), 7, 6, [255]) }; + /// # assert_eq!(b.buffer(), b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\xff\x00\x00\x00\x00\x00\x00\xff\x00\x00\xff\x00\x00\x00\x00\x00\x00\xff\x00\x00\xff\x00\x00\x00\x00\x00\x00\xff\x00\x00\xff\x00\x00\x00\x00\x00\x00\xff\x00\x00\xff\x00\x00\x00\x00\x00\x00\xff\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00") + /// ``` + pub unsafe fn r#box( + &mut self, + (x1, y1): (u32, u32), + width: u32, + height: u32, + c: [u8; CHANNELS], + ) { + // skip sides, leave that to second loop + for x in x1 + 1..width + x1 { + // top line + // SAFETY: responsibility is on caller + unsafe { self.set_pixel(x, x1, c) }; + // bottom line + // SAFETY: shift responsibility + unsafe { self.set_pixel(x, x1 + height, c) }; + } + for y in y1..=height + y1 { + // SAFETY: >> responsibility + unsafe { self.set_pixel(y1, y, c) }; + // SAFETY: << responsibility + unsafe { self.set_pixel(y1 + width, y, c) }; + } + } + + /// Draw a *filled* box. + /// + /// # Safety + /// + /// UB if box is out of bounds + /// ``` + /// # use fimg::Image; + /// let mut b = Image::alloc(10, 9); + /// unsafe { b.as_mut().filled_box((1, 1), 7, 6, [255]) }; + /// # assert_eq!(b.buffer(), b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\xff\xff\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00") + /// ``` + pub unsafe fn filled_box( + &mut self, + (x1, y1): (u32, u32), + width: u32, + height: u32, + c: [u8; CHANNELS], + ) { + for x in x1..=width + x1 { + for y in y1..=height + y1 { + // SAFETY: fill it + unsafe { self.set_pixel(x, y, c) }; + } + } + } +} diff --git a/src/drawing/line.rs b/src/drawing/line.rs new file mode 100644 index 0000000..c48f923 --- /dev/null +++ b/src/drawing/line.rs @@ -0,0 +1,174 @@ +//! adds a `line` function to Image +#![allow(clippy::missing_docs_in_private_items)] +use crate::Image; +use std::iter::Iterator; + +/// taken from https://github.com/mbr/bresenham-rs/ +pub struct Bresenham { + x: i32, + y: i32, + dx: i32, + dy: i32, + x1: i32, + diff: i32, + octant: Octant, +} + +#[derive(Copy, Clone)] +struct Octant(u8); + +impl Octant { + #[inline] + const fn from_points(start: (i32, i32), end: (i32, i32)) -> Octant { + let mut dx = end.0 - start.0; + let mut dy = end.1 - start.1; + + let mut octant = 0; + + if dy < 0 { + dx = -dx; + dy = -dy; + octant += 4; + } + + if dx < 0 { + let tmp = dx; + dx = dy; + dy = -tmp; + octant += 2 + } + + if dx < dy { + octant += 1 + } + + Octant(octant) + } + + #[inline] + const fn to_octant0(self, p: (i32, i32)) -> (i32, i32) { + match self.0 { + 0 => (p.0, p.1), + 1 => (p.1, p.0), + 2 => (p.1, -p.0), + 3 => (-p.0, p.1), + 4 => (-p.0, -p.1), + 5 => (-p.1, -p.0), + 6 => (-p.1, p.0), + 7 => (p.0, -p.1), + _ => unreachable!(), + } + } + + #[inline] + #[allow(clippy::wrong_self_convention)] + fn from_octant0(self, p: (i32, i32)) -> (i32, i32) { + match self.0 { + 0 => (p.0, p.1), + 1 => (p.1, p.0), + 2 => (-p.1, p.0), + 3 => (-p.0, p.1), + 4 => (-p.0, -p.1), + 5 => (-p.1, -p.0), + 6 => (p.1, -p.0), + 7 => (p.0, -p.1), + _ => unreachable!(), + } + } +} + +impl Bresenham { + /// Creates a new iterator. Yields intermediate points between `start` + /// and `end`. Includes `start` and `end`. + #[inline] + pub const fn new(start: (i32, i32), end: (i32, i32)) -> Bresenham { + let octant = Octant::from_points(start, end); + + let start = octant.to_octant0(start); + let end = octant.to_octant0(end); + + let dx = end.0 - start.0; + let dy = end.1 - start.1; + + Bresenham { + x: start.0, + y: start.1, + dy, + dx, + x1: end.0, + diff: dy - dx, + octant, + } + } +} + +impl Iterator for Bresenham { + type Item = (i32, i32); + + #[inline] + fn next(&mut self) -> Option<Self::Item> { + if self.x > self.x1 { + return None; + } + + let p = (self.x, self.y); + + if self.diff >= 0 { + self.y += 1; + self.diff -= self.dx; + } + + self.diff += self.dy; + + // loop inc + self.x += 1; + + Some(self.octant.from_octant0(p)) + } +} + +impl<const CHANNELS: usize> Image<&mut [u8], CHANNELS> { + /// Draw a line from point a to point b + /// + /// Points not in bounds will not be included. + /// + /// Uses [bresenshams](https://en.wikipedia.org/wiki/Bresenham%27s_line_algorithm) line algorithm. + pub fn line(&mut self, a: (i32, i32), b: (i32, i32), color: [u8; CHANNELS]) { + for (x, y) in Bresenham::new(a, b).map(|(x, y)| (x as u32, y as u32)) { + if x < self.width() && y < self.height() { + // SAFETY: bound are checked ^ + unsafe { self.set_pixel(x, y, color) }; + } + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn bresenham() { + macro_rules! test_bresenham { + ($a:expr, $b:expr => [$(($x:expr, $y:expr)),+]) => {{ + let mut bi = Bresenham::new($a, $b); + $(assert_eq!(bi.next(), Some(($x, $y)));)+ + assert_eq!(bi.next(), None); + }} + } + test_bresenham!((6, 4), (0, 1) => [(6, 4), (5, 4), (4, 3), (3, 3), (2, 2), (1, 2), (0, 1)]); + test_bresenham!((2, 3), (2, 6) => [(2, 3), (2, 4), (2, 5), (2, 6)]); + test_bresenham!((2, 3), (5, 3) => [(2, 3), (3, 3), (4, 3), (5, 3)]); + test_bresenham!((0, 1), (6, 4) => [(0, 1), (1, 1), (2, 2), (3, 2), (4, 3), (5, 3), (6, 4)]); + } + + #[test] + fn line() { + let mut a = Image::build(5, 5).alloc(); + a.as_mut().line((0, 1), (6, 4), [255]); + assert_eq!( + a.buffer, + b"\x00\x00\x00\x00\x00\xff\xff\x00\x00\x00\x00\x00\xff\xff\x00\x00\x00\x00\x00\xff\x00\x00\x00\x00\x00" + ) + } +} diff --git a/src/drawing/mod.rs b/src/drawing/mod.rs new file mode 100644 index 0000000..6ffdf23 --- /dev/null +++ b/src/drawing/mod.rs @@ -0,0 +1,4 @@ +//! contains drawing operations, like {line, box, triangle} drawing +mod r#box; +mod line; +mod tri; diff --git a/src/drawing/tri.rs b/src/drawing/tri.rs new file mode 100644 index 0000000..3cc390b --- /dev/null +++ b/src/drawing/tri.rs @@ -0,0 +1,45 @@ +//! trongle drawing +use crate::Image; + +impl<const CHANNELS: usize> Image<&mut [u8], CHANNELS> { + /// Draw a (filled) triangle + /// + /// # Safety + /// + /// UB if any point is out of bounds + /// ``` + /// # use fimg::*; + /// let mut a = Image::alloc(10, 10); + /// // draw a triangle from point a v point b v point c v + /// // with color white + /// unsafe { a.as_mut().tri((3.0, 2.0), (8.0, 7.0), (1.0, 8.0), [255]) }; + /// # assert_eq!(a.buffer(), b"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\xff\xff\xff\xff\x00\x00\x00\x00\x00\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\xff\xff\xff\xff\xff\xff\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"); + /// ``` + pub unsafe fn tri( + &mut self, + (x1, y1): (f32, f32), + (x2, y2): (f32, f32), + (x3, y3): (f32, f32), + c: [u8; CHANNELS], + ) { + // TODO optimize + for y in y1.min(y2).min(y3) as u32..y1.max(y2).max(y3) as u32 { + for x in x1.min(x2).min(x3) as u32..x1.max(x2).max(x3) as u32 { + let s = (x1 - x3) * (y as f32 - y3) - (y1 - y2) * (x as f32 - x3); + let t = (x2 - x1) * (y as f32 - y1) - (y2 - y1) * (x as f32 - x1); + + if (s < 0.0) != (t < 0.0) && s != 0.0 && t != 0.0 { + continue; + } + + let d = (x3 - x2) * (y as f32 - y2) - (y3 - y2) * (x as f32 - x2); + if d == 0.0 || (d < 0.0) == (s + t <= 0.0) { + // SAFETY: + // caller gurantees triangle is in bounds, this loops over the + // bounding box of the triangle, therefore this is fine. + unsafe { self.set_pixel(x, y, c) }; + } + } + } + } +} @@ -1,37 +1,42 @@ +//! # fimg +//! +//! Provides fast image operations, such as rotation, flipping, and overlaying. #![feature( slice_swap_unchecked, + generic_const_exprs, slice_as_chunks, unchecked_math, portable_simd, + const_option, array_chunks, test )] #![warn( + clippy::missing_docs_in_private_items, clippy::multiple_unsafe_ops_per_block, + clippy::undocumented_unsafe_blocks, clippy::missing_const_for_fn, clippy::missing_safety_doc, unsafe_op_in_unsafe_fn, clippy::dbg_macro, - clippy::perf + missing_docs )] -#![allow(clippy::zero_prefixed_literal)] +#![allow(clippy::zero_prefixed_literal, incomplete_features)] use std::{num::NonZeroU32, slice::SliceIndex}; mod affine; +pub mod builder; +mod drawing; mod overlay; -pub use affine::{Flips, Rotations}; +pub mod scale; pub use overlay::{Overlay, OverlayAt}; -pub trait RepeatNew { - type Output; - /// Repeat self till it fills a new image of size x, y - /// # Safety - /// - /// UB if self's width is not a multiple of x, or self's height is not a multiple of y - unsafe fn repeated(&self, x: u32, y: u32) -> Self::Output; -} - +/// like assert!(), but causes undefined behaviour at runtime when the condition is not met. +/// +/// # Safety +/// +/// UB if condition is false. macro_rules! assert_unchecked { ($cond:expr) => {{ if !$cond { @@ -46,9 +51,12 @@ macro_rules! assert_unchecked { } use assert_unchecked; -impl RepeatNew for Image<&[u8], 3> { - type Output = Image<Vec<u8>, 3>; - unsafe fn repeated(&self, x: u32, y: u32) -> Self::Output { +impl Image<&[u8], 3> { + /// Repeat self till it fills a new image of size x, y + /// # Safety + /// + /// UB if self's width is not a multiple of x, or self's height is not a multiple of y + pub unsafe fn repeated(&self, x: u32, y: u32) -> Image<Vec<u8>, 3> { let mut img = Image::alloc(x, y); // could probably optimize this a ton but eh for x in 0..(x / self.width()) { for y in 0..(y / self.height()) { @@ -61,54 +69,96 @@ impl RepeatNew for Image<&[u8], 3> { } } +/// calculates a column major index, with unchecked math #[inline] unsafe fn really_unsafe_index(x: u32, y: u32, w: u32) -> usize { // y * w + x + // SAFETY: FIXME make safe math let tmp = unsafe { (y as usize).unchecked_mul(w as usize) }; + // SAFETY: FIXME make safe math unsafe { tmp.unchecked_add(x as usize) } } +/// A image with a variable number of channels, and a nonzero size. #[derive(Clone, Debug, PartialEq, Eq)] pub struct Image<T, const CHANNELS: usize> { - pub buffer: T, - pub width: NonZeroU32, - pub height: NonZeroU32, -} - -impl<const CHANNELS: usize> Default for Image<&'static [u8], CHANNELS> { - fn default() -> Self { - Self { - buffer: &[0; CHANNELS], - width: NonZeroU32::new(1).unwrap(), - height: NonZeroU32::new(1).unwrap(), - } - } + /// column order 2d slice/vec + buffer: T, + /// image horizontal size + width: NonZeroU32, + /// image vertical size + height: NonZeroU32, } impl<T, const CHANNELS: usize> Image<T, CHANNELS> { #[inline] + /// get the height as a [`u32`] pub fn height(&self) -> u32 { self.height.into() } #[inline] + /// get the width as a [`u32`] pub fn width(&self) -> u32 { self.width.into() } #[inline] - pub const fn new(width: NonZeroU32, height: NonZeroU32, buffer: T) -> Self { - Image { + /// create a new image + /// + /// # Safety + /// + /// does not check that buffer.len() == w * h * C + /// + /// using this with invalid values may result in future UB + pub const unsafe fn new(width: NonZeroU32, height: NonZeroU32, buffer: T) -> Self { + Self { buffer, width, height, } } + + /// consumes the image, returning the image buffer + pub fn take_buffer(self) -> T { + self.buffer + } + + /// returns a immutable reference to the backing buffer + pub const fn buffer(&self) -> &T { + &self.buffer + } + + /// returns a mutable(!) reference to the backing buffer + /// + /// # Safety + /// + /// please do not change buffer size. + pub unsafe fn buffer_mut(&mut self) -> &mut T { + &mut self.buffer + } +} + +impl<const CHANNELS: usize, T: Clone> Image<&[T], CHANNELS> { + /// Allocate a new `Image<Vec<T>>` from this imageref. + pub fn to_owned(&self) -> Image<Vec<T>, CHANNELS> { + // SAFETY: we have been constructed already, so must be valid + unsafe { Image::new(self.width, self.height, self.buffer.to_vec()) } + } +} + +impl<const CHANNELS: usize, T: Clone> Image<&mut [T], CHANNELS> { + /// Allocate a new `Image<Vec<T>>` from this mutable imageref. + pub fn to_owned(&self) -> Image<Vec<T>, CHANNELS> { + // SAFETY: we have been constructed already, so must be valid + unsafe { Image::new(self.width, self.height, self.buffer.to_vec()) } + } } impl<const CHANNELS: usize> Image<&[u8], CHANNELS> { #[inline] #[must_use] + /// Copy this ref image pub const fn copy(&self) -> Self { Self { width: self.width, @@ -116,6 +166,28 @@ impl<const CHANNELS: usize> Image<&[u8], CHANNELS> { buffer: self.buffer, } } + + /// Create a new immutable image of width x, y. + /// + /// # Panics + /// + /// if width || height == 0 + /// + /// ``` + /// # use fimg::Image; + /// let img = Image::make::<5, 5>(); + /// # let img: Image<_, 4> = img; + /// ``` + pub const fn make<'a, const WIDTH: u32, const HEIGHT: u32>() -> Image<&'a [u8], CHANNELS> + where + [(); CHANNELS * WIDTH as usize * HEIGHT as usize]: Sized, + { + Image { + width: NonZeroU32::new(WIDTH).expect("passed zero width to builder"), + height: NonZeroU32::new(HEIGHT).expect("passed zero height to builder"), + buffer: &[0; CHANNELS * WIDTH as usize * HEIGHT as usize], + } + } } impl<T: std::ops::Deref<Target = [u8]>, const CHANNELS: usize> Image<T, CHANNELS> { @@ -127,9 +199,12 @@ impl<T: std::ops::Deref<Target = [u8]>, const CHANNELS: usize> Image<T, CHANNELS unsafe fn slice(&self, x: u32, y: u32) -> impl SliceIndex<[u8], Output = [u8]> { debug_assert!(x < self.width(), "x out of bounds"); debug_assert!(y < self.height(), "y out of bounds"); + // SAFETY: me when uncheck math: 😧 let index = unsafe { really_unsafe_index(x, y, self.width()) }; + // SAFETY: 🧐 is unsound? 😖 let index = unsafe { index.unchecked_mul(CHANNELS) }; debug_assert!(self.buffer.len() > index); + // SAFETY: as long as the buffer isnt wrong, this is 😄 index..unsafe { index.unchecked_add(CHANNELS) } } @@ -150,8 +225,11 @@ impl<T: std::ops::Deref<Target = [u8]>, const CHANNELS: usize> Image<T, CHANNELS /// - UB if buffer is too small #[inline] pub unsafe fn pixel(&self, x: u32, y: u32) -> [u8; CHANNELS] { + // SAFETY: we have been told x, y is in bounds let idx = unsafe { self.slice(x, y) }; + // SAFETY: slice always returns a valid index let ptr = unsafe { self.buffer.get_unchecked(idx).as_ptr().cast() }; + // SAFETY: slice always returns a length of `CHANNELS`, so we `cast()` it for convenience. unsafe { *ptr } } } @@ -164,7 +242,9 @@ impl<T: std::ops::DerefMut<Target = [u8]>, const CHANNELS: usize> Image<T, CHANN /// - UB if buffer is too small #[inline] pub unsafe fn pixel_mut(&mut self, x: u32, y: u32) -> &mut [u8] { + // SAFETY: we have been told x, y is in bounds. let idx = unsafe { self.slice(x, y) }; + // SAFETY: slice should always return a valid index unsafe { self.buffer.get_unchecked_mut(idx) } } @@ -192,37 +272,39 @@ impl<T: std::ops::DerefMut<Target = [u8]>, const CHANNELS: usize> Image<T, CHANN } } -pub trait FromRef<const CHANNELS: usize> { - /// Reference the buffer - fn as_ref(&self) -> Image<&[u8], CHANNELS>; -} - -pub trait FromRefMut<const CHANNELS: usize> { - /// Reference the buffer, mutably - fn as_mut(&mut self) -> Image<&mut [u8], CHANNELS>; -} - -impl<const CHANNELS: usize> FromRef<CHANNELS> for Image<&mut [u8], CHANNELS> { - fn as_ref(&self) -> Image<&[u8], CHANNELS> { - Image::new(self.width, self.height, self.buffer) +impl<const CHANNELS: usize> Image<&mut [u8], CHANNELS> { + /// Downcast the mutable reference + pub fn as_ref(&self) -> Image<&[u8], CHANNELS> { + // SAFETY: we got constructed okay, parameters must be valid + unsafe { Image::new(self.width, self.height, self.buffer) } } -} -impl<const CHANNELS: usize> FromRefMut<CHANNELS> for Image<&mut [u8], CHANNELS> { - fn as_mut(&mut self) -> Image<&mut [u8], CHANNELS> { - Image::new(self.width, self.height, self.buffer) + /// Copy this ref image + pub fn copy(&mut self) -> Image<&mut [u8], CHANNELS> { + #[allow(clippy::undocumented_unsafe_blocks)] + unsafe { + Image::new(self.width, self.height, self.buffer) + } } } -impl<const CHANNELS: usize> FromRef<CHANNELS> for Image<Vec<u8>, CHANNELS> { - fn as_ref(&self) -> Image<&[u8], CHANNELS> { - Image::new(self.width, self.height, &self.buffer) +impl<const CHANNELS: usize> Image<Vec<u8>, CHANNELS> { + /// Create a reference to this owned image + pub fn as_ref(&self) -> Image<&[u8], CHANNELS> { + #[allow(clippy::undocumented_unsafe_blocks)] + unsafe { + Image::new(self.width, self.height, &self.buffer) + } } } -impl<const CHANNELS: usize> FromRefMut<CHANNELS> for Image<Vec<u8>, CHANNELS> { - fn as_mut(&mut self) -> Image<&mut [u8], CHANNELS> { - Image::new(self.width, self.height, &mut self.buffer) +impl<const CHANNELS: usize> Image<Vec<u8>, CHANNELS> { + /// Create a mutable reference to this owned image + pub fn as_mut(&mut self) -> Image<&mut [u8], CHANNELS> { + #[allow(clippy::undocumented_unsafe_blocks)] + unsafe { + Image::new(self.width, self.height, &mut self.buffer) + } } } @@ -234,15 +316,27 @@ impl<const CHANNELS: usize> Image<Vec<u8>, CHANNELS> { /// if width || height == 0 #[must_use] pub fn alloc(width: u32, height: u32) -> Self { - Image { + Self { width: width.try_into().unwrap(), height: height.try_into().unwrap(), buffer: vec![0; CHANNELS * width as usize * height as usize], } } } + +/// helper macro for defining the save() method. macro_rules! save { ($channels:literal == $clr:ident ($clrhuman:literal)) => { + impl Image<Vec<u8>, $channels> { + #[cfg(feature = "save")] + #[doc = "Save this "] + #[doc = $clrhuman] + #[doc = " image."] + pub fn save(&self, f: impl AsRef<std::path::Path>) { + self.as_ref().save(f) + } + } + impl Image<&[u8], $channels> { #[cfg(feature = "save")] #[doc = "Save this "] @@ -268,6 +362,29 @@ macro_rules! save { }; } +impl<const CHANNELS: usize> Image<Vec<u8>, CHANNELS> { + #[cfg(feature = "save")] + /// Open a PNG image + pub fn open(f: impl AsRef<std::path::Path>) -> Self { + let p = std::fs::File::open(f).unwrap(); + let r = std::io::BufReader::new(p); + let dec = png::Decoder::new(r); + let mut reader = dec.read_info().unwrap(); + let mut buf = vec![0; reader.output_buffer_size()]; + let info = reader.next_frame(&mut buf).unwrap(); + use png::ColorType::*; + match info.color_type { + Indexed | Grayscale => { + assert_eq!(CHANNELS, 1, "indexed | grayscale requires one channel") + } + Rgb => assert_eq!(CHANNELS, 3, "rgb requires three channels"), + Rgba => assert_eq!(CHANNELS, 4, "rgba requires four channels"), + GrayscaleAlpha => assert_eq!(CHANNELS, 2, "ya requires two channels"), + } + Self::build(info.width, info.height).buf(buf) + } +} + save!(3 == Rgb("RGB")); save!(4 == Rgba("RGBA")); save!(2 == GrayscaleAlpha("YA")); @@ -275,14 +392,19 @@ save!(1 == Grayscale("Y")); #[cfg(test)] macro_rules! img { - [[$($v:literal),+] [$($v2:literal),+]] => {{ - let from: Image<Vec<u8>, 1> = Image::new( - 2.try_into().unwrap(), - 2.try_into().unwrap(), - vec![$($v,)+ $($v2,)+] - ); - from - }} + [[$($v:literal),+] [$($v2:literal),+]] => { + Image::<Vec<u8>, 1>::build(2,2).buf(vec![$($v,)+ $($v2,)+]) + } } #[cfg(test)] use img; + +#[cfg(test)] +mod tests { + use super::*; + #[test] + fn repeat() { + let x: Image<&[u8], 3> = Image::build(8, 8).buf(include_bytes!("../benches/3_8x8.imgbuf")); + unsafe { x.repeated(128, 128) }; // repeat 16 times + } +} diff --git a/src/overlay.rs b/src/overlay.rs index d1211a9..3dbc45e 100644 --- a/src/overlay.rs +++ b/src/overlay.rs @@ -1,8 +1,10 @@ +//! Handles image overlay use super::{assert_unchecked, really_unsafe_index, Image}; use std::simd::SimdInt; use std::simd::SimdPartialOrd; use std::simd::{simd_swizzle, Simd}; +/// Trait for layering a image ontop of another, with a offset to the second image. pub trait OverlayAt<W> { /// Overlay with => self at coordinates x, y, without blending /// # Safety @@ -10,7 +12,8 @@ pub trait OverlayAt<W> { /// UB if x, y is out of bounds unsafe fn overlay_at(&mut self, with: &W, x: u32, y: u32) -> &mut Self; } - +/// Trait for layering images ontop of each other. +/// Think `magick a b -layers flatten a` pub trait Overlay<W> { /// Overlay with => self (does not blend) /// # Safety @@ -20,15 +23,20 @@ pub trait Overlay<W> { } #[inline] +/// SIMD accelerated rgba => rgb overlay. +/// +/// See [blit](https://en.wikipedia.org/wiki/Bit_blit) +/// +/// # Safety +/// - UB if rgb.len() % 3 != 0 +/// - UB if rgba.len() % 4 != 0 unsafe fn blit(rgb: &mut [u8], rgba: &[u8]) { - const LAST4: Simd<u8, 16> = Simd::from_array([ - 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, - ]); - let mut srci = 0; let mut dsti = 0; while dsti + 16 <= rgb.len() { + // SAFETY: i think it ok let old: Simd<u8, 16> = Simd::from_slice(unsafe { rgb.get_unchecked(dsti..dsti + 16) }); + // SAFETY: definetly ok let new: Simd<u8, 16> = Simd::from_slice(unsafe { rgba.get_unchecked(srci..srci + 16) }); let threshold = new.simd_ge(Simd::splat(128)).to_int().cast::<u8>(); @@ -36,10 +44,13 @@ unsafe fn blit(rgb: &mut [u8], rgba: &[u8]) { threshold, [3, 3, 3, 7, 7, 7, 11, 11, 11, 15, 15, 15, 0, 0, 0, 0] ); - mask &= LAST4; + mask &= Simd::from_array([ + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 0, 0, 0, + ]); let new_rgb = simd_swizzle!(new, [0, 1, 2, 4, 5, 6, 8, 9, 10, 12, 13, 14, 0, 0, 0, 0]); let blended = (new_rgb & mask) | (old & !mask); + // SAFETY: 4 * 4 == 16, so in bounds blended.copy_to_slice(unsafe { rgb.get_unchecked_mut(dsti..dsti + 16) }); srci += 16; @@ -47,10 +58,13 @@ unsafe fn blit(rgb: &mut [u8], rgba: &[u8]) { } while dsti + 3 <= rgb.len() { + // SAFETY: caller gurantees slice is big enough if unsafe { *rgba.get_unchecked(srci + 3) } >= 128 { - let src = unsafe { rgba.get_unchecked(srci..srci + 3) }; - let end = unsafe { rgb.get_unchecked_mut(dsti..dsti + 3) }; - unsafe { std::ptr::copy_nonoverlapping(src.as_ptr(), end.as_mut_ptr(), 3) }; + // SAFETY: slice is big enough! + let src = unsafe { rgba.get_unchecked(srci..=srci + 2) }; + // SAFETY: i hear it bound + let end = unsafe { rgb.get_unchecked_mut(dsti..=dsti + 2) }; + end.copy_from_slice(src); } srci += 4; @@ -65,16 +79,9 @@ impl Overlay<Image<&[u8], 4>> for Image<&mut [u8], 4> { debug_assert!(self.height() == with.height()); for (i, other_pixels) in with.chunked().enumerate() { if other_pixels[3] >= 128 { - let idx_begin = unsafe { i.unchecked_mul(4) }; - let idx_end = unsafe { idx_begin.unchecked_add(4) }; - let own_pixels = unsafe { self.buffer.get_unchecked_mut(idx_begin..idx_end) }; - unsafe { - std::ptr::copy_nonoverlapping( - other_pixels.as_ptr(), - own_pixels.as_mut_ptr(), - 4, - ); - }; + // SAFETY: outside are bounds of index from slice + let own_pixels = unsafe { self.buffer.get_unchecked_mut(i * 4..i * 4 + 4) }; + own_pixels.copy_from_slice(other_pixels); } } self @@ -84,9 +91,9 @@ impl Overlay<Image<&[u8], 4>> for Image<&mut [u8], 4> { impl OverlayAt<Image<&[u8], 4>> for Image<&mut [u8], 3> { #[inline] unsafe fn overlay_at(&mut self, with: &Image<&[u8], 4>, x: u32, y: u32) -> &mut Self { - // SAFETY: caller upholds these + // SAFETY: caller upholds this unsafe { assert_unchecked!(x + with.width() <= self.width()) }; - unsafe { assert_unchecked!(y + with.height() <= self.height()) }; + debug_assert!(y + with.height() <= self.height()); for j in 0..with.height() { let i_x = j as usize * with.width() as usize * 4 ..(j as usize + 1) * with.width() as usize * 4; @@ -95,8 +102,11 @@ impl OverlayAt<Image<&[u8], 4>> for Image<&mut [u8], 3> { + x as usize + with.width() as usize) * 3; + // SAFETY: index is in bounds let rgb = unsafe { self.buffer.get_unchecked_mut(o_x) }; + // SAFETY: bounds are outside index let rgba = unsafe { with.buffer.get_unchecked(i_x) }; + // SAFETY: arguments are 🟢 unsafe { blit(rgb, rgba) } } self @@ -104,8 +114,16 @@ impl OverlayAt<Image<&[u8], 4>> for Image<&mut [u8], 3> { } impl OverlayAt<Image<&[u8], 3>> for Image<&mut [u8], 3> { + /// Overlay a RGB image(with) => self at coordinates x, y. + /// As this is a `RGBxRGB` operation, blending is unnecessary, + /// and this is simply a copy. + /// + /// # Safety + /// + /// UB if x, y is out of bounds #[inline] unsafe fn overlay_at(&mut self, with: &Image<&[u8], 3>, x: u32, y: u32) -> &mut Self { + /// helper macro for defining rgb=>rgb overlays. allows unrolling macro_rules! o3x3 { ($n:expr) => {{ for j in 0..($n as usize) { @@ -113,7 +131,12 @@ impl OverlayAt<Image<&[u8], 3>> for Image<&mut [u8], 3> { let o_x = ((j + y as usize) * self.width() as usize + x as usize) * 3 ..((j + y as usize) * self.width() as usize + x as usize + ($n as usize)) * 3; + // <= because ".." range + debug_assert!(o_x.end <= self.buffer().len()); + debug_assert!(i_x.end <= with.buffer().len()); + // SAFETY: bounds are ✅ let a = unsafe { self.buffer.get_unchecked_mut(o_x) }; + // SAFETY: we are in ⬜! let b = unsafe { with.buffer.get_unchecked(i_x) }; a.copy_from_slice(b); } @@ -139,11 +162,13 @@ impl Overlay<Image<&[u8], 4>> for Image<&mut [u8], 3> { .chunks_exact(with.width() as usize * 4) .enumerate() { + // SAFETY: all the bounds are good let rgb = unsafe { self.buffer.get_unchecked_mut( i * with.width() as usize * 3..(i + 1) * with.width() as usize * 3, ) }; + // SAFETY: we have the rgb and rgba arguments right unsafe { blit(rgb, chunk) }; } self @@ -152,15 +177,30 @@ impl Overlay<Image<&[u8], 4>> for Image<&mut [u8], 3> { impl OverlayAt<Image<&[u8], 4>> for Image<&mut [u8], 4> { #[inline] + /// Overlay with => self at coordinates x, y, without blending + /// + /// # Safety + /// - UB if x, y is out of bounds + /// - UB if x + with.width() > [`u32::MAX`] + /// - UB if y + with.height() > [`u32::MAX`] unsafe fn overlay_at(&mut self, with: &Image<&[u8], 4>, x: u32, y: u32) -> &mut Self { for j in 0..with.height() { for i in 0..with.width() { + // SAFETY: i, j is in bounds. let index = unsafe { really_unsafe_index(i, j, with.width()) }; + // SAFETY: using .pixel() results in horrible asm (+5k ns/iter) let their_px = unsafe { with.buffer.get_unchecked(index * 4..index * 4 + 4) }; + // SAFETY: must be sized right if unsafe { *their_px.get_unchecked(3) } >= 128 { + // SAFETY: + // they said it cant go over. + // i dont know why, but this has performance importance™ let x = unsafe { i.unchecked_add(x) }; + // SAFETY: caller gurantees this cannot overflow. let y = unsafe { j.unchecked_add(y) }; + // SAFETY: compute the offset index. let index = unsafe { really_unsafe_index(x, y, self.width()) }; + // SAFETY: if everything else goes well, this is fine let our_px = unsafe { self.buffer.get_unchecked_mut(index * 4..index * 4 + 4) }; our_px.copy_from_slice(their_px); } diff --git a/src/scale.rs b/src/scale.rs new file mode 100644 index 0000000..0ba614b --- /dev/null +++ b/src/scale.rs @@ -0,0 +1,41 @@ +//! holds scaling operations, at current only the Nearest Neighbor +use crate::Image; + +/// [Nearest Neighbor](https://en.wikipedia.org/wiki/Nearest-neighbor_interpolation) image scaling algorithm implementation. +/// Use [`Nearest::scale`]. +pub struct Nearest; +impl Nearest { + /// Resize a image. + /// # Safety + /// + /// `image` must be as big or bigger than `width`, `height. + pub unsafe fn scale<const N: usize>( + image: Image<&[u8], N>, + width: u32, + height: u32, + ) -> Image<Vec<u8>, N> { + let x_scale = image.width() as f32 / width as f32; + let y_scale = image.height() as f32 / height as f32; + let mut out = Image::alloc(width, height); + for y in 0..height { + for x in 0..width { + let x1 = ((x as f32 + 0.5) * x_scale).floor() as u32; + let y1 = ((y as f32 + 0.5) * y_scale).floor() as u32; + // SAFETY: i asked the caller to make sure its ok + let px = unsafe { image.pixel(x1, y1) }; + // SAFETY: were looping over the width and height of out. its ok. + unsafe { out.set_pixel(x, y, px) }; + } + } + out + } +} + +#[test] +fn test_nearest() { + let i = Image::<_, 3>::open("src/cat.png"); + assert_eq!( + unsafe { Nearest::scale(i.as_ref(), 268, 178) }.buffer, + Image::<_, 3>::open("src/small_cat.png").buffer + ); +} diff --git a/src/small_cat.png b/src/small_cat.png Binary files differnew file mode 100644 index 0000000..ec6e5bf --- /dev/null +++ b/src/small_cat.png |