Compare commits

...

5 Commits

Author SHA1 Message Date
Jaby aae57e47e4 Implement writing image and half the platte information 2025-03-19 22:01:32 +01:00
Jaby d1d590d32a Save tim_project files 2025-03-19 21:32:18 +01:00
Jaby 18e6800e0f Implement browsing project save location 2025-03-19 21:17:26 +01:00
Jaby d42fe2776c Serialize project to json 2025-03-19 20:47:21 +01:00
Jaby 7a197d9b71 Create a simple Job 2025-03-19 20:21:45 +01:00
9 changed files with 139 additions and 36 deletions

View File

@ -3677,9 +3677,9 @@ dependencies = [
[[package]] [[package]]
name = "serde_json" name = "serde_json"
version = "1.0.137" version = "1.0.140"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "930cfb6e6abf99298aaad7d29abbef7a9999a9a8806a40088f55f0dcec03146b" checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373"
dependencies = [ dependencies = [
"itoa", "itoa",
"memchr", "memchr",
@ -4113,6 +4113,8 @@ version = "0.1.0"
dependencies = [ dependencies = [
"png", "png",
"rfd", "rfd",
"serde",
"serde_json",
"slint", "slint",
"slint-build", "slint-build",
"tiny-skia", "tiny-skia",

View File

@ -11,6 +11,8 @@ png = "0.17.16"
rfd = "0.15.2" rfd = "0.15.2"
slint = "1.9.2" slint = "1.9.2"
tiny-skia = "0.11.4" tiny-skia = "0.11.4"
serde = {version = "1.0.140", features = ["derive"]}
serde_json = "1.0"
tool_helper = {path = "../tool_helper"} tool_helper = {path = "../tool_helper"}
[build-dependencies] [build-dependencies]

View File

@ -1,10 +1,10 @@
use std::fmt::format; use std::{fs::File, io::Write};
use crate::{gui::{main_tab::MainTab, MutexTIMManager, VRAM_HEIGHT, VRAM_WIDTH}, MainWindow, VRAMInfo}; use crate::{gui::{self, main_tab::MainTab, MutexTIMManager, VRAM_HEIGHT, VRAM_WIDTH}, MainWindow};
use super::FileTab; use super::FileTab;
use rfd::FileDialog; use rfd::FileDialog;
use slint::SharedString; use slint::SharedString;
use tim_tool::logic::{project::{Job, Project}, tim::{self, types::Encoding}}; use tim_tool::logic::{project::{Job, PaletteRect, Project}, tim::types::Encoding};
use tool_helper::Error; use tool_helper::Error;
pub(super) fn on_browse_file(tim_manager: MutexTIMManager) -> impl FnMut(&mut FileTab, &MainWindow) -> Result<(), Error> + 'static { pub(super) fn on_browse_file(tim_manager: MutexTIMManager) -> impl FnMut(&mut FileTab, &MainWindow) -> Result<(), Error> + 'static {
@ -34,7 +34,7 @@ pub(super) fn on_browse_file(tim_manager: MutexTIMManager) -> impl FnMut(&mut Fi
}; };
} }
pub(super) fn on_update_palette_size(tim_manager: MutexTIMManager) -> impl FnMut(&mut FileTab, &MainWindow, u32, u32) -> Result<(), Error> + 'static { pub(super) fn on_update_palette_size(tim_manager: MutexTIMManager) -> impl FnMut(&mut FileTab, &MainWindow, u16, u16) -> Result<(), Error> + 'static {
return move |file_tab, _main_window, width, height| -> Result<(), Error> { return move |file_tab, _main_window, width, height| -> Result<(), Error> {
file_tab.update_palette(tim_manager.lock().expect("VRAM already locked").change_unadded_tim_palette_size(width, height)?); file_tab.update_palette(tim_manager.lock().expect("VRAM already locked").change_unadded_tim_palette_size(width, height)?);
Ok(()) Ok(())
@ -68,7 +68,12 @@ pub(super) fn on_load_project_clicked() -> impl FnMut(&mut FileTab, &MainWindow)
} }
pub(super) fn on_save_project_clicked(tim_manager: MutexTIMManager) -> impl FnMut(&mut FileTab, &MainTab, &MainWindow) -> Result<(), Error> + 'static { pub(super) fn on_save_project_clicked(tim_manager: MutexTIMManager) -> impl FnMut(&mut FileTab, &MainTab, &MainWindow) -> Result<(), Error> + 'static {
move |_file_tab, main_tab, _main_window| { move |_file_tab, main_tab, main_window| {
let save_location = main_window.get_project_widget_save_project_path();
if save_location.is_empty() {
return Err(Error::from_str("Please specify save location for project file"));
}
let tim_info = tim_manager.lock().expect("VRAM already locked").clone_added_tims(); let tim_info = tim_manager.lock().expect("VRAM already locked").clone_added_tims();
let vram_info = main_tab.clone_vram_info(); let vram_info = main_tab.clone_vram_info();
@ -77,7 +82,7 @@ pub(super) fn on_save_project_clicked(tim_manager: MutexTIMManager) -> impl FnMu
} }
let mut cur_job = None; let mut cur_job = None;
let mut project = Project::new(tim_info.into_iter().zip(vram_info.into_iter()).filter_map(|(tim, vram)| { let mut project = Project::new(tim_info.into_iter().zip(vram_info.into_iter()).filter_map(|(tim, (file_name, vram))| {
if vram.is_palette { if vram.is_palette {
// Add palette to cur_job // Add palette to cur_job
None None
@ -87,7 +92,14 @@ pub(super) fn on_save_project_clicked(tim_manager: MutexTIMManager) -> impl FnMu
// We are done with the old job // We are done with the old job
let prev_job = std::mem::replace(&mut cur_job, None); let prev_job = std::mem::replace(&mut cur_job, None);
cur_job = Some(Job::new("Planschi".to_owned(), std::path::PathBuf::from("value"))); if let Some(tim) = tim {
let mut job = Job::new(file_name, tim.get_path(), (vram.x as u16, vram.y as u16));
if let Some((width, height)) = tim.get_palette_size() {
job.add_palette(PaletteRect::new(0, 0, width, height));
}
cur_job = Some(job);
}
prev_job prev_job
} }
}).collect()); }).collect());
@ -95,7 +107,18 @@ pub(super) fn on_save_project_clicked(tim_manager: MutexTIMManager) -> impl FnMu
project.push(cur_job); project.push(cur_job);
} }
Err(Error::from_text(format!("Adding {} jobs", project.len()))) let json_content = match serde_json::to_string(&project) {
Ok(json_content) => json_content,
Err(error) => {
return Err(Error::from_error(error));
}
};
let mut file = File::create(save_location)?;
file.write_all(json_content.as_bytes())?;
gui::display_information("Saving project", "Project saved successfully");
Ok(())
} }
} }
@ -106,7 +129,19 @@ pub(super) fn on_browse_open_project_clicked() -> impl FnMut(&mut FileTab, &Main
} }
pub(super) fn on_browse_save_project_clicked() -> impl FnMut(&mut FileTab, &MainWindow) -> Result<(), Error> + 'static { pub(super) fn on_browse_save_project_clicked() -> impl FnMut(&mut FileTab, &MainWindow) -> Result<(), Error> + 'static {
move |_file_tab, _main_window| { move |_file_tab, main_window| {
Err(Error::not_implemented("on_browse_save_project_clicked")) let file_path = FileDialog::new()
.add_filter("TIM project file (.tim_project)", &["tim_project"])
.add_filter("JSON file (.json; .jsn)", &["json", "jsn"])
.add_filter("All", &["*"])
.set_title("Save location for TIM project file")
.save_file();
if let Some(file_path) = file_path {
if let Some(file_path) = file_path.to_str() {
main_window.set_project_widget_save_project_path(SharedString::from(file_path));
}
}
Ok(())
} }
} }

