npm package: @krwu/img-resize-wasm
Description: High-performance image resizing in WebAssembly - Built with Rust
Latest version: 0.3.0

# img-resize-wasm

高性能 WebAssembly 图像缩放库，基于 Rust 构建

[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
[![Rust](https://img.shields.io/badge/rust-1.70%2B-orange.svg)](https://www.rust-lang.org/)
[![WebAssembly](https://img.shields.io/badge/WebAssembly-enabled-blue.svg)](https://webassembly.org/)

> **最新版本 v0.2.x** - 使用 `fast_image_resize` 重构，性能提升 2-5 倍，支持单边限制缩放

## ✨ 特性

- 🚀 **超高性能**: 使用 `fast_image_resize` 库和 SIMD 优化，比传统实现快 2-5 倍
- 🎯 **多格式支持**: 支持 JPEG、PNG、GIF、WebP、BMP、TIFF 等主流格式
- 🔧 **多种缩放算法**: 支持 Nearest、Bilinear、CatmullRom、Lanczos3 等高质量算法
- 📐 **等比例缩放**: 自动保持图片宽高比，支持单边限制
- 🎬 **动图保护**: 自动检测 GIF/WebP 动图并保持原样，避免变成静态图
- 💾 **格式保持**: 输出保持原始图片格式
- 🌐 **零依赖**: 纯 WebAssembly 实现，无需额外的 JavaScript 库
- 🎛️ **单边限制**: max_width 或 max_height 可设为 0，表示不限制该维度

## 📦 安装

```bash
npm install @krwu/img-resize-wasm
```

## 🚀 快速开始

```javascript
import init, { resize_image_aspect_ratio } from '@krwu/img-resize-wasm';

// 初始化 WASM 模块
await init();

// 读取图片
const file = document.querySelector('input[type="file"]').files[0];
const imageData = new Uint8Array(await file.arrayBuffer());

// 缩放图片（保持宽高比，最大 800×600）
const result = resize_image_aspect_ratio(imageData, 800, 600, 'lanczos3');

// 使用结果
console.log(`缩放后尺寸: ${result.width}×${result.height}`);
const blob = new Blob([result.data], { type: `image/${result.format}` });
const url = URL.createObjectURL(blob);
```

## 📚 详细用法

### 在 Vite + Vue3 中使用

### 基础用法

```javascript
import { resize_image_aspect_ratio } from '@krwu/img-resize-wasm';

// 读取图片文件
const file = document.getElementById('imageInput').files[0];
const arrayBuffer = await file.arrayBuffer();
const imageData = new Uint8Array(arrayBuffer);

// 等比例缩放（同时限制宽度和高度，取较小比例）
const result = resize_image_aspect_ratio(imageData, 800, 600, 'lanczos3');
console.log(`缩放完成: ${result.width}x${result.height}`);

// 单边限制缩放
// 只限制宽度，高度按比例自动计算
const result2 = resize_image_aspect_ratio(imageData, 800, 0, 'lanczos3');

// 只限制高度，宽度按比例自动计算
const result3 = resize_image_aspect_ratio(imageData, 0, 600, 'lanczos3');

// 不限制任何维度（返回原图）
const result4 = resize_image_aspect_ratio(imageData, 0, 0, 'lanczos3');

// ⚠️ 注意：GIF/WebP 动图会被自动检测并返回原图，避免丢失动画效果
```

### Vue3 组件示例

```vue
<template>
  <div>
    <input type="file" @change="handleImageUpload" accept="image/*">
    <button @click="resizeImage" :disabled="!imageData">缩放图片</button>
    <img v-if="resizedImage" :src="resizedImage" alt="缩放后的图片">
    <div v-if="info">{{ info }}</div>
  </div>
</template>

<script setup>
import { ref } from 'vue';
import { resize_image_aspect_ratio, get_image_dimensions } from '@krwu/img-resize-wasm';

const imageData = ref(null);
const resizedImage = ref(null);
const info = ref('');

async function handleImageUpload(event) {
  const file = event.target.files[0];
  if (!file) {
    return;
  }

  imageData.value = new Uint8Array(await file.arrayBuffer());
  
  // 获取原始尺寸
  const dimensions = get_image_dimensions(imageData.value);
  info.value = `原始尺寸: ${dimensions.width}×${dimensions.height}`;
}

function resizeImage() {
  if (!imageData.value) {
    return;
  }

  try {
    const startTime = performance.now();
    
    const result = resize_image_aspect_ratio(
      imageData.value,
      800,
      600,
      'lanczos3'
    );

    const endTime = performance.now();
    
    // 将图片数据转换为 Blob URL（自动使用原格式）
    const mimeType = `image/${result.format}`;
    const blob = new Blob([result.data], { type: mimeType });
    resizedImage.value = URL.createObjectURL(blob);
    
    info.value = `缩放后: ${result.width}×${result.height}, 格式: ${result.format}, 耗时: ${(endTime - startTime).toFixed(2)}ms`;
  } catch (error) {
    console.error('缩放失败:', error);
    info.value = `缩放失败: ${error}`;
  }
}
</script>
```

### 高级用法示例

#### 1. 响应式图片生成

```javascript
import { resize_image_aspect_ratio } from '@krwu/img-resize-wasm';

// 生成多种尺寸的响应式图片
async function generateResponsiveImages(imageData) {
  const sizes = [
    { name: 'thumbnail', width: 200, height: 0 },
    { name: 'small', width: 480, height: 0 },
    { name: 'medium', width: 800, height: 0 },
    { name: 'large', width: 1200, height: 0 },
  ];

  const results = {};
  
  for (const size of sizes) {
    const result = resize_image_aspect_ratio(
      imageData,
      size.width,
      size.height,
      'lanczos3'
    );
    
    results[size.name] = {
      data: result.data,
      width: result.width,
      height: result.height,
      format: result.format,
    };
  }
  
  return results;
}
```

#### 2. 图片上传前压缩

```javascript
import { resize_image_aspect_ratio } from '@krwu/img-resize-wasm';

async function compressImageBeforeUpload(file, maxWidth = 1920, maxHeight = 1080) {
  // 读取文件
  const arrayBuffer = await file.arrayBuffer();
  const imageData = new Uint8Array(arrayBuffer);
  
  // 缩放压缩
  const result = resize_image_aspect_ratio(
    imageData,
    maxWidth,
    maxHeight,
    'lanczos3'
  );
  
  // 转换为 File 对象
  const blob = new Blob([result.data], { type: `image/${result.format}` });
  const compressedFile = new File([blob], file.name, { type: blob.type });
  
  console.log(`原始大小: ${file.size} bytes`);
  console.log(`压缩后: ${compressedFile.size} bytes`);
  console.log(`压缩率: ${((1 - compressedFile.size / file.size) * 100).toFixed(2)}%`);
  
  return compressedFile;
}
```

#### 3. 批量处理图片

```javascript
import { resize_image_aspect_ratio } from '@krwu/img-resize-wasm';

async function batchResizeImages(files, maxWidth = 800, maxHeight = 600) {
  const results = [];
  
  for (const file of files) {
    try {
      const arrayBuffer = await file.arrayBuffer();
      const imageData = new Uint8Array(arrayBuffer);
      
      const result = resize_image_aspect_ratio(
        imageData,
        maxWidth,
        maxHeight,
        'bilinear'  // 批量处理使用较快的算法
      );
      
      results.push({
        originalName: file.name,
        width: result.width,
        height: result.height,
        format: result.format,
        data: result.data,
      });
    } catch (error) {
      console.error(`处理 ${file.name} 失败:`, error);
    }
  }
  
  return results;
}
```

## API

### `resize_image_aspect_ratio(imageData, maxWidth, maxHeight, filter)`

等比例缩放（保持宽高比）

**参数：**
- `imageData`: `Uint8Array` - 图片二进制数据
- `maxWidth`: `number` - 最大宽度（可设为 0 表示不限制宽度）
- `maxHeight`: `number` - 最大高度（可设为 0 表示不限制高度）
- `filter`: `string` - 缩放算法（`"nearest"`, `"bilinear"`, `"catmullrom"`, `"lanczos3"`）
  - 兼容旧算法名：`"linear"` → `"bilinear"`, `"cubic"` → `"catmullrom"`, `"gaussian"` → `"lanczos3"`

**返回值：** `ResizeResult` 对象

**特殊行为：**
- 🎬 **动图保护**：自动检测 GIF 和 WebP 动图，如果检测到动图会直接返回原图，避免丢失动画效果
- 📏 **智能跳过**：当图片尺寸未超过限制时，自动返回原图（重新编码）
- 🔄 **等比例**：始终保持图片的宽高比，不会产生拉伸变形

### `get_image_dimensions(imageData)`

获取图片尺寸信息

**参数：**
- `imageData`: `Uint8Array` - 图片二进制数据

**返回值：** `ImageDimensions` 对象
- `width`: `number` - 图片宽度
- `height`: `number` - 图片高度

### `get_supported_formats()`

获取支持的图片格式列表

**返回值：** `string[]` - 支持的格式数组

### `get_supported_algorithms()`

获取支持的缩放算法列表

**返回值：** `string[]` - 支持的算法数组

### `ResizeResult`

**属性：**
- `data`: `Uint8Array` - 编码后的图片数据（保持原格式）
- `width`: `number` - 实际宽度
- `height`: `number` - 实际高度
- `format`: `string` - 图片格式（`"jpeg"`, `"png"`, `"gif"`, `"webp"`, `"bmp"`, `"tiff"`）

## 技术特性

### 动图检测与保护

本库内置智能动图检测功能，确保动画不会在缩放过程中丢失：

#### 🎬 自动检测机制

**GIF 动图检测：**
- 使用 `image` crate 的 `AnimationDecoder` 解析 GIF 文件
- 检测帧数：超过 1 帧即判定为动图
- 准确率 100%，不会误报或漏报

**WebP 动图检测：**
- 验证 WebP 文件头（RIFF + WEBP 标记）
- 在文件的 chunks 区域搜索 `ANIM` chunk 标记
- 符合 WebP 格式规范，所有动画 WebP 必须包含 ANIM chunk

#### ⚡ 处理流程

```
输入图片
  ↓
检测格式 (GIF/WebP?)
  ↓
  ├─ 是动图 → 直接返回原图（保护动画）
  └─ 是静态图 → 进行缩放处理
```

#### 💡 为什么这样设计？

1. **技术限制**：`image` crate 在解码动图时只会读取第一帧，如果进行缩放会导致动图变成静态图
2. **性能考虑**：逐帧处理动图性能开销大，在浏览器中可能导致卡顿
3. **用户体验**：保持原始动图总比变成静态图好
4. **简单可靠**：检测并跳过的方案实现简单，不会出错

### 格式处理
- **保持原格式输出**：缩放后的图片保持原始格式，避免不必要的格式转换
  - JPEG → JPEG（质量 85）
  - PNG → PNG（高压缩率）
  - GIF/WebP 动图 → 原图（保护动画）
  - 其他格式也会保持不变
- **避免文件膨胀**：JPEG 图片不会被强制转换为 PNG，保持较小的文件体积
- **保留颜色信息**：保持原始图片的颜色空间和压缩特性

### 缩放流程
```
图片解码 (image crate) → 高性能缩放 (fast_image_resize) → 格式编码 (image crate)
```

### 智能优化
- **自动跳过不必要的缩放**：当图片尺寸未超限时，直接返回原图
- **内存高效**：优化的内存分配和复用策略
- **SIMD 加速**：自动利用 CPU 的 SIMD 指令集

## 🚀 性能优势

### 重构后的性能提升
- **使用 `fast_image_resize` 库**：专为高性能设计，使用 SIMD 指令优化
- **性能提升 2-5 倍**：相比之前的 `image` crate 实现
- **更好的算法支持**：优化的缩放算法映射，质量更高

### 算法性能对比
| 算法 | 质量 | 速度 | 适用场景 |
|------|------|------|----------|
| `nearest` | 低 | 最快 | 像素艺术、图标 |
| `bilinear` | 中 | 快 | 一般用途、实时预览 |
| `catmullrom` | 高 | 中 | 照片缩放 |
| `lanczos3` | 最高 | 慢 | 高质量照片处理 |

### 算法名称更新（推荐）

虽然旧算法名仍然兼容，但推荐更新为新的标准名称：

| 旧名称 | 新名称 | 说明 |
|--------|--------|------|
| `"linear"` | `"bilinear"` | 双线性插值 |
| `"cubic"` | `"catmullrom"` | Catmull-Rom 三次插值 |
| `"gaussian"` | `"lanczos3"` | Lanczos3 算法 |

## 🧪 开发和测试

### 构建项目

```bash
# 安装依赖
cargo build

# 构建 WebAssembly
wasm-pack build --target web --out-dir pkg

# 运行测试
cargo test
```

### 本地测试

```bash
# 启动本地服务器
python -m http.server 8000

# 访问测试页面
# http://localhost:8000/test-new-features.html
```

### 性能测试

在测试页面中可以：
- 测试不同算法的性能差异
- 测试单边限制功能
- 对比不同尺寸图片的处理速度
- 查看详细的处理时间和输出信息

## 📂 项目结构

```
img-resize-wasm/
├── src/
│   └── lib.rs              # 核心 Rust 代码（使用 fast_image_resize）
├── pkg/                    # 构建输出目录（wasm-pack 生成）
├── example/                # 示例文件
├── test-new-features.html  # 新功能测试页面
├── Cargo.toml              # Rust 项目配置
├── README.md               # 项目文档
├── CHANGELOG.md            # 更新日志
├── REFACTOR_SUMMARY.md     # 重构总结
└── build-npm.sh            # NPM 构建脚本
```

## 📊 性能基准

基于实际测试的性能数据（参考值，实际性能因设备而异）：

| 场景 | 原实现 | 新实现 | 提升倍数 |
|------|--------|--------|----------|
| 大图缩放 (4K→1080p) | ~200ms | ~50ms | 4x |
| 中图缩放 (1080p→720p) | ~80ms | ~20ms | 4x |
| 小图缩放 (720p→480p) | ~30ms | ~10ms | 3x |

## ❓ 常见问题

### Q: 为什么要使用 WebAssembly 而不是纯 JavaScript？
A: WebAssembly 提供接近原生的性能，特别是在图像处理这种计算密集型任务中，性能优势明显。本库使用 Rust + WASM，比纯 JS 实现快 2-5 倍。

### Q: 支持哪些图片格式？
A: 支持 JPEG、PNG、GIF、WebP、BMP、TIFF 等主流格式。可以通过 `get_supported_formats()` 函数查看完整列表。

### Q: 如何选择合适的缩放算法？
A: 
- **nearest**: 最快，适合像素艺术、图标等不需要平滑的场景
- **bilinear**: 速度快，质量中等，适合实时预览
- **catmullrom**: 质量高，速度中等，适合照片缩放
- **lanczos3**: 质量最高，速度较慢，适合高质量照片处理

### Q: 单边限制有什么用？
A: 单边限制允许你只限制宽度或高度，另一边自动按比例缩放。例如：
- 制作固定宽度的响应式图片（高度自适应）
- 制作固定高度的横幅图片（宽度自适应）
- 更灵活地控制输出尺寸

### Q: 会改变图片格式吗？
A: 不会。本库会自动保持原始图片格式，JPEG 输出 JPEG，PNG 输出 PNG，GIF/WebP 动图会保持原样，避免不必要的格式转换和文件膨胀。

### Q: 为什么动图不能缩放？
A: GIF 和 WebP 动图包含多帧，如果使用常规方法缩放会丢失动画效果，只剩下第一帧。为了保护动画，本库会自动检测动图并返回原图。如果需要缩放动图，建议：
- 使用专门的动图处理工具
- 在服务端使用支持动图的库（如 `ffmpeg`）
- 或者接受静态化的结果

### Q: 如何判断返回的是原图还是缩放后的图？
A: 可以对比返回结果的尺寸与原图尺寸，或检查控制台日志。当检测到动图时，控制台会输出 "检测到动图，跳过处理，返回原图"。

### Q: 如何处理大图片？
A: 本库使用高效的内存管理和 SIMD 优化，可以处理大图片。但建议在浏览器环境中处理超大图片（如 8K）时注意内存限制。

### Q: 性能提升是如何实现的？
A: 主要通过以下方式：
1. 使用 `fast_image_resize` 库，专门为高性能设计
2. SIMD 指令集优化，充分利用 CPU 并行计算能力
3. 优化的内存管理，减少不必要的内存分配
4. WebAssembly 的接近原生性能

### Q: 兼容性如何？
A: 支持所有现代浏览器（Chrome、Firefox、Safari、Edge）。需要浏览器支持 WebAssembly。

## 🤝 贡献

欢迎提交 Issue 和 Pull Request！

### 贡献指南
1. Fork 本仓库
2. 创建特性分支 (`git checkout -b feature/AmazingFeature`)
3. 提交更改 (`git commit -m 'Add some AmazingFeature'`)
4. 推送到分支 (`git push origin feature/AmazingFeature`)
5. 开启 Pull Request

## 📄 许可证

MIT License - 详见 [LICENSE](LICENSE) 文件

## 🔗 相关链接

- [fast_image_resize](https://github.com/Cykooz/fast_image_resize) - 高性能图像缩放库
- [image-rs](https://github.com/image-rs/image) - Rust 图像处理库
- [wasm-pack](https://rustwasm.github.io/wasm-pack/) - Rust WebAssembly 工具链

---

**Made with ❤️ using Rust and WebAssembly**