Commit Graph
23 Commits
Author SHA1 Message Date
Dylan M. Taylor 2ed75b4ec3 Refactor geom for strict Clippy checks 2026-10-27 23:47:06 +00:00
aiongg 22352ef317 refactor(geom): move warp.rs tests to warp/tests.rs 2026-10-26 22:30:12 +00:00
Dylan M. Taylor a0ce166acd refactor: lower cognitive complexity in styles apply, affinity text read and warp identity 2026-10-26 16:01:43 -04:00
David Mann 8c04b175ec Merge remote-tracking branch 'origin/main' into integration-batch-7
# Conflicts:
#	crates/automation/src/rpc.rs
#	crates/automation/src/server.rs
#	crates/gpu/src/health.rs
#	crates/text/src/tests.rs
#	crates/ui-egui/src/i18n/zh-hans.tsv
#	crates/ui-egui/src/panels.rs
#	docs/control-protocol.md
#	docs/ui-parity.md
2026-10-24 00:23:15 +02:00
SimoneandLucenx9 5948a3fb2a fix(geom): reject non-finite affine inverses (#8651)
NaN determinants bypassed the singularity check, while determinant and translation overflow could return Some with a bogus or non-finite inverse. Reject non-finite determinants and computed inverse coefficients so callers can take their existing failure paths.

Cover all six coefficients with NaN and both infinities, determinant overflow, translation overflow with a finite determinant, and valid extreme translations.

Co-authored-by: Lucenx9 <185146821+Lucenx9@users.noreply.github.com>
2026-10-23 17:38:57 -04:00
aiongg 7aaa59712a Merge main into integration-batch-7 2026-10-23 20:37:05 +00:00
Dylan M. Taylor 3729c3f664 security(raster,doc): count tile grids without walking them; share rebuilt tiles; index slice merges
Rect::tile_count counts a rect's tiles in u64; try_take_tiles uses it (no usize overflow on 32-bit). Surface::translated reuses one rebuilt tile for equal source-tile windows. auto_slices merges vertically adjacent runs through a hash index instead of searching the output.
2026-10-23 14:41:44 -04:00
Dylan M. TaylorandCodebuff f346b5c8f4 fix(vector): bound hostile geometry and raster work (#8769)
* fix(vector): bound hostile geometry raster work

Guard geometry and tracing operations against malformed dimensions and degenerate inputs with bounded work and safe failures.

🤖 Generated with Codebuff
Co-Authored-By: Codebuff <noreply@codebuff.com>

* fix(vector): address review (knot cap on split, relative identity tol, non-vacuous test)

* revert(geom): splitting past the quilt limit is intentional (smart_map test)

* fix(geom): cap identity tolerance at 1e-6 px

---------

Co-authored-by: Codebuff <noreply@codebuff.com>
2026-10-23 09:56:08 -04:00
David MannandClaude Opus 5.5 f5533e5d82 Integration: merge main into batch 6
Main merged #5940, #6472, #7838 and #7922 directly. Kept from the batch:
the snapshot/History-follow identity at the history limit (#5940,
c7ef3676a; main still had the 3-tuple HistorySpot) and the strokeless
shape at a 0 px width step (#6472, 962539607; main still added the
stroke). Main already carries the other two fix-ups (34c9d0ea4, dff8bfd0e).

Overlaps with main resolved:
- #5842 vs main's #5568: one path, apply_color, tool-gated as #5842
  has it, with #5568's float fill, kept fill opacity and lock skip;
  shape_fill_ui's duplicate foreground_changed/gesture dropped; #5568's
  tests run with a shape tool.
- #6230 vs main's #5180: one Type tool path click (path_target), no
  second type.attachToPath; vertical type does not start type on a path,
  as main has it; main's path_at/path_cursor and canvas arm dropped.
- #5452 moved the shape swatch to shape_paint_ui.rs: main's hex field
  (#6201) ported there.
- type_extra_cmds::with_text takes main's dpi form, pub(crate) for
  type_area_cmds.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-22 19:20:26 +02:00
David MannandClaude Opus 5.5 f13e215eb4 Integration: merge main, resolve conflicts (#5769)
docs/file-format-parity.md and docs/gaps.md: main's status lines and rows, with #4112's entries added.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-21 00:22:49 +02:00
Lucenx9 82f456b262 fix(geom): reject extreme warp split indices without overflow 2026-10-20 19:11:01 +02:00
wwew e33384c9ba docs: write product name as PhotoCraft in crate module docs (#5455) 2026-10-16 16:58:50 -06:00
Saad AbdullahandSaad Abdullah e1706507b5 fix(geom): clamp huge sizes in Rect::from_xywh instead of wrapping, and saturate TileCoord::rect (#5161)
Co-authored-by: Saad Abdullah <saadkhantareen@users.noreply.github.com>
2026-10-14 15:03:16 -07:00
Claude d709fd81a1 Warp: Photoshop's Cylinder warp as a 4 × 1 patch mesh (#4112)
Transform Warp's Cylinder (`warpCylinder`) stores seven `warpValues`
instead of a bend. WarpStyle gains Cylinder (parsed, named, not one of
the fifteen bend presets and not listed in the Warp menus yet), and
Warp::cylinder builds its mesh: four patches across (back, front left,
front right, back) and one down, in the warp's value space like a
custom mesh, so map, to_mesh and is_identity treat it as one. Warp keeps
the values for writing them back (serde: omitted when empty).

The model and its table of shape factors were measured against
Photoshop 2026's renders by the reporter of #4112 (not from Adobe's
code).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EuC9R3K5QeMBrDkixa1jnt
2026-10-14 20:33:52 +00:00
MrCloudy2andClaude Opus 5.5 7e5d2ce6da Warp presets: Photoshop's Bezier control grids instead of approximations (#3444)
The 15 warp presets (Edit > Transform > Warp, smart object warps, Warp
Text) were our own closed-form approximations, up to 70 px off
Photoshop's: Arc kept its centre fixed and pulled the ends in where
Photoshop keeps the inner edge's corners and raises the middle, Flag
waved the wrong way at a third of the height, Squeeze shrank instead of
pinching.

Measured on Photoshop 27.11 (scripted renders of a grid image whose
colours encode each pixel's position, 102 cases: every preset at bend
±50 and ±100, both distortions, vertical orientation, square, wide and
tall boxes), every preset is a single bicubic Bezier patch (fits within
0.5 px). Its control grid:
- Arc: each row a circular arc of b·π around one centre, the inner edge
  keeping its chord; Arch, Bulge, Arc Lower/Upper and Shell built from
  the same arcs. Circular in pixels on any box.
- Flag, Wave, Fish, Rise, Fisheye, Inflate, Squeeze, Twist: control
  points moved linearly with the bend, in box units.
- Vertical distortion scales the top and bottom rows about their
  midpoints and blends the rows between; horizontal distortion scales
  each column. Vertical orientation swaps the axes and the two
  distortions.
All 102 cases land within 1 px (median) of Photoshop. On the corpus's
Photoshop-made warped text, warp-arc-50 now spans 340x85 at (-3, 3)
against Photoshop's 350x88 at (-7, 3), where it was 278x74 at (28, 62).

Presets also convert to custom meshes exactly now (to_mesh splits the
patch instead of fitting). Tests that pinned the old shapes now assert
Photoshop's; presets_match_photoshop checks measured points.

Still open: Warp Text warps over our line box, while Photoshop uses the
type layer's stored `bounds`; with those the five corpus warp files land
within a few px (arc, flag, bulge with distortion, twist, vertical rise).

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 23:56:17 -04:00
eab15ffdf6 Image warp: grab anywhere on the grid, Ctrl-click to cut, Warp in the transform menu (#2621)
* Image warp: grab anywhere on the grid, Ctrl-click to cut, Warp in the transform menu

Warp mode of Free Transform now works from anywhere inside the grid, not
only on its points:

- Press and drag on any part of the grid and the surface under the pointer
  follows it. The surrounding anchors bend with it, fading out with
  distance. The picture's border stays put unless the grab is made near
  it, and then the border bows through its own handles. The four corners
  never move.
- Ctrl-click (or Option-click) cuts the grid both ways at the pointer.
- Right-clicking a Free Transform box lists Warp after Perspective. The
  menu marks only the box's real current mode.
- Anchor and handle drags keep every grid line smooth through its anchors.
- Grid points are quieter: anchors are small, and a handle with its arm
  shows only near the pointer or while it is dragged.
- The preview draws the most displaced cells first and samples finer where
  the warp pulls hard, so a strong pull no longer shows as a comb.

Refs #214

* Warp options bar: Split, Grid and Warp controls with orientation and bend always shown, Reset, and the buttons right after the fields

The Warp bar now reads Split, Grid, Warp style, orientation and Bend / H / V.
The orientation and bend fields are always there and only act on a preset
style. A new Reset button puts the grid back to the default untouched one
while staying in warp mode.

Cancel and Commit sit right after the fields instead of at the far right of
the window. A bar too narrow for both pins them to the right edge and clips
the fields, so they stay reachable. The Free Transform bar shares the same
function and gets the same placement.

---------

Co-authored-by: Brandon Thomas <echelon@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-10 14:22:46 -04:00
LunaandLuna 0c976f42d5 fix(geom): Rect::width/height no longer overflow on i32-extreme edges (#675) (#808)
width() and height() computed `x1 - x0` / `y1 - y0` with plain i32
subtraction. For extreme edges (e.g. an inverted i32::MAX..i32::MIN
rect, or a valid rect spanning most of the i32 range) the subtraction
overflows: debug builds panic, release builds wrap. warp.* commands
build a Rect straight from the JSON `rect` param, so an extreme value
reached this from user input.

Both now return the exact extent via `abs_diff` when the far edge is
past the near one, and 0 otherwise. The extent of any i32 pair fits in
u32, so no clamping or casting is needed. A plain `saturating_sub`
would avoid the panic but turn a wide valid rect into a width of
i32::MAX, which is wrong.

Follow-up (not in this change): warp_cmds still accepts absurd `rect`
params; it should reject them with a parameter error up front.

Co-authored-by: Luna <luna161@users.noreply.github.com>
2026-10-07 18:00:59 -04:00
a202171891 fix(geom): saturate Rect::translate and inflate on i32 overflow (#159)
* fix(geom): saturate Rect::translate and inflate on i32 overflow

from_xywh already uses saturating_add. translate and inflate used
plain +/- and panicked in debug (or wrapped in release) when a layer
near the i32 edges was nudged off-canvas.

* fmt: Rect::translate/inflate at the workspace width (#159)

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

---------

Co-authored-by: Brandon Thomas <echelon@gmail.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-05 04:14:40 -04:00
Brandon ThomasandClaude Opus 5.5 296ced38d6 Never crash: text, color, doc, paint, geom, raster, compose, vector (#39)
- Real crash: build_engine_data took its template from the PSD's text
  engine data and did expect("EngineDict"), so a text layer whose engine
  data lacked EngineDict (or wasn't a dictionary) crashed PSD export.
  The template is now repaired; regression test
  engine_data_template_without_engine_dict.
- color: the built-in CMYK<->sRGB transforms fall back to the naive
  formulas instead of expect if they ever fail to link.
- doc: insert_above falls back to the top of the root instead of
  expect("valid path"); next_layer_name searches a bounded range.
- paint: Mips::new loops with while-let instead of last().expect.
- All eight crates deny unwrap/expect/panic/unimplemented/todo/unreachable.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 13:25:44 -04:00
Brandon ThomasandClaude Opus 5.5 f7eb6ea9e7 Format: adopt the craft-family rustfmt.toml and format the workspace
CI's `cargo fmt --all -- --check` has failed on every run because the
repo had no rustfmt config and was never formatted. Use the same settings
as EffectCraft/FilmCraft (max_width 160, use_small_heuristics Max), which
also keeps the churn small. Formatting only; no code changes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-04 11:31:17 -04:00
Brandon Thomas 536a7346fb more algorithms, progress 2026-10-01 08:25:06 -04:00
Brandon Thomas ae92ac20bf continued wip 2026-09-30 09:37:18 -04:00
Brandon Thomas 16941f7198 one-shot 2026-09-30 05:02:08 -04:00