jimp — 纯 JS 位图图像处理库
已复核jimp 是一个纯 JavaScript 图像处理库。日常类比:它像一张铺在桌上的像素桌布——先把图解码成 { width, height, data: Buffer } 的 RGBA 位图,每个插件立刻改这张桌布,最后再按 MIME 编码回去。
默认导出是工厂拼出来的:
import { Jimp, createJimp } from "jimp";import png from "@jimp/js-png";import resize from "@jimp/plugin-resize";
const image = await Jimp.read("./input.png");image.resize({ w: 300 }).blur(2).greyscale();await image.write("./output.jpg");
const Tiny = createJimp({ formats: [png], plugins: [resize.methods] });jimp@1.6.1 的便利包已经挂上默认 formats 与 plugins;需要瘦身时才自己 createJimp。
不理解固定 1.6.1 的合同,下面这些事都没法解释:
- 为什么
image.resize({ w: 300 })能跑,而便利包注释里的resize(256, 100)对不上 plugin schema - 为什么默认
Jimp能写 JPEG/PNG,却不能假装自带 WebP - 为什么
greyscale()不是把 RGB 简单平均 - 为什么
Jimp.read("./x.png")在找不到文件时会改去fetch这段字符串
jimp 的工作可以拆成四段:
-
工厂装配:
createJimp({ formats, plugins })生成 class。默认 formats 是 bmp / GIF / JPEG / PNG / TIFF;WebP、AVIF 在@jimp/wasm-*,没有进便利导出。 -
读入位图:
fromBuffer用file-type认 MIME,再交给对应 decoder,然后attemptExifRotate。read(string)先existsSync,否则当 URLfetch。 -
立刻改
this.bitmap:plugin 返回带bitmap的对象时,包装器写回当前实例并return this。这是同步原地修改,不是 sharp 那种延迟 options。 -
按 MIME 写出:
getBuffer("image/jpeg")走 encoder;没有 alpha 的格式会先把图合成到background上。write(path)用mime.getType(path)推 MIME。
案例 1:v1 按需装配,不要抄错 resize 签名
Section titled “案例 1:v1 按需装配,不要抄错 resize 签名”import { createJimp } from "@jimp/core";import png from "@jimp/js-png";import * as resize from "@jimp/plugin-resize";
const Jimp = createJimp({ formats: [png], plugins: [resize.methods],});const image = await Jimp.read("./input.png");image.resize({ w: 300, h: 200 });plugin-resize 的 zod schema 只要 { w, h?, mode? }(或只给 h)。空图构造则是 new Jimp({ width, height, color }),不再接受旧的位置参数。
案例 2:Buffer 入口,而不是假装任意运行时都能 read(path)
Section titled “案例 2:Buffer 入口,而不是假装任意运行时都能 read(path)”const buf = Buffer.from(await (await fetch(url)).arrayBuffer());const img = await Jimp.fromBuffer(buf);img.resize({ w: 200 });const out = await img.getBuffer("image/jpeg");Node 的 @jimp/file-ops 直接 re-export fs。路径读写依赖文件系统;跨环境应走 fromBuffer / getBuffer。核心类型里 bitmap.data 仍是 Buffer。本文未在 Worker 里跑通这条链。
案例 3:clone 才会复制像素,链式调用已经改完原图
Section titled “案例 3:clone 才会复制像素,链式调用已经改完原图”const original = await Jimp.read("./input.png");const small = original.clone().resize({ w: 300 });original.greyscale();clone() 是 Buffer.from(bitmap.data) 的新实例。resize / blur / greyscale 都已经改掉调用者的位图;需要原图时必须先 clone。
-
把便利包 README 式
resize(w, h)当真:固定 plugin 只要对象。w或h可以省略一个,按宽高比补齐。 -
默认包没有 WebP/AVIF:要这两种格式需另接
@jimp/wasm-webp/@jimp/wasm-avif,不能从JimpMime里找。 -
greyscale是 Rec. 709:系数0.2126 / 0.7152 / 0.0722。旧印象里的“三通道平均”不成立。 -
blur也不是 StackBlur:plugin 头注释写的是 Superfast Blur / FastBlur.js。 -
read的字符串语义有分叉:本地文件不存在时会当 URL 去 fetch,错误信息也会变成Could not load Buffer from URL。 -
空图内存按
w * h * 4分配:这是 Buffer 字节数合同,不是对 4K 图 64MB 或编码耗时的测量。
适用 vs 不适用场景
Section titled “适用 vs 不适用场景”适用:
- 需要纯 JS、可按格式/插件裁剪包体的 Node 18+ 或浏览器打包目标
- 中小位图的缩放、裁剪、水印、简单颜色变换
- 已经能提供 Buffer / ArrayBuffer,而不是必须走
fs
不适用:
- 高吞吐图床或服务端热路径——应评估 sharp 的 libvips 管线
- 默认就要 WebP/AVIF——便利包没有这两条 decoder/encoder
- 把“Cloudflare Worker 一定能跑”写成保证——固定核心仍用
Buffer与可选fs - 需要延迟融合多步操作——jimp 每步都同步改位图
固定版本边界
Section titled “固定版本边界”- 本文绑定
jimp-dev/jimp@7e6a9569...,Git tag 与 npmgitHead均为1.6.1。 jimp与@jimp/core在该提交都是1.6.1,engines.node为>=18。- JPEG/PNG 内核分别是
jpeg-js与pngjs;MIME 探测走file-type。 - 本文未安装依赖、运行 vitest、访问远程图或测量 heap,状态保持
UNVERIFIED。
- 便利包 ≠ 最小核——
Jimp预装默认插件;瘦身必须自己createJimp。 - 立刻执行是特征不是缺陷——和 sharp 对照时,先问“options 还是 bitmap 已经被改了”。
- 格式矩阵要读导出,不读印象——默认没有 WebP,wasm 包是另一条装配线。
- 路径 API 绑定 Node fs——跨运行时先改走 Buffer,再谈环境能不能跑。
image.resize(300, 200)在固定 1.6.1 的@jimp/plugin-resize上会按宽高缩放吗?- 只
import { Jimp } from "jimp",不额外挂 wasm 插件,getBuffer("image/webp")会成功吗? Jimp.read("missing.png")在当前工作目录没有该文件时,下一步做什么?
检查点:
- 不会按这个签名工作。plugin 解析的是
{ w, h?, mode? }。 - 不会。默认 formats 不含 WebP。
existsSync失败后把字符串当 URLfetch。
- 固定源码:jimp-dev/jimp —— 本文绑定提交
7e6a95694e00a8b6f1bdd9aad709f5413eb9b08c - 对照:lovell/sharp —— 延迟 options + libvips
- PNG 内核:lukeapage/pngjs
- 模糊算法出处:quasimondo FastBlur