Commit Graph

3 Commits

Author SHA1 Message Date
3dtours c70edce8c1 web: mirror the frame with H-FLIP and V-FLIP, and stamp a typed place
ROTATE gains the two mirrors: H-FLIP and V-FLIP toggle one at a time and
stay on through the quarter turns and STRAIGHTEN, which makes them compose
with every rotation the strip already offers. ROTATE's own RESET levels the
whole frame, mirrors included.

The flip itself lands last, in screen space, so a mirrored photo is what the
eye sees rather than what the sensor saw; the pixels are copied axis-aligned,
so there is nothing to resample. Session state carries the two flags, so a
reopened photo comes back mirrored.

Also fixes the stamp: a typed PLACE NAME with no GPS fix now prints on its
own (latitude/longitude ride in as NaN), instead of the whole stamp and its
box being skipped for want of coordinates.
2026-09-25 08:46:27 +07:00
3dtours 3a09674831 web: filter the straighten draw so a rotated edge stops staircasing
The FRAME tab's fine rotation drew the photo through canvas.rotate() +
canvas.scale() with a plain drawImage, which CanvasKit samples with
nearest: the edge landed on the same pixel in every row, so a rotated
edge came out as 1px steps every 1/tan(angle) rows. Measured on the 30deg
export of a hard black/white edge: 42.3% of rows repeated the previous
row's edge position, the step across the edge was 252.9 of 255, and there
were no intermediate pixels at all.

Only the *Options/*Cubic call shapes take a sampling option, and
drawImageRectOptions exists in RN Skia too, so the shared renderer can use
it unchanged. The same export now moves the edge in every row (0.2% of
rows repeat, 0.35 intermediate pixels per row) and its edge step drops to
222. Cost: the filtered draw takes 0.35s against 0.24s for the 1600px
preview copy and 2.0s against 1.4s for a 12MP photo, once per render.

Preview and export share the function, so both change together.
2026-09-23 07:33:44 +07:00
3dtours 8c6e7930db Add self-contained docker/ stack for the web UI
`docker/` now holds the whole web build — frontend (Vite + React + CanvasKit),
backend (Fastify + SQLite) and the compose file — so the folder can be moved to
another machine and run without the React Native project:

    cd docker && cp .env.example .env && docker compose up -d --build

Only `${WEB_PORT:-8090}` is published; nginx serves the SPA and proxies /api to
the `api` container over Docker's DNS. Photos never reach the server.

The shared render code is vendored into `docker/frontend/shared/` and aliased to
a CanvasKit shim, so the app's own frameUtils/toneShader/jpegDpi run unchanged.

Fix the all-black render on GPU surfaces: `MakeWebGLCanvasSurface` creates a
separate WebGL context per call, and a texture from one context cannot be
sampled by a surface on another — so any pass that drew a snapshot onto a second
surface (output sharpen, screen sharpen, polaroid/wallframe cards) came out
solid black, while the raster fallback was correct. Use one shared
GrDirectContext + MakeRenderTarget instead.

Verified in headless Chromium against the running stack: 12MP JPEG in, preview
mean=120.5 sd=60.5, export 2048x1536 mean=107.2 sd=62.1, JFIF density 300/300,
EXIF present, no console errors; health/signup/login/me/recipes all 2xx through
the nginx proxy.
2026-09-17 17:43:03 +07:00