Move to egui + winit, add rating

This commit is contained in:
Dawid Pietrykowski 2025-04-05 22:57:03 +02:00
parent 7b475a969c
commit f55424b98f
8 changed files with 1015 additions and 419 deletions

325
Cargo.lock generated
View File

@ -597,18 +597,6 @@ dependencies = [
"windows-targets 0.52.6", "windows-targets 0.52.6",
] ]
[[package]]
name = "base64"
version = "0.21.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567"
[[package]]
name = "base64"
version = "0.22.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
[[package]] [[package]]
name = "bit-set" name = "bit-set"
version = "0.5.3" version = "0.5.3"
@ -1269,12 +1257,6 @@ dependencies = [
"zbus", "zbus",
] ]
[[package]]
name = "data-url"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c297a1c74b71ae29df00c3e22dd9534821d60eb9af5a0192823fa2acea70c2a"
[[package]] [[package]]
name = "dconf_rs" name = "dconf_rs"
version = "0.3.0" version = "0.3.0"
@ -1511,23 +1493,6 @@ dependencies = [
"winit", "winit",
] ]
[[package]]
name = "egui_extras"
version = "0.31.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "624659a2e972a46f4d5f646557906c55f1cd5a0836eddbe610fdf1afba1b4226"
dependencies = [
"ahash 0.8.11",
"egui",
"ehttp",
"enum-map",
"image 0.25.6",
"log",
"mime_guess2",
"profiling",
"resvg",
]
[[package]] [[package]]
name = "egui_glow" name = "egui_glow"
version = "0.31.1" version = "0.31.1"
@ -1546,20 +1511,6 @@ dependencies = [
"winit", "winit",
] ]
[[package]]
name = "ehttp"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "59a81c221a1e4dad06cb9c9deb19aea1193a5eea084e8cd42d869068132bf876"
dependencies = [
"document-features",
"js-sys",
"ureq",
"wasm-bindgen",
"wasm-bindgen-futures",
"web-sys",
]
[[package]] [[package]]
name = "either" name = "either"
version = "1.15.0" version = "1.15.0"
@ -1581,27 +1532,6 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a3d8a32ae18130a3c84dd492d4215c3d913c3b07c6b63c2eb3eb7ff1101ab7bf" checksum = "a3d8a32ae18130a3c84dd492d4215c3d913c3b07c6b63c2eb3eb7ff1101ab7bf"
[[package]]
name = "enum-map"
version = "2.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6866f3bfdf8207509a033af1a75a7b08abda06bbaaeae6669323fd5a097df2e9"
dependencies = [
"enum-map-derive",
"serde",
]
[[package]]
name = "enum-map-derive"
version = "0.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f282cfdfe92516eb26c2af8589c274c7c17681f5ecc03c18255fe741c6aa64eb"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.100",
]
[[package]] [[package]]
name = "enumflags2" name = "enumflags2"
version = "0.7.11" version = "0.7.11"
@ -1778,12 +1708,6 @@ dependencies = [
"miniz_oxide", "miniz_oxide",
] ]
[[package]]
name = "float-cmp"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "98de4bbd547a563b716d8dfa9aad1cb19bfab00f4fa09a6a4ed21dbcf44ce9c4"
[[package]] [[package]]
name = "float_next_after" name = "float_next_after"
version = "1.0.0" version = "1.0.0"
@ -1811,7 +1735,7 @@ version = "0.5.7"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c1fcfcd44ca6e90c921fee9fa665d530b21ef1327a4c1a6c5250ea44b776ada7" checksum = "c1fcfcd44ca6e90c921fee9fa665d530b21ef1327a4c1a6c5250ea44b776ada7"
dependencies = [ dependencies = [
"roxmltree 0.20.0", "roxmltree",
] ]
[[package]] [[package]]
@ -2474,7 +2398,7 @@ dependencies = [
"bytemuck", "bytemuck",
"cosmic-text", "cosmic-text",
"iced_graphics", "iced_graphics",
"kurbo 0.10.4", "kurbo",
"log", "log",
"rustc-hash 2.1.1", "rustc-hash 2.1.1",
"softbuffer", "softbuffer",
@ -2727,21 +2651,17 @@ dependencies = [
"quick-error", "quick-error",
] ]
[[package]]
name = "imagesize"
version = "0.12.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "029d73f573d8e8d63e6d5020011d3255b28c3ba85d6cf870a07184ed23de9284"
[[package]] [[package]]
name = "imflow" name = "imflow"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"bytemuck",
"clap 4.5.34", "clap 4.5.34",
"criterion", "criterion",
"eframe", "eframe",
"egui", "egui",
"egui_extras", "egui-wgpu",
"egui-winit",
"env_logger", "env_logger",
"iced", "iced",
"image 0.25.6", "image 0.25.6",
@ -3061,15 +2981,6 @@ version = "3.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc" checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc"
[[package]]
name = "kurbo"
version = "0.9.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bd85a5776cd9500c2e2059c8c76c3b01528566b7fcbaf8098b55a33fc298849b"
dependencies = [
"arrayvec",
]
[[package]] [[package]]
name = "kurbo" name = "kurbo"
version = "0.10.4" version = "0.10.4"
@ -3340,22 +3251,6 @@ dependencies = [
"paste", "paste",
] ]
[[package]]
name = "mime"
version = "0.3.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
[[package]]
name = "mime_guess2"
version = "2.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "25a3333bb1609500601edc766a39b4c1772874a4ce26022f4d866854dc020c41"
dependencies = [
"mime",
"unicase",
]
[[package]] [[package]]
name = "minifb" name = "minifb"
version = "0.28.0" version = "0.28.0"
@ -4080,15 +3975,9 @@ version = "0.11.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5" checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5"
dependencies = [ dependencies = [
"siphasher 1.0.1", "siphasher",
] ]
[[package]]
name = "pico-args"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5be167a7af36ee22fe3115051bc51f6e6c7054c9348e28deb4f49bd6f705a315"
[[package]] [[package]]
name = "pin-project" name = "pin-project"
version = "1.1.10" version = "1.1.10"
@ -4434,12 +4323,6 @@ dependencies = [
"crossbeam-utils", "crossbeam-utils",
] ]
[[package]]
name = "rctree"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3b42e27ef78c35d3998403c1d26f3efd9e135d3e5121b0a4845cc5cc27547f4f"
[[package]] [[package]]
name = "read-fonts" name = "read-fonts"
version = "0.22.7" version = "0.22.7"
@ -4523,20 +4406,6 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "19b30a45b0cd0bcca8037f3d0dc3421eaf95327a17cad11964fb8179b4fc4832" checksum = "19b30a45b0cd0bcca8037f3d0dc3421eaf95327a17cad11964fb8179b4fc4832"
[[package]]
name = "resvg"
version = "0.37.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cadccb3d99a9efb8e5e00c16fbb732cbe400db2ec7fc004697ee7d97d86cf1f4"
dependencies = [
"log",
"pico-args",
"rgb",
"svgtypes",
"tiny-skia",
"usvg",
]
[[package]] [[package]]
name = "rexiv2" name = "rexiv2"
version = "0.10.0" version = "0.10.0"
@ -4553,29 +4422,6 @@ name = "rgb"
version = "0.8.50" version = "0.8.50"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "57397d16646700483b67d2dd6511d79318f9d057fdbd21a4066aeac8b41d310a" checksum = "57397d16646700483b67d2dd6511d79318f9d057fdbd21a4066aeac8b41d310a"
dependencies = [
"bytemuck",
]
[[package]]
name = "ring"
version = "0.17.14"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7"
dependencies = [
"cc",
"cfg-if",
"getrandom 0.2.15",
"libc",
"untrusted",
"windows-sys 0.52.0",
]
[[package]]
name = "roxmltree"
version = "0.19.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3cd14fd5e3b777a7422cca79358c57a8f6e3a703d9ac187448d0daf220c2407f"
[[package]] [[package]]
name = "roxmltree" name = "roxmltree"
@ -4637,38 +4483,6 @@ dependencies = [
"windows-sys 0.59.0", "windows-sys 0.59.0",
] ]
[[package]]
name = "rustls"
version = "0.23.25"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "822ee9188ac4ec04a2f0531e55d035fb2de73f18b41a63c70c2712503b6fb13c"
dependencies = [
"log",
"once_cell",
"ring",
"rustls-pki-types",
"rustls-webpki",
"subtle",
"zeroize",
]
[[package]]
name = "rustls-pki-types"
version = "1.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "917ce264624a4b4db1c364dcc35bfca9ded014d0a958cd47ad3e960e988ea51c"
[[package]]
name = "rustls-webpki"
version = "0.103.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fef8b8769aaccf73098557a87cd1816b4f9c7c16811c9c77142aa695c16f2c03"
dependencies = [
"ring",
"rustls-pki-types",
"untrusted",
]
[[package]] [[package]]
name = "rustversion" name = "rustversion"
version = "1.0.20" version = "1.0.20"
@ -4873,21 +4687,6 @@ dependencies = [
"quote", "quote",
] ]
[[package]]
name = "simplecss"
version = "0.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a9c6883ca9c3c7c90e888de77b7a5c849c779d25d74a1269b0218b14e8b136c"
dependencies = [
"log",
]
[[package]]
name = "siphasher"
version = "0.3.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d"
[[package]] [[package]]
name = "siphasher" name = "siphasher"
version = "1.0.1" version = "1.0.1"
@ -5031,9 +4830,6 @@ name = "strict-num"
version = "0.1.1" version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6637bab7722d379c8b41ba849228d680cc12d0a45ba1fa2b48f2a30577a06731" checksum = "6637bab7722d379c8b41ba849228d680cc12d0a45ba1fa2b48f2a30577a06731"
dependencies = [
"float-cmp",
]
[[package]] [[package]]
name = "strsim" name = "strsim"
@ -5063,28 +4859,12 @@ dependencies = [
"syn 2.0.100", "syn 2.0.100",
] ]
[[package]]
name = "subtle"
version = "2.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
[[package]] [[package]]
name = "svg_fmt" name = "svg_fmt"
version = "0.4.4" version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ce5d813d71d82c4cbc1742135004e4a79fd870214c155443451c139c9470a0aa" checksum = "ce5d813d71d82c4cbc1742135004e4a79fd870214c155443451c139c9470a0aa"
[[package]]
name = "svgtypes"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6e44e288cd960318917cbd540340968b90becc8bc81f171345d706e7a89d9d70"
dependencies = [
"kurbo 0.9.5",
"siphasher 0.3.11",
]
[[package]] [[package]]
name = "swash" name = "swash"
version = "0.1.19" version = "0.1.19"
@ -5477,12 +5257,6 @@ dependencies = [
"winapi", "winapi",
] ]
[[package]]
name = "unicase"
version = "2.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "75b844d17643ee918803943289730bec8aac480150456169e647ed0b576ba539"
[[package]] [[package]]
name = "unicode-bidi" name = "unicode-bidi"
version = "0.3.18" version = "0.3.18"
@ -5543,28 +5317,6 @@ version = "0.2.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853"
[[package]]
name = "untrusted"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
[[package]]
name = "ureq"
version = "2.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "02d1a66277ed75f640d608235660df48c8e3c19f3b4edb6a263315626cc3c01d"
dependencies = [
"base64 0.22.1",
"flate2",
"log",
"once_cell",
"rustls",
"rustls-pki-types",
"url",
"webpki-roots",
]
[[package]] [[package]]
name = "url" name = "url"
version = "2.5.4" version = "2.5.4"
@ -5576,50 +5328,6 @@ dependencies = [
"percent-encoding", "percent-encoding",
] ]
[[package]]
name = "usvg"
version = "0.37.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "38b0a51b72ab80ca511d126b77feeeb4fb1e972764653e61feac30adc161a756"
dependencies = [
"base64 0.21.7",
"log",
"pico-args",
"usvg-parser",
"usvg-tree",
"xmlwriter",
]
[[package]]
name = "usvg-parser"
version = "0.37.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9bd4e3c291f45d152929a31f0f6c819245e2921bfd01e7bd91201a9af39a2bdc"
dependencies = [
"data-url",
"flate2",
"imagesize",
"kurbo 0.9.5",
"log",
"roxmltree 0.19.0",
"simplecss",
"siphasher 0.3.11",
"svgtypes",
"usvg-tree",
]
[[package]]
name = "usvg-tree"
version = "0.37.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ee3d202ebdb97a6215604b8f5b4d6ef9024efd623cf2e373a6416ba976ec7d3"
dependencies = [
"rctree",
"strict-num",
"svgtypes",
"tiny-skia-path",
]
[[package]] [[package]]
name = "utf16_iter" name = "utf16_iter"
version = "1.0.5" version = "1.0.5"
@ -6005,15 +5713,6 @@ dependencies = [
"web-sys", "web-sys",
] ]
[[package]]
name = "webpki-roots"
version = "0.26.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2210b291f7ea53617fbafcc4939f10914214ec15aace5ba62293a668f322c5c9"
dependencies = [
"rustls-pki-types",
]
[[package]] [[package]]
name = "weezl" name = "weezl"
version = "0.1.8" version = "0.1.8"
@ -6748,12 +6447,6 @@ version = "0.8.25"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c5b940ebc25896e71dd073bad2dbaa2abfe97b0a391415e22ad1326d9c54e3c4" checksum = "c5b940ebc25896e71dd073bad2dbaa2abfe97b0a391415e22ad1326d9c54e3c4"
[[package]]
name = "xmlwriter"
version = "0.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec7a2a501ed189703dba8b08142f057e887dfc4b2cc4db2d343ac6376ba3e0b9"
[[package]] [[package]]
name = "yazi" name = "yazi"
version = "0.1.6" version = "0.1.6"
@ -6950,12 +6643,6 @@ dependencies = [
"synstructure", "synstructure",
] ]
[[package]]
name = "zeroize"
version = "1.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde"
[[package]] [[package]]
name = "zerovec" name = "zerovec"
version = "0.10.4" version = "0.10.4"

