Commit Graph
68 Commits
Author SHA1 Message Date
aiongg 9acf9a0cf9 CI: the Linux gate runs through the suite's shared gate workflow 2026-10-23 18:32:21 +00:00
aiongg cf0960b520 Merge pull request #8915 from aiongg/ci-speed-2
CI: no debug info for dependencies, nextest in xtask ci, long sweeps spread over days
2026-10-23 13:48:36 -04:00
Brandon ThomasandClaude Opus 5.5 e6d6fb45a4 Release 0.15.0
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-23 09:59:59 -04:00
aiongg 6ed69874fd Profiles: no debug info for dependencies in dev and test builds
Workspace crates keep line tables, so panics in PhotoCraft code still show file and line.
2026-10-23 06:00:19 +00:00
Brandon ThomasandClaude Opus 5.5 ff47388101 Release 0.14.0
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 20:06:08 -04:00
Brandon ThomasandClaude Opus 5.5 ce2a1e337a Release 0.13.0
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 15:39:21 -04:00
Dylan M. Taylor 6649b78526 Use chrono instead of hand-rolled UTC conversion (#8234)
* Use chrono for UTC/calendar conversion instead of hand-rolled copies

Replaces the civil_from_days copies behind the app's log timestamp, the
Measurement Log's date/time, and the perf report date with chrono as a
workspace dependency (reusing the version already in the tree). Output formats
are unchanged and pinned by the existing tests. AGENTS.md now says time goes
through chrono.

Follow-up to storytold/cadcraft#1785

* Never panic on a clock outside chrono's range; keep 5-digit years unprefixed

chrono's From<SystemTime> unwraps internally, so a clock past year 262143
panicked. logging::timestamp and analysis_cmds::now_iso now read the duration
since the epoch and go through the fallible DateTime::from_timestamp, reading
an out-of-range or pre-epoch clock as the epoch as the old saturating code
did. All three ISO helpers (timestamp, now_iso, perf::iso_utc) write the year
with {:04} rather than %Y, which prefixes a '+' past year 9999.

Tests cover the far-future and year-10000 cases for timestamp and iso_utc.

* Don't build an unrepresentable SystemTime in the far-future test

UNIX_EPOCH + Duration::from_secs(u64::MAX / 2) overflows Windows'
SystemTime and panicked before timestamp() ran, failing the Windows
test job. Build it with checked_add and only assert where it exists.

* time: preserve old out-of-range date conversion (no epoch fallback)

`DateTime::from_timestamp(...).unwrap_or_default()` silently reset any
instant past chrono's year range to 1970-01-01, changing output the old
hand-rolled Howard-Hinnant code converted exactly (e.g. 10_000_000_000_000 s
was 318857-05-20 17:46:40, not the epoch). Restore the old conversion domain
with a cycle-based helper that routes the calendar through chrono (the
calendar repeats every 400 years = 146_097 days, so month/day/time come from
chrono and only the year is carried in i128, keeping every i64 exact).

- apps/photocraft/src/logging.rs: `timestamp` via `utc_parts`; keep the
  pre-1970 epoch clamp; u64 secs past i64::MAX saturate, not epoch.
- crates/engine/src/analysis_cmds.rs: extract pure `iso_from_unix` (cycle
  based) from `now_iso` so the conversion is tested directly.
- xtask/src/perf.rs: `iso_utc` cycle-based, accepts the full u64 range the
  old code did.

Pinned old outputs in tests:
  logging u64::MAX/2 s -> 292277026596-12-04T15:30:07.000Z (was epoch),
          10_000_000_000_000 s -> 318857-05-20T17:46:40.000Z
  iso_from_unix 10_000_000_000_000 -> 318857-05-20T17:46:40Z,
          -10_000_000_000_000 -> -314918-08-13T06:13:20Z, i64 extremes
  iso_utc u64::MAX -> 584554051223-11-09T07:00:15Z (was epoch),
          10_000_000_000_000 -> 318857-05-20T17:46:40Z

* Don't build a year-318857 SystemTime unchecked in the logging test

Windows' SystemTime only reaches about year 30828, so
UNIX_EPOCH + Duration::from_secs(10_000_000_000_000) panicked before
timestamp() ran and failed the Windows test job. Build it with
checked_add and assert where the platform can hold it. The year-10000
case fits everywhere and stays unconditional.

* Name the 400-year range extension as the one exception to the time rule

The rule said never to hand-roll calendar math, but the range fix
(shifting by whole 400-year Gregorian cycles so dates past chrono's year
range keep their old output) is cycle arithmetic, so the rule and the code
disagreed. The rule now names that exception: the library still computes
month, day and time, and only whole cycles are carried in the year.

* Say the library computes month and day; time-of-day is plain arithmetic

Narrows the range-extension wording: the date library computes the
calendar fields, and splitting seconds into hours, minutes and seconds
is ordinary arithmetic, which is what the code does.
2026-10-22 13:54:10 -04:00
Brandon ThomasandClaude Opus 5.5 6a2d027ee2 Release: version 0.12.0
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 13:59:16 -04:00
Brandon ThomasandClaude Opus 5.5 46e538b3a2 Release 0.11.1
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 08:47:46 -04:00
Brandon ThomasandClaude Opus 5.5 cd79a5a9e8 Release 0.11.0
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 08:23:58 -04:00
Brandon ThomasandClaude Opus 5.5 8407d9ccb1 Release 0.10.0 (#7192)
* Release 0.10.0

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* Cargo.lock: photocraft-ipp at 0.10.0

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-17 21:28:00 -04:00
Diego Chagas 235e7578f3 Print through CUPS over IPP and the print portal, so File › Print works in the Flatpak (#2728) (#3017)
Main prints through craft-print, which runs `lp`; a Flatpak has no `lp`,
so File › Print fails there. This keeps craft-print as the route wherever
`lp` is on the path (and on Windows) and adds two more behind it:

- `photocraft-ipp`, a pure-Rust IPP client (CUPS-Get-Printers/Classes/
  Default, Print-Job over HTTP/1.1) that talks to CUPS over its socket or
  TCP; the Flatpak manifests grant --socket=cups for it. A `printServer`
  named in the request always takes this route. `file.print.printers`
  lists the queues.
- The XDG Print portal as the app's spooler (`Session::print_spooler`),
  for a sandbox without the CUPS socket or a system without a printer:
  the system print dialog, seeded with copies, paper and orientation.
  PHOTOCRAFT_PRINT_PORTAL=1 prefers it, =0 removes it.

A route that exists but fails (unknown printer, refused job) is an error;
only a missing route moves on. Results gain `backend` (lp, windows, ipp,
portal). docs/printing.md describes the routes.
2026-10-17 15:55:54 -06:00
skylar taylor-barrick 039b86f8f6 Merge pull request #7149 from skylartaylor/linked-path-checks
Follow linked paths only on this computer or the document's share
2026-10-17 12:13:47 -06:00
skylar taylor-barrick 482fd899cf Build: take craft-docpath from the merged craft-libs rev
craft-libs#169 is merged, so the fork pin is no longer needed. Pinned to
storytold/craft-libs 82a24b006eefa036b43c09a67fb9a3fbd17ffe63 (main after the merge) rather than the older 12281d07,
which predates resolve_absolute and the Windows colon refusal.
2026-10-17 12:02:56 -06:00
aiongg 7c44b2bc15 Build: use craft-docpath from craft-libs 2026-10-17 02:19:47 +00:00
Gustavo Ziger d900daa0ba fix(web): File › Place Embedded is enabled in the browser and places the picked file's bytes (#1209, #5749) (#5585)
With a document open in the web app, Place Embedded was disabled because its menu enablement inherited the engine's native-filesystem check, although the menu path reads the picker's bytes and never touches a path. Place Embedded is now enabled whenever a document and a file-dialog service exist; Place Linked keeps the engine's native guard, since the web could only link to a bare file name (caught in #5817 by Higgs32584). An enablement test runs with the native check forced off, and a file-dialog test covers the browser answer shape, cancel, invalid bytes and the Free Transform handoff.

Fixes #1209. Fixes #5749.
2026-10-16 17:38:56 -07:00
aionggandDavid Mann 87918a6c85 Linked smart objects: follow stored paths only to local files or the document's share
A linked smart object's path comes from the document. Every place that
follows it now goes through one resolver (photocraft_io::doc_path, built on
craft-docpath): a relative path is taken from the document's folder, and
only local files or files on the share the document itself is on are
opened. Paths to other computers, device paths and URLs are treated like a
missing file: the layer keeps its last rendering.

- Link watch: such links are not stamped, and no background look starts
  when no link of the document is followed.
- Update Modified Content and other re-renders read only followed files;
  the error names why a link is not followed.
- Package lists such links as missing instead of copying them.
- Reveal in Finder for a linked layer shows only a followed file; the
  document's own Reveal is unchanged. The macOS invocation ends options
  with `--`, and Explorer is not asked to select a path containing a comma
  or a quotation mark.
- Place Linked stores a relative path as an absolute one.

The document path travels to smart-object renders in a thread-local scope
entered by Session::edit, command dispatch and background jobs.

Co-authored-by: David Mann <daaavid.mann@gmail.com>
2026-10-16 16:53:13 +00:00
skylartaylor 8f58ea4629 ci: integrate current main while retaining strict test gates 2026-10-16 05:25:24 +02:00
skylar taylor-barrick b630d7cb9a Security: enforce the automation read boundary on stored smart-filter params (#5684)
* Security: enforce the automation read boundary on stored smart-filter params

Smart filters store a command id and params inside the document, so a
crafted .pcraft could resolve stored ambient paths (displace mapPath,
color-lookup LUT files) at render time without the authorize gate:
synchronous smart-object rendering has no Session argument, spawned job
threads drop the requesting session's authority, and a trusted UI
render could warm the process-wide stack cache with a stage the gate
never saw.

Thread the requesting session's gate through a thread-local in jobs.rs
(entered around command execution, reinstalled on spawned workers and on
job apply, restored on drop), re-check stored filter commands in
render_in before source resolution and any cache lookup and in
apply_filter_to_surface, and re-authorize layer.smartFilter.setParam
with the complete merged params after merging stored document params —
including edits whose request contains no path and actions.play steps.
Denial is conservative: the document is unchanged and no worker starts.

Regression tests in the automation crate cover rerender, cached-stage
reuse, merged-param edits, action playback, background jobs, and filter
transfer, for both the displace map and color-lookup LUT vectors.

* Review: carry render authority through Session::edit and surface denials

Session::edit is the engine's universal mutation entry (linked-file
refresh, transforms, place); renders reached through it ran with no
authorization scope installed, so stored smart-filter params read
ambiently there even after the execute-path fix. Install the scope in
Session::edit as well, keyed on the session's authorize gate.

apply_filter_to_surface also conflated a denied filter with an unknown
one: authorize_render_command's Result was discarded with .ok()?, so a
denial silently rendered unfiltered pixels and the stage entered the
process-wide stack cache. The signature becomes
Result<Option<Surface>>: Err for denials (callers propagate), None for
ids that aren't pixel filters (pass-through semantics unchanged), and an
unreadable map keeps its deliberate pass-through. apply_smart_filter and
apply_smart_filters propagate instead of cur.clone().

Two tests: a refresh driven outside command execution (Session::edit)
is denied and leaves the document unchanged; a denied render caches no
stage. Nine authorization tests pass, engine suite and strict Clippy
clean, panic hunt green.

* ui_set description: name the layerFilter field main added

The Layers-panel filter dropdown (#5048) added layerFilter to the
control method's UI_SET_FIELDS without extending the ui_set tool's
description, so the drift guard (mcp.rs, ui_set_description_names_every_
accepted_field) fails on Windows CI for any branch built on main. Name
the field with its patch shape: by (kind, name or mode), kinds (a list
of layer kinds), name (matches part of the layer name), mode (a blend
mode name). The 40 MCP tests pass; strict Clippy clean.

* wasm size: compose and algo join cms at opt-level "z" to clear the 24 MiB gate

Main's browser build is 171 KB over the 24 MiB wasm gate in CI
(25,337,343 bytes), so every branch built on current main fails the
web size check. Demote three pixel crates in the wasm-release profile:

- cms: transforms are built once and cached (transform::cached),
  so "z" costs little runtime (measured -42 KB).
- compose: the browser app composites per frame on the GPU (crates/gpu,
  WebGPU/WebGL2), so the CPU compositor is not the browser's hot path
  there (measured -122 KB).
- algo: filters run per pixel on every interactive filter application.
  "z" there is the lever of last resort (measured -331 KB), taken
  because the gate is enforced in CI; if browser filters feel too slow,
  restore 3 here and reclaim the bytes from a feature-gated decoder.

Final size 24,811,949 bytes locally, 354 KB under the gate; the native
profiles are untouched. If a maintainer prefers keeping algo at 3, the
alternative is raising the self-imposed 24 MiB early-warning limit
(Cloudflare's hard cap is 25 MiB) - a policy call in packaging/web/
package.sh, not a code fix.
2026-10-15 18:19:26 -06:00
Brandon ThomasandClaude Opus 5.5 c699e3cda6 Release 0.9.0
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 10:16:05 -04:00
skylar taylor-barrick d6fbac1720 Pin qualification fixes integrated with current print progress 2026-10-13 05:04:13 +02:00
skylar taylor-barrick 70059ac1b4 Pin driver option retention qualification fixes 2026-10-13 04:56:32 +02:00
skylar taylor-barrick 4de93daa91 Pin pre-submission annotation visibility guard 2026-10-13 04:25:32 +02:00
skylartaylor d9bc5a8cd5 Use shared native print transport and keep page composition in PhotoCraft 2026-10-13 02:56:11 +02:00
skylartaylor 3c4e1536a0 Merge current PhotoCraft and preserve asynchronous native printing 2026-10-13 01:48:54 +02:00
skylar taylor-barrick 7b0317ff41 Merge PhotoCraft 0.8.2 and stabilize background-job tests 2026-10-12 20:21:10 +02:00
skylar taylor-barrick a301e31864 Merge current main fixes and validate craft-print rollout 2026-10-12 19:39:05 +02:00
Claude d43db3e153 Release 0.8.2: web build back under the wasm size gate
0.8.1's Web job failed: photocraft-web's wasm was 25,587,059 bytes, over
packaging/web/package.sh's 24 MiB gate. #4253 merged with #4147 brings
it to 24,161,781.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 12:57:00 -04:00
Claude 7f9c4394c1 Merge #4253: wasm back under the 24 MiB gate
Combined with #4147's opt-level "z" list. Conflict in Cargo.toml's
wasm-release profile: kept #4147's photocraft-psd = "z" and dropped
#4253's "s" entry for it (one table per package). Measured with trunk
--release: main 184c226a is 25,235,857 bytes (over the 25,165,824 gate);
with this merge, 24,161,781.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 12:56:54 -04:00
Brandon ThomasandClaude Opus 5.5 184c226a76 Fix main: wasm size cap, Windows Export As test, filter preview window (scope and a crash), CLI duplicate font warnings (#4147)
* test(ui-egui): Export As artboards test accepts the Windows path separator

The test added in #3909 compared the second file's path with a literal
'/', but the exporter joins the sibling name with the platform separator,
so Windows CI saw '/out\boards-2-Capsule.jpg' and failed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(ui): only Filter menu dialogs get the in-dialog preview window

#3877 drew the preview window in every schema dialog with a Preview
checkbox, including Image > Rotation > Arbitrary. Photoshop's Rotate
Canvas and adjustment dialogs have no preview window, and the taller
dialog covered the canvas, which broke
arbitrary_rotation_preview::size_changing_rotation_preview_is_visible_and_preserves_the_panned_center
on main. The window and its reserved height now apply to filter.*
commands only.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* build(web): opt-level z for large cold crates in the wasm release profile

The browser wasm sat at the 24 MiB gate. The document model, file
parsers (psd, format, affinity), serde_json, usvg and naga are off the
per-frame and per-pixel paths; opt-level "z" for them takes the
release wasm from 25,186,233 to 24,829,840 bytes (-356 KB) with no
behaviour change.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(cli): run prints each missing-font warning once

Since #3630 the headless open and save also report missing fonts, so
`run <file> --out` and `run --cmd ... --out` printed the same warning
two or three times and missing_typeface_is_reported_by_run_info_and_convert
failed on main. Warnings from the open and the save now go through the
same already-reported set as the per-command ones.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* fix(ui): the filter preview window never uploads a texture past the GPU limit

With low-resolution previews off, the preview window from #3877 uploaded
the whole document as one texture. egui panics on a texture bigger than
max_texture_side, so opening a filter dialog on a large image crashed
(full_res_preview::filter_dialog_previews_at_full_resolution_when_low_res_is_off).
The texture is now reduced to fit; the view maps through UV fractions,
so it still lines up.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 11:48:28 -04:00
Brandon ThomasandClaude Opus 5.5 2db6b7d8f9 Release 0.8.1: rebuild with the fixed shared release workflow
The previous release build failed in craft-common-actions, not in this
repo: riscv64 packaging got --formats deb,rpm,tar (fixed in
craft-common-actions#3), Linux jobs lacked the ALSA headers
(craft-common-actions#5), and Windows signing needed the AZURE_* secrets
listed in the caller.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 11:11:02 -04:00
skylar taylor-barrick 5fe4d09478 Merge current main while preserving craft-print transport contracts 2026-10-12 16:17:51 +02:00
JGFandClaude Opus 5.5 5a0e2e2658 Web: wasm back under the 24 MiB gate with ~1 MiB headroom (25.38 -> 24.17 MB)
The "web build and wasm size gate" job failed on main and every PR: the
release `_bg.wasm` measured 25,379,677 bytes at 8d49ecf5, over the
25,165,824-byte limit in packaging/web/package.sh.

- egui's default fonts (Ubuntu Light, Hack, Noto Emoji, emoji-icon-font)
  were embedded uncompressed (1.4 MB) by egui's `default_fonts` feature.
  The app now builds egui without it; ui-egui's build.rs deflates the
  same files from the `epaint_default_fonts` crate (a build-dependency,
  so no font files are committed) and `egui_fonts.rs` rebuilds egui's
  exact default stack from them on first use. Test builds keep the
  feature as the oracle: the rebuilt definitions equal egui's byte for
  byte.
- Translation catalogs: every catalog repeated the same English sources.
  build.rs now stores the `context<TAB>source` keys once and each catalog
  as references plus translations, losslessly (`text()` still returns
  each .tsv byte for byte; malformed lines are kept as they are).
- wasm-release profile: opt-level "z" for PhotoCraft's own crates.
  Dependencies stay at "s" (at "z" the wasm grew 0.4 MB, measured), the
  file-decoding crates (codecs, psd, raw, heif, vector) stay at "s" and
  the pixel crates at 3, so decoding and filters keep their speed.

Measured with `trunk build --release` (trunk 0.21.14, wasm-opt v123 -Oz):
  upstream main 8d49ecf5      25,379,677 bytes
  this change                 24,167,186 bytes (-1,212,491)
  gate                        25,165,824 bytes (998,638 headroom)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 09:59:25 -04:00
Brandon ThomasandClaude Opus 5.5 3d0c819d46 Release prep: 0.8.0
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 05:44:37 -04:00
skylar taylor-barrick 8c557fcb7d Place identical browser engine profile beside upstream override 2026-10-12 05:47:22 +02:00
skylar taylor-barrick d380309338 CI: restore tests, catalogs, and web size gate (#3923)
* ci: restore test and Clippy gates

* ci: keep browser wasm below size gate

* CI: restore plural catalog and MCP resource test gates

Adapted the catalog, parser, and shared-cache test fixes from PhotoCraft PR #3821 by @bommerts. The source-key test, bundled-catalog test, and MCP resource test pass locally.

* CI: expect saved native document name in CLI test

The native save path intentionally writes the output filename into the reopened bundle. Keep the pre-save Run assertion and assert r.pcraft after reopening.

* CI: compare orientation round trips without saved filenames

Native saves encode the output filename in document name. Check each expected name, then compare the remaining document fields and pixels.
2026-10-11 21:37:09 -06:00
skylar taylor-barrick 9db0b30910 Keep browser command orchestration within the existing size budget 2026-10-12 03:53:52 +02:00
skylar taylor-barrick ccee4894bd Use craft-print for PhotoCraft system printing 2026-10-12 02:28:40 +02:00
MoiztermindandClaude Opus 5.5 628a54b564 Print on Windows: printer list, driver Print Settings, GDI printing
- photocraft-winprint: an isolated unsafe crate (like photocraft-tablet)
  for Windows printing: printer list, the driver's Print Settings dialog,
  page geometry, and GDI printing with ICM off.
- file.print prints on Windows (explicit "send": true only); new
  file.print.printers and file.print.page commands; printerSettings param.
- Print dialog on Windows: Printer menu, Print Settings…, paper from the
  driver.
- panic_hunt fuzzes print commands as dry runs.

Closes #2697.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 23:18:58 +01:00
IvanandClaude Opus 5.5 39904a27ce Docs: CI runs nextest, the craft-fonts tests have their own job
Comments and docs still said CI runs cargo test, that the craft-fonts tests run a second time in
the Linux job, and that CI's debuginfo matches the dev profile everywhere. development.md also
notes that test builds compile ui-egui unoptimized.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 11:32:35 +03:00
IvanandClaude Opus 5.5 2ff756b51a Tests: build ui-egui without optimization in the test profile
Its library and unit tests were the biggest test-build units after engine's (12.6 of ~63 compile
minutes on the Windows CI runner), and its tests run no heavy pixel code of their own. On the
Windows runner (EPYC 7763, 3 runs vs 7): build 18.9 -> 15.4 min, test run 10.0 -> 10.5. Only test
builds change; cargo run and cargo build use the dev profile.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 06:36:32 +03:00
skylar taylor-barrick d0a18cfd83 UI.9: keep browser bundle under size budget on current main 2026-10-10 20:56:34 -06:00
Brandon ThomasandClaude Opus 5.5 7926161595 Release prep 0.7.0: Linux riscv64 tarball + version bump (#2965)
* Release prep 0.7.0: add Linux riscv64 tar.gz to the release; bump to 0.7.0

- release.yml: new linux-riscv64 job, ported from GridCraft: cross-compiled on ubuntu-24.04
  against ports.ubuntu.com riscv64 libs, tar.gz only, photocraft-cli --version smoke-tested
  under QEMU. The draft-release job now needs it, so SHA256SUMS.txt covers it.
- packaging/linux/package.sh: CROSS_ARCH / CROSS_TARGET / CROSS_COMPILE / EMULATOR support.
- linux_libs: search /usr/lib/riscv64-linux-gnu on riscv64 (was the x86_64 triplet).
- README and docs/releasing.md list the riscv64 tarball.
- Version 0.6.0 -> 0.7.0 (cargo xtask version set), Cargo.lock refreshed; contributors refreshed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* canvas: crisp elliptical marquee ants again in an unrotated view

#2732 (a27d7175) built the elliptical marquee's live ants in document space and snapped every
vertex to the half-pixel grid on its own. Near the edge midpoints the nearly flat arcs became
1 px stairs, whose antialiased steps are grey, so the ants no longer contrast on white or black:
drag_preview_canvas's marquee test fails on main (ubuntu and windows). In an axis-aligned view
(rotation a multiple of 90 degrees) draw the ellipse inscribed in the snapped screen box again,
as before #2732; rotated views keep the mapped ellipse.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 21:36:30 -04:00
skylar taylor-barrick 77f50330c6 UI.9: refresh shared panel integration against current main 2026-10-10 19:30:56 -06:00
skylar taylor-barrick 254450f38e UI.8: pin final shared dock action labels 2026-10-10 16:20:57 -06:00
skylar taylor-barrick 99a3970399 UI.9: preserve PhotoCraft controls in shared UI migration 2026-10-10 12:51:56 -06:00
3092d3b6f9 Tests: line-tables-only dev debuginfo, nextest GPU group, faster loop docs (#2204) (#2414)
- [profile.dev] debug = "line-tables-only", matching CI's CARGO_PROFILE_DEV_DEBUG; halves link
  time and PDB size for test binaries.
- .config/nextest.toml: optional cargo-nextest runs with the GPU tests capped at 4 concurrent
  processes (unlimited ones time out in wgpu).
- AGENTS.md / docs/development.md: iteration commands (--lib --tests, --test <file>, nextest,
  doctests separately). CI is unchanged.

Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-authored-by: Brandon Thomas <echelon@gmail.com>
2026-10-10 14:03:30 -04:00
Elvis33LE b75267899d DNG: lossy compression 34892 (baseline JPEG tiles) (#2021)
* DNG: lossy compression 34892 (baseline JPEG tiles)

Adobe's lossy DNG (compression 34892) decodes: each tile is a complete
baseline JPEG (8-bit luma at tile size), decoded by zune-jpeg into the
tile geometry (a mismatch is an error), widened 0xFF -> 0xFFFF like
Photoshop's read of 8-bit CFA data. Predictors and the rest of the
pipeline are unchanged.

testgen grows LossyJpegTiles (image-rs encodes the Q60 fixtures behind
the testgen feature), so CI runs a decode oracle: a smooth ramp stays
within a few quantisation steps of the 8-bit source, and a tile cut
mid-stream fails cleanly.

* Lossy DNG: check the tile JPEG header before decoding; zune deps go workspace

decode_headers() runs and the (width, height) == (s.w, rows) check fires
before decode(), so a hostile SOF cannot allocate the decoder's buffers
(up to 16384x16364) before the size mismatch is caught. The mismatch has
a regression test that swaps a 32x32 tile JPEG for a 16x16 one. zune-core
and zune-jpeg move to [workspace.dependencies] next to image and flate2.

* chore(ui): format prefs_ui (the workspace fmt gate is red on main until #2417 lands)

* Lossy DNG: 3-sample LinearRaw (YCbCr) decodes to RGB; tiles size-check

Real lossy DNGs are 3-sample YCbCr LinearRaw, not single-channel CFA:
the tile now decodes through zune-jpeg's YCbCr-to-RGB output when
SamplesPerPixel is 3 (gray stays for CFA), the decoded length is checked
against the tile's sample count, and other sample counts say unsupported.
The generator writes RGB tiles for 3-sample specs. A LinearizationTable
maps the decoded samples in the develop stage as before - nothing to add
in the sensor. Test: a 3-sample ramp with 64-step channel separation
(surviving Q60 chroma quantisation) decodes within the quality band.
2026-10-10 13:39:57 -04:00
Brandon ThomasandClaude Opus 5.5 d3fa5e4c6f Bump version to 0.6.0 (from 0.5.0) for release; refresh contributors
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 21:02:23 -04:00
callmeBalloch e3932fa7a3 Merge remote-tracking branch 'upstream/main' into claude/kind-darwin-btjse2 2026-10-08 16:08:36 +00:00