From 9f7f9f6ab71e669458411b7b2d902d1700d92c72 Mon Sep 17 00:00:00 2001 From: Filipe Rodrigues Date: Sat, 29 May 2021 22:00:27 +0100 Subject: [PATCH] Moved `PreviewPanel` to it's own module. --- dcb-tools/dcb-file-editor/src/main.rs | 171 ++---------------- .../dcb-file-editor/src/preview_panel.rs | 167 +++++++++++++++++ 2 files changed, 180 insertions(+), 158 deletions(-) create mode 100644 dcb-tools/dcb-file-editor/src/preview_panel.rs diff --git a/dcb-tools/dcb-file-editor/src/main.rs b/dcb-tools/dcb-file-editor/src/main.rs index 21069a0..737e429 100644 --- a/dcb-tools/dcb-file-editor/src/main.rs +++ b/dcb-tools/dcb-file-editor/src/main.rs @@ -12,24 +12,17 @@ )] // Modules +pub mod preview_panel; pub mod tree; // Imports use anyhow::Context; use dcb_cdrom_xa::CdRomCursor; use dcb_io::{game_file::Path, GameFile}; -use dcb_tim::{Tim, Tis}; -use eframe::{ - egui::{self, Color32, TextureId}, - epi, NativeOptions, -}; +use eframe::{egui, epi, NativeOptions}; use native_dialog::{FileDialog, MessageDialog, MessageType}; -use std::{ - fs, - io::{BufReader, Write}, - mem, - path::PathBuf, -}; +use preview_panel::PreviewPanel; +use std::{fs, io::Write, mem, path::PathBuf}; use tree::FsTree; fn main() { @@ -204,138 +197,25 @@ impl epi::App for FileEditor { .show(ui, |ui| loaded_game.p_tree.display(ui, "P:\\", &mut display_ctx)); if let Some(path) = preview_path { - let panel: Result<_, anyhow::Error> = match path { - path if path.ends_with(".TIM") => { - try { - // Deserialize the tim - let path = Path::from_ascii(&path).context("Unable to create path")?; - let mut file = - loaded_game.game_file.open_file(path).context("Unable to open file")?; - let image: Tim = Tim::deserialize(&mut file).context("Unable to parse file")?; - - // Then create a texture with it - let [width, height] = image.size(); - let textures = (0..image.pallettes()) - .map(|pallette| { - let colors: Box<[_]> = image - .colors(Some(pallette)) - .context("Unable to get image colors")? - .to_vec() - .into_iter() - .map(|[r, g, b, a]| Color32::from_rgba_premultiplied(r, g, b, a)) - .collect(); - let texture_id = frame - .tex_allocator() - .alloc_srgba_premultiplied((width, height), &*colors); - Ok(texture_id) - }) - .collect::>()?; - - - Some(PreviewPanel::Tim { image, textures }) - } - }, - path if path.ends_with(".TIS") => { - try { - // Deserialize the tis - let path = Path::from_ascii(&path).context("Unable to create path")?; - let file = loaded_game.game_file.open_file(path).context("Unable to open file")?; - let mut file = BufReader::new(file); - let images: Tis = Tis::deserialize(&mut file).context("Unable to parse file")?; - - // Then create all textures - let images = images - .tims - .into_iter() - .map(|image| { - // Create a texture with it - let [width, height] = image.size(); - let textures = (0..image.pallettes()) - .map(|pallette| { - let colors: Box<[_]> = image - .colors(Some(pallette)) - .context("Unable to get image colors")? - .to_vec() - .into_iter() - .map(|[r, g, b, a]| { - Color32::from_rgba_premultiplied(r, g, b, a) - }) - .collect(); - let texture_id = frame - .tex_allocator() - .alloc_srgba_premultiplied((width, height), &*colors); - Ok(texture_id) - }) - .collect::>()?; - - Ok((image, textures)) - }) - .collect::, anyhow::Error>>()?; - - Some(PreviewPanel::Tis { images }) - } - }, - _ => Ok(None), - }; + let panel = PreviewPanel::new(loaded_game, &path, frame.tex_allocator()) + .context("Unable to preview file"); // Drop previous images, if they exist - #[allow(clippy::single_match)] // We'll have more in the future - match &*preview_panel { - Some(PreviewPanel::Tim { textures, .. }) => { - for &texture_id in textures { - frame.tex_allocator().free(texture_id); - } - }, - Some(PreviewPanel::Tis { images }) => { - for (_, textures) in images { - for &texture_id in textures { - frame.tex_allocator().free(texture_id); - } - } - }, - _ => (), + if let Some(preview_panel) = preview_panel { + preview_panel.drop_textures(frame.tex_allocator()); } - match panel { - Ok(panel) => *preview_panel = panel, - Err(err) => { - *preview_panel = Some(PreviewPanel::Error { - err: format!("{err:?}"), - }) - }, - } + *preview_panel = panel.unwrap_or_else(|err| { + let err = format!("{err:?}"); + Some(PreviewPanel::Error { err }) + }); } }); } }); if let Some(preview_panel) = preview_panel { - egui::CentralPanel::default().show(ctx, |ui| match &*preview_panel { - PreviewPanel::Tim { textures, image } => { - egui::ScrollArea::auto_sized().show(ui, |ui| { - for &texture_id in textures { - ui.image(texture_id, image.size().map(|dim| dim as f32)); - ui.separator(); - } - }); - }, - PreviewPanel::Tis { images } => { - egui::ScrollArea::auto_sized().show(ui, |ui| { - for (image, textures) in images { - for &texture_id in textures { - ui.image(texture_id, image.size().map(|dim| dim as f32)); - ui.separator(); - } - } - }); - }, - PreviewPanel::Error { err } => { - ui.group(|ui| { - ui.heading("Error"); - ui.label(err); - }); - }, - }); + preview_panel.display(ctx); } if let (Some(swap_window), Some(loaded_game)) = (swap_window, loaded_game) { @@ -424,31 +304,6 @@ pub struct SwapWindow { second: SwapFileStatus, } -/// Preview panel -#[derive(PartialEq, Clone)] -pub enum PreviewPanel { - /// Tim image - Tim { - /// Image - image: Tim, - - /// All textures - textures: Vec, - }, - - /// Tim collection - Tis { - /// Images - images: Vec<(Tim, Vec)>, - }, - - /// Error - Error { - /// Error - err: String, - }, -} - /// Status of a file being swapped #[derive(PartialEq, Clone)] pub enum SwapFileStatus { diff --git a/dcb-tools/dcb-file-editor/src/preview_panel.rs b/dcb-tools/dcb-file-editor/src/preview_panel.rs new file mode 100644 index 0000000..779294c --- /dev/null +++ b/dcb-tools/dcb-file-editor/src/preview_panel.rs @@ -0,0 +1,167 @@ +//! Preview panel + +// Imports +use crate::LoadedGame; +use anyhow::Context; +use dcb_io::game_file::Path; +use dcb_tim::{Tim, Tis}; +use eframe::{ + egui::{self, Color32, TextureId}, + epi::TextureAllocator, +}; +use std::io::BufReader; + +/// Preview panel +#[derive(PartialEq, Clone)] +pub enum PreviewPanel { + /// Tim image + Tim { + /// Image + image: Tim, + + /// All textures + pallettes: Vec, + }, + + /// Tim collection + Tis { + /// Images + images: Vec<(Tim, Vec)>, + }, + + /// Error + Error { + /// Error + err: String, + }, +} + +impl PreviewPanel { + /// Creates a preview panel + pub fn new( + loaded_game: &mut LoadedGame, path: &str, tex_allocator: &mut dyn TextureAllocator, + ) -> Result, anyhow::Error> { + let panel = match path { + path if path.ends_with(".TIM") => { + // Deserialize the tim + let path = Path::from_ascii(&path).context("Unable to create path")?; + let mut file = loaded_game.game_file.open_file(path).context("Unable to open file")?; + let image: Tim = Tim::deserialize(&mut file).context("Unable to parse file")?; + + // Then create all pallettes + let pallettes = create_image_pallettes(&image, tex_allocator)?; + + Self::Tim { image, pallettes } + }, + path if path.ends_with(".TIS") => { + // Deserialize the tis + let path = Path::from_ascii(&path).context("Unable to create path")?; + let file = loaded_game.game_file.open_file(path).context("Unable to open file")?; + let mut file = BufReader::new(file); + let images: Tis = Tis::deserialize(&mut file).context("Unable to parse file")?; + + // Then create all images + let images = images + .tims + .into_iter() + .map(|image| { + let pallettes = create_image_pallettes(&image, tex_allocator) + .context("Unable to create image pallettes")?; + + Ok((image, pallettes)) + }) + .collect::, anyhow::Error>>()?; + + Self::Tis { images } + }, + _ => return Ok(None), + }; + + Ok(Some(panel)) + } + + /// Drops all textures in this panel + pub fn drop_textures(&mut self, tex_allocator: &mut dyn TextureAllocator) { + match self { + PreviewPanel::Tim { pallettes, .. } => { + for texture_id in pallettes.drain(..) { + tex_allocator.free(texture_id); + } + }, + PreviewPanel::Tis { images } => { + for (_, textures) in images { + for texture_id in textures.drain(..) { + tex_allocator.free(texture_id); + } + } + }, + _ => (), + } + } + + /// Displays this panel + pub fn display(&self, ctx: &egui::CtxRef) { + egui::CentralPanel::default().show(ctx, |ui| match self { + Self::Tim { pallettes, image } => { + egui::ScrollArea::auto_sized().show(ui, |ui| { + for &texture_id in pallettes { + ui.image(texture_id, image.size().map(|dim| dim as f32)); + ui.separator(); + } + }); + }, + Self::Tis { images } => { + egui::ScrollArea::auto_sized().show(ui, |ui| { + for (image, pallettes) in images { + for &texture_id in pallettes { + ui.image(texture_id, image.size().map(|dim| dim as f32)); + ui.separator(); + } + } + }); + }, + Self::Error { err } => { + ui.group(|ui| { + ui.heading("Error"); + ui.label(err); + }); + }, + }); + } +} + +impl Drop for PreviewPanel { + fn drop(&mut self) { + // Make sure we don't have any textures remaining + match self { + PreviewPanel::Tim { pallettes, .. } => assert_eq!(pallettes.len(), 0), + PreviewPanel::Tis { images } => { + for (_, pallettes) in images { + assert_eq!(pallettes.len(), 0) + } + }, + PreviewPanel::Error { .. } => {}, + }; + } +} + +/// Creates all pallettes for an image +fn create_image_pallettes( + image: &Tim, tex_allocator: &mut dyn TextureAllocator, +) -> Result, anyhow::Error> { + let [width, height] = image.size(); + let textures = (0..image.pallettes()) + .map(|pallette| { + let colors: Box<[_]> = image + .colors(Some(pallette)) + .context("Unable to get image colors")? + .to_vec() + .into_iter() + .map(|[r, g, b, a]| Color32::from_rgba_premultiplied(r, g, b, a)) + .collect(); + let texture_id = tex_allocator.alloc_srgba_premultiplied((width, height), &*colors); + Ok(texture_id) + }) + .collect::>()?; + Ok(textures) +}