View File

@ -4,10 +4,16 @@ version = "0.1.0"
edition = "2024" edition = "2024"
[dependencies] [dependencies]
egui = "0.31.1"
egui-wgpu = { version = "0.31.1",features = ["winit"] }
egui-winit = "0.31.1"
winit = "0.30.9"
pollster = "0.4.0"
clap = { version = "4.5.34", features = ["derive"] } clap = { version = "4.5.34", features = ["derive"] }
eframe = "0.31.1" eframe = "0.31.1"
egui = "0.31.1" # egui = "0.31.1"
egui_extras = { version = "0.31.1", features = ["all_loaders"] } # egui_extras = { version = "0.31.1", features = ["all_loaders"] }
env_logger = "0.11.7" env_logger = "0.11.7"
iced = { version = "0.13.1", features = ["debug", "canvas", "tokio", "image"]} iced = { version = "0.13.1", features = ["debug", "canvas", "tokio", "image"]}
image = "0.25.6" image = "0.25.6"
@ -15,15 +21,16 @@ image = "0.25.6"
itertools = "0.12" itertools = "0.12"
memmap2 = "0.9.5" memmap2 = "0.9.5"
minifb = "0.28.0" minifb = "0.28.0"
pollster = "0.4.0" # pollster = "0.4.0"
rexiv2 = "0.10.0" rexiv2 = "0.10.0"
threadpool = "1.8.1" threadpool = "1.8.1"
# rustc-hash.workspace = true # rustc-hash.workspace = true
tokio = { version = "1.44.1", features = ["sync"] } tokio = { version = "1.44.1", features = ["sync"] }
tracing-subscriber = "0.3" tracing-subscriber = "0.3"
wgpu = "24.0.3" wgpu = "24.0.3"
winit = "0.30.9" # winit = "0.30.9"
zune-image = {version = "0.4.15", features = ["all"]} zune-image = {version = "0.4.15", features = ["all"]}
bytemuck = "1.22.0"
[profile.release] [profile.release]
opt-level = 3 opt-level = 3
debug = false debug = false

