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

格式
导出面板顶部的格式分段控件提供三种选择:JPEG、TIFF 和 PNG。每种格式对应 ExportFormat 接口的一个分支(kind: "jpeg" | "tiff" | "png")。
JPEG 提供两个滑块。质量滑块(quality,范围 1–100)控制 JPEG 压缩级别,默认值为 90。最大文件大小滑块(maxBytes)是可选的文件大小上限:将其拖动到零以上可将输出限制在指定兆字节以内。设置后,后端会以逐渐降低的质量重新编码,直到文件大小符合要求。将滑块保持在零表示不限制(maxBytes: null)。
TIFF 和 PNG 共享一个位深度控件(bitDepth: 8 | 16)。选择 16 位可保留渲染的完整精度,这也是默认值。8 位将文件大小减半,但平滑渐变区域会产生量化误差。JPEG 导出不设置 bitDepth 字段,其位深由容器格式固定为 8 位。
分辨率
分辨率控件以像素为单位限制输出图像的最长边。三个预设按钮涵盖最常见的尺寸 —— 完整(不缩小)、4096 px 和 2048 px —— 另有一个自定义模式,展开后显示自由输入框。所选值通过 resizeLongEdge 传递给后端;后端缩小图像,使其最长边等于该值,同时保持原始宽高比。选择完整(或输入 0)会将 resizeLongEdge 设为 null,以原生分辨率导出。
该上限在裁剪和旋转之后应用:若将竖幅图像裁剪为正方形后以 2048 px 导出,输出尺寸为 2048 × 2048。不执行放大 —— 如果原生最长边已短于上限,则以原生尺寸导出。
批量裁剪
导出面板中的裁剪所选开关启用一个统一裁剪,在导出时应用于每张所选图像。关闭开关时,每张图像使用其在裁剪与除尘面板中保存的裁剪 —— 若未设置则不裁剪。
打开开关后,裁剪预览和完整的裁剪控件(宽高比预设、旋转、翻转、校正角度)随即显示。预览以显示顺序中第一张所选图像为目标。裁剪从该图像已提交的裁剪参数中取种,若无则默认为全幅。更改选中图像不会重新取种;调整选择时控件保持稳定。
分辨率逻辑由 batchCrop.ts 中的 resolveCrop 处理:批量模式开启时返回共享草稿作为已提交的 CropRect;关闭时返回每张图像自身存储的 CropRect(未裁剪图像返回 null)。导出不会将批量裁剪写回目录,每张图像自身保存的裁剪完好无损。
文件名冲突处理
写入任何文件之前,导出会预先解析所有输出路径,并检查哪些路径在磁盘上已存在。对于每个冲突的文件名,会弹出提示并提供四个选项:
- 覆盖 —— 替换已有文件。
- 跳过 —— 保留已有文件,从本次导出中略过该图像。
- 两者都保留 —— 后端生成唯一文件名(例如
frame-1.jpg→frame-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 位输出为 Uint8Array(exportPixels 上传的原始扫描数据是另一个独立的整数缓冲区)。CPU 回退(api.exportImage)在 Rust 中运行等效管线。
并发:有界工作者池大小为 4(CONCURRENCY = 4),并行处理导出。同步 GPU 渲染步骤(renderExport)绝不并发等待 —— 每个工作者在任何 await 之前以同步索引自增领取下一张图像,因此不同工作者的 GPU 调用不会交错。
HDR 导出:当 wantsHdrExport(kind, params) 返回 true(适用于 HDR 色调映射内容的增益图 JPEG)时,完全跳过 GPU/SDR 路径,由 api.exportImageHdr 在 Rust 后端执行双重渲染。该路径独立于上述标准导出流程。