Skip to content

Commit

Permalink
Images can now be viewed. There's a BIG crash when non-image files ar…
Browse files Browse the repository at this point in the history
…e drag-dropped.
  • Loading branch information
ianelsbree committed Jan 6, 2024
1 parent 3b17980 commit 63b0f17
Show file tree
Hide file tree
Showing 2 changed files with 182 additions and 78 deletions.
78 changes: 78 additions & 0 deletions src/image.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
use crate::path_buf_to_filename_string;
use eframe::egui;
use eframe::egui::{ColorImage, TextureHandle, TextureOptions};
use image as image_lib;
use image_lib::io::Reader as ImageReader;
use image_lib::DynamicImage;
use std::fmt::{Debug, Formatter};
use std::path::PathBuf;
use std::sync::atomic::{AtomicUsize, Ordering};

/// An image as defined by this tool, containing image data and other metadata
#[derive(Default, Clone, PartialEq)]
pub struct Image {
id: usize,
name: String,
path: PathBuf,
data: DynamicImage,
texture: Option<TextureHandle>,
}

impl Debug for Image {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
let has_texture = &self.texture.is_some();
f.debug_struct("Image")
.field("id", &self.id)
.field("path", &self.path)
.field("data", &self.data)
.field("texture", has_texture)
.field("name", &self.name)
.finish()
}
}

#[allow(dead_code)]
impl Image {
pub fn new(filename: PathBuf) -> Self {
static ID_COUNTER: AtomicUsize = AtomicUsize::new(1);
let id = ID_COUNTER.fetch_add(1, Ordering::Relaxed);
let data = ImageReader::open(&filename)
.expect("could not open image")
.decode()
.expect("could not decode image");
let name = path_buf_to_filename_string(&filename);

Self {
id,
name,
path: filename,
data,
texture: None,
}
}

pub fn load_texture(&mut self, ctx: &egui::Context) {
let image_buffer = self.data.to_rgba8();
let size = (self.data.width() as usize, self.data.height() as usize);
let pixels = image_buffer.into_vec();
assert_eq!(size.0 * size.1 * 4, pixels.len());
let pixels = ColorImage::from_rgba_unmultiplied([size.0, size.1], &pixels);
self.texture = Some(ctx.load_texture(&self.name, pixels, TextureOptions::default()));
}

pub fn id(&self) -> usize {
self.id
}
pub fn name(&self) -> &str {
&self.name
}
pub fn path(&self) -> &PathBuf {
&self.path
}
pub fn data(&self) -> &DynamicImage {
&self.data
}
pub fn texture(&self) -> &Option<TextureHandle> {
&self.texture
}
}
182 changes: 104 additions & 78 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ use std::path::PathBuf;
use eframe::egui;
use eframe::egui::{menu, CollapsingHeader, ScrollArea};

use image::Image;

mod image;

