Initial commit: Add cutPic, dexcel, and zip tools

This commit is contained in:
macro
2026-04-25 00:01:40 +08:00
commit 8fb110adbd
15 changed files with 2704 additions and 0 deletions
+1
View File
@@ -0,0 +1 @@
/target
+8
View File
@@ -0,0 +1,8 @@
[package]
name = "cutpic"
version = "0.1.0"
edition = "2024"
[dependencies]
image = "0.25"
clap = { version = "4.5", features = ["derive"] }
+283
View File
@@ -0,0 +1,283 @@
# cutpic - 图片裁切工具 🖼️
一个简单易用的命令行图片裁切工具,可以将图片按指定的行列数均匀分割成多个小图片。
## ✨ 功能特性
- 📐 **灵活分割**: 支持自定义水平和垂直分割数
- 🎯 **均匀裁切**: 自动计算每块尺寸,均匀分割图片
- 📁 **智能输出**: 默认在输入文件同级目录创建 dist 文件夹
- 🖼️ **多格式支持**: 支持 PNG, JPG, JPEG, BMP, GIF, WebP 等常见图片格式
- 🔢 **智能命名**: 自动生成带序号的文件名,方便识别和管理
-**高性能**: 基于 Rust 开发,处理速度快
## 📦 安装
### 从源码编译
```bash
cd cutPic
cargo build --release
```
编译后的可执行文件位于 `target/release/cutpic.exe`
### 添加到系统路径(可选)
`target/release/cutpic.exe` 复制到系统 PATH 目录,或将其所在目录添加到 PATH 环境变量中。
## 🚀 快速开始
### 基本用法
```bash
# 将图片分割成 3x2 的网格(共6块)
cutpic -i image.png -w 3 -h 2
# 将图片分割成 4x4 的网格(共16块)
cutpic -i photo.jpg -w 4 -h 4
```
### 指定输出目录
```bash
# 指定输出到 custom_output 目录
cutpic -i image.png -w 2 -h 2 --out ./custom_output
```
## 📖 命令参数
```bash
cutpic [OPTIONS] -i <INPUT>
参数:
-i, --input <FILE> 📁 输入图片路径(必需)
-w, --width <NUM> 📐 水平方向分割数(默认: 1)
-h, --height <NUM> 📏 垂直方向分割数(默认: 1)
--out <DIR> 📂 输出目录(默认: 输入文件同级的 dist 文件夹)
-h, --help 显示帮助信息
-V, --version 显示版本信息
```
## 💡 使用示例
### 示例 1: 制作拼图素材
将一张大图分割成 5x5 的拼图块:
```bash
cutpic -i puzzle.png -w 5 -h 5
```
**输出:**
- 生成 25 张图片
- 文件命名: `puzzle_1_25.png`, `puzzle_2_25.png`, ..., `puzzle_25_25.png`
- 输出位置: `./dist/` 目录
### 示例 2: 提取图标集中的单个图标
从 4x4 排列的图标集中提取每个图标:
```bash
cutpic -i icons.png -w 4 -h 4 --out ./individual_icons
```
**输出:**
- 生成 16 张单独的图标
- 输出到 `./individual_icons/` 目录
### 示例 3: 分割游戏精灵图(Sprite Sheet
横向分割角色动画帧(8 帧动画):
```bash
cutpic -i character.png -w 8 -h 1 --out ./animation_frames
```
**输出:**
- 生成 8 张动画帧图片
- 适合用于游戏开发中的帧动画
### 示例 4: 只进行水平分割
将横幅广告图分割成 4 部分:
```bash
cutpic -i banner.png -w 4 -h 1
```
### 示例 5: 只进行垂直分割
将长图分割成 3 段:
```bash
cutpic -i long-image.jpg -w 1 -h 3
```
## 📊 输出说明
### 文件命名规则
生成的文件遵循以下命名格式:
```
{原文件名}_{序号}_{总数}.{扩展名}
```
**示例:**
- 原文件: `image.png`
- 分割: 3x2 (共6块)
- 输出文件:
- `image_1_6.png` - 第1块
- `image_2_6.png` - 第2块
- ...
- `image_6_6.png` - 第6块
### 输出目录结构
```
项目目录/
├── image.png # 原始图片
└── dist/ # 默认输出目录
├── image_1_6.png
├── image_2_6.png
├── image_3_6.png
├── image_4_6.png
├── image_5_6.png
└── image_6_6.png
```
如果指定了 `--out` 参数:
```
项目目录/
├── image.png # 原始图片
└── my_output/ # 自定义输出目录
├── image_1_6.png
├── image_2_6.png
└── ...
```
## ⚙️ 工作原理
1. **加载图片**: 读取输入图片并获取尺寸信息
2. **计算尺寸**:
- 每块宽度 = 总宽度 ÷ 水平分割数
- 每块高度 = 总高度 ÷ 垂直分割数
3. **逐块裁切**: 按行列顺序遍历,裁切每一块
4. **处理边界**: 最后一行/列可能包含剩余像素,自动调整尺寸
5. **保存文件**: 按命名规则保存到输出目录
### 边界处理示例
假设图片尺寸为 100x100 像素,分割为 3x3
- 理论每块: 33.33 x 33.33 像素
- 实际处理:
- 第1、2块: 33 x 33 像素
- 第3块: 34 x 34 像素(包含剩余像素)
这样确保所有像素都被保留,不会丢失任何内容。
## 🎯 应用场景
### 1. 游戏开发
- 分割 Sprite Sheet 为单独的动画帧
- 提取瓦片地图(Tilemap)的单个瓦片
- 处理角色、道具等资源图片
### 2. 网页设计
- 将大图分割成小块用于懒加载
- 制作图片拼图效果
- 优化大图的加载性能
### 3. 社交媒体
- 制作 Instagram 九宫格图片
- 分割长图为适合移动端查看的多张图片
- 创建有趣的图片分割效果
### 4. 数据处理
- 批量处理扫描文档
- 分割大幅面图纸
- 处理卫星图像或地图切片
### 5. 教育用途
- 制作教学用拼图
- 分解复杂图表
- 创建互动学习材料
## ⚠️ 注意事项
1. **图片格式**: 输出格式与输入格式相同
2. **最小分割**: 分割数为 1x1 时,会生成原图的副本
3. **内存使用**: 处理超大图片时会占用较多内存
4. **文件数量**: 分割数越多,生成的文件越多,注意磁盘空间
5. **索引顺序**: 文件按先行后列的顺序编号(从左到右,从上到下)
## 🔧 技术细节
### 依赖库
- **clap**: 命令行参数解析
- **image**: 图片处理和裁切
### 性能特点
- 使用 Rust 编写,性能优异
- 支持多线程(取决于 image 库的实现)
- 内存效率高,逐块处理
### 平台支持
- ✅ Windows
- ✅ macOS
- ✅ Linux
## 📝 完整示例工作流
### 工作流 1: 制作 Instagram 九宫格
```bash
# 1. 准备一张正方形图片(建议 1080x1080 或更高)
# 2. 分割成 3x3 的九宫格
cutpic -i instagram-post.png -w 3 -h 3 --out ./instagram-grid
# 3. 上传生成的 9 张图片到 Instagram(按逆序上传以获得正确显示顺序)
```
### 工作流 2: 提取游戏资源
```bash
# 1. 从游戏中提取 sprite sheet
# 2. 分析 sprite sheet 的布局(例如 8x4 排列)
cutpic -i spritesheet.png -w 8 -h 4 --out ./game-assets
# 3. 重命名生成的文件为有意义的名称
# 4. 在游戏引擎中使用这些单独的资源
```
### 工作流 3: 创建拼图游戏
```bash
# 1. 选择一张有趣的图片
# 2. 根据难度选择分割数(简单: 3x3, 中等: 5x5, 困难: 8x8
cutpic -i puzzle-image.jpg -w 5 -h 5 --out ./puzzle-pieces
# 3. 打乱文件顺序
# 4. 开发拼图游戏逻辑
```
## 🤝 贡献
欢迎提交 Issue 和 Pull Request
## 📄 许可证
本项目采用 MIT 许可证。
## 🙏 致谢
感谢以下开源项目:
- [clap](https://github.com/clap-rs/clap) - 优秀的命令行参数解析库
- [image](https://github.com/image-rs/image) - 强大的图片处理库
---
**Made with ❤️ using Rust**
+108
View File
@@ -0,0 +1,108 @@
use clap::Parser;
use image::GenericImageView;
use std::path::PathBuf;
/// 图片裁切工具 - 将图片按行列分割成多份
#[derive(Parser, Debug)]
#[command(name = "cutpic")]
#[command(author, version, about, long_about = None)]
#[command(
about = "🖼️ 图片裁切工具 - 将图片按指定的行列数分割成多个小图片",
long_about = "🖼️ 图片裁切工具\n\n将一张图片按照指定的水平和垂直分割数,均匀裁切成多个小图片。\n适用于制作拼图、图标集、 sprite sheet 等场景。\n\n示例:\n cutpic -i image.png -w 3 -h 2\n cutpic -i photo.jpg --width 4 --height 4 --out ./output"
)]
struct Args {
/// 📁 输入图片路径(支持 PNG, JPG, JPEG, BMP, GIF, WebP 等格式)
#[arg(short = 'i', long = "input", value_name = "FILE")]
input: PathBuf,
/// 📐 水平方向分割数(将宽度分成几份,默认 1)
#[arg(short = 'w', long = "width", default_value_t = 1, value_name = "NUM")]
width: u32,
/// 📏 垂直方向分割数(将高度分成几份,默认 1)
#[arg(short = 'h', long = "height", default_value_t = 1, value_name = "NUM")]
height: u32,
/// 📂 输出目录(默认为输入文件同级目录下的 dist 文件夹)
#[arg(long = "out", value_name = "DIR", default_value = ".")]
output: PathBuf,
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
let args = Args::parse();
// 加载图片
println!("\n🔄 正在加载图片: {:?}", args.input);
let img = image::open(&args.input)?;
let (width, height) = img.dimensions();
println!("📊 图片尺寸: {}x{} 像素", width, height);
println!("✂️ 分割方式: {}x{} (共 {} 份)", args.width, args.height, args.width * args.height);
// 计算每个子图的尺寸
let tile_width = width / args.width;
let tile_height = height / args.height;
println!("📏 每块尺寸: {}x{} 像素", tile_width, tile_height);
// 确定输出目录:如果用户指定了输出目录则使用,否则在输入文件旁边创建 dist 目录
let output_dir = if args.output != PathBuf::from(".") {
args.output.clone()
} else {
let mut default_output = args.input.parent().unwrap_or(std::path::Path::new(".")).to_path_buf();
default_output.push("dist");
default_output
};
println!("📁 输出目录: {:?}\n", output_dir);
// 创建输出目录(如果不存在)
std::fs::create_dir_all(&output_dir)?;
// 获取文件名(不含扩展名)和扩展名
let file_stem = args
.input
.file_stem()
.and_then(|s| s.to_str())
.unwrap_or("image");
let extension = args
.input
.extension()
.and_then(|s| s.to_str())
.unwrap_or("png");
// 裁切并保存每一块
for row in 0..args.height {
for col in 0..args.width {
let x = col * tile_width;
let y = row * tile_height;
// 最后一块可能需要调整尺寸以适应剩余部分
let actual_width = if col == args.width - 1 {
width - x
} else {
tile_width
};
let actual_height = if row == args.height - 1 {
height - y
} else {
tile_height
};
// 裁切图片
let tile = img.crop_imm(x, y, actual_width, actual_height);
// 生成输出文件名
let index = row * args.width + col + 1;
let output_filename = format!("{}_{}_{}.{}", file_stem, index, args.width * args.height, extension);
let output_path = output_dir.join(output_filename);
// 保存图片
tile.save(&output_path)?;
println!("已保存: {:?}", output_path);
}
}
println!("\n✅ 完成! 共生成 {} 张图片", args.width * args.height);
Ok(())
}