609
src/app.rs Normal file
View File

@ -0,0 +1,609 @@
use crate::egui_tools::EguiRenderer;
use eframe::WindowAttributes;
use egui::{Event, Key};
use egui_wgpu::wgpu::SurfaceError;
use egui_wgpu::{ScreenDescriptor, wgpu};
use imflow::store::ImageStore;
use std::any::Any;
use std::process::exit;
use std::sync::Arc;
use std::time;
use wgpu::util::DeviceExt;
use wgpu::{PipelineCompilationOptions, SurfaceConfiguration};
use winit::application::ApplicationHandler;
use winit::dpi::{LogicalSize, PhysicalSize};
use winit::event::WindowEvent;
use winit::event_loop::ActiveEventLoop;
use winit::platform::x11::WindowAttributesExtX11;
use winit::window::{Window, WindowId};
fn setup_texture(
device: &wgpu::Device,
surface_config: SurfaceConfiguration,
width: u32,
height: u32,
) -> (wgpu::Texture, wgpu::BindGroup, wgpu::RenderPipeline) {
// Create your texture (one-time setup)
let texture = device.create_texture(&wgpu::TextureDescriptor {
label: Some("Image texture"),
size: wgpu::Extent3d {
width,
height,
depth_or_array_layers: 1,
},
mip_level_count: 1,
sample_count: 1,
dimension: wgpu::TextureDimension::D2,
format: wgpu::TextureFormat::Bgra8UnormSrgb,
usage: wgpu::TextureUsages::TEXTURE_BINDING | wgpu::TextureUsages::COPY_DST,
view_formats: &[],
});
// Create texture view and sampler
let texture_view = texture.create_view(&wgpu::TextureViewDescriptor::default());
let sampler = device.create_sampler(&wgpu::SamplerDescriptor {
address_mode_u: wgpu::AddressMode::ClampToEdge,
address_mode_v: wgpu::AddressMode::ClampToEdge,
address_mode_w: wgpu::AddressMode::ClampToEdge,
mag_filter: wgpu::FilterMode::Linear,
min_filter: wgpu::FilterMode::Linear,
mipmap_filter: wgpu::FilterMode::Linear,
..Default::default()
});
// Create bind group layout for the texture
let bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
label: Some("Texture Bind Group Layout"),
entries: &[
wgpu::BindGroupLayoutEntry {
binding: 0,
visibility: wgpu::ShaderStages::FRAGMENT,
ty: wgpu::BindingType::Texture {
sample_type: wgpu::TextureSampleType::Float { filterable: true },
view_dimension: wgpu::TextureViewDimension::D2,
multisampled: false,
},
count: None,
},
wgpu::BindGroupLayoutEntry {
binding: 1,
visibility: wgpu::ShaderStages::FRAGMENT,
ty: wgpu::BindingType::Sampler(wgpu::SamplerBindingType::Filtering),
count: None,
},
],
});
// Create bind group with your texture
let bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
label: Some("Texture Bind Group"),
layout: &bind_group_layout,
entries: &[
wgpu::BindGroupEntry {
binding: 0,
resource: wgpu::BindingResource::TextureView(&texture_view),
},
wgpu::BindGroupEntry {
binding: 1,
resource: wgpu::BindingResource::Sampler(&sampler),
},
],
});
// Define vertex buffer layout
let vertex_buffer_layout = wgpu::VertexBufferLayout {
array_stride: 5 * std::mem::size_of::<f32>() as wgpu::BufferAddress,
step_mode: wgpu::VertexStepMode::Vertex,
attributes: &[
// Position
wgpu::VertexAttribute {
offset: 0,
shader_location: 0,
format: wgpu::VertexFormat::Float32x3,
},
// UV
wgpu::VertexAttribute {
offset: 3 * std::mem::size_of::<f32>() as wgpu::BufferAddress,
shader_location: 1,
format: wgpu::VertexFormat::Float32x2,
},
],
};
// Create shader modules
let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
label: Some("Texture Shader"),
source: wgpu::ShaderSource::Wgsl(std::borrow::Cow::Borrowed(include_str!("shader.wgsl"))),
});
// Create the render pipeline (simplified, you'd need to define vertex buffers, etc.)
let render_pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
label: Some("Texture Render Pipeline"),
layout: Some(
&device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
label: Some("Texture Pipeline Layout"),
bind_group_layouts: &[&bind_group_layout],
push_constant_ranges: &[],
}),
),
vertex: wgpu::VertexState {
module: &shader,
entry_point: Some("vs_main"),
buffers: &[vertex_buffer_layout],
compilation_options: PipelineCompilationOptions::default(),
},
fragment: Some(wgpu::FragmentState {
module: &shader,
entry_point: Some("fs_main"),
targets: &[Some(wgpu::ColorTargetState {
format: surface_config.format,
blend: Some(wgpu::BlendState::REPLACE),
write_mask: wgpu::ColorWrites::ALL,
})],
compilation_options: PipelineCompilationOptions::default(),
}),
primitive: wgpu::PrimitiveState {
topology: wgpu::PrimitiveTopology::TriangleList,
..Default::default()
},
depth_stencil: None,
multisample: wgpu::MultisampleState::default(),
multiview: None,
cache: None,
});
(texture, bind_group, render_pipeline)
}
pub struct AppState {
pub device: wgpu::Device,
pub queue: wgpu::Queue,
pub surface_config: wgpu::SurfaceConfiguration,
pub surface: wgpu::Surface<'static>,
pub scale_factor: f32,
pub egui_renderer: EguiRenderer,
pub store: ImageStore,
pub image_texture: wgpu::Texture,
pub bind_group: wgpu::BindGroup,
pub render_pipeline: wgpu::RenderPipeline,
}
impl AppState {
async fn new(
instance: &wgpu::Instance,
surface: wgpu::Surface<'static>,
window: &Window,
width: u32,
height: u32,
) -> Self {
let power_pref = wgpu::PowerPreference::default();
let adapter = instance
.request_adapter(&wgpu::RequestAdapterOptions {
power_preference: power_pref,
force_fallback_adapter: false,
compatible_surface: Some(&surface),
})
.await
.expect("Failed to find an appropriate adapter");
let features = wgpu::Features::empty();
let (device, queue) = adapter
.request_device(
&wgpu::DeviceDescriptor {
label: None,
required_features: features,
required_limits: Default::default(),
memory_hints: Default::default(),
},
None,
)
.await
.expect("Failed to create device");
let swapchain_capabilities = surface.get_capabilities(&adapter);
let selected_format = wgpu::TextureFormat::Bgra8UnormSrgb;
let swapchain_format = swapchain_capabilities
.formats
.iter()
.find(|d| **d == selected_format)
.expect("failed to select proper surface texture format!");
let surface_config = wgpu::SurfaceConfiguration {
usage: wgpu::TextureUsages::RENDER_ATTACHMENT,
format: *swapchain_format,
width,
height,
present_mode: wgpu::PresentMode::AutoVsync,
desired_maximum_frame_latency: 0,
alpha_mode: swapchain_capabilities.alpha_modes[0],
view_formats: vec![],
};
surface.configure(&device, &surface_config);
let egui_renderer = EguiRenderer::new(&device, surface_config.format, None, 1, window);
let scale_factor = 1.0;
let store = ImageStore::new("./test_images".into());
let (image_texture, bind_group, render_pipeline) =
setup_texture(&device, surface_config.clone(), 6000, 4000);
Self {
device,
queue,
surface,
surface_config,
egui_renderer,
scale_factor,
store,
image_texture,
bind_group,
render_pipeline,
}
}
fn resize_surface(&mut self, width: u32, height: u32) {
self.surface_config.width = width;
self.surface_config.height = height;
self.surface.configure(&self.device, &self.surface_config);
}
}
pub struct App {
instance: wgpu::Instance,
state: Option<AppState>,
window: Option<Arc<Window>>,
}
impl App {
pub fn new() -> Self {
let instance = egui_wgpu::wgpu::Instance::new(&wgpu::InstanceDescriptor::default());
Self {
instance,
state: None,
window: None,
}
}
async fn set_window(&mut self, window: Window) {
let window = Arc::new(window);
let initial_width = 1500;
let initial_height = 1000;
let _ = window.request_inner_size(PhysicalSize::new(initial_width, initial_height));
let surface = self
.instance
.create_surface(window.clone())
.expect("Failed to create surface!");
let state = AppState::new(
&self.instance,
surface,
&window,
initial_width,
initial_width,
)
.await;
self.window.get_or_insert(window);
self.state.get_or_insert(state);
self.update_texture();
}
fn handle_resized(&mut self, width: u32, height: u32) {
println!("Resized {} {}", width, height);
if width > 0 && height > 0 {
self.state.as_mut().unwrap().resize_surface(width, height);
}
}
pub fn update_texture(&mut self) {
let state = self.state.as_mut().unwrap();
state.store.check_loaded_images();
let imbuf = if let Some(full) = state.store.get_current_image() {
println!("full");
full
} else {
state.store.get_thumbnail()
};
let width = imbuf.width as u32;
let height = imbuf.height as u32;
let buffer_u8 = unsafe {
std::slice::from_raw_parts(
imbuf.argb_buffer.as_ptr() as *const u8,
imbuf.argb_buffer.len() * 4,
)
};
state.queue.write_texture(
wgpu::TexelCopyTextureInfo {
texture: &state.image_texture,
mip_level: 0,
origin: wgpu::Origin3d::ZERO,
aspect: wgpu::TextureAspect::All,
},
&buffer_u8,
wgpu::TexelCopyBufferLayout {
offset: 0,
bytes_per_row: Some(4 * width), // 4 bytes per ARGB pixel
rows_per_image: Some(height),
},
wgpu::Extent3d {
width,
height,
depth_or_array_layers: 1,
},
);
}
fn handle_redraw(&mut self) {
// Attempt to handle minimizing window
if let Some(window) = self.window.as_ref() {
if let Some(min) = window.is_minimized() {
if min {
println!("Window is minimized");
return;
}
}
}
let state = self.state.as_mut().unwrap();
let screen_descriptor = ScreenDescriptor {
size_in_pixels: [state.surface_config.width, state.surface_config.height],
pixels_per_point: self.window.as_ref().unwrap().scale_factor() as f32
* state.scale_factor,
};
let surface_texture = state.surface.get_current_texture();
let surface_texture = match surface_texture {
Err(SurfaceError::Outdated) => {
// Ignoring outdated to allow resizing and minimization
println!("wgpu surface outdated");
return;
}
Err(SurfaceError::Timeout) => {
println!("wgpu surface timeout");
return;
}
Err(_) => {
surface_texture.expect("Failed to acquire next swap chain texture");
return;
}
Ok(surface_texture) => surface_texture,
};
let surface_view = surface_texture
.texture
.create_view(&wgpu::TextureViewDescriptor::default());
let mut encoder = state
.device
.create_command_encoder(&wgpu::CommandEncoderDescriptor { label: None });
// Add this render pass to clear the screen with green
{
let _ = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
label: None,
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
view: &surface_view,
resolve_target: None,
ops: wgpu::Operations {
load: wgpu::LoadOp::Clear(wgpu::Color {
r: 0.0,
g: 1.0, // Green
b: 0.0,
a: 1.0,
}),
store: wgpu::StoreOp::Store,
},
})],
depth_stencil_attachment: None,
timestamp_writes: None,
occlusion_query_set: None,
});
}
{
// Define vertices for your quad
#[repr(C)]
#[derive(Copy, Clone, bytemuck::Pod, bytemuck::Zeroable)]
struct Vertex {
position: [f32; 3],
tex_coords: [f32; 2],
}
// Define a quad (two triangles)
let vertices = [
// Position (x, y, z), Texture coords (u, v)
Vertex {
position: [-1.0, -1.0, 0.0],
tex_coords: [0.0, 1.0],
}, // bottom left
Vertex {
position: [-1.0, 1.0, 0.0],
tex_coords: [0.0, 0.0],
}, // top left
Vertex {
position: [1.0, -1.0, 0.0],
tex_coords: [1.0, 1.0],
}, // bottom right
Vertex {
position: [1.0, 1.0, 0.0],
tex_coords: [1.0, 0.0],
}, // top right
];
// Create indices for drawing two triangles
let indices: [u16; 6] = [0, 1, 2, 2, 1, 3];
// Create vertex buffer
let vertex_buffer =
state
.device
.create_buffer_init(&wgpu::util::BufferInitDescriptor {
label: Some("Vertex Buffer"),
contents: bytemuck::cast_slice(&vertices),
usage: wgpu::BufferUsages::VERTEX,
});
// Create index buffer
let index_buffer = state
.device
.create_buffer_init(&wgpu::util::BufferInitDescriptor {
label: Some("Index Buffer"),
contents: bytemuck::cast_slice(&indices),
usage: wgpu::BufferUsages::INDEX,
});
let mut render_pass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
label: Some("Texture Render Pass"),
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
view: &surface_view,
resolve_target: None,
ops: wgpu::Operations {
// Use Load instead of Clear so we don't erase the green background
load: wgpu::LoadOp::Load,
store: wgpu::StoreOp::Store,
},
})],
depth_stencil_attachment: None,
timestamp_writes: None,
occlusion_query_set: None,
});
render_pass.set_pipeline(&state.render_pipeline);
render_pass.set_bind_group(0, &state.bind_group, &[]);
// Bind the vertex buffer
render_pass.set_vertex_buffer(0, vertex_buffer.slice(..));
// Draw using the index buffer (more efficient)
render_pass.set_index_buffer(index_buffer.slice(..), wgpu::IndexFormat::Uint16);
render_pass.draw_indexed(0..6, 0, 0..1);
}
let rating = state.store.get_current_rating();
let window = self.window.as_ref().unwrap();
{
state.egui_renderer.begin_frame(window);
egui::Window::new("Rating")
.collapsible(false)
.resizable(false)
.default_width(5.0)
.show(state.egui_renderer.context(), |ui| {
ui.vertical_centered(|ui| {
ui.label(
egui::RichText::new(format!("{:.1}", rating))
.size(42.0)
.strong(),
);
// ui.add_space(10.0);
});
});
state.egui_renderer.end_frame_and_draw(
&state.device,
&state.queue,
&mut encoder,
window,
&surface_view,
screen_descriptor,
);
}
state.queue.submit(Some(encoder.finish()));
surface_texture.present();
}
}
impl ApplicationHandler for App {
fn resumed(&mut self, event_loop: &ActiveEventLoop) {
let attributes = Window::default_attributes()
.with_base_size(LogicalSize::new(2000, 4000))
.with_resizable(false);
let window = event_loop.create_window(attributes).unwrap();
pollster::block_on(self.set_window(window));
}
fn window_event(&mut self, event_loop: &ActiveEventLoop, _: WindowId, event: WindowEvent) {
// let egui render to process the event first
self.state
.as_mut()
.unwrap()
.egui_renderer
.handle_input(self.window.as_ref().unwrap(), &event);
match event {
WindowEvent::CloseRequested => {
println!("The close button was pressed; stopping");
event_loop.exit();
}
WindowEvent::RedrawRequested => {
let start = time::Instant::now();
self.handle_redraw();
// println!("Updated in: {}ms", start.elapsed().as_millis());
// Extract the events by cloning them from the input context
let events = self
.state
.as_ref()
.unwrap()
.egui_renderer
.context()
.input(|i| {
i.events.clone() // Clone the events to own them outside the closure
});
// Now use the extracted events outside the closure
events.iter().for_each(|e| {
if let Event::Key { key, pressed, .. } = e {
if !*pressed {
return;
}
match *key {
Key::ArrowLeft => {
self.state.as_mut().unwrap().store.next_image(-1);
self.update_texture();
}
Key::ArrowRight => {
self.state.as_mut().unwrap().store.next_image(1);
self.update_texture();
}
Key::ArrowUp => {
let rating =
self.state.as_mut().unwrap().store.get_current_rating();
self.state.as_mut().unwrap().store.set_rating(rating + 1);
}
Key::ArrowDown => {
let rating =
self.state.as_mut().unwrap().store.get_current_rating();
self.state.as_mut().unwrap().store.set_rating(rating - 1);
}
Key::Backtick => self.state.as_mut().unwrap().store.set_rating(0),
Key::Num0 => self.state.as_mut().unwrap().store.set_rating(0),
Key::Num1 => self.state.as_mut().unwrap().store.set_rating(1),
Key::Num2 => self.state.as_mut().unwrap().store.set_rating(2),
Key::Num3 => self.state.as_mut().unwrap().store.set_rating(3),
Key::Num4 => self.state.as_mut().unwrap().store.set_rating(4),
Key::Num5 => self.state.as_mut().unwrap().store.set_rating(5),
Key::Escape => exit(0),
_ => {}
}
}
});
self.window.as_ref().unwrap().request_redraw();
}
WindowEvent::Resized(new_size) => {
self.handle_resized(new_size.width, new_size.height);
}
_ => (),
}
}
}

