Load and save image files
This commit is contained in:
116
src/main.rs
116
src/main.rs
@@ -3,11 +3,19 @@ use serde::{ Serialize, Deserialize };
|
||||
use fltk::*;
|
||||
use fltk::prelude::*;
|
||||
use fltk::enums::*;
|
||||
use fltk::image::{ SharedImage, PngImage };
|
||||
|
||||
extern crate image;
|
||||
use image::io::Reader as ImageReader;
|
||||
use image::{ DynamicImage };
|
||||
|
||||
use std::cell::RefCell;
|
||||
use std::rc::Rc;
|
||||
use std::collections::HashMap;
|
||||
|
||||
use std::io::{ Write, Cursor, BufWriter };
|
||||
use std::fs::File;
|
||||
|
||||
type Id = usize;
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
struct Node {
|
||||
@@ -20,13 +28,27 @@ struct Node {
|
||||
#[derive(Serialize, Deserialize, Debug)]
|
||||
struct Board {
|
||||
nodes: HashMap<Id, Node>,
|
||||
image: Option<String>,
|
||||
}
|
||||
impl Board {
|
||||
pub fn new() -> Self {
|
||||
let nodes = HashMap::new();
|
||||
let image = None;
|
||||
Board { nodes, image }
|
||||
Board { nodes }
|
||||
}
|
||||
}
|
||||
|
||||
struct AppState {
|
||||
pub board: Board,
|
||||
pub image_raw: Option<DynamicImage>,
|
||||
pub image: Option<PngImage>,
|
||||
}
|
||||
|
||||
impl AppState {
|
||||
fn new() -> Self {
|
||||
AppState {
|
||||
board: Board::new(),
|
||||
image_raw: None,
|
||||
image: None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,34 +56,94 @@ fn menu_cb(m: &mut impl MenuExt) {
|
||||
todo!();
|
||||
}
|
||||
|
||||
fn encode_png(image: &DynamicImage) -> Vec<u8> {
|
||||
let mut cursor = Cursor::new(Vec::new());
|
||||
image.write_to(&mut cursor, image::ImageOutputFormat::Png).unwrap();
|
||||
cursor.into_inner()
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let app = app::App::default();
|
||||
let app = app::App::default()
|
||||
.with_scheme(app::Scheme::Gtk);
|
||||
let mut win = window::Window::default()
|
||||
.with_size(400, 300);
|
||||
.with_size(400, 300)
|
||||
.with_label("Board Builder");
|
||||
let mut flex = group::Flex::default()
|
||||
.size_of_parent();
|
||||
flex.set_type(group::FlexType::Column);
|
||||
|
||||
// Board
|
||||
let board = Rc::new(RefCell::new(Board::new()));
|
||||
let menu = Rc::clone(&board);
|
||||
// State
|
||||
let state = Rc::new(RefCell::new(AppState::new()));
|
||||
|
||||
// Menu
|
||||
let mut menubar = menu::MenuBar::default();
|
||||
menubar.add("File/New" , Shortcut::None, menu::MenuFlag::Normal, menu_cb);
|
||||
menubar.add("File/Open..." , Shortcut::None, menu::MenuFlag::Normal, menu_cb);
|
||||
menubar.add("File/Open Image..." , Shortcut::None, menu::MenuFlag::Normal, menu_cb);
|
||||
menubar.add("File/Save As ..." , Shortcut::None, menu::MenuFlag::Normal, move |_| { println!("{}", serde_json::to_string(menu.as_ref()).unwrap()); });
|
||||
menubar.add("Edit/Edit Nodes" , Shortcut::None, menu::MenuFlag::Normal, menu_cb);
|
||||
menubar.add("Edit/Edit Edges" , Shortcut::None, menu::MenuFlag::Normal, menu_cb);
|
||||
let state_clone = Rc::clone(&state);
|
||||
menubar.add("File/New" , Shortcut::None, menu::MenuFlag::Normal, move |_| {
|
||||
state_clone.replace(AppState::new());
|
||||
app::redraw();
|
||||
});
|
||||
let state_clone = Rc::clone(&state);
|
||||
menubar.add("File/Open..." , Shortcut::None, menu::MenuFlag::Normal, move |_| {
|
||||
let mut fc = dialog::NativeFileChooser::new(dialog::NativeFileChooserType::BrowseFile);
|
||||
fc.show();
|
||||
|
||||
let file = File::open(fc.filename()).unwrap();
|
||||
let mut ar = zip::ZipArchive::new(file).unwrap();
|
||||
let json_file = ar.by_name("graph.json").unwrap();
|
||||
let mut state = state_clone.borrow_mut();
|
||||
state.board = serde_json::from_reader(json_file).unwrap();
|
||||
if let Some(image_file) = ar.by_name("image").ok() {
|
||||
todo!();
|
||||
}
|
||||
app::redraw();
|
||||
});
|
||||
let state_clone = Rc::clone(&state);
|
||||
menubar.add("File/Open Image..." , Shortcut::None, menu::MenuFlag::Normal, move |_| {
|
||||
let mut fc = dialog::NativeFileChooser::new(dialog::NativeFileChooserType::BrowseFile);
|
||||
fc.show();
|
||||
let filename = fc.filename();
|
||||
|
||||
match ImageReader::open(filename).map(|i| i.decode()) {
|
||||
Ok(Ok(image)) => {
|
||||
let mut state = state_clone.borrow_mut();
|
||||
let data = encode_png(&image);
|
||||
state.image = Some(PngImage::from_data(&data).unwrap());
|
||||
state.image_raw = Some(image);
|
||||
app::redraw();
|
||||
}
|
||||
_ => dialog::alert_default("Error opening file"),
|
||||
}
|
||||
});
|
||||
let state_clone = Rc::clone(&state);
|
||||
menubar.add("File/Save As ..." , Shortcut::None, menu::MenuFlag::Normal, move |_| {
|
||||
let mut fc = dialog::NativeFileChooser::new(dialog::NativeFileChooserType::BrowseSaveFile);
|
||||
fc.show();
|
||||
|
||||
let file = File::create(fc.filename()).unwrap();
|
||||
let mut ar = zip::ZipWriter::new(file);
|
||||
let options = zip::write::FileOptions::default();
|
||||
let state = state_clone.borrow();
|
||||
|
||||
ar.start_file("graph.json", options).ok();
|
||||
ar.write(&serde_json::to_vec(&state.board).unwrap()).ok();
|
||||
if let Some(image) = state.image_raw.as_ref() {
|
||||
ar.start_file("image.png", options).ok();
|
||||
let data = encode_png(image);
|
||||
ar.write_all(&data).unwrap();
|
||||
ar.flush().unwrap();
|
||||
}
|
||||
ar.finish().ok();
|
||||
});
|
||||
menubar.add("Edit/Edit Nodes" , Shortcut::None, menu::MenuFlag::Normal, menu_cb);
|
||||
menubar.add("Edit/Edit Edges" , Shortcut::None, menu::MenuFlag::Normal, menu_cb);
|
||||
flex.set_size(&mut menubar, 40);
|
||||
|
||||
// Canvas
|
||||
let image = Rc::new(RefCell::new(Option::<image::SharedImage>::None));
|
||||
let bg_image = Rc::clone(&image);
|
||||
let mut frame = frame::Frame::default();
|
||||
let state_clone = Rc::clone(&state);
|
||||
frame.draw(move |f| {
|
||||
let mut image = bg_image.borrow_mut();
|
||||
let mut state = state_clone.borrow_mut();
|
||||
let mut image = &mut state.image;
|
||||
if let Some(image) = image.as_mut() {
|
||||
image.scale(f.w(), f.h(), true, true);
|
||||
image.draw(f.x(), f.y(), f.w(), f.h());
|
||||
|
||||
Reference in New Issue
Block a user