mirror of
https://github.com/lifegpc/msg-tool.git
synced 2026-07-08 01:31:53 +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"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487"
|
checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "arrayvec"
|
||||||
|
version = "0.7.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "autocfg"
|
name = "autocfg"
|
||||||
version = "1.5.0"
|
version = "1.5.0"
|
||||||
@@ -130,6 +136,12 @@ dependencies = [
|
|||||||
"cipher",
|
"cipher",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "bytemuck"
|
||||||
|
version = "1.23.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5c76a5792e44e4abe34d3abf15636779261d45a7450612059293d1d2cfc63422"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "byteorder"
|
name = "byteorder"
|
||||||
version = "1.5.0"
|
version = "1.5.0"
|
||||||
@@ -304,6 +316,12 @@ dependencies = [
|
|||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "dunce"
|
||||||
|
version = "1.0.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "either"
|
name = "either"
|
||||||
version = "1.15.0"
|
version = "1.15.0"
|
||||||
@@ -672,6 +690,31 @@ dependencies = [
|
|||||||
"simd-adler32",
|
"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]]
|
[[package]]
|
||||||
name = "msg_tool"
|
name = "msg_tool"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
@@ -691,6 +734,7 @@ dependencies = [
|
|||||||
"lazy_static",
|
"lazy_static",
|
||||||
"libtlg-rs",
|
"libtlg-rs",
|
||||||
"memchr",
|
"memchr",
|
||||||
|
"mozjpeg",
|
||||||
"msg_tool_macro",
|
"msg_tool_macro",
|
||||||
"overf",
|
"overf",
|
||||||
"png",
|
"png",
|
||||||
@@ -713,6 +757,15 @@ dependencies = [
|
|||||||
"syn",
|
"syn",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "nasm-rs"
|
||||||
|
version = "0.3.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "12fcfa1bd49e0342ec1d07ed2be83b59963e7acbeb9310e1bb2c07b69dadd959"
|
||||||
|
dependencies = [
|
||||||
|
"jobserver",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "nix"
|
name = "nix"
|
||||||
version = "0.30.1"
|
version = "0.30.1"
|
||||||
@@ -876,6 +929,15 @@ version = "0.8.5"
|
|||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
|
checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rgb"
|
||||||
|
version = "0.8.52"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0c6a884d2998352bb4daf0183589aec883f16a6da1f4dde84d8e2e9a5409a1ce"
|
||||||
|
dependencies = [
|
||||||
|
"bytemuck",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ryu"
|
name = "ryu"
|
||||||
version = "1.0.20"
|
version = "1.0.20"
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ json = { version = "0.12", optional = true }
|
|||||||
lazy_static = "1.5.0"
|
lazy_static = "1.5.0"
|
||||||
libtlg-rs = { version = "0.1", optional = true }
|
libtlg-rs = { version = "0.1", optional = true }
|
||||||
memchr = { version = "2.7", optional = true }
|
memchr = { version = "2.7", optional = true }
|
||||||
|
mozjpeg = { version = "0.10.13", optional = true }
|
||||||
msg_tool_macro = { path = "./msg_tool_macro" }
|
msg_tool_macro = { path = "./msg_tool_macro" }
|
||||||
overf = "0.1"
|
overf = "0.1"
|
||||||
png = { version = "0.17", optional = true }
|
png = { version = "0.17", optional = true }
|
||||||
@@ -32,7 +33,12 @@ utf16string = "0.2"
|
|||||||
zstd = { version = "0.13", optional = true }
|
zstd = { version = "0.13", optional = true }
|
||||||
|
|
||||||
[features]
|
[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 = ["utils-escape"]
|
||||||
artemis-arc = ["artemis", "msg_tool_macro/artemis-arc", "sha1"]
|
artemis-arc = ["artemis", "msg_tool_macro/artemis-arc", "sha1"]
|
||||||
bgi = []
|
bgi = []
|
||||||
@@ -56,6 +62,7 @@ yaneurao = []
|
|||||||
yaneurao-itufuru = ["yaneurao"]
|
yaneurao-itufuru = ["yaneurao"]
|
||||||
# basic feature
|
# basic feature
|
||||||
image = ["png"]
|
image = ["png"]
|
||||||
|
image-jpg = ["mozjpeg"]
|
||||||
# utils feature
|
# utils feature
|
||||||
utils-bit-stream = []
|
utils-bit-stream = []
|
||||||
utils-crc32 = []
|
utils-crc32 = []
|
||||||
|
|||||||
@@ -2,6 +2,14 @@ import toml
|
|||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
|
def filter_name(name):
|
||||||
|
if name.startswith("utils-"):
|
||||||
|
return False
|
||||||
|
if name.startswith("all-"):
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
# 检查cargo是否可用
|
# 检查cargo是否可用
|
||||||
try:
|
try:
|
||||||
@@ -31,7 +39,7 @@ def main():
|
|||||||
|
|
||||||
features = cargo_toml.get("features", {})
|
features = cargo_toml.get("features", {})
|
||||||
feature_names = list(features.keys())
|
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:
|
if not feature_names:
|
||||||
print("No features defined in Cargo.toml.")
|
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)
|
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")]
|
#[cfg(feature = "zstd")]
|
||||||
fn parse_zstd_compression_level(level: &str) -> Result<i32, String> {
|
fn parse_zstd_compression_level(level: &str) -> Result<i32, String> {
|
||||||
let lower = level.to_ascii_lowercase();
|
let lower = level.to_ascii_lowercase();
|
||||||
@@ -289,6 +298,10 @@ pub struct Arg {
|
|||||||
#[arg(long, global = true, value_name = "PATH")]
|
#[arg(long, global = true, value_name = "PATH")]
|
||||||
/// Path to the ExHibit rld def keys file, which contains the keys in BINARY format.
|
/// Path to the ExHibit rld def keys file, which contains the keys in BINARY format.
|
||||||
pub ex_hibit_rld_def_keys: Option<String>,
|
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(subcommand)]
|
||||||
/// Command
|
/// Command
|
||||||
pub command: Command,
|
pub command: Command,
|
||||||
|
|||||||
@@ -1631,6 +1631,8 @@ fn main() {
|
|||||||
arg.ex_hibit_rld_def_keys.as_ref(),
|
arg.ex_hibit_rld_def_keys.as_ref(),
|
||||||
)
|
)
|
||||||
.expect("Failed to load RLD DEF keys"),
|
.expect("Failed to load RLD DEF keys"),
|
||||||
|
#[cfg(feature = "mozjpeg")]
|
||||||
|
jpeg_quality: arg.jpeg_quality,
|
||||||
};
|
};
|
||||||
match &arg.command {
|
match &arg.command {
|
||||||
args::Command::Export { input, output } => {
|
args::Command::Export { input, output } => {
|
||||||
|
|||||||
@@ -255,6 +255,8 @@ pub struct ExtraConfig {
|
|||||||
pub ex_hibit_rld_keys: Option<Box<[u32; 0x100]>>,
|
pub ex_hibit_rld_keys: Option<Box<[u32; 0x100]>>,
|
||||||
#[cfg(feature = "ex-hibit")]
|
#[cfg(feature = "ex-hibit")]
|
||||||
pub ex_hibit_rld_def_keys: Option<Box<[u32; 0x100]>>,
|
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)]
|
#[derive(Clone, Copy, Debug, ValueEnum, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
@@ -465,6 +467,8 @@ impl ImageColorType {
|
|||||||
#[derive(Clone, Copy, Debug, ValueEnum, PartialEq, Eq, PartialOrd, Ord)]
|
#[derive(Clone, Copy, Debug, ValueEnum, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
pub enum ImageOutputType {
|
pub enum ImageOutputType {
|
||||||
Png,
|
Png,
|
||||||
|
#[cfg(feature = "image-jpg")]
|
||||||
|
Jpg,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "image")]
|
#[cfg(feature = "image")]
|
||||||
@@ -472,6 +476,8 @@ impl AsRef<str> for ImageOutputType {
|
|||||||
fn as_ref(&self) -> &str {
|
fn as_ref(&self) -> &str {
|
||||||
match self {
|
match self {
|
||||||
ImageOutputType::Png => "png",
|
ImageOutputType::Png => "png",
|
||||||
|
#[cfg(feature = "image-jpg")]
|
||||||
|
ImageOutputType::Jpg => "jpg",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -189,6 +189,36 @@ pub fn encode_img(
|
|||||||
writer.finish()?;
|
writer.finish()?;
|
||||||
Ok(())
|
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)?;
|
let file = crate::utils::files::read_file(filename)?;
|
||||||
load_png(&file[..])
|
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