fn main() -> Result<(), eframe::Error> {
env_logger::init(); // Log to stderr (if you run with `RUST_LOG=debug`).
let options = eframe::NativeOptions {
Expand All @@ -24,29 +28,28 @@ fn main() -> Result<(), eframe::Error> {
)
}

#[derive(Default, Debug, Clone, PartialEq)]
struct Image {
path: PathBuf,
data: Vec<u8>,
}

impl Image {
pub fn new(filename: PathBuf) -> Self {
Self {
path: filename,
data: Vec::new(),
}
}

fn import_from_path(&mut self) {}
}

#[derive(Default)]
struct MyApp {
image_sequence: Vec<Image>,
selected_image: Option<Image>,
}

impl MyApp {
fn import_images(&mut self) {
if let Some(paths) = rfd::FileDialog::new()
.add_filter("PNG", &["png"])
.pick_files()
{
for path in paths {
self.image_sequence.push(Image::new(path));
}
}
}
fn no_images_loaded(&self) -> bool {
self.image_sequence.len() == 0
}
}

/*impl Default for MyApp {
fn default() -> Self {
Self {
Expand All @@ -59,21 +62,12 @@ struct MyApp {

impl eframe::App for MyApp {
fn update(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame) {
// system menu
egui::TopBottomPanel::top("top_panel").show(ctx, |ui| {
menu::bar(ui, |ui| {
ui.menu_button("File", |ui| {
if ui.button("Import Images").clicked() {
if let Some(paths) = rfd::FileDialog::new()
.add_filter("PNG", &["png"])
.pick_files()
{
for path in paths {
self.image_sequence.push(Image::new(path));
}
for image in &mut self.image_sequence {
image.import_from_path();
}
}
self.import_images();
ui.close_menu();
}
if ui.button("Unload All Images").clicked() {
Expand All @@ -92,64 +86,96 @@ impl eframe::App for MyApp {
})
});
});

// main window
egui::CentralPanel::default().show(ctx, |ui| {
ui.heading("Speedy Spritesheets");

ui.label(format!(
"Number of images imported: {}",
self.image_sequence.len()
));

// if self.image_sequence.len() > 0 {
ui.heading("Imported Images");
ui.horizontal(|ui| {
ui.label("Show data for image:");
egui::ComboBox::from_label("")
.selected_text(if let Some(selected_image) = &self.selected_image {
path_buf_to_string(&selected_image.path)
} else if self.image_sequence.len() == 0 {
"No imported images".to_string()
} else {
"Select image".to_string()
})
.show_ui(ui, |ui| {
ui.style_mut().wrap = Some(false);
for image in &self.image_sequence {
ui.selectable_value(
&mut self.selected_image,
Some(image.clone()),
path_buf_to_string(&image.path),
);
ScrollArea::vertical().auto_shrink(false).show(ui, |ui| {
ui.heading("Speedy Spritesheets");

ui.label(format!(
"Number of images imported: {}",
self.image_sequence.len()
));

ui.heading("Frames");
if self.no_images_loaded() {
ui.horizontal(|ui| {
ui.label("No images loaded");
if ui.button("Import images").clicked() {
self.import_images();
}
ui.style_mut().wrap = Some(true);
});
});
ScrollArea::vertical().auto_shrink(true).show(ui, |ui| {
// ui.monospace(format!("{:?}", self.selected_image));
if let Some(image) = &self.selected_image {
ui.add(egui::Image::from_bytes("bytes://image", image.data.clone()));
}
});
CollapsingHeader::new("Imported Image Paths").show(ui, |ui| {
ui.horizontal(|ui| {
ui.label("Show data for image:");
egui::ComboBox::from_label("")
.selected_text(if let Some(selected_image) = &self.selected_image {
&selected_image.name()
} else if self.no_images_loaded() {
"No imported images"
} else {
"Select image"
})
.show_ui(ui, |ui| {
ui.style_mut().wrap = Some(false);
for image in &self.image_sequence {
ui.selectable_value(
&mut self.selected_image,
Some(image.clone()),
image.name(),
);
}
ui.style_mut().wrap = Some(true);
});
});
ScrollArea::vertical().auto_shrink(true).show(ui, |ui| {
for image in &self.image_sequence {
ui.monospace(format!("{}", image.path.display().to_string()));
// ui.monospace(format!("{:?}", self.selected_image));
if let Some(image) = &mut self.selected_image {
// ui.add(egui::Image::from_bytes("bytes://image", image.data.clone()));
if image.texture().is_none() {
image.load_texture(&ctx);
}
if let Some(texture) = &image.texture() {
ui.add(egui::Image::new(texture).max_size([100.0, 100.0].into()));
} else {
ui.label("Image is loaded but does not have a texture.");
}
}
});
});
// }

// file drag-and-drop
preview_files_being_dropped(ctx);
ctx.input(|i| {
if !i.raw.dropped_files.is_empty() {
let dropped_files = i.raw.dropped_files.clone();
for file in dropped_files {
if let Some(path) = file.path {
self.image_sequence.push(Image::new(path));
CollapsingHeader::new("Loaded Images").show(ui, |ui| {
ScrollArea::vertical().auto_shrink(true).show(ui, |ui| {
for image in &mut self.image_sequence {
if image.texture().is_none() {
image.load_texture(&ctx);
}
if let Some(texture) = &image.texture() {
ui.add(egui::Image::new(texture).max_size([100.0, 100.0].into()));
} else {
ui.label("Image is loaded but does not have a texture.");
}
}
});
});
CollapsingHeader::new("Imported Image Paths").show(ui, |ui| {
ScrollArea::vertical().auto_shrink(true).show(ui, |ui| {
for image in &self.image_sequence {
ui.monospace(format!("{}", image.path().display().to_string()));
}
});
});

// file drag-and-drop
preview_files_being_dropped(ctx);
ctx.input(|i| {
if !i.raw.dropped_files.is_empty() {
let dropped_files = i.raw.dropped_files.clone();
for file in dropped_files {
if let Some(path) = file.path {
self.image_sequence.push(Image::new(path));
}
}
}
}
});
});
});
}
Expand Down Expand Up @@ -189,7 +215,7 @@ fn preview_files_being_dropped(ctx: &egui::Context) {
}
}

fn path_buf_to_string(path_buf: &PathBuf) -> String {
fn path_buf_to_filename_string(path_buf: &PathBuf) -> String {
path_buf
.file_name()
.expect("selected file terminated in `..` or was empty")
Expand Down

0 comments on commit 63b0f17

Please sign in to comment.