View File

@ -60,7 +60,7 @@ impl FileTab {
let (cloned_object, cloned_main_window, mut function) = (object.clone(), main_window.clone(), callbacks::on_update_palette_size(tim_manager.clone())); let (cloned_object, cloned_main_window, mut function) = (object.clone(), main_window.clone(), callbacks::on_update_palette_size(tim_manager.clone()));
main_window.borrow().on_file_tab_update_palette_size(move |width, height| { main_window.borrow().on_file_tab_update_palette_size(move |width, height| {
if let Err(error) = function(&mut cloned_object.borrow_mut(), &cloned_main_window.borrow(), width as u32, height as u32) { if let Err(error) = function(&mut cloned_object.borrow_mut(), &cloned_main_window.borrow(), width as u16, height as u16) {
display_error("Updating palette failed", &error.to_string()); display_error("Updating palette failed", &error.to_string());
} }
}); });

View File

@ -155,13 +155,11 @@ impl MainTab {
} }
} }
pub fn clone_vram_info(&self) -> Vec<VRAMInfo> { pub fn clone_vram_info(&self) -> Vec<(String, VRAMInfo)> {
let vram_data = self.vram.lock().expect("VRAM already locked"); let vram_data = self.vram.lock().expect("VRAM already locked");
let mut infos = Vec::new();
for vram_data in vram_data.info.iter() { vram_data.info.iter().zip(vram_data.file_list.iter()).map(|(vram_data, file_name)| {
infos.push(vram_data.info.clone()); (file_name.text.to_string(), vram_data.info.clone())
} }).collect()
infos
} }
} }

