Skip to content

Downscale previews and cap export decode to fix mobile blackouts#74

Merged
ankitsejwal merged 1 commit into
mainfrom
perf-downscale-previews
Jul 2, 2026
Merged

Downscale previews and cap export decode to fix mobile blackouts#74
ankitsejwal merged 1 commit into
mainfrom
perf-downscale-previews

Conversation

@ankitsejwal

Copy link
Copy Markdown
Member

Fixes intermittent blank/black photos on mobile (both on-screen and, correspondingly, missing from the exported PDF).

Root cause. Every on-screen frame rendered the original multi-megapixel file, so a sheet of phone photos held hundreds of MB of decoded bitmaps — past the mobile browser's image-memory budget, which silently drops some to blank/black. The PDF export decoded each file at full resolution too, so a photo that went dark on screen also went missing from the export.

Preview pipeline. New lib/image.ts + a serialized decode queue in the photo store. Each photo is decoded once (respecting EXIF orientation) to capture its natural size, default its window orientation, and build a ≤1280px JPEG preview object URL. Every on-screen <img> (sheet cards, filmstrip, sidebar, strips) uses previewUrl ?? url. Previews are built for uploads, restored sessions, and opened projects, and revoked alongside the original URL. Decoding is serialized so we never hold several full-res bitmaps at once.

Export pipeline. rasterizeJpeg decodes via createImageBitmap capped at ~3600px (well above 300 DPI for any A4 card) and close()s each bitmap immediately, so a large sheet can't exhaust memory mid-render. Falls back gracefully where the resize/orientation options aren't supported.

Print quality unchanged; EXIF orientation preserved; entirely client-side (nothing leaves the device). Typecheck, lint, build green.

On-screen frames rendered the original multi-megapixel files, so a sheet
full of phone photos held hundreds of MB of decoded bitmaps — past the
mobile browser image-memory budget, which silently dropped some to
blank/black. The PDF export decoded each file at full resolution too, so
a photo that went dark on screen also went missing from the export.

Add a client-side preview pipeline: each photo is decoded once (serially,
respecting EXIF orientation) to capture its natural size, default its
window orientation, and build a small (<=1280px) JPEG preview object URL.
Every on-screen <img> now uses that preview; the full-resolution file is
kept only for export. Previews are generated for uploads, restored
sessions, and opened projects, and revoked alongside the original URL.

For export, decode via createImageBitmap capped to ~3600px (well above
300 DPI for any A4 card) and close() each bitmap immediately, so a large
sheet can't exhaust memory mid-render. There is no server involved — the
resize happens entirely in the browser, so nothing leaves the device.
@ankitsejwal ankitsejwal merged commit 4d6820f into main Jul 2, 2026
1 check passed
@ankitsejwal ankitsejwal deleted the perf-downscale-previews branch July 2, 2026 21:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant