From 4b203a297223a07c3857a0847fc3f2ee9b2af51a Mon Sep 17 00:00:00 2001 From: lifegpc Date: Wed, 30 Jul 2025 16:05:28 +0800 Subject: [PATCH] Add support to create crx image --- src/scripts/circus/image/crx.rs | 92 +++++++++++++++++++++++++++++++++ 1 file changed, 92 insertions(+) diff --git a/src/scripts/circus/image/crx.rs b/src/scripts/circus/image/crx.rs index 96c7ebd..1488a04 100644 --- a/src/scripts/circus/image/crx.rs +++ b/src/scripts/circus/image/crx.rs @@ -116,6 +116,19 @@ impl ScriptBuilder for CrxImageBuilder { } None } + + fn can_create_image_file(&self) -> bool { + true + } + + fn create_image_file<'a>( + &'a self, + data: ImageData, + writer: Box, + options: &ExtraConfig, + ) -> Result<()> { + CrxImage::create_image(data, writer, options) + } } #[derive(Clone, Debug, StructPack, StructUnpack)] @@ -698,6 +711,85 @@ impl CrxImage { } Ok(dst) } + + pub fn create_image( + mut data: ImageData, + mut writer: T, + config: &ExtraConfig, + ) -> Result<()> { + let header = Header { + inner_x: 0, + inner_y: 0, + width: data.width as u16, + height: data.height as u16, + version: 2, + flags: 0x10, // Force add compressed data length + bpp: match data.color_type { + ImageColorType::Bgr => 0, + ImageColorType::Bgra => 1, + ImageColorType::Rgb => { + convert_rgb_to_bgr(&mut data)?; + 0 + } + ImageColorType::Rgba => { + convert_rgba_to_bgra(&mut data)?; + 1 + } + _ => { + return Err(anyhow::anyhow!( + "Unsupported color type: {:?}", + data.color_type + )); + } + }, + mode: 0, + clips: Vec::new(), + }; + let pixel_size = data.color_type.bpp(1) as u8; + if data.color_type == ImageColorType::Bgra && header.mode != 1 { + let alpha_flip = if header.mode == 2 { 0 } else { 0xFF }; + for i in (0..data.data.len()).step_by(4) { + let b = data.data[i]; + let g = data.data[i + 1]; + let r = data.data[i + 2]; + let a = data.data[i + 3]; + data.data[i] = a ^ alpha_flip; + data.data[i + 1] = b; + data.data[i + 2] = g; + data.data[i + 3] = r; + } + } + let mode = config.circus_crx_mode.for_creating(); + let encoded = if mode.is_best() { + Self::encode_image_best(&data.data, header.width, header.height, pixel_size)? + } else if let CircusCrxMode::Fixed(mode) = mode { + Self::encode_image_fixed(&data.data, header.width, header.height, pixel_size, mode)? + } else { + return Err(anyhow::anyhow!( + "Unsupported row type for creating: {:?}", + mode + )); + }; + let compressed = if config.circus_crx_zstd { + let mut encoder = zstd::Encoder::new(MemWriter::new(), config.zstd_compression_level)?; + encoder.write_all(&encoded)?; + let compressed_data = encoder.finish()?; + compressed_data.into_inner() + } else { + let mut encoder = flate2::write::ZlibEncoder::new( + MemWriter::new(), + flate2::Compression::new(config.zlib_compression_level), + ); + encoder.write_all(&encoded)?; + let compressed_data = encoder.finish()?; + compressed_data.into_inner() + }; + writer.write_all(b"CRXG")?; + header.pack(&mut writer, false, Encoding::Utf8)?; + writer.write_u32(compressed.len() as u32)?; + writer.write_all(&compressed)?; + Ok(()) + } } impl Script for CrxImage {