Skip to content

Perspective Crop: the Crop tool's Perspective switch straightens what four corners enclose - #324

Open
MyronKoch wants to merge 1 commit into
robbietilton:mainfrom
MyronKoch:feat/perspective-crop
Open

MyronKoch wants to merge 1 commit into
robbietilton:mainfrom
MyronKoch:feat/perspective-crop

Conversation

@MyronKoch

Copy link
Copy Markdown
Contributor

For #206. The Crop tool's bar gets a Perspective switch, with no new menu item. With it on, each corner of the frame moves on its own. Dragging inside moves the whole shape, and dragging outside starts a new frame. Apply or Return takes what lies inside the corners to an upright rectangle. The frame shows a 4 × 4 grid in the same perspective, to line up with something square in the picture.

Corners on a tilted square After Apply
corners applied

How it works (PerspectiveCrop.swift)

  • The new size is the mean of each pair of opposite sides, shown in the bar while you drag. Core Image's own estimate for CIPerspectiveCorrection wasn't used: a 400-over-800 trapezoid came out 800 × 1265.
  • Each layer is resampled through the same perspective, the way Image Size resamples layers:
    • 16-bit layers stay 16-bit.
    • Linked masks are cropped with their layer.
    • Unlinked and placed masks cover the new canvas.
    • Folders and adjustment layers cover the new canvas.
    • Live text and shapes become pixels.
    • Each layer is trimmed to what's visible.
  • It samples back from each output pixel into the shape, so it still works when the vanishing point is inside the picture. Warping each layer's own corners forward, as Distort does, can't do that.
  • One undo step ("Perspective Crop"). It runs off the main thread, like Crop and Image Size.
  • Animated GIF layers are refused with a message rather than flattened.
  • Shapes that are folded, mirrored or larger than the size limits are ignored while dragging.

Not included: width and height fields, or a front-image size the way Photoshop has. Happy to add those if you want them.

Testing

  • 5 new tests in PerspectiveCropTests:
    • A square tilted with DistortWarp comes out upright, with each color in its quadrant and the corners filled.
    • The size rule, including that mirrored and folded shapes are refused.
    • Masks and folders.
    • GIF refusal.
    • Toggling Perspective on and off.
  • Sabotage check: with the perspective step replaced by a plain stretch of the shape's bounds, the upright test fails (TEST FAILED, exit 65).
  • Full CompositorTests on macOS 27: 884 pass and 2 fail. The 2 are ExternalChangeTests timeouts (about 105 s each, waiting on file events). All six ExternalChangeTests pass when run on their own.
  • Release build of this branch, driven by script:
    • Opened a 1200 × 900 photo of a tilted checkerboard, pressed C, switched on Perspective, and dragged the four corners onto the board.
    • The bar read 700 × 606 px, and Return produced the upright board shown above.
    • ⌘Z restored the 1200 × 900 original in one step.

🤖 Generated with Claude Code

https://claude.ai/code/session_01WC9aF7W8inTF7p9LZbNXRe

… four corners enclose

With Perspective on, each corner of the crop frame moves on its own, and
Apply takes what lies inside them to an upright rectangle: a document,
sign or painting photographed at an angle comes out square. The size is
the mean of each pair of opposite sides. The frame shows a grid drawn in
the same perspective, to line up with something square in the picture.

Every layer goes through the same perspective, as Image Size resamples
them: 16-bit layers stay 16-bit, linked masks crop with their layer,
folders and adjustment layers cover the new canvas, and live text and
shapes become pixels. Core Image samples back from each output pixel, so
it holds when the vanishing point falls inside the picture. Animated GIF
layers are refused rather than flattened.

Refs robbietilton#206.
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