View File

@ -1,16 +1,68 @@
use serde::{Deserialize, Serialize};
use std::path::PathBuf; use std::path::PathBuf;
#[derive(Serialize, Deserialize)]
struct ImagePosition {
pub x: u16,
pub y: u16,
}
impl std::default::Default for ImagePosition {
fn default() -> Self {
ImagePosition{x: 0, y: 0}
}
}
#[derive(Serialize, Deserialize)]
struct ImageSize {
pub width: u16,
pub height: u16,
}
impl std::default::Default for ImageSize {
fn default() -> Self {
ImageSize{width: 0, height: 0}
}
}
#[derive(Serialize, Deserialize)]
pub struct PaletteRect {
pos: ImagePosition,
size: ImageSize,
}
impl PaletteRect {
pub fn new(x: u16, y: u16, width: u16, height: u16) -> PaletteRect {
PaletteRect{pos: ImagePosition{x, y}, size: ImageSize{width, height}}
}
}
impl std::default::Default for PaletteRect {
fn default() -> Self {
PaletteRect{pos: ImagePosition::default(), size: ImageSize::default()}
}
}
#[derive(Serialize, Deserialize)]
pub struct Job { pub struct Job {
name: String, name: String,
file_path: PathBuf, file_path: PathBuf,
image_pos: ImagePosition,
palette_rect: PaletteRect,
} }
impl Job { impl Job {
pub fn new(name: String, file_path: PathBuf) -> Job { pub fn new(name: String, file_path: PathBuf, image_pos: (u16, u16)) -> Job {
Job{name, file_path} Job{name, file_path, image_pos: ImagePosition{x: image_pos.0, y: image_pos.1}, palette_rect: PaletteRect::default()}
}
pub fn add_palette(&mut self, palette_rect: PaletteRect) -> &mut Self {
self.palette_rect = palette_rect;
self
} }
} }
#[derive(Serialize, Deserialize)]
pub struct Project { pub struct Project {
jobs: Vec<Job> jobs: Vec<Job>
} }

View File