118
src/egui_tools.rs Normal file
View File

@ -0,0 +1,118 @@
use egui::Context;
use egui_wgpu::wgpu::{CommandEncoder, Device, Queue, StoreOp, TextureFormat, TextureView};
use egui_wgpu::{wgpu, Renderer, ScreenDescriptor};
use egui_winit::State;
use winit::event::WindowEvent;
use winit::window::Window;
pub struct EguiRenderer {
state: State,
renderer: Renderer,
frame_started: bool,
}
impl EguiRenderer {
pub fn context(&self) -> &Context {
self.state.egui_ctx()
}
pub fn new(
device: &Device,
output_color_format: TextureFormat,
output_depth_format: Option<TextureFormat>,
msaa_samples: u32,
window: &Window,
) -> EguiRenderer {
let egui_context = Context::default();
let egui_state = egui_winit::State::new(
egui_context,
egui::viewport::ViewportId::ROOT,
&window,
Some(window.scale_factor() as f32),
None,
Some(2 * 1024), // default dimension is 2048
);
let egui_renderer = Renderer::new(
device,
output_color_format,
output_depth_format,
msaa_samples,
true,
);
EguiRenderer {
state: egui_state,
renderer: egui_renderer,
frame_started: false,
}
}
pub fn handle_input(&mut self, window: &Window, event: &WindowEvent) {
let _ = self.state.on_window_event(window, event);
}
pub fn ppp(&mut self, v: f32) {
self.context().set_pixels_per_point(v);
}
pub fn begin_frame(&mut self, window: &Window) {
let raw_input = self.state.take_egui_input(window);
self.state.egui_ctx().begin_pass(raw_input);
self.frame_started = true;
}
pub fn end_frame_and_draw(
&mut self,
device: &Device,
queue: &Queue,
encoder: &mut CommandEncoder,
window: &Window,
window_surface_view: &TextureView,
screen_descriptor: ScreenDescriptor,
) {
if !self.frame_started {
panic!("begin_frame must be called before end_frame_and_draw can be called!");
}
self.ppp(screen_descriptor.pixels_per_point);
let full_output = self.state.egui_ctx().end_pass();
self.state
.handle_platform_output(window, full_output.platform_output);
let tris = self
.state
.egui_ctx()
.tessellate(full_output.shapes, self.state.egui_ctx().pixels_per_point());
for (id, image_delta) in &full_output.textures_delta.set {
self.renderer
.update_texture(device, queue, *id, image_delta);
}
self.renderer
.update_buffers(device, queue, encoder, &tris, &screen_descriptor);
let rpass = encoder.begin_render_pass(&wgpu::RenderPassDescriptor {
color_attachments: &[Some(wgpu::RenderPassColorAttachment {
view: window_surface_view,
resolve_target: None,
ops: egui_wgpu::wgpu::Operations {
load: egui_wgpu::wgpu::LoadOp::Load,
store: StoreOp::Store,
},
})],
depth_stencil_attachment: None,
timestamp_writes: None,
label: Some("egui main render pass"),
occlusion_query_set: None,
});
self.renderer
.render(&mut rpass.forget_lifetime(), &tris, &screen_descriptor);
for x in &full_output.textures_delta.free {
self.renderer.free_texture(x)
}
self.frame_started = false;
}
}

