Add import support for Qlie tiled PNG image (.png)

This commit is contained in:
2026-01-31 16:36:49 +08:00
parent 3c424a28c0
commit 7f8d1daf9d
13 changed files with 202 additions and 16 deletions

View File

@@ -202,12 +202,14 @@ pub trait ScriptBuilder: std::fmt::Debug {
/// Creates an image file from the given data.
///
/// * `data` - The image data to write.
/// * `filename` - The path to the image file.
/// * `writer` - A writer with seek capabilities to write the image data.
/// * `options` - Additional configuration options.
#[cfg(feature = "image")]
fn create_image_file<'a>(
&'a self,
_data: ImageData,
_filename: &str,
_writer: Box<dyn WriteSeek + 'a>,
_options: &ExtraConfig,
) -> Result<()> {
@@ -221,16 +223,18 @@ pub trait ScriptBuilder: std::fmt::Debug {
/// * `data` - The image data to write.
/// * `filename` - The path to the output file.
/// * `options` - Additional configuration options.
/// * `image_filename` - The path to the image file.
#[cfg(feature = "image")]
fn create_image_file_filename(
&self,
data: ImageData,
filename: &str,
image_filename: &str,
options: &ExtraConfig,
) -> Result<()> {
let f = std::fs::File::create(filename)?;
let f = std::io::BufWriter::new(f);
self.create_image_file(data, Box::new(f), options)
self.create_image_file(data, image_filename, Box::new(f), options)
}
}
@@ -517,8 +521,14 @@ pub trait Script: std::fmt::Debug + std::any::Any {
/// Imports an image into this script.
///
/// * `data` - The image data to import.
/// * `filename` - The path of the image file.
/// * `file` - A writer with seek capabilities to write the patched scripts.
fn import_image<'a>(&'a self, _data: ImageData, _file: Box<dyn WriteSeek + 'a>) -> Result<()> {
fn import_image<'a>(
&'a self,
_data: ImageData,
_filename: &str,
_file: Box<dyn WriteSeek + 'a>,
) -> Result<()> {
Err(anyhow::anyhow!(
"This script type does not support to import image."
))
@@ -529,10 +539,16 @@ pub trait Script: std::fmt::Debug + std::any::Any {
///
/// * `data` - The image data to import.
/// * `filename` - The path of the file to write the patched scripts.
fn import_image_filename(&self, data: ImageData, filename: &str) -> Result<()> {
/// * `image_filename` - The path of the image file.
fn import_image_filename(
&self,
data: ImageData,
image_filename: &str,
filename: &str,
) -> Result<()> {
let f = std::fs::File::create(filename)?;
let f = std::io::BufWriter::new(f);
self.import_image(data, Box::new(f))
self.import_image(data, image_filename, Box::new(f))
}
#[cfg(feature = "image")]

View File

@@ -67,6 +67,7 @@ impl ScriptBuilder for BgiCBGBuilder {
fn create_image_file<'a>(
&'a self,
data: ImageData,
_filename: &str,
mut writer: Box<dyn WriteSeek + 'a>,
_options: &ExtraConfig,
) -> Result<()> {
@@ -200,6 +201,7 @@ impl Script for BgiCBG {
fn import_image<'a>(
&'a self,
data: ImageData,
_filename: &str,
mut file: Box<dyn WriteSeek + 'a>,
) -> Result<()> {
let encoder = CbgEncoder::new(data)?;

View File

@@ -85,6 +85,7 @@ impl ScriptBuilder for BgiImageBuilder {
fn create_image_file<'a>(
&'a self,
data: ImageData,
_filename: &str,
writer: Box<dyn WriteSeek + 'a>,
options: &ExtraConfig,
) -> Result<()> {
@@ -257,7 +258,12 @@ impl Script for BgiImage {
})
}
fn import_image<'a>(&'a self, data: ImageData, file: Box<dyn WriteSeek + 'a>) -> Result<()> {
fn import_image<'a>(
&'a self,
data: ImageData,
_filename: &str,
file: Box<dyn WriteSeek + 'a>,
) -> Result<()> {
create_image(
data,
file,

View File

@@ -144,6 +144,7 @@ impl ScriptBuilder for CrxImageBuilder {
fn create_image_file<'a>(
&'a self,
data: ImageData,
_filename: &str,
writer: Box<dyn WriteSeek + 'a>,
options: &ExtraConfig,
) -> Result<()> {
@@ -1156,6 +1157,7 @@ impl Script for CrxImage {
fn import_image<'a>(
&'a self,
mut data: ImageData,
_filename: &str,
mut file: Box<dyn WriteSeek + 'a>,
) -> Result<()> {
let mut color_type = match data.color_type {

View File

@@ -63,6 +63,7 @@ impl ScriptBuilder for TlgImageBuilder {
fn create_image_file<'a>(
&'a self,
mut data: ImageData,
_filename: &str,
writer: Box<dyn WriteSeek + 'a>,
_options: &ExtraConfig,
) -> Result<()> {
@@ -142,6 +143,7 @@ impl Script for TlgImage {
fn import_image<'a>(
&'a self,
mut data: ImageData,
_filename: &str,
file: Box<dyn WriteSeek + 'a>,
) -> Result<()> {
if data.depth != 8 {

View File

@@ -86,15 +86,34 @@ impl ScriptBuilder for DpngImageBuilder {
None
}
}
fn can_create_image_file(&self) -> bool {
true
}
fn create_image_file<'a>(
&'a self,
data: ImageData,
filename: &str,
writer: Box<dyn WriteSeek + 'a>,
options: &ExtraConfig,
) -> Result<()> {
if options.qlie_dpng_use_raw_png {
create_raw_png_image(filename, writer, None)
} else {
create_image(data, writer, options)
}
}
}
#[derive(Debug)]
pub struct DpngImage {
img: DpngFile,
config: ExtraConfig,
}
impl DpngImage {
pub fn new<T: Read + Seek>(mut data: T, _config: &ExtraConfig) -> Result<Self> {
pub fn new<T: Read + Seek>(mut data: T, config: &ExtraConfig) -> Result<Self> {
let img = DpngFile::unpack(&mut data, false, Encoding::Utf8, &None)?;
if img.header.magic != *b"DPNG" {
anyhow::bail!("Not a valid DPNG image");
@@ -102,7 +121,10 @@ impl DpngImage {
if img.tiles.is_empty() {
anyhow::bail!("DPNG image has no tiles");
}
Ok(DpngImage { img })
Ok(DpngImage {
img,
config: config.clone(),
})
}
}
@@ -146,4 +168,111 @@ impl Script for DpngImage {
}
Ok(base)
}
fn import_image<'a>(
&'a self,
data: ImageData,
filename: &str,
file: Box<dyn WriteSeek + 'a>,
) -> Result<()> {
if self.config.qlie_dpng_use_raw_png {
let img = load_png(std::fs::File::open(filename)?)?;
if img.width != self.img.header.image_width
|| img.height != self.img.header.image_height
{
eprintln!(
"Warning: Image dimensions do not match original DPNG image (expected {}x{}, got {}x{})",
self.img.header.image_width,
self.img.header.image_height,
img.width,
img.height
);
crate::COUNTER.inc_warning();
}
create_raw_png_image(filename, file, Some(img))?;
} else {
if data.width != self.img.header.image_width
|| data.height != self.img.header.image_height
{
eprintln!(
"Warning: Image dimensions do not match original DPNG image (expected {}x{}, got {}x{})",
self.img.header.image_width,
self.img.header.image_height,
data.width,
data.height
);
crate::COUNTER.inc_warning();
}
create_image(data, file, &self.config)?;
}
Ok(())
}
}
fn create_raw_png_image<'a>(
filename: &str,
mut file: Box<dyn WriteSeek + 'a>,
img: Option<ImageData>,
) -> Result<()> {
let img = match img {
Some(img) => img,
None => load_png(std::fs::File::open(filename)?)?,
};
let header = DpngHeader {
magic: *b"DPNG",
_unk1: 1,
tile_count: 1,
image_width: img.width,
image_height: img.height,
};
let png_data = crate::utils::files::read_file(filename)?;
let tile = Tile {
x: 0,
y: 0,
width: img.width,
height: img.height,
size: png_data.len() as u32,
_unk: 0,
png_data,
};
let dpng = DpngFile {
header,
tiles: vec![tile],
};
dpng.pack(&mut file, false, Encoding::Utf8, &None)?;
Ok(())
}
fn create_image<'a>(
image: ImageData,
mut writer: Box<dyn WriteSeek + 'a>,
config: &ExtraConfig,
) -> Result<()> {
let header = DpngHeader {
magic: *b"DPNG",
_unk1: 1,
tile_count: 1,
image_width: image.width,
image_height: image.height,
};
let mut png_data = MemWriter::new();
let width = image.width;
let height = image.height;
encode_img_writer(image, ImageOutputType::Png, &mut png_data, config)?;
let png_data = png_data.into_inner();
let tile = Tile {
x: 0,
y: 0,
width,
height,
size: png_data.len() as u32,
_unk: 0,
png_data,
};
let dpng = DpngFile {
header,
tiles: vec![tile],
};
dpng.pack(&mut writer, false, Encoding::Utf8, &None)?;
Ok(())
}

View File

@@ -58,6 +58,7 @@ impl ScriptBuilder for PgdGeBuilder {
fn create_image_file<'a>(
&'a self,
data: ImageData,
_filename: &str,
mut writer: Box<dyn WriteSeek + 'a>,
options: &ExtraConfig,
) -> Result<()> {
@@ -125,6 +126,7 @@ impl Script for PgdGe {
fn import_image<'a>(
&'a self,
data: ImageData,
_filename: &str,
mut file: Box<dyn WriteSeek + 'a>,
) -> Result<()> {
let mut header = self.header.clone();

View File

@@ -153,6 +153,7 @@ impl Script for Pgd3 {
fn import_image<'a>(
&'a self,
data: ImageData,
_filename: &str,
mut file: Box<dyn WriteSeek + 'a>,
) -> Result<()> {
let mut header = PgdGeHeader {