52 lines
4.1 KiB
Markdown
52 lines
4.1 KiB
Markdown
# Architecture
|
||
|
||
Image Tools is a relocatable Vite/React application using the shared Toolbox shell. It has no server component, account, database, telemetry or remote processing API.
|
||
|
||
## Trust boundary and lifecycle
|
||
|
||
Every selected `File` is untrusted. `src/image/headers.ts` reads a metadata window of at most the first 1 MiB with `Blob.slice()` and performs checked-offset parsing for JPEG markers, PNG chunks and WebP RIFF chunks. PNG and WebP receive an additional structural pass that reads only eight-byte chunk headers, skips payloads by checked offset, stops at image data, and refuses more than 4,096 pre-image chunks. This prevents APNG or animated-WebP control chunks hidden behind a large ancillary payload from bypassing static-only enforcement. Inspection establishes dimensions, recognized animation/multi-picture state, EXIF orientation and a bounded metadata inventory. The image is not given to a browser decoder until source, dimension, pixel and estimated-memory policies pass.
|
||
|
||
Each preview or export gets its own generation-bound module worker through the
|
||
`@add-ideas/toolbox-helpers` worker-job protocol. The caller filters typed
|
||
progress by job ID and terminates the worker on replacement, cancellation,
|
||
unmount or the 120-second deadline, so a stale result cannot replace current
|
||
state. Files are structured-cloned to the worker; results return as `Blob`
|
||
objects. Browsers without worker `OffscreenCanvas` use the same pure pipeline
|
||
on the main thread and report missing capabilities instead of substituting a
|
||
remote service.
|
||
|
||
Object URLs are scoped to React lifecycles and revoked on replacement/unmount. Source files are never mutated. Downloads happen only after an explicit user action. The crop overlay edits the same normalized rectangle consumed by the pipeline; zoom is view-only CSS state and never enters the recipe.
|
||
|
||
## Operation order
|
||
|
||
The canvas pipeline has one documented order:
|
||
|
||
1. Decode with `createImageBitmap(..., { imageOrientation: "none" })`.
|
||
2. Normalize EXIF orientation 1–8 into visible pixel coordinates.
|
||
3. Apply the normalized crop rectangle.
|
||
4. Apply the selected quarter-turn and flips around the image centre.
|
||
5. Apply fit, centre-cropped fill, exact resize, or keep the transformed size.
|
||
6. Composite transparency over the selected background for JPEG only.
|
||
7. Encode with the browser's PNG, JPEG or WebP canvas encoder.
|
||
|
||
The returned MIME type must exactly equal the requested type. A browser that substitutes PNG for an unsupported encoder produces an error and no misleading filename.
|
||
|
||
Intermediate canvases are released as soon as their next stage is drawn. The conservative preflight estimate allows twelve live bytes per source pixel, covers three RGBA-sized surfaces, and rejects sources above 40 megapixels or 512 MiB estimated working memory. Preview output is capped to a 1,600-pixel edge, while reports retain intended export dimensions. Batch execution is sequential and keeps at most 256 MiB of completed output blobs. A separate fflate ZIP step reads at most 128 MiB of those blobs, uses fixed entry dates/collision-free names, includes operation reports and a manifest, and refuses output beyond 160 MiB.
|
||
|
||
Recipe JSON has an explicit `de.add-ideas.image-tools.recipe.v1` identity. Import validates every normalized crop, rotation, resize and output field before replacing state; unknown output formats cannot enter the processing pipeline through a recipe.
|
||
|
||
## Domain boundaries
|
||
|
||
Format inspection, checked image limits, geometry, filenames and reports remain
|
||
domain modules under `src/image/`. Generic byte-size display, filename safety,
|
||
Blob URL leases, single downloads and disposable-worker lifecycle come
|
||
from `@add-ideas/toolbox-helpers` 0.2.0.
|
||
|
||
No third-party image codec is added. The exactly pinned MIT-licensed `fflate`
|
||
dependency writes the optional batch container only; it never decodes image
|
||
pixels. Native codecs avoid a large
|
||
WASM/source-identity release burden, at the cost of explicitly documented
|
||
browser-dependent encoding and colour-management behaviour. A future
|
||
controlled codec adapter can implement the same processing request/result
|
||
contract without changing the UI model.
|