| 1 | // Copyright © SixtyFPS GmbH <info@slint.dev> |
| 2 | // SPDX-License-Identifier: MIT |
| 3 | |
| 4 | use slint::SharedString; |
| 5 | use std::rc::Rc; |
| 6 | |
| 7 | slint::include_modules!(); |
| 8 | |
| 9 | #[cfg (target_arch = "wasm32" )] |
| 10 | use wasm_bindgen::prelude::*; |
| 11 | |
| 12 | struct Filter { |
| 13 | name: SharedString, |
| 14 | apply_function: fn(&image::RgbaImage) -> image::RgbaImage, |
| 15 | } |
| 16 | |
| 17 | struct Filters(Vec<Filter>); |
| 18 | |
| 19 | impl slint::Model for Filters { |
| 20 | type Data = SharedString; |
| 21 | |
| 22 | fn row_count(&self) -> usize { |
| 23 | self.0.len() |
| 24 | } |
| 25 | |
| 26 | fn row_data(&self, row: usize) -> Option<Self::Data> { |
| 27 | self.0.get(index:row).map(|x: &Filter| x.name.clone()) |
| 28 | } |
| 29 | |
| 30 | fn model_tracker(&self) -> &dyn slint::ModelTracker { |
| 31 | &() |
| 32 | } |
| 33 | } |
| 34 | |
| 35 | #[cfg_attr (target_arch = "wasm32" , wasm_bindgen(start))] |
| 36 | pub fn main() { |
| 37 | // This provides better error messages in debug mode. |
| 38 | // It's disabled in release mode so it doesn't bloat up the file size. |
| 39 | #[cfg (all(debug_assertions, target_arch = "wasm32" ))] |
| 40 | console_error_panic_hook::set_once(); |
| 41 | |
| 42 | let main_window = MainWindow::new().unwrap(); |
| 43 | |
| 44 | #[cfg (target_arch = "wasm32" )] |
| 45 | let source_image = |
| 46 | image::load_from_memory(include_bytes!("../assets/cat.jpg" )).unwrap().into_rgba8(); |
| 47 | #[cfg (not(target_arch = "wasm32" ))] |
| 48 | let source_image = { |
| 49 | let mut cat_path = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR" )); |
| 50 | cat_path.push("../assets/cat.jpg" ); |
| 51 | image::open(&cat_path).expect("Error loading cat image" ).into_rgba8() |
| 52 | }; |
| 53 | |
| 54 | main_window.set_original_image(slint::Image::from_rgba8( |
| 55 | slint::SharedPixelBuffer::clone_from_slice( |
| 56 | source_image.as_raw(), |
| 57 | source_image.width(), |
| 58 | source_image.height(), |
| 59 | ), |
| 60 | )); |
| 61 | |
| 62 | let filters = Filters(vec![ |
| 63 | Filter { |
| 64 | name: "Blur" .into(), |
| 65 | apply_function: |image: &image::RgbaImage| image::imageops::blur(image, 4.), |
| 66 | }, |
| 67 | Filter { |
| 68 | name: "Brighten" .into(), |
| 69 | apply_function: |image: &image::RgbaImage| { |
| 70 | image::imageops::colorops::brighten(image, 30) |
| 71 | }, |
| 72 | }, |
| 73 | Filter { |
| 74 | name: "Darken" .into(), |
| 75 | apply_function: |image: &image::RgbaImage| { |
| 76 | image::imageops::colorops::brighten(image, -30) |
| 77 | }, |
| 78 | }, |
| 79 | Filter { |
| 80 | name: "Increase Contrast" .into(), |
| 81 | apply_function: |image: &image::RgbaImage| { |
| 82 | image::imageops::colorops::contrast(image, 30.) |
| 83 | }, |
| 84 | }, |
| 85 | Filter { |
| 86 | name: "Decrease Contrast" .into(), |
| 87 | apply_function: |image: &image::RgbaImage| { |
| 88 | image::imageops::colorops::contrast(image, -30.) |
| 89 | }, |
| 90 | }, |
| 91 | Filter { |
| 92 | name: "Invert" .into(), |
| 93 | apply_function: |image: &image::RgbaImage| { |
| 94 | let mut inverted = image.clone(); |
| 95 | image::imageops::colorops::invert(&mut inverted); |
| 96 | inverted |
| 97 | }, |
| 98 | }, |
| 99 | ]); |
| 100 | let filters = Rc::new(filters); |
| 101 | |
| 102 | main_window.set_filters(slint::ModelRc::from(filters.clone())); |
| 103 | |
| 104 | main_window.on_filter_image(move |filter_index| { |
| 105 | let filter_fn = filters.0[filter_index as usize].apply_function; |
| 106 | let filtered_image = filter_fn(&source_image); |
| 107 | slint::Image::from_rgba8(slint::SharedPixelBuffer::clone_from_slice( |
| 108 | filtered_image.as_raw(), |
| 109 | filtered_image.width(), |
| 110 | filtered_image.height(), |
| 111 | )) |
| 112 | }); |
| 113 | |
| 114 | main_window.run().unwrap(); |
| 115 | } |
| 116 | |