mirror of
https://github.com/lifegpc/msg-tool.git
synced 2026-06-06 12:58:45 +08:00
Add jpeg support
This commit is contained in:
62
Cargo.lock
generated
62
Cargo.lock
generated
@@ -79,6 +79,12 @@ version = "1.0.98"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487"
|
||||
|
||||
[[package]]
|
||||
name = "arrayvec"
|
||||
version = "0.7.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50"
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.5.0"
|
||||
@@ -130,6 +136,12 @@ dependencies = [
|
||||
"cipher",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bytemuck"
|
||||
version = "1.23.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c76a5792e44e4abe34d3abf15636779261d45a7450612059293d1d2cfc63422"
|
||||
|
||||
[[package]]
|
||||
name = "byteorder"
|
||||
version = "1.5.0"
|
||||
@@ -304,6 +316,12 @@ dependencies = [
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "dunce"
|
||||
version = "1.0.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813"
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.15.0"
|
||||
@@ -672,6 +690,31 @@ dependencies = [
|
||||
"simd-adler32",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mozjpeg"
|
||||
version = "0.10.13"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b7891b80aaa86097d38d276eb98b3805d6280708c4e0a1e6f6aed9380c51fec9"
|
||||
dependencies = [
|
||||
"arrayvec",
|
||||
"bytemuck",
|
||||
"libc",
|
||||
"mozjpeg-sys",
|
||||
"rgb",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "mozjpeg-sys"
|
||||
version = "2.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7f0dc668bf9bf888c88e2fb1ab16a406d2c380f1d082b20d51dd540ab2aa70c1"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"dunce",
|
||||
"libc",
|
||||
"nasm-rs",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "msg_tool"
|
||||
version = "0.1.0"
|
||||
@@ -691,6 +734,7 @@ dependencies = [
|
||||
"lazy_static",
|
||||
"libtlg-rs",
|
||||
"memchr",
|
||||
"mozjpeg",
|
||||
"msg_tool_macro",
|
||||
"overf",
|
||||
"png",
|
||||
@@ -713,6 +757,15 @@ dependencies = [
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nasm-rs"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "12fcfa1bd49e0342ec1d07ed2be83b59963e7acbeb9310e1bb2c07b69dadd959"
|
||||
dependencies = [
|
||||
"jobserver",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nix"
|
||||
version = "0.30.1"
|
||||
@@ -876,6 +929,15 @@ version = "0.8.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
|
||||
|
||||
[[package]]
|
||||
name = "rgb"
|
||||
version = "0.8.52"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0c6a884d2998352bb4daf0183589aec883f16a6da1f4dde84d8e2e9a5409a1ce"
|
||||
dependencies = [
|
||||
"bytemuck",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "ryu"
|
||||
version = "1.0.20"
|
||||
|
||||
@@ -19,6 +19,7 @@ json = { version = "0.12", optional = true }
|
||||
lazy_static = "1.5.0"
|
||||
libtlg-rs = { version = "0.1", optional = true }
|
||||
memchr = { version = "2.7", optional = true }
|
||||
mozjpeg = { version = "0.10.13", optional = true }
|
||||
msg_tool_macro = { path = "./msg_tool_macro" }
|
||||
overf = "0.1"
|
||||
png = { version = "0.17", optional = true }
|
||||
@@ -32,7 +33,12 @@ utf16string = "0.2"
|
||||
zstd = { version = "0.13", optional = true }
|
||||
|
||||
[features]
|
||||
default = ["artemis", "artemis-arc", "bgi", "bgi-arc", "bgi-img", "cat-system", "cat-system-arc", "cat-system-img", "circus", "circus-arc", "circus-audio", "circus-img", "escude", "escude-arc", "ex-hibit", "hexen-haus", "kirikiri", "kirikiri-img", "will-plus", "yaneurao", "yaneurao-itufuru"]
|
||||
default = ["all-fmt", "image-jpg"]
|
||||
all-fmt = ["all-script", "all-img", "all-arc", "all-audio"]
|
||||
all-script = ["artemis", "bgi", "cat-system", "circus", "escude", "ex-hibit", "hexen-haus", "kirikiri", "will-plus", "yaneurao", "yaneurao-itufuru"]
|
||||
all-img = ["bgi-img", "cat-system-img", "circus-img", "kirikiri-img"]
|
||||
all-arc = ["artemis-arc", "bgi-arc", "cat-system-arc", "circus-arc", "escude-arc"]
|
||||
all-audio = ["circus-audio"]
|
||||
artemis = ["utils-escape"]
|
||||
artemis-arc = ["artemis", "msg_tool_macro/artemis-arc", "sha1"]
|
||||
bgi = []
|
||||
@@ -56,6 +62,7 @@ yaneurao = []
|
||||
yaneurao-itufuru = ["yaneurao"]
|
||||
# basic feature
|
||||
image = ["png"]
|
||||
image-jpg = ["mozjpeg"]
|
||||
# utils feature
|
||||
utils-bit-stream = []
|
||||
utils-crc32 = []
|
||||
|
||||
@@ -2,6 +2,14 @@ import toml
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
def filter_name(name):
|
||||
if name.startswith("utils-"):
|
||||
return False
|
||||
if name.startswith("all-"):
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def main():
|
||||
# 检查cargo是否可用
|
||||
try:
|
||||
@@ -31,7 +39,7 @@ def main():
|
||||
|
||||
features = cargo_toml.get("features", {})
|
||||
feature_names = list(features.keys())
|
||||
feature_names = [name for name in feature_names if not name.startswith("utils-")]
|
||||
feature_names = [name for name in feature_names if filter_name(name)]
|
||||
|
||||
if not feature_names:
|
||||
print("No features defined in Cargo.toml.")
|
||||
|
||||
13
src/args.rs
13
src/args.rs
@@ -16,6 +16,15 @@ fn parse_compression_level(level: &str) -> Result<u32, String> {
|
||||
clap_num::number_range(level, 0, 9)
|
||||
}
|
||||
|
||||
#[cfg(feature = "mozjpeg")]
|
||||
fn parse_jpeg_quality(quality: &str) -> Result<u8, String> {
|
||||
let lower = quality.to_ascii_lowercase();
|
||||
if lower == "best" {
|
||||
return Ok(100);
|
||||
}
|
||||
clap_num::number_range(quality, 0, 100)
|
||||
}
|
||||
|
||||
#[cfg(feature = "zstd")]
|
||||
fn parse_zstd_compression_level(level: &str) -> Result<i32, String> {
|
||||
let lower = level.to_ascii_lowercase();
|
||||
@@ -289,6 +298,10 @@ pub struct Arg {
|
||||
#[arg(long, global = true, value_name = "PATH")]
|
||||
/// Path to the ExHibit rld def keys file, which contains the keys in BINARY format.
|
||||
pub ex_hibit_rld_def_keys: Option<String>,
|
||||
#[cfg(feature = "mozjpeg")]
|
||||
#[arg(long, global = true, default_value_t = 80, value_parser = parse_jpeg_quality)]
|
||||
/// JPEG quality for output images, 0-100. 100 means best quality.
|
||||
pub jpeg_quality: u8,
|
||||
#[command(subcommand)]
|
||||
/// Command
|
||||
pub command: Command,
|
||||
|
||||
@@ -1631,6 +1631,8 @@ fn main() {
|
||||
arg.ex_hibit_rld_def_keys.as_ref(),
|
||||
)
|
||||
.expect("Failed to load RLD DEF keys"),
|
||||
#[cfg(feature = "mozjpeg")]
|
||||
jpeg_quality: arg.jpeg_quality,
|
||||
};
|
||||
match &arg.command {
|
||||
args::Command::Export { input, output } => {
|
||||
|
||||
@@ -255,6 +255,8 @@ pub struct ExtraConfig {
|
||||
pub ex_hibit_rld_keys: Option<Box<[u32; 0x100]>>,
|
||||
#[cfg(feature = "ex-hibit")]
|
||||
pub ex_hibit_rld_def_keys: Option<Box<[u32; 0x100]>>,
|
||||
#[cfg(feature = "mozjpeg")]
|
||||
pub jpeg_quality: u8,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, ValueEnum, PartialEq, Eq, PartialOrd, Ord)]
|
||||
@@ -465,6 +467,8 @@ impl ImageColorType {
|
||||
#[derive(Clone, Copy, Debug, ValueEnum, PartialEq, Eq, PartialOrd, Ord)]
|
||||
pub enum ImageOutputType {
|
||||
Png,
|
||||
#[cfg(feature = "image-jpg")]
|
||||
Jpg,
|
||||
}
|
||||
|
||||
#[cfg(feature = "image")]
|
||||
@@ -472,6 +476,8 @@ impl AsRef<str> for ImageOutputType {
|
||||
fn as_ref(&self) -> &str {
|
||||
match self {
|
||||
ImageOutputType::Png => "png",
|
||||
#[cfg(feature = "image-jpg")]
|
||||
ImageOutputType::Jpg => "jpg",
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -189,6 +189,36 @@ pub fn encode_img(
|
||||
writer.finish()?;
|
||||
Ok(())
|
||||
}
|
||||
#[cfg(feature = "image-jpg")]
|
||||
ImageOutputType::Jpg => {
|
||||
let file = crate::utils::files::write_file(filename)?;
|
||||
let color_type = match data.color_type {
|
||||
ImageColorType::Grayscale => mozjpeg::ColorSpace::JCS_GRAYSCALE,
|
||||
ImageColorType::Rgb => mozjpeg::ColorSpace::JCS_RGB,
|
||||
ImageColorType::Rgba => mozjpeg::ColorSpace::JCS_EXT_RGBA,
|
||||
ImageColorType::Bgr => {
|
||||
convert_bgr_to_rgb(&mut data)?;
|
||||
mozjpeg::ColorSpace::JCS_RGB
|
||||
}
|
||||
ImageColorType::Bgra => {
|
||||
convert_bgra_to_rgba(&mut data)?;
|
||||
mozjpeg::ColorSpace::JCS_EXT_RGBA
|
||||
}
|
||||
};
|
||||
if data.depth != 8 {
|
||||
return Err(anyhow::anyhow!(
|
||||
"JPEG encoding only supports 8-bit depth, found: {}",
|
||||
data.depth
|
||||
));
|
||||
}
|
||||
let mut encoder = mozjpeg::compress::Compress::new(color_type);
|
||||
encoder.set_size(data.width as usize, data.height as usize);
|
||||
encoder.set_quality(config.jpeg_quality as f32);
|
||||
let mut start = encoder.start_compress(file)?;
|
||||
start.write_scanlines(&data.data)?;
|
||||
start.finish()?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -231,6 +261,37 @@ pub fn decode_img(typ: ImageOutputType, filename: &str) -> Result<ImageData> {
|
||||
let file = crate::utils::files::read_file(filename)?;
|
||||
load_png(&file[..])
|
||||
}
|
||||
#[cfg(feature = "image-jpg")]
|
||||
ImageOutputType::Jpg => {
|
||||
let file = crate::utils::files::read_file(filename)?;
|
||||
let decoder = mozjpeg::decompress::Decompress::new_mem(&file)?;
|
||||
let color_type = match decoder.color_space() {
|
||||
mozjpeg::ColorSpace::JCS_GRAYSCALE => ImageColorType::Grayscale,
|
||||
mozjpeg::ColorSpace::JCS_RGB => ImageColorType::Rgb,
|
||||
mozjpeg::ColorSpace::JCS_EXT_RGBA => ImageColorType::Rgba,
|
||||
_ => ImageColorType::Rgb, // Convert other types to RGB
|
||||
};
|
||||
let width = decoder.width() as u32;
|
||||
let height = decoder.height() as u32;
|
||||
let stride = width as usize * color_type.bpp(8) as usize / 8;
|
||||
let mut data = vec![0; stride * height as usize];
|
||||
let mut re = match color_type {
|
||||
ImageColorType::Grayscale => decoder.grayscale()?,
|
||||
ImageColorType::Rgb => decoder.rgb()?,
|
||||
ImageColorType::Rgba => decoder.rgba()?,
|
||||
_ => {
|
||||
unreachable!(); // We already checked the color type above
|
||||
}
|
||||
};
|
||||
re.read_scanlines_into(&mut data)?;
|
||||
Ok(ImageData {
|
||||
width,
|
||||
height,
|
||||
depth: 8,
|
||||
color_type,
|
||||
data,
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user