View File

@ -1,6 +1,7 @@
use iced::widget::image::Handle; use iced::widget::image::Handle;
use image::DynamicImage; use image::DynamicImage;
use image::imageops::FilterType; use image::imageops::FilterType;
use rexiv2::Metadata;
use zune_image::codecs::jpeg::JpegDecoder; use zune_image::codecs::jpeg::JpegDecoder;
use zune_image::codecs::qoi::zune_core::colorspace::ColorSpace; use zune_image::codecs::qoi::zune_core::colorspace::ColorSpace;
use zune_image::codecs::qoi::zune_core::options::DecoderOptions; use zune_image::codecs::qoi::zune_core::options::DecoderOptions;
@ -17,16 +18,38 @@ pub struct ImflowImageBuffer {
pub width: usize, pub width: usize,
pub height: usize, pub height: usize,
pub argb_buffer: Vec<u32>, pub argb_buffer: Vec<u32>,
pub rating: i32
} }
pub fn create_iced_handle(width: u32, height: u32, rgba: Vec<u8>) -> Handle { pub fn create_iced_handle(width: u32, height: u32, rgba: Vec<u8>) -> Handle {
Handle::from_rgba(width, height, rgba) Handle::from_rgba(width, height, rgba)
} }
fn get_rating(filename: PathBuf) -> i32 {
// if !path_exists(filename.clone()) {
// anyhow::bail!("File doesn't exist");
// }
// // Use xmp-toolkit for video files
// if is_video(&filename) {
// return Ok(read_rating_xmp(filename.clone()).unwrap_or(0));
// }
// Use rexiv2 for image files
let meta = Metadata::new_from_path(filename);
match meta {
Ok(meta) => {
let rating = meta.get_tag_numeric("Xmp.xmp.Rating");
rating
}
Err(e) => panic!("{:?}", e),
}
}
pub fn load_image(path: PathBuf) -> ImflowImageBuffer { pub fn load_image(path: PathBuf) -> ImflowImageBuffer {
let total_start = Instant::now(); let total_start = Instant::now();
let file = read(path).unwrap(); let file = read(path.clone()).unwrap();
let mut decoder = JpegDecoder::new(&file); let mut decoder = JpegDecoder::new(&file);
let options = DecoderOptions::new_fast().jpeg_set_out_colorspace(ColorSpace::BGRA); let options = DecoderOptions::new_fast().jpeg_set_out_colorspace(ColorSpace::BGRA);
decoder.set_options(options); decoder.set_options(options);
@ -52,10 +75,13 @@ pub fn load_image(path: PathBuf) -> ImflowImageBuffer {
let total_time = total_start.elapsed(); let total_time = total_start.elapsed();
println!("Total loading time: {:?}", total_time); println!("Total loading time: {:?}", total_time);
let rating = get_rating(path);
ImflowImageBuffer { ImflowImageBuffer {
width, width,
height, height,
argb_buffer: buffer_u32, argb_buffer: buffer_u32,
rating
} }
} }
@ -63,12 +89,14 @@ pub fn image_to_argb_buffer(img: DynamicImage) -> Vec<u32> {
let flat = img.into_rgba8(); let flat = img.into_rgba8();
let buf = flat.as_raw(); let buf = flat.as_raw();
buf.chunks_exact(4).map(|rgba| { buf.chunks_exact(4)
let r = rgba[0] as u32; .map(|rgba| {
let g = rgba[1] as u32; let r = rgba[0] as u32;
let b = rgba[2] as u32; let g = rgba[1] as u32;
r << 16 | g << 8 | b let b = rgba[2] as u32;
}).collect() r << 16 | g << 8 | b
})
.collect()
} }
pub fn load_available_images(dir: PathBuf) -> Vec<PathBuf> { pub fn load_available_images(dir: PathBuf) -> Vec<PathBuf> {
@ -121,10 +149,13 @@ pub fn load_thumbnail_exif(path: &PathBuf) -> Option<ImflowImageBuffer> {
*argb = r << 16 | g << 8 | b; *argb = r << 16 | g << 8 | b;
} }
let rating = get_rating(path.into());
Some(ImflowImageBuffer { Some(ImflowImageBuffer {
width, width,
height, height,
argb_buffer: buffer, argb_buffer: buffer,
rating
}) })
} }
_ => None, _ => None,
@ -143,10 +174,12 @@ pub fn load_thumbnail_full(path: &PathBuf) -> ImflowImageBuffer {
let width = image.width() as usize; let width = image.width() as usize;
let height = image.height() as usize; let height = image.height() as usize;
let buffer = image_to_argb_buffer(image); let buffer = image_to_argb_buffer(image);
let rating = get_rating(path.into());
ImflowImageBuffer { ImflowImageBuffer {
width, width,
height, height,
argb_buffer: buffer, argb_buffer: buffer,
rating
} }
} }

