DPaint.js颜色量化原理揭秘:Web Worker + 中位切分算法实现16位色减色

📅 2026/8/27 15:24:02
DPaint.js颜色量化原理揭秘:Web Worker + 中位切分算法实现16位色减色
DPaint.js颜色量化原理揭秘Web Worker 中位切分算法实现16位色减色【免费下载链接】DPaint-jsWebbased image editor, modeled after the legendary Deluxe Paint with a focus on retro Amiga file formats: read and write Amiga icon files and IFF ILBM images/animations项目地址: https://gitcode.com/gh_mirrors/dp/DPaint-jsDPaint.js 是一款致敬 Amiga 传奇软件 Deluxe Paint 的网页版图像编辑器而颜色量化把全彩图压缩成 16 色正是它的看家本领。本文将带你快速看懂它的实现主线程把像素数据丢给Web WorkerWorker 内用中位切分算法Median Cut从颜色直方图中切出色板再可选切换到 K-Means 方案全程不卡顿。为什么减色要搬去后台在浏览器里把一张 320×200 的动画每一帧都从 24 位色减到 16 色计算量不小扫描全部像素统计每个颜色出现多少次直方图用算法从几千个颜色里挑出最有代表性的 16 个把每个像素重映射到色板上的最近色并叠加抖动Dithering。如果这些工作放在主线程界面会明显冻住。DPaint.js 的解法很经典调度入口在_script/util/imageProcessing.js的processImage函数约第 566 行它负责准备数据真正耗时的量化工作则交给独立的 Web Worker 文件_script/workers/quantize.js—— 中位切分默认方案_script/workers/quantize2.js—— K-Means备选方案调度逻辑非常直观简化后let worker quantizationMethod 1 ? new Worker(new URL(../workers/quantize2.js, import.meta.url)) : new Worker(new URL(../workers/quantize.js, import.meta.url)); worker.postMessage({ imageData, count: colorCount }); worker.addEventListener(message, (e) { remapImage(canvas, e.data.palette, ditherPattern); // 拿到色板后回到主线程重映射像素 });也就是说Worker 只负责算出色板主线程负责按色板改像素职责分离干净利落。中位切分算法把颜色空间切成 16 个盒子quantize.js是全文最值得细读的一个文件核心流程分四步① 建直方图——遍历像素透明像素直接跳过用Map统计每个 RGB 值出现的次数。② 压成扁平数组——把直方图转成Uint32Array格式为[r, g, b, count, r, g, b, count, ...]。用 TypedArray 而不是对象数组是为了让后续排序和统计跑得飞快。③ 中位切分——medianCut函数约第 58 行是算法心脏思路可以一句话概括反复挑选人数最多的颜色盒子沿它最宽的 RGB 通道排序后从正中间一刀切成两半直到切出目标数量比如 16个盒子为止。具体细节每个盒子只是数组的一个区间[start, end)不复制数据选盒子时比较的是盒子内像素总数totalCount而不是颜色种类数——这样高频区域会被优先细分色板更贴合画面切分方向选 R/G/B 中范围最宽的那个通道maxRange切点找的是像素数中位数位置累计 count 达到一半处所以是中位切分排序用的是针对 4 元组写的快速排序quicksortColors按通道值交换整组r, g, b, count。④ 取平均色——每个盒子最后取按出现次数加权的平均颜色getAverageColor16 个盒子得到 16 色色板postMessage回主线程。备选方案K-Means Lab 色空间在调色板面板_script/ui/palette.js第 31 行里用户可以二选一0 Median Cut中位切分1 K-Means对应的quantize2.js走的是另一条技术路线Lab 色空间rgbToLab先把 RGB 转成人眼感知更均匀的 CIE Lab感知距离用 ΔE76getDeltaE76代替朴素 RGB 欧氏距离来衡量两个颜色看起来有多近这样选出的 16 色在观感上更接近原图迭代聚类随机取像素当质心 → 分配像素 → 重算质心最多 10 轮或收敛即止位深约束若目标是 EHB 之外的低位深如 3bit/通道quantizeColor会把质心吸附到合法的色阶上。两种算法的取舍一目了然中位切分快且结果稳定K-Means 感知更准但更耗时间。三个隐藏的性能细节读源码时还能看到几处很实用的优化值得学习像素抽样图像超过 25 万像素时自动按步长抽样step Math.ceil(pixelCount / 250000)直方图质量和速度取得平衡——对量化来说抽样几乎不影响结果色板缓存imageProcessing.js会把算出的色板存进paletteReduced同一图像再换抖动方案时直接复用跳过 Worker一次性 WorkerWorker 算完立即self.close()释放内存每次新建、用完即弃简单可靠。自己动手跑一遍想要完整体验这条量化流水线只需按下面步骤操作在 DPaint.js 中打开或新建一张 320×200 的 24 位色图打开调色板编辑器把目标颜色数设为16对应 4bit 色深即 ILBM 常见的 16 色模式选择量化算法Median Cut 或 K-Means与抖动方式点击应用观察状态栏进度主线程全程保持流畅减色完成后色板即替换生效可继续导出 IFF ILBM。配合项目内的测试用例如tests/ham_export.spec.js还能看到量化与 HAM 导出是如何衔接的。小结模块文件路径职责主线程调度_script/util/imageProcessing.js准备像素数据、缓存色板、应用抖动重映射中位切分 Worker_script/workers/quantize.js直方图 Median Cut 生成 16 色色板K-Means Worker_script/workers/quantize2.jsLab 色空间 ΔE 距离聚类量化算法选择_script/ui/palette.jsUI 中的 Median Cut / K-Means 开关DPaint.js 用一套主线程轻量调度 Worker 内经典算法 TypedArray 数据布局的组合把颜色量化这件重活做得又快又稳——这正是复古像素工具在现代浏览器中流畅运行的关键。✨【免费下载链接】DPaint-jsWebbased image editor, modeled after the legendary Deluxe Paint with a focus on retro Amiga file formats: read and write Amiga icon files and IFF ILBM images/animations项目地址: https://gitcode.com/gh_mirrors/dp/DPaint-js创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考