topic/jb/tim_tool/risize_image #18
|
@ -1,32 +1,78 @@
|
|||
use crate::MainWindow;
|
||||
use super::{GUIElements, GUIElementsRef, MainWindowRef, display_error};
|
||||
use slint::Image;
|
||||
use slint::{Image, SharedString};
|
||||
use std::rc::Rc;
|
||||
use tim_tool::logic::tim::Encoding;
|
||||
use tool_helper::Error;
|
||||
|
||||
pub struct FileTab {
|
||||
main_window: MainWindowRef
|
||||
main_window: MainWindowRef,
|
||||
encoding_options: Rc<slint::VecModel<SharedString>>
|
||||
}
|
||||
|
||||
impl FileTab {
|
||||
fn update_encoding_options(&self, width: u32, height: u32, has_palette: bool) -> Result<&str, Error> {
|
||||
self.encoding_options.clear();
|
||||
|
||||
if has_palette {
|
||||
let mut selected_str = Encoding::EightBit.to_str();
|
||||
let has_4bit = width%4 == 0;
|
||||
let has_8bit = width%2 == 0;
|
||||
|
||||
if !has_4bit && !has_8bit {
|
||||
return Err(Error::from_text(format!("Image width must be multiple of 2 and or 4 ({}/{})", width, height)));
|
||||
}
|
||||
|
||||
if has_4bit {
|
||||
self.encoding_options.push(SharedString::from(format!("{} ({}/{})", Encoding::FourBit.to_str(), width/4, height)));
|
||||
selected_str = Encoding::FourBit.to_str();
|
||||
}
|
||||
|
||||
if has_8bit {
|
||||
self.encoding_options.push(SharedString::from(format!("{} ({}/{})", Encoding::EightBit.to_str(), width/2, height)));
|
||||
}
|
||||
Ok(selected_str)
|
||||
}
|
||||
|
||||
else {
|
||||
self.encoding_options.push(SharedString::from(format!("{} ({}/{})", Encoding::FullColor.to_str(), width, height)));
|
||||
Ok(Encoding::FullColor.to_str())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn new(main_window: MainWindowRef) -> FileTab {
|
||||
FileTab{main_window}
|
||||
let encoding_options = Rc::new(slint::VecModel::from(Vec::<SharedString>::new()));
|
||||
|
||||
main_window.borrow().set_file_tab_encoding_options(encoding_options.clone().into());
|
||||
FileTab{main_window, encoding_options}
|
||||
}
|
||||
|
||||
pub fn clear_load(&self) {
|
||||
let main_window = self.main_window.borrow();
|
||||
|
||||
self.encoding_options.clear();
|
||||
main_window.set_file_tab_browse_path("".into());
|
||||
main_window.set_file_tab_image_data(Image::default());
|
||||
main_window.set_file_tab_image_width(0);
|
||||
main_window.set_file_tab_image_height(0);
|
||||
main_window.set_file_tab_palette_data(Image::default());
|
||||
main_window.set_file_tab_palette_width(0);
|
||||
main_window.set_file_tab_palette_height(0);
|
||||
main_window.set_file_tab_image_name("".into());
|
||||
main_window.set_file_tab_enable(false);
|
||||
}
|
||||
|
||||
pub fn update_new_loaded_file(&self, file_name: Option<String>, image: Image, palette: Option<Image>) {
|
||||
pub fn update_new_loaded_file(&self, file_name: Option<String>, image: Image, palette: Option<Image>) -> Result<(), Error> {
|
||||
let has_palette = palette.is_some();
|
||||
|
||||
self.update_palette(palette);
|
||||
|
||||
let main_window = self.main_window.borrow();
|
||||
let image_size = image.size();
|
||||
|
||||
main_window.set_file_tab_image_data(image);
|
||||
main_window.set_file_tab_image_width(image_size.width as i32);
|
||||
main_window.set_file_tab_image_height(image_size.height as i32);
|
||||
if let Some(file_name) = file_name {
|
||||
main_window.set_file_tab_image_name(file_name.into());
|
||||
}
|
||||
|
@ -34,7 +80,10 @@ impl FileTab {
|
|||
else {
|
||||
main_window.set_file_tab_image_name("".into());
|
||||
}
|
||||
|
||||
main_window.set_file_tab_selected_encoding(SharedString::from(self.update_encoding_options(image_size.width, image_size.height, has_palette)?));
|
||||
main_window.set_file_tab_enable(true);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn update_palette(&self, palette: Option<Image>) {
|
||||
|
@ -60,6 +109,26 @@ impl FileTab {
|
|||
self.main_window.borrow().get_file_tab_image_name().to_string()
|
||||
}
|
||||
|
||||
pub fn get_encoding(&self) -> Result<Encoding, Error> {
|
||||
let selected_encoding = self.main_window.borrow().get_file_tab_selected_encoding();
|
||||
|
||||
if selected_encoding.starts_with(Encoding::FourBit.to_str()) {
|
||||
Ok(Encoding::FourBit)
|
||||
}
|
||||
|
||||
else if selected_encoding.starts_with(Encoding::EightBit.to_str()) {
|
||||
Ok(Encoding::EightBit)
|
||||
}
|
||||
|
||||
else if selected_encoding.starts_with(Encoding::FullColor.to_str()) {
|
||||
Ok(Encoding::FullColor)
|
||||
}
|
||||
|
||||
else {
|
||||
Err(Error::from_str("Failed to obtain encoding"))
|
||||
}
|
||||
}
|
||||
|
||||
pub fn on_browse_file(&self, gui_elements: GUIElementsRef, mut function: impl FnMut(&mut GUIElements, &MainWindow) -> Result<(), Error> + 'static) {
|
||||
let main_window_cloned = self.main_window.clone();
|
||||
let gui_cloned = gui_elements.clone();
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
use crate::{gui::{VRAM_HEIGHT, VRAM_WIDTH}, MainWindow, VRAMImage};
|
||||
use super::{GUIElementsRef, MainWindowRef};
|
||||
|
||||
use slint::Model;
|
||||
use slint::{Model, SharedString};
|
||||
use tim_tool::logic::tim::Encoding;
|
||||
use std::{rc::Rc, sync::{Arc, Mutex}};
|
||||
|
||||
struct VRAM {
|
||||
|
@ -26,13 +27,15 @@ impl MainTab {
|
|||
MainTab{main_window, vram: Arc::new(Mutex::new(VRAM{file_list: vram_file_list, image_list: vram_image_list}))}
|
||||
}
|
||||
|
||||
pub fn add_new_vram_file(&mut self, file_name: &String, image: slint::Image, image_palette: Option<slint::Image>) {
|
||||
pub fn add_new_vram_file(&mut self, file_name: &String, image: slint::Image, encoding: Encoding, image_palette: Option<slint::Image>) {
|
||||
let vram_data = self.vram.lock().expect("VRAM already locked");
|
||||
let add_new_image = |file_name: &String, image: slint::Image, palette_count: i32, is_palette: bool| {
|
||||
let add_new_image = |file_name: &String, image: slint::Image, encoding: Encoding, palette_count: i32, is_palette: bool| {
|
||||
let encoding_str = if is_palette {"<Palette>"} else {encoding.to_str()};
|
||||
let vram_image = VRAMImage{
|
||||
img: image,
|
||||
x: 0,
|
||||
y: 0,
|
||||
encoding_str: SharedString::from(encoding_str),
|
||||
palette_count,
|
||||
is_palette,
|
||||
};
|
||||
|
@ -41,10 +44,10 @@ impl MainTab {
|
|||
vram_data.image_list.push(vram_image);
|
||||
};
|
||||
|
||||
add_new_image(file_name, image, if image_palette.is_some() {1} else {0}, false);
|
||||
add_new_image(file_name, image, encoding, if image_palette.is_some() {1} else {0}, false);
|
||||
if let Some(image_palette) = image_palette {
|
||||
let file_name = " => ".to_owned() + file_name.as_str() + " (Palette)";
|
||||
add_new_image(&file_name, image_palette, 0, true);
|
||||
add_new_image(&file_name, image_palette, encoding, 0, true);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -2,7 +2,7 @@ pub mod tim;
|
|||
|
||||
use std::path::PathBuf;
|
||||
use slint::Image;
|
||||
use tim::TIMInfo;
|
||||
use tim::{Encoding, TIMInfo};
|
||||
use tool_helper::Error;
|
||||
|
||||
pub struct Logic {
|
||||
|
@ -16,18 +16,18 @@ impl Logic {
|
|||
|
||||
pub fn set_unadded_tim(&mut self, path: &PathBuf) -> Result<(Image, Option<Image>), Error> {
|
||||
let tim_info = TIMInfo::from_image(path)?;
|
||||
let image = tim_info.get_slint_images();
|
||||
let image = tim_info.get_slint_images(Encoding::FullColor);
|
||||
|
||||
self.unadded_tim = Some(tim_info);
|
||||
Ok(image)
|
||||
}
|
||||
|
||||
pub fn add_unadded_tim_as(&mut self, _name: &String) -> Result<(Image, Option<Image>), Error> {
|
||||
pub fn add_unadded_tim_as(&mut self, _name: &String, encoding: Encoding) -> Result<(Image, Option<Image>), Error> {
|
||||
if let Some(unadded_tim) = &self.unadded_tim {
|
||||
let image = unadded_tim.get_slint_images();
|
||||
let (image, palette) = unadded_tim.get_slint_images(encoding);
|
||||
|
||||
self.unadded_tim = None;
|
||||
Ok(image)
|
||||
Ok((image, palette))
|
||||
}
|
||||
|
||||
else {
|
||||
|
|
|
@ -2,6 +2,27 @@ use std::{fs::File, path::PathBuf};
|
|||
use slint::{Rgba8Pixel, SharedPixelBuffer};
|
||||
use tool_helper::Error;
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
pub enum Encoding {
|
||||
FourBit,
|
||||
EightBit,
|
||||
FullColor,
|
||||
}
|
||||
|
||||
impl Encoding {
|
||||
const FOUR_BIT_NAME:&str = "4 bit";
|
||||
const EIGHT_BIT_NAME:&str = "8 bit";
|
||||
const FULL_COLOR_NAME:&str = "Full color";
|
||||
|
||||
pub const fn to_str(&self) -> &str {
|
||||
match self {
|
||||
Encoding::FourBit => Self::FOUR_BIT_NAME,
|
||||
Encoding::EightBit => Self::EIGHT_BIT_NAME,
|
||||
Encoding::FullColor => Self::FULL_COLOR_NAME,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct TIMInfo {
|
||||
image_data: SharedPixelBuffer<Rgba8Pixel>,
|
||||
palette: Option<PaletteInfo>,
|
||||
|
@ -86,8 +107,28 @@ impl TIMInfo {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn get_slint_images(&self) -> (slint::Image, Option<slint::Image>) {
|
||||
(slint::Image::from_rgba8_premultiplied(self.image_data.clone()), if let Some(palette) = &self.palette {
|
||||
pub fn get_slint_images(&self, encoding: Encoding) -> (slint::Image, Option<slint::Image>) {
|
||||
fn scaled_copy(src: &SharedPixelBuffer<Rgba8Pixel>, factor: u32) -> SharedPixelBuffer<Rgba8Pixel> {
|
||||
let mut image_data = SharedPixelBuffer::<Rgba8Pixel>::new(src.width()/factor, src.height());
|
||||
let mut dst_pixel = image_data.make_mut_slice();
|
||||
let src_pixel = src.as_slice();
|
||||
|
||||
for (idx, pixel) in src_pixel.into_iter().enumerate() {
|
||||
if idx%factor as usize == 0 {
|
||||
dst_pixel[0] = *pixel;
|
||||
dst_pixel = &mut dst_pixel[1..];
|
||||
}
|
||||
}
|
||||
image_data
|
||||
}
|
||||
|
||||
let image_data = match encoding {
|
||||
Encoding::FourBit => scaled_copy(&self.image_data, 4),
|
||||
Encoding::EightBit => scaled_copy(&self.image_data, 2),
|
||||
Encoding::FullColor => self.image_data.clone()
|
||||
};
|
||||
|
||||
(slint::Image::from_rgba8_premultiplied(image_data), if let Some(palette) = &self.palette {
|
||||
Some(palette.get_image())
|
||||
} else {
|
||||
None
|
||||
|
|
|
@ -72,7 +72,7 @@ fn setup_file_tab(gui_elements_ref: Rc<RefCell<GUIElements>>, logic_ref: Rc<RefC
|
|||
if img_size.width > VRAM_WIDTH as u32 || img_size.height > VRAM_HEIGHT as u32 {
|
||||
return Err(Error::from_text(format!("Image size ({}; {}) is to big for VRAM ({}, {})", img_size.width, img_size.height, VRAM_WIDTH, VRAM_HEIGHT)));
|
||||
}
|
||||
file_tab.update_new_loaded_file(file_name, image, palette);
|
||||
return file_tab.update_new_loaded_file(file_name, image, palette);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
@ -84,9 +84,10 @@ fn setup_file_tab(gui_elements_ref: Rc<RefCell<GUIElements>>, logic_ref: Rc<RefC
|
|||
let file_tab = &gui_elements.file_tab;
|
||||
|
||||
let file_name = file_tab.get_file_name();
|
||||
let (image, palette_image) = logic.borrow_mut().add_unadded_tim_as(&file_name)?;
|
||||
let encoding = file_tab.get_encoding()?;
|
||||
let (image, palette_image) = logic.borrow_mut().add_unadded_tim_as(&file_name, encoding)?;
|
||||
|
||||
main_tab.add_new_vram_file(&file_name, image, palette_image);
|
||||
main_tab.add_new_vram_file(&file_name, image, encoding, palette_image);
|
||||
file_tab.clear_load();
|
||||
main_window.invoke_change_to_main();
|
||||
Ok(())
|
||||
|
|
|
@ -13,12 +13,16 @@ export component MainWindow inherits Window {
|
|||
callback move_vram_image <=> main_tab.move_vram_image;
|
||||
|
||||
// Convert Image values
|
||||
in-out property file_tab-encoding_options <=> file_tab.conv-encoding_options;
|
||||
in-out property file_tab-browse_path <=> file_tab.conv-image_path;
|
||||
in-out property file_tab-image_data <=> file_tab.conv-image_data;
|
||||
in-out property file_tab-image_width <=> file_tab.conv-image_width;
|
||||
in-out property file_tab-image_height <=> file_tab.conv-image_height;
|
||||
in-out property file_tab-palette_data <=> file_tab.conv-palette_data;
|
||||
in-out property file_tab-palette_width <=> file_tab.conv-palette_width;
|
||||
in-out property file_tab-palette_height <=> file_tab.conv-palette_height;
|
||||
in-out property file_tab-palette_visible <=> file_tab.conv-palette_enable;
|
||||
in-out property file_tab-selected_encoding <=> file_tab.conv-selected_encoding;
|
||||
in-out property file_tab-image_name <=> file_tab.conv-image_name;
|
||||
in-out property file_tab-enable <=> file_tab.conv-enable_view;
|
||||
callback file_tab-update_palette_size <=> file_tab.conv-image_update_palette_size;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { Button, TabWidget, LineEdit, GroupBox } from "std-widgets.slint";
|
||||
import { Button, TabWidget, LineEdit, GroupBox, ComboBox } from "std-widgets.slint";
|
||||
|
||||
export enum State {
|
||||
Project,
|
||||
|
@ -12,12 +12,16 @@ component ProjectWidget inherits Rectangle {
|
|||
}
|
||||
|
||||
component ConvertImageWidget inherits Rectangle {
|
||||
in-out property <[string]> encoding_options: [];
|
||||
in-out property <string> image_path;
|
||||
in-out property <string> image_name;
|
||||
in-out property <image> image_data;
|
||||
in-out property <int> image-width;
|
||||
in-out property <int> image-height;
|
||||
in-out property <image> palette_data;
|
||||
in-out property <int> palette_width: 0;
|
||||
in-out property <int> palette_height: 0;
|
||||
in-out property <string> selected_encoding;
|
||||
in-out property <bool> enable_view: false;
|
||||
in-out property <bool> palette_visible: false;
|
||||
|
||||
|
@ -59,6 +63,7 @@ component ConvertImageWidget inherits Rectangle {
|
|||
padding: 4px;
|
||||
HorizontalLayout {
|
||||
alignment: center;
|
||||
VerticalLayout {
|
||||
Rectangle {
|
||||
width: 256px + 2*4px; // < Because of border
|
||||
height: 256px + 2*4px; // < Because of border
|
||||
|
@ -73,6 +78,30 @@ component ConvertImageWidget inherits Rectangle {
|
|||
image-rendering: pixelated;
|
||||
}
|
||||
}
|
||||
VerticalLayout {
|
||||
alignment: center;
|
||||
Text {
|
||||
text: "Width: " + root.image-width;
|
||||
}
|
||||
}
|
||||
VerticalLayout {
|
||||
alignment: center;
|
||||
Text {
|
||||
text: "Height: " + root.image-height;
|
||||
}
|
||||
}
|
||||
VerticalLayout {
|
||||
alignment: center;
|
||||
ComboBox {
|
||||
model: root.encoding_options;
|
||||
enabled: root.enable_view;
|
||||
|
||||
selected(current-value) => {
|
||||
root.selected_encoding = current-value;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Rectangle {
|
||||
// Fake padding because the padding setting for the HorizontalLayout would not work
|
||||
width: 4px;
|
||||
|
@ -172,17 +201,19 @@ component ConvertImageWidget inherits Rectangle {
|
|||
}
|
||||
|
||||
export component FileTab inherits Rectangle {
|
||||
// TODO: Names are messed up here!
|
||||
in-out property <[string]> conv-encoding_options;
|
||||
in-out property <string> conv-image_path;
|
||||
in-out property <string> conv-image_name;
|
||||
in-out property <image> conv-image_data;
|
||||
in-out property <int> conv-image_width;
|
||||
in-out property <int> conv-image_height;
|
||||
in-out property <image> conv-palette_data;
|
||||
in-out property <int> conv-palette_width;
|
||||
in-out property <int> conv-palette_height;
|
||||
in-out property <string> conv-selected_encoding;
|
||||
in-out property <bool> conv-palette_enable;
|
||||
in-out property <bool> conv-enable_view;
|
||||
|
||||
|
||||
in-out property <State> state;
|
||||
callback conv-image_update_palette_size(int, int);
|
||||
callback conv-image_browse_clicked;
|
||||
|
@ -217,13 +248,18 @@ export component FileTab inherits Rectangle {
|
|||
alignment: start;
|
||||
if root.state == State.Project : ProjectWidget {
|
||||
}
|
||||
|
||||
if root.state == State.ConvertImage : ConvertImageWidget {
|
||||
encoding_options <=> root.conv-encoding_options;
|
||||
image_path <=> root.conv-image_path;
|
||||
image_data <=> root.conv-image_data;
|
||||
image-width <=> root.conv-image_width;
|
||||
image-height <=> root.conv-image_height;
|
||||
palette_data <=> root.conv-palette_data;
|
||||
palette_width <=> root.conv-palette_width;
|
||||
palette_height <=> root.conv-palette_height;
|
||||
palette_visible <=> root.conv-palette_enable;
|
||||
selected_encoding <=> root.conv-selected_encoding;
|
||||
image_name <=> root.conv-image_name;
|
||||
enable_view <=> root.conv-enable_view;
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@ struct VRAMImage {
|
|||
img: image,
|
||||
x: int,
|
||||
y: int,
|
||||
encoding_str: string,
|
||||
palette_count: int,
|
||||
is_palette: bool,
|
||||
}
|
||||
|
@ -81,7 +82,9 @@ export component MainTab inherits Rectangle {
|
|||
cur_sel_x.text = parent.img_x;
|
||||
cur_sel_y.text = parent.img_y;
|
||||
cur_sel_img.source = parent.img;
|
||||
encoding_text.encoding_str = vram-image.encoding_str;
|
||||
cur_sel_img.visible = true;
|
||||
|
||||
vram_files_list.current-item = i;
|
||||
}
|
||||
}
|
||||
|
@ -153,6 +156,7 @@ export component MainTab inherits Rectangle {
|
|||
cur_sel_x.text = root.vram_images[current-item].x;
|
||||
cur_sel_y.text = root.vram_images[current-item].y;
|
||||
cur_sel_img.source = root.vram_images[current-item].img;
|
||||
encoding_text.encoding_str = root.vram_images[current-item].encoding_str;
|
||||
cur_sel_img.visible = true;
|
||||
}
|
||||
}
|
||||
|
@ -232,9 +236,9 @@ export component MainTab inherits Rectangle {
|
|||
}
|
||||
}
|
||||
}
|
||||
ComboBox {
|
||||
model: ["4-bit", "16-bit", "24-bit"];
|
||||
current-value: "4-bit";
|
||||
encoding_text := Text {
|
||||
in-out property <string> encoding_str;
|
||||
text: "Encoding: " + encoding_str;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue