CLI Basis format support

This commit is contained in:
Piotr Siuszko 2025-01-18 11:49:57 +01:00
parent 7afc214e78
commit 71908c659b
3 changed files with 24 additions and 92 deletions

View File

@ -5,11 +5,11 @@ description = "CLI application for generating rpack atlases"
repository = "https://github.com/Leinnan/rpack.git" repository = "https://github.com/Leinnan/rpack.git"
homepage = "https://github.com/Leinnan/rpack" homepage = "https://github.com/Leinnan/rpack"
license = "MIT OR Apache-2.0" license = "MIT OR Apache-2.0"
version = "0.1.0" version = "0.2.0"
edition = "2021" edition = "2021"
[features] [features]
default = ["cli", "dds"] default = ["cli", "dds", "basis"]
cli = ["dep:clap", "dep:glob"] cli = ["dep:clap", "dep:glob"]
basis = ["dep:basis-universal"] basis = ["dep:basis-universal"]
dds = ["dep:image_dds"] dds = ["dep:image_dds"]
@ -24,7 +24,7 @@ thiserror = "2"
[target.'cfg(not(target_arch = "wasm32"))'.dependencies] [target.'cfg(not(target_arch = "wasm32"))'.dependencies]
clap = { version = "4", features = ["derive"], optional = true } clap = { version = "4", features = ["derive"], optional = true }
glob = { version = "0.3", optional = true }
basis-universal = { version = "0.3.1", optional = true } basis-universal = { version = "0.3.1", optional = true }
image_dds = { version = "0.7", optional = true } image_dds = { version = "0.7", optional = true }
glob = { version = "0.3", optional = true }
anyhow = "1" anyhow = "1"

View File

@ -79,6 +79,7 @@ pub enum SaveImageFormat {
#[default] #[default]
Png, Png,
Dds, Dds,
Basis,
} }
impl Display for SaveImageFormat { impl Display for SaveImageFormat {
@ -86,15 +87,7 @@ impl Display for SaveImageFormat {
match self { match self {
SaveImageFormat::Png => f.write_str(".png"), SaveImageFormat::Png => f.write_str(".png"),
SaveImageFormat::Dds => f.write_str(".dds"), SaveImageFormat::Dds => f.write_str(".dds"),
} SaveImageFormat::Basis => f.write_str(".basis"),
}
}
impl From<SaveImageFormat> for image::ImageFormat {
fn from(val: SaveImageFormat) -> Self {
match val {
SaveImageFormat::Png => image::ImageFormat::Png,
SaveImageFormat::Dds => image::ImageFormat::Dds,
} }
} }
} }
@ -185,14 +178,11 @@ impl Spritesheet {
} }
#[cfg(all(feature = "basis", not(target_arch = "wasm32")))] #[cfg(all(feature = "basis", not(target_arch = "wasm32")))]
pub fn save_as_basis<R>(&self, output_path: R) pub fn save_as_basis<R>(&self, output_path: R) -> anyhow::Result<()>
where where
R: AsRef<Path>, R: AsRef<Path>,
{ {
use basis_universal::{ use basis_universal::{BasisTextureFormat, Compressor, Transcoder};
BasisTextureFormat, Compressor, TranscodeParameters, Transcoder,
TranscoderTextureFormat,
};
use image::{EncodableLayout, GenericImageView}; use image::{EncodableLayout, GenericImageView};
let rgba_image = self.image_data.to_rgba8(); let rgba_image = self.image_data.to_rgba8();
@ -201,8 +191,8 @@ impl Spritesheet {
let (pixel_width, pixel_height) = self.image_data.dimensions(); let (pixel_width, pixel_height) = self.image_data.dimensions();
let mut compressor_params = basis_universal::CompressorParams::new(); let mut compressor_params = basis_universal::CompressorParams::new();
compressor_params.set_generate_mipmaps(true); compressor_params.set_generate_mipmaps(true);
compressor_params.set_basis_format(BasisTextureFormat::UASTC4x4); compressor_params.set_basis_format(BasisTextureFormat::ETC1S);
compressor_params.set_uastc_quality_level(basis_universal::UASTC_QUALITY_DEFAULT); compressor_params.set_etc1s_quality_level(basis_universal::ETC1S_QUALITY_MAX);
compressor_params.set_print_status_to_stdout(false); compressor_params.set_print_status_to_stdout(false);
let mut compressor_image = compressor_params.source_image_mut(0); let mut compressor_image = compressor_params.source_image_mut(0);
compressor_image.init( compressor_image.init(
@ -219,16 +209,14 @@ impl Spritesheet {
let compression_time = unsafe { let compression_time = unsafe {
compressor.init(&compressor_params); compressor.init(&compressor_params);
let t0 = std::time::Instant::now(); let t0 = std::time::Instant::now();
compressor.process().unwrap(); compressor.process().expect("Failed to compress the image.");
let t1 = std::time::Instant::now(); let t1 = std::time::Instant::now();
t1 - t0 t1 - t0
}; };
// You could write it to disk like this // You could write it to disk like this
let basis_file = compressor.basis_file(); let basis_file = compressor.basis_file();
// std::fs::write("example_encoded_image.basis", basis_file).unwrap(); let transcoder = Transcoder::new();
let mut transcoder = Transcoder::new();
let mip_level_count = transcoder.image_level_count(basis_file, 0); let mip_level_count = transcoder.image_level_count(basis_file, 0);
println!( println!(
"Compressed {} mip levels to {} total bytes in {} ms", "Compressed {} mip levels to {} total bytes in {} ms",
@ -236,70 +224,8 @@ impl Spritesheet {
compressor.basis_file_size(), compressor.basis_file_size(),
compression_time.as_secs_f64() * 1000.0 compression_time.as_secs_f64() * 1000.0
); );
std::fs::write(output_path.as_ref(), basis_file)?;
let userdata = transcoder.user_data(basis_file).unwrap(); Ok(())
println!("Basis file has user data {:?}", userdata);
//
// Now lets transcode it back to raw images
//
transcoder.prepare_transcoding(basis_file).unwrap();
let t0 = std::time::Instant::now();
let result = transcoder
.transcode_image_level(
basis_file,
TranscoderTextureFormat::ASTC_4x4_RGBA,
TranscodeParameters {
image_index: 0,
level_index: 0,
..Default::default()
},
)
.unwrap();
let t1 = std::time::Instant::now();
println!(
"Transcoded mip level 0 to ASTC_4x4_RGBA: {} bytes {} ms",
result.len(),
(t1 - t0).as_secs_f64() * 1000.0
);
let t0 = std::time::Instant::now();
let result = transcoder
.transcode_image_level(
basis_file,
TranscoderTextureFormat::RGBA32,
TranscodeParameters {
image_index: 0,
level_index: 0,
..Default::default()
},
)
.unwrap();
let t1 = std::time::Instant::now();
println!(
"Transcoded mip level 0 to RGBA32: {} bytes {} ms",
result.len(),
(t1 - t0).as_secs_f64() * 1000.0
);
transcoder.end_transcoding();
let description = transcoder
.image_level_description(basis_file, 0, 0)
.unwrap();
let image = image::RgbaImage::from_raw(
description.original_width,
description.original_height,
result,
)
.unwrap();
// TODO THIS DOESNT WORK, NEED TO FIX THIS
image
.save_with_format(output_path.as_ref(), image::ImageFormat::Png)
.unwrap();
} }
} }
@ -414,10 +340,16 @@ impl TilemapGenerationConfig {
#[cfg(not(feature = "dds"))] #[cfg(not(feature = "dds"))]
panic!("Program is compiled without support for dds. Compile it yourself with feature `dds` enabled."); panic!("Program is compiled without support for dds. Compile it yourself with feature `dds` enabled.");
} }
f => { SaveImageFormat::Png => {
spritesheet spritesheet
.image_data .image_data
.save_with_format(&atlas_image_path, f.into())?; .save_with_format(&atlas_image_path, image::ImageFormat::Png)?;
}
SaveImageFormat::Basis => {
#[cfg(feature = "basis")]
spritesheet.save_as_basis(&atlas_image_path)?;
#[cfg(not(feature = "basis"))]
panic!("Program is compiled without support for basis. Compile it yourself with feature `basis` enabled.");
} }
} }
let json = serde_json::to_string_pretty(&spritesheet.atlas_asset_json)?; let json = serde_json::to_string_pretty(&spritesheet.atlas_asset_json)?;

View File

@ -1,6 +1,6 @@
[package] [package]
name = "rpack_egui" name = "rpack_egui"
version = "0.1.0" version = "0.2.0"
description = "GUI application for generating rpack atlases" description = "GUI application for generating rpack atlases"
authors = ["Piotr Siuszko <siuszko@zoho.com>"] authors = ["Piotr Siuszko <siuszko@zoho.com>"]
edition = "2021" edition = "2021"
@ -19,7 +19,7 @@ eframe = { version = "0.30", default-features = false, features = [
] } ] }
log = "0.4" log = "0.4"
egui_json_tree = "0.10" egui_json_tree = "0.10"
rpack_cli = { default-features = false, path = "../rpack_cli", version = "0.1" } rpack_cli = { default-features = false, path = "../rpack_cli", version = "0.2" }
# You only need serde if you want app persistence: # You only need serde if you want app persistence:
serde = { version = "1", features = ["derive"] } serde = { version = "1", features = ["derive"] }