@ -7,7 +7,7 @@ use types::Encoding;
#[derive(Clone)] #[derive(Clone)]
pub struct TIMInfo { pub struct TIMInfo {
_path: PathBuf, path: PathBuf,
image_data: SharedPixelBuffer<Rgba8Pixel>, image_data: SharedPixelBuffer<Rgba8Pixel>,
palette: Option<PaletteInfo>, palette: Option<PaletteInfo>,
} }
@ -53,7 +53,7 @@ impl TIMInfo {
_ => {return Err(Error::from_str("Only 4 and 8bit color depth are supported for indexed color images"));} _ => {return Err(Error::from_str("Only 4 and 8bit color depth are supported for indexed color images"));}
} }
} }
Ok(TIMInfo{_path: path.clone(), image_data, palette: Some(PaletteInfo::new(palette_colors))}) Ok(TIMInfo{path: path.clone(), image_data, palette: Some(PaletteInfo::new(palette_colors))})
} }
else { else {
@ -71,11 +71,11 @@ impl TIMInfo {
_ => {return Err(Error::from_str("Only 8bit color depth are supported for direct color images"));} _ => {return Err(Error::from_str("Only 8bit color depth are supported for direct color images"));}
} }
} }
Ok(TIMInfo{_path: path.clone(), image_data, palette: None}) Ok(TIMInfo{path: path.clone(), image_data, palette: None})
} }
} }
pub fn change_palette_size(&mut self, width: u32, height: u32) -> Result<Option<slint::Image>, Error> { pub fn change_palette_size(&mut self, width: u16, height: u16) -> Result<Option<slint::Image>, Error> {
if let Some(palette) = &mut self.palette { if let Some(palette) = &mut self.palette {
if width == 0 || height == 0 { if width == 0 || height == 0 {
return Err(Error::from_text(format!("{}px x {}px is not a valid size for palette", width, height))); return Err(Error::from_text(format!("{}px x {}px is not a valid size for palette", width, height)));
@ -118,13 +118,27 @@ impl TIMInfo {
None None
}) })
} }
pub fn get_palette_size(&self) -> Option<(u16, u16)> {
if let Some(palette_info) = &self.palette {
Some((palette_info.width, palette_info.height))
}
else {
None
}
}
pub fn get_path(&self) -> std::path::PathBuf {
self.path.clone()
}
} }
#[derive(Clone)] #[derive(Clone)]
struct PaletteInfo { struct PaletteInfo {
data: Vec<Rgba8Pixel>, data: Vec<Rgba8Pixel>,
width: u32, width: u16,
height: u32, height: u16,
} }
impl PaletteInfo { impl PaletteInfo {
@ -134,7 +148,7 @@ impl PaletteInfo {
} }
pub fn get_image(&self) -> slint::Image { pub fn get_image(&self) -> slint::Image {
let mut image_data = SharedPixelBuffer::new(self.width, self.height); let mut image_data = SharedPixelBuffer::new(self.width as u32, self.height as u32);
let dst_pixels = image_data.make_mut_slice(); let dst_pixels = image_data.make_mut_slice();
for (idx, byte) in dst_pixels.iter_mut().enumerate() { for (idx, byte) in dst_pixels.iter_mut().enumerate() {

View File

@ -60,7 +60,7 @@ impl TIMManager {
} }
} }
pub fn change_unadded_tim_palette_size(&mut self, width: u32, height: u32) -> Result<Option<Image>, Error> { pub fn change_unadded_tim_palette_size(&mut self, width: u16, height: u16) -> Result<Option<Image>, Error> {
if let Some(unadded_tim) = &mut self.unadded_tim { if let Some(unadded_tim) = &mut self.unadded_tim {
unadded_tim.change_palette_size(width, height) unadded_tim.change_palette_size(width, height)
} }

View File

@ -11,7 +11,7 @@ struct VRAMInfo {
y: int, y: int,
encoding_str: string, encoding_str: string,
palette_count: int, palette_count: int,
is_palette: bool, is_palette: bool, // < Can we combine the palette into this, instead of having it separate?
} }
struct VRAMData { struct VRAMData {