View File

@ -317,6 +317,66 @@ use imflow::store::ImageStore;
use eframe::egui; use eframe::egui;
use egui::{ColorImage, Image, TextureHandle, TextureOptions}; use egui::{ColorImage, Image, TextureHandle, TextureOptions};
mod app;
mod egui_tools;
use winit::event_loop::{ControlFlow, EventLoop};
fn main() {
#[cfg(not(target_arch = "wasm32"))]
{
pollster::block_on(run());
}
}
async fn run() {
let event_loop = EventLoop::new().unwrap();
event_loop.set_control_flow(ControlFlow::Poll);
let mut app = app::App::new();
event_loop.run_app(&mut app).expect("Failed to run app");
// let path = args.path.unwrap_or("./test_images".into());
// let mut state = ImageStore::new(path);
// let mut waiting = true;
// window.set_key_repeat_delay(0.1);
// window.set_key_repeat_rate(0.1);
// show_image(&mut window, state.get_thumbnail());
// while window.is_open() && !window.is_key_down(Key::Escape) {
// window.update();
// state.check_loaded_images();
// if window.is_key_pressed(Key::Right, minifb::KeyRepeat::Yes) {
// state.next_image(1);
// if let Some(full) = state.get_current_image() {
// show_image(&mut window, full);
// } else {
// show_image(&mut window, state.get_thumbnail());
// waiting = true;
// }
// } else if window.is_key_pressed(Key::Left, minifb::KeyRepeat::Yes) {
// state.next_image(-1);
// if let Some(full) = state.get_current_image() {
// show_image(&mut window, full);
// } else {
// show_image(&mut window, state.get_thumbnail());
// waiting = true;
// }
// }
// if waiting {
// if let Some(image) = state.get_current_image() {
// waiting = false;
// show_image(&mut window, &image);
// }
// }
// }
}
struct MyApp { struct MyApp {
// image: Image, // image: Image,
store: ImageStore, store: ImageStore,
@ -361,105 +421,133 @@ struct Args {
path: Option<PathBuf>, path: Option<PathBuf>,
} }
fn main() { // fn init_app() {
let native_options = eframe::NativeOptions {
viewport: egui::ViewportBuilder::default().with_inner_size((400.0, 400.0)),
..eframe::NativeOptions::default()
};
eframe::run_native( // let mut store = ImageStore::new("./test_images".into());
"aaa",
native_options,
Box::new(|cc| {
// Initialize image loaders
egui_extras::install_image_loaders(&cc.egui_ctx);
let mut store = ImageStore::new("./test_images".into());
let mut imbuf = store.get_current_image().unwrap(); // let mut imbuf = store.get_current_image().unwrap();
let width = imbuf.width; // let width = imbuf.width;
let height = imbuf.height; // let height = imbuf.height;
let mut buffer = imbuf.argb_buffer.clone(); // let mut buffer = imbuf.argb_buffer.clone();
// Reinterpret to avoid copying // // Reinterpret to avoid copying
let buffer_u8 = unsafe { // let buffer_u8 = unsafe {
Vec::from_raw_parts( // Vec::from_raw_parts(
buffer.as_mut_ptr() as *mut u8, // buffer.as_mut_ptr() as *mut u8,
buffer.len() * 4, // buffer.len() * 4,
buffer.capacity() * 4, // buffer.capacity() * 4,
) // )
}; // };
std::mem::forget(buffer); // std::mem::forget(buffer);
let color_image = ColorImage::from_rgba_unmultiplied([width, height], &buffer_u8); // let color_image = ColorImage::from_rgba_unmultiplied([width, height], &buffer_u8);
let texture = cc // let texture = cc
.egui_ctx // .egui_ctx
.load_texture("img", color_image, TextureOptions::LINEAR); // .load_texture("img", color_image, TextureOptions::LINEAR);
Ok(Box::new(MyApp::new(store, texture))) // Ok(Box::new(MyApp::new(store, texture)))
}), // }
)
.unwrap();
// eframe::run_native(Box::new(MyApp::default()), options);
let args = Args::parse(); // fn main() {
const WIDTH: usize = 2000; // let native_options = eframe::NativeOptions {
const HEIGHT: usize = 1000; // viewport: egui::ViewportBuilder::default().with_inner_size((400.0, 400.0)),
let mut window = Window::new( // ..eframe::NativeOptions::default()
"Test - ESC to exit", // };
WIDTH,
HEIGHT,
WindowOptions::default(),
)
.unwrap_or_else(|e| {
panic!("{}", e);
});
window.set_target_fps(120); // eframe::run_native(
// "aaa",
// native_options,
// Box::new(|cc| {
// // Initialize image loaders
// egui_extras::install_image_loaders(&cc.egui_ctx);
// let mut store = ImageStore::new("./test_images".into());
let path = args.path.unwrap_or("./test_images".into()); // let mut imbuf = store.get_current_image().unwrap();
let mut state = ImageStore::new(path);
let mut waiting = true;
window.set_key_repeat_delay(0.1);
window.set_key_repeat_rate(0.1);
show_image(&mut window, state.get_thumbnail()); // let width = imbuf.width;
// let height = imbuf.height;
while window.is_open() && !window.is_key_down(Key::Escape) { // let mut buffer = imbuf.argb_buffer.clone();
window.update(); // // Reinterpret to avoid copying
state.check_loaded_images(); // let buffer_u8 = unsafe {
if window.is_key_pressed(Key::Right, minifb::KeyRepeat::Yes) { // Vec::from_raw_parts(
state.next_image(1); // buffer.as_mut_ptr() as *mut u8,
if let Some(full) = state.get_current_image() { // buffer.len() * 4,
show_image(&mut window, full); // buffer.capacity() * 4,
} else { // )
show_image(&mut window, state.get_thumbnail()); // };
waiting = true; // std::mem::forget(buffer);
}
} else if window.is_key_pressed(Key::Left, minifb::KeyRepeat::Yes) {
state.next_image(-1);
if let Some(full) = state.get_current_image() {
show_image(&mut window, full);
} else {
show_image(&mut window, state.get_thumbnail());
waiting = true;
}
}
if waiting {
if let Some(image) = state.get_current_image() {
waiting = false;
show_image(&mut window, &image); // let color_image = ColorImage::from_rgba_unmultiplied([width, height], &buffer_u8);
} // let texture = cc
} // .egui_ctx
} // .load_texture("img", color_image, TextureOptions::LINEAR);
}
fn show_image(window: &mut Window, image: &ImflowImageBuffer) { // Ok(Box::new(MyApp::new(store, texture)))
window // }),
.update_with_buffer(&image.argb_buffer, image.width, image.height) // )
.unwrap(); // .unwrap();
} // // eframe::run_native(Box::new(MyApp::default()), options);
// let args = Args::parse();
// const WIDTH: usize = 2000;
// const HEIGHT: usize = 1000;
// let mut window = Window::new(
// "Test - ESC to exit",
// WIDTH,
// HEIGHT,
// WindowOptions::default(),
// )
// .unwrap_or_else(|e| {
// panic!("{}", e);
// });
// window.set_target_fps(120);
// let path = args.path.unwrap_or("./test_images".into());
// let mut state = ImageStore::new(path);
// let mut waiting = true;
// window.set_key_repeat_delay(0.1);
// window.set_key_repeat_rate(0.1);
// show_image(&mut window, state.get_thumbnail());
// while window.is_open() && !window.is_key_down(Key::Escape) {
// window.update();
// state.check_loaded_images();
// if window.is_key_pressed(Key::Right, minifb::KeyRepeat::Yes) {
// state.next_image(1);
// if let Some(full) = state.get_current_image() {
// show_image(&mut window, full);
// } else {
// show_image(&mut window, state.get_thumbnail());
// waiting = true;
// }
// } else if window.is_key_pressed(Key::Left, minifb::KeyRepeat::Yes) {
// state.next_image(-1);
// if let Some(full) = state.get_current_image() {
// show_image(&mut window, full);
// } else {
// show_image(&mut window, state.get_thumbnail());
// waiting = true;
// }
// }
// if waiting {
// if let Some(image) = state.get_current_image() {
// waiting = false;
// show_image(&mut window, &image);
// }
// }
// }
// }
// fn show_image(window: &mut Window, image: &ImflowImageBuffer) {
// window
// .update_with_buffer(&image.argb_buffer, image.width, image.height)
// .unwrap();
// }
// struct MainApp { // struct MainApp {
// is_playing: bool, // is_playing: bool,

25
src/shader.wgsl Normal file
View File

@ -0,0 +1,25 @@
struct VertexInput {
@location(0) position: vec3<f32>,
@location(1) uv: vec2<f32>,
};
struct VertexOutput {
@builtin(position) position: vec4<f32>,
@location(0) uv: vec2<f32>,
};
@vertex
fn vs_main(in: VertexInput) -> VertexOutput {
var out: VertexOutput;
out.position = vec4<f32>(in.position, 1.0);
out.uv = in.uv;
return out;
}
@group(0) @binding(0) var texture: texture_2d<f32>;
@group(0) @binding(1) var texture_sampler: sampler;
@fragment
fn fs_main(@location(0) uv: vec2<f32>) -> @location(0) vec4<f32> {
return textureSample(texture, texture_sampler, uv);
}

View File

@ -1,7 +1,6 @@
use crate::image::load_thumbnail; use crate::image::load_thumbnail;
use crate::image::{ use crate::image::{ImflowImageBuffer, load_available_images, load_image};
ImflowImageBuffer, load_available_images, load_image, use rexiv2::Metadata;
};
use std::collections::HashMap; use std::collections::HashMap;
use std::collections::HashSet; use std::collections::HashSet;
use std::path::PathBuf; use std::path::PathBuf;
@ -73,6 +72,36 @@ impl ImageStore {
state state
} }
pub fn set_rating(&mut self, rating: i32) {
let meta = Metadata::new_from_path(self.current_image_path.clone());
match meta {
Ok(meta) => {
meta.set_tag_numeric("Xmp.xmp.Rating", rating).unwrap();
meta.save_to_file(self.current_image_path.clone()).unwrap();
}
Err(e) => panic!("{:?}", e),
}
if let Some(full) = self.loaded_images.get_mut(&self.current_image_path.clone()) {
full.rating = rating;
}
if let Some(thumbnail) = self.loaded_images_thumbnails.get_mut(&self.current_image_path.clone()) {
thumbnail.rating = rating;
}
}
pub fn get_current_rating(&self) -> i32 {
let imbuf = if let Some(full) = self.get_current_image() {
println!("full");
full
} else {
// TODO: this assumes loaded thumbnail
self.loaded_images_thumbnails
.get(&self.current_image_path)
.unwrap()
};
imbuf.rating
}
pub fn preload_next_images(&mut self, n: usize) { pub fn preload_next_images(&mut self, n: usize) {
for image in self for image in self
.available_images .available_images