> ## Documentation Index
> Fetch the complete documentation index at: https://bun.ll1025.cn/llms.txt
> Use this file to discover all available pages before exploring further.

# Image

> 使用快速原生管线解码、转换和编码图像

`Bun.Image` 是一个可链式调用的图像管线，用于解码、调整大小、旋转和重新编码 JPEG、PNG、WebP、HEIC 和 AVIF — 基于 libjpeg-turbo、spng、libwebp 和 SIMD 几何内核，无 npm 依赖，无原生插件构建步骤。

```ts theme={null}
await Bun.file("photo.jpg").image().resize(400, 400, { fit: "inside" }).webp({ quality: 80 }).write("thumb.webp");
```

API 设计仿照 [Sharp](https://sharp.pixelplumbing.com/)：从输入构建，链式调用转换，选择输出格式，然后 `await` 一个终端方法。在终端方法被 await 之前不会执行任何工作，并且工作在线程外运行。

## 输入

构造函数接受路径、字节或 `Blob` — 包括 `Bun.file()` 和 `Bun.s3.file()`。`Blob#image()` 是 `new Bun.Image(blob)` 的简写：

```ts theme={null}
new Bun.Image("./photo.jpg"); // 文件路径
new Bun.Image(buffer); // Buffer / ArrayBuffer / TypedArray
new Bun.Image(Bun.file("photo.jpg")); // BunFile（延迟读取，线程外）
Bun.file("photo.jpg").image(); // 与上面相同
Bun.s3.file("bucket/photo.jpg").image(); // S3File
```

格式从字节中嗅探 — 扩展名和 `Content-Type` 被忽略。

**路径字符串是文件系统路径。** 不要直接将用户控制的字符串传递给构造函数 — 那是任意文件读取的原语。将不受信任的输入读取到 `Buffer` 中（使用 `fetch` 或 `Bun.file` 和您自己的验证）然后传递字节。

当传递 `TypedArray`/`ArrayBuffer` 时，不要在终端操作挂起时对其进行变异 — 解码在线程外运行并借用字节。`SharedArrayBuffer` 和可调整大小的缓冲区会被拒绝；使用 `buf.slice()` 传递固定视图。

第二个 `options` 参数用于防止解压炸弹并控制 EXIF 处理：

```ts theme={null}
new Bun.Image(input, {
  // 如果 width*height > 此值则拒绝。读取头部后检查，
  // 在分配像素缓冲区之前。默认值与 Sharp 相同（~268 MP）。
  maxPixels: 4096 * 4096,
  // 在任何其他操作之前应用 JPEG EXIF 方向。默认：true。
  autoOrient: true,
});
```

## 元数据

无需解码像素数据即可读取 `width`、`height` 和 `format`：

```ts theme={null}
const { width, height, format } = await new Bun.Image(input).metadata();
// => { width: 1920, height: 1080, format: "jpeg" }
```

## 调整大小

```ts theme={null}
img.resize(800); // 宽度 800，保持宽高比
img.resize(800, 600); // 精确 800×600（拉伸）
img.resize(800, 600, { fit: "inside" }); // 适应 800×600 内
img.resize(800, 600, { withoutEnlargement: true }); // 从不放大
img.resize(800, 600, { filter: "mitchell" });
```

| `fit`        | 行为                      |
| ------------ | ----------------------- |
| `"fill"`（默认） | 拉伸为精确的 `width × height` |
| `"inside"`   | 保持宽高比；结果适应于盒子**内部**     |

`filter` 选择重采样内核。默认的 `"lanczos3"` 是照片的正确选择。

| 过滤器                       | 使用场景                                       |
| ------------------------- | ------------------------------------------ |
| `"lanczos3"` *（默认）*       | 通用，照片最清晰                                   |
| `"lanczos2"`              | 稍柔和，较少的振铃伪影                                |
| `"mitchell"`              | 平滑渐变；经典双三次折衷                               |
| `"cubic"`                 | Catmull-Rom — 比 Mitchell 更锐利，可能会振铃         |
| `"mks2013"` / `"mks2021"` | "Magic Kernel Sharp"；Facebook/Instagram 使用 |
| `"bilinear"` / `"linear"` | 快速，柔和                                      |
| `"box"`                   | 区域平均；适合大的整数缩小                              |
| `"nearest"`               | 像素艺术 / 硬边缘                                 |

当源文件是 JPEG 且目标大小不超过源文件一半时，解码会直接跳到最近的 M/8 IDCT 缩放，因此从 24 MP 照片生成缩略图永远不会创建全分辨率缓冲区。

## 旋转 · 翻转

```ts theme={null}
img.rotate(90); // 顺时针 90°（仅支持 90 的倍数）
img.flip(); // 垂直镜像（关于 x 轴）
img.flop(); // 水平镜像（关于 y 轴）
```

## 调制

```ts theme={null}
img.modulate({
  brightness: 1.2, // 1 = 不变
  saturation: 0, // 0 = 灰度，1 = 不变，>1 = 增强
});
```

## 输出格式

调用格式方法设置编码目标；如果没有设置，则复用源格式。

```ts theme={null}
img.jpeg({ quality: 85 }); // 1–100，默认 80
img.png({ compressionLevel: 6 }); // zlib 级别 0–9
img.png({ palette: true, colors: 64, dither: true }); // 索引 PNG
img.webp({ quality: 80 });
img.webp({ lossless: true });
img.heic({ quality: 80 }); // 仅 macOS / Windows
img.avif({ quality: 60 }); // 仅 macOS / Windows
```

`palette: true` 将图像量化到 ≤256 色调色板，并输出索引（颜色类型 3）PNG，可选 Floyd–Steinberg `dither`。对于截图和 UI 资源，这通常比真彩色小 3–5 倍。

## 终端方法

管线在 await 以下方法之一之前不会执行任何工作：

```ts theme={null}
await img.bytes(); // Uint8Array
await img.buffer(); // Buffer
await img.blob(); // Blob，.type 设置为输出 MIME
await img.toBase64(); // string
await img.dataurl(); // "data:image/png;base64,…"
await img.write("out.webp"); // number（写入的字节数）
await img.write(Bun.s3.file("bucket/out.webp"));
```

`.write()` 接受与 `Bun.write` 相同的目的地 — 路径字符串、`Bun.file()`、`Bun.s3.file()` 或 fd。如果您没有链式调用格式方法，并且目的地是路径字符串，则扩展名决定格式（`.jpg`/`.png`/`.webp`/`.heic`/`.avif`）。

## 占位符

对于在真实图片加载之前嵌入 HTML 的低质量占位符，`.placeholder()` 返回一个 [ThumbHash](https://evanw.github.io/thumbhash/) 渲染的 ≤32px 模糊 `data:` URL — 大约 400–700 字节，无需客户端解码器：

```ts theme={null}
const lqip = await Bun.file("hero.jpg").image().placeholder();
// <img src={lqip} … /> — 然后在加载时切换到真实 URL。
```

对于图像**本身**的由粗到细渲染，编码为渐进 JPEG：

```ts theme={null}
img.jpeg({ progressive: true });
```

第一个终端方法 resolve 后，`img.width` 和 `img.height` 反映**输出**尺寸（在此之前为 `-1`）。

## `Bun.serve` 集成

`Bun.Image` 管线是有效的 `Response` 主体，并自动设置 `Content-Type`。要在服务器处理器中将编码保持在线程外，首先 await 一个终端方法：

```ts theme={null}
Bun.serve({
  routes: {
    "/avatar/:id": async req => {
      // 在触及文件系统之前验证（参见上方的输入说明）。
      if (!/^[a-z0-9]+$/.test(req.params.id)) return new Response(null, { status: 400 });
      const out = await Bun.file(`avatars/${req.params.id}.png`).image().resize(128, 128).webp().blob();
      return new Response(out);
    },
  },
});
```

直接传递管线（`new Response(img)`）也可以，但会在主体初始化期间同步运行编码。

## 剪贴板

```ts theme={null}
const img = Bun.Image.fromClipboard();
if (img) {
  const png = await img.resize(800, 800, { fit: "inside" }).png().bytes();
}
```

`fromClipboard()` 从 macOS 和 Windows 的系统粘贴板读取 PNG、TIFF、HEIC、JPEG、WebP、GIF 或 BMP；常规解码管线会接管后续处理。如果没有图像则返回 `null`，在 Linux 上始终返回 `null` — 自行调用 `wl-paste`/`xclip` 并将字节传递给构造函数。

对于"图像在剪贴板，按 ⌘V"的被动提示，轮询 `clipboardChangeCount()`（单次整数读取）并在其变化时调用 `hasClipboardImage()`；macOS 没有剪贴板变化通知，因此这是文档化的模式。

## 平台后端

|                   | Linux                            | macOS             | Windows      |
| ----------------- | -------------------------------- | ----------------- | ------------ |
| JPEG / PNG / WebP | libjpeg-turbo · spng · libwebp   | 相同                | 相同           |
| BMP / GIF（解码）     | 内置                               | ImageIO           | WIC          |
| TIFF（解码）          | ❌                                | ImageIO           | WIC          |
| 调整大小 / 旋转 / 翻转    | Highway SIMD                     | Accelerate vImage | Highway SIMD |
| HEIC / AVIF       | ❌ `ERR_IMAGE_FORMAT_UNSUPPORTED` | ImageIO ²         | WIC ¹        |
| 剪贴板               | ❌ 返回 `null`                      | NSPasteboard      | Win32        |

¹ Windows 需要来自 Microsoft Store 的 **HEIF Image Extensions** / **AV1 Video Extension**。
² AVIF *编码* 需要操作系统的 AV1 编码器 — 仅 Apple Silicon M3+。Intel Mac 和 M1/M2 会拒绝并返回 `ERR_IMAGE_FORMAT_UNSUPPORTED`；AVIF *解码* 在 ImageIO 支持的任何地方都可以工作（macOS 13+）。

当系统后端的格式在当前机器上不可用时，终端方法会 reject 并返回 `error.code === "ERR_IMAGE_FORMAT_UNSUPPORTED"` — 在此条件分支上回退到可移植格式：

```ts theme={null}
const out = await img
  .avif({ quality: 50 })
  .bytes()
  .catch(e => {
    if (e.code === "ERR_IMAGE_FORMAT_UNSUPPORTED") return img.webp({ quality: 80 }).bytes();
    throw e;
  });
```

由系统后端处理的格式（TIFF、HEIC、AVIF、剪贴板）继承**操作系统的**补丁级别 — 请保持 macOS / Windows 更新。JPEG、PNG 和 WebP 在所有平台上都经过相同的静态链接编解码器，因此编码输出在 Linux、macOS 和 Windows 上字节相同。要强制几何操作也使用可移植的 Highway 路径（例如用于金图测试），请设置进程级后端：

```ts theme={null}
Bun.Image.backend = "bun"; // macOS/Windows 上默认为 "system"
```
