diff --git a/docker/frontend/scripts/raw-orf-check.mjs b/docker/frontend/scripts/raw-orf-check.mjs index 2ede25d..8036ab7 100644 --- a/docker/frontend/scripts/raw-orf-check.mjs +++ b/docker/frontend/scripts/raw-orf-check.mjs @@ -24,8 +24,9 @@ const transpile = (path) => // `self.onmessage` / `self.postMessage`, where Node has parentPort. So the worker // is booted behind a shim that renames one into the other, and the package's own // message protocol ({ id, fn, args } / { id, out, error }) is spoken here. -// The worker is handed the file bytes copy itself — the transfer list would -// detach the caller's. +// The file bytes are handed over in the transfer list exactly as the package +// does — so opening detaches them, and this check fails on any develop that +// forgets to keep the caller's array intact. const LIBRAW_NODE_SRC = ` import { Worker } from 'node:worker_threads'; @@ -68,10 +69,7 @@ export default class LibRaw { this.worker.postMessage({ id, fn, args }, transfer); }); } - open(bytes, settings) { - const copy = Uint8Array.from(bytes); - return this.run('open', copy, settings); - } + open(bytes, settings) { return this.run('open', bytes, settings); } metadata(full) { return this.run('metadata', !!full); } imageData() { return this.run('imageData'); } thumbnailData() { return this.run('thumbnailData'); } diff --git a/docker/frontend/src/engine/rawDevelop.ts b/docker/frontend/src/engine/rawDevelop.ts index 23eeb89..a81c5e9 100644 --- a/docker/frontend/src/engine/rawDevelop.ts +++ b/docker/frontend/src/engine/rawDevelop.ts @@ -386,7 +386,9 @@ export async function rawThumbnail(bytes: Uint8Array, fileName?: string): Promis const raw = new LibRaw(); try { - await raw.open(bytes as unknown as BufferSource, SETTINGS); + // The same transfer as in the develop: the worker is handed a copy, so the + // caller's bytes stay whole for whatever the catalogue reads off them next. + await raw.open(bytes.slice() as unknown as BufferSource, SETTINGS); return await cameraPreview(raw); } catch { return tiffThumbnail(bytes); @@ -565,8 +567,17 @@ export async function developRaw(bytes: Uint8Array): Promise { try { // Extract largest embedded JPEG preview from file bytes first preview = extractEmbeddedJpeg(bytes); - // LibRaw copies the buffer it is handed, so the caller's bytes stay intact. - await raw.open(bytes as unknown as BufferSource, SETTINGS); + // libraw-wasm moves the buffer it is handed into its worker — the argument is + // in the transfer list of its postMessage — so opening DETACHES the array the + // caller passed. This line used to believe the package's own doc that it + // copies: the preview above is a view of that same buffer, so the open emptied + // it, previewGrid handed Skia a detached array ("Cannot perform Construct on a + // detached or out-of-bounds ArrayBuffer"), and the catch then returned that + // same emptied preview AS the develop — every RAW reached the studio as zero + // bytes, which is the "Could not develop this RAW file" the LIBRARY reported. + // The caller's bytes are also what the EXIF stamps are read off afterwards, so + // the worker is handed a copy of its own to move instead. + await raw.open(bytes.slice() as unknown as BufferSource, SETTINGS); if (!preview) { preview = await cameraPreview(raw); }