导出

导出将已显影的图像转换为成品文件。对话框让你选择格式、设置分辨率上限、为所有所选图像应用可选的统一裁剪,并处理文件名冲突 —— 整个过程通过与单帧实时预览相同的管线渲染。

OpenEnlarge 导出对话框,显示图像网格、格式控件和导出按钮
导出对话框 —— 在网格中选择图像,配置格式与分辨率,然后导出。

格式

导出面板顶部的格式分段控件提供三种选择:JPEGTIFFPNG。每种格式对应 ExportFormat 接口的一个分支(kind: "jpeg" | "tiff" | "png")。

JPEG 提供两个滑块。质量滑块(quality,范围 1–100)控制 JPEG 压缩级别,默认值为 90。最大文件大小滑块(maxBytes)是可选的文件大小上限:将其拖动到零以上可将输出限制在指定兆字节以内。设置后,后端会以逐渐降低的质量重新编码,直到文件大小符合要求。将滑块保持在零表示不限制(maxBytes: null)。

TIFFPNG 共享一个位深度控件(bitDepth: 8 | 16)。选择 16 位可保留渲染的完整精度,这也是默认值。8 位将文件大小减半,但平滑渐变区域会产生量化误差。JPEG 导出不设置 bitDepth 字段,其位深由容器格式固定为 8 位。

分辨率

分辨率控件以像素为单位限制输出图像的最长边。三个预设按钮涵盖最常见的尺寸 —— 完整(不缩小)、4096 px2048 px —— 另有一个自定义模式,展开后显示自由输入框。所选值通过 resizeLongEdge 传递给后端;后端缩小图像,使其最长边等于该值,同时保持原始宽高比。选择完整(或输入 0)会将 resizeLongEdge 设为 null,以原生分辨率导出。

该上限在裁剪和旋转之后应用:若将竖幅图像裁剪为正方形后以 2048 px 导出,输出尺寸为 2048 × 2048。不执行放大 —— 如果原生最长边已短于上限,则以原生尺寸导出。

批量裁剪

导出面板中的裁剪所选开关启用一个统一裁剪,在导出时应用于每张所选图像。关闭开关时,每张图像使用其在裁剪与除尘面板中保存的裁剪 —— 若未设置则不裁剪。

打开开关后,裁剪预览和完整的裁剪控件(宽高比预设、旋转、翻转、校正角度)随即显示。预览以显示顺序中第一张所选图像为目标。裁剪从该图像已提交的裁剪参数中取种,若无则默认为全幅。更改选中图像不会重新取种;调整选择时控件保持稳定。

分辨率逻辑由 batchCrop.ts 中的 resolveCrop 处理:批量模式开启时返回共享草稿作为已提交的 CropRect;关闭时返回每张图像自身存储的 CropRect(未裁剪图像返回 null)。导出不会将批量裁剪写回目录,每张图像自身保存的裁剪完好无损。

文件名冲突处理

写入任何文件之前,导出会预先解析所有输出路径,并检查哪些路径在磁盘上已存在。对于每个冲突的文件名,会弹出提示并提供四个选项:

  • 覆盖 —— 替换已有文件。
  • 跳过 —— 保留已有文件,从本次导出中略过该图像。
  • 两者都保留 —— 后端生成唯一文件名(例如 frame-1.jpgframe-1 (2).jpg),两个文件共存。
  • 取消 —— 立即中止整个导出,此阶段尚未写入任何文件。

当存在多个冲突时,提示会显示「冲突 N / M」,以及一个应用于其余所有冲突复选框。在关闭提示前勾选它,所选操作将应用于本次运行中的所有后续冲突,大批量导出无需反复手动选择。

跳过的图像不计入最终的「已导出 N 张照片」摘要,而是单独报告为「跳过 N 张」。如果所有选中图像均被跳过,导出以零文件写入完成,并显示跳过数量。

GPU/CPU 导出一致性

导出通过与实时预览相同的管线渲染。一个专用的离屏 FinishRenderer(即单帧视口所用的同一套 WebGL 着色器)执行完整的反转与润色处理 —— 结果与屏幕上所见的内容像素精确一致。只要 WebGL 可用且图像尺寸在设备最大纹理限制以内,均采用 GPU 路径。

若 GPU 路径不可用 —— 无 WebGL 支持、图像超过纹理尺寸限制,或运行时 GL 错误 —— 导出会自动无感回退到 CPU 路径。CPU 回退使用相同的反转参数,产生相同的结果;仅对大文件速度较慢。无需任何手动操作,回退自动触发。

导出最多同时处理四张图像。GPU 渲染步骤是同步的(每张图像原子执行),因此并发工作者的 GPU 调用不会交错;并发仅发生在后端处理的异步解码和编码步骤上。

深入原理

ExportFormat(定义于 api.ts):kind: "jpeg" | "tiff" | "png"bitDepth?: 8 | 16(仅 TIFF/PNG);quality?: number(仅 JPEG,1–100);maxBytes?: number | null(仅 JPEG —— 后端以逐步降低质量的方式重新编码,直到文件字节数不超过该值);resizeLongEdge?: number | null(null 或 0 → 完整分辨率)。

可导出图像:导出对话框仅列出当前文件夹范围内已显影的图像(eligible.ts 中的 developedFolderImages)。已导入但从未在单帧界面打开的图像不在导出选项中。选择集从当前网格/胶片条选择中初始化,限制在可导出集合内;若所选图像中无可导出项,则默认选中所有可导出图像。

输出文件名naming.ts 中的 outName 将源文件扩展名替换为目标格式的扩展名(JPEG 为 jpg,TIFF 为 tiff,PNG 为 png),保留原始文件名主干。例如,SCAN_0042.tif 导出为 JPEG 后变为 SCAN_0042.jpg

GPU 导出流程api.exportBegin 在 Rust 后端解码 RAW 文件,返回像素数据和已计算的 InvertParams 统一变量;api.exportPixels 传输原始像素缓冲区;离屏 FinishRenderer 执行 renderExport(反转 + 润色着色器);api.exportFinish 将渲染结果编码为所选格式并写入文件。renderExport 交给 exportFinish渲染后缓冲区,16 位 TIFF/PNG 为 Float32Array,8 位输出为 Uint8ArrayexportPixels 上传的原始扫描数据是另一个独立的整数缓冲区)。CPU 回退(api.exportImage)在 Rust 中运行等效管线。

并发:有界工作者池大小为 4(CONCURRENCY = 4),并行处理导出。同步 GPU 渲染步骤(renderExport)绝不并发等待 —— 每个工作者在任何 await 之前以同步索引自增领取下一张图像,因此不同工作者的 GPU 调用不会交错。

HDR 导出:当 wantsHdrExport(kind, params) 返回 true(适用于 HDR 色调映射内容的增益图 JPEG)时,完全跳过 GPU/SDR 路径,由 api.exportImageHdr 在 Rust 后端执行双重渲染。该路径独立于上述标准导出流程。

下一篇:术语表 →

OpenEnlarge —— 开源胶片扫描编辑器 · MIT 许可