Skip to content

Implements rotation for paths, brush strokes and gradients.#21349

Open
masterpiga wants to merge 1 commit into
darktable-org:masterfrom
masterpiga:rotate_paths
Open

Implements rotation for paths, brush strokes and gradients.#21349
masterpiga wants to merge 1 commit into
darktable-org:masterfrom
masterpiga:rotate_paths

Conversation

@masterpiga

@masterpiga masterpiga commented Jun 18, 2026

Copy link
Copy Markdown
Collaborator

TL;DR

  • Add support for path, and brush shape rotation, with a consistent gesture scheme also wired up for gradients.
  • Uses CTRL+drag for all shape rotations (already used by ellipse rotation).
  • Changes CTRL+click to add a node to a path or brush stroke to SHIFT+click (to prevent ambiguity w/ CTRL+drag).
  • Allows independent rotation of source/target shapes in retouch (heal and clone) so a copied/healed patch can be reoriented — not just translated — to better match the destination, as well as joint source/target rotation w/ CTRL+SHIFT+drag.

Demo

Screen.Recording.2026-06-18.at.22.32.53.mp4

What changed

  • New per-form rotation angle. dt_masks_form_t.source grows from float[2] (dx, dy) to float[3] (dx, dy, angle). Reading is length-aware (handles 2/3/4-float blobs), the DB write/hash use 3 floats, and duplicate/XMP round-trip the new field. Old edits load unchanged (angle = 0); downgrades degrade gracefully (the angle is simply dropped).
  • Pixel sampler. rt_copy_in_to_out (C + the two OpenCL kernels) gains a rotated path: each destination pixel samples the source bilinearly under centroid + R(angle)·offset. The non-rotated path is unchanged (fast memcpy).
  • GUI gestures (ellipse/path/brush, plus gradient):
    • CTRL+drag — rotate the shape under the pointer (target, or source on a clone source).
    • CTRL+SHIFT+drag — rotate target and source together.
    • On clone targets, the source's orientation is held or co-rotated via the source[2] angle.
  • SHIFT+click to add a node on a path/brush segment (previously CTRL+click), since CTRL is now the rotate modifier. Path creation (ctrl+click = sharp node) is unchanged.
  • Mouse-action lists and on-canvas hints updated; the "rotate both" hint is shown only for forms that actually have a source (clone/heal).
  • ROI extension for rotated sources, and edge-clamping instead of black-filling out-of-bounds samples.

Key design choices

  • Rotation pivot = area centroid (center of mass), not bounding-box center. The centroid is rotation-invariant: rotating a shape about it leaves it fixed, so the source spins in place and repeated rotations don't drift. A bbox center shifts whenever a non-square shape rotates, causing the source to creep and acquire spurious rotation. The overlay (outline centroid) and the sampler (opacity-weighted raster centroid) compute the same conceptual point from their respective representations — they agree up to the feather margin. Brush strokes have no enclosed area, so they use the centerline mean (also rotation-invariant) instead of a shoelace centroid.
  • Backward/forward-compatible serialization via blob-length detection rather than a schema-version bump, keeping .xmp sidecars and the library DB interoperable across versions.
  • ROI via dt_masks_get_source_area. It already applies source[2], so it yields the true rotated source footprint pivot-agnostically — simpler and more correct than reconstructing a rotated AABB. Edge-clamping then covers the residual cases the ROI can't (image border, masked-out box corners), avoiding black seams in heal.

Co-authored with Claude.

@wpferguson

wpferguson commented Jun 18, 2026

Copy link
Copy Markdown
Member

How well does this play with gradient drawn masks?

Does this change any existing mask manipulation behaviors?

EDIT: Did a quick test. Gradient masks work fine and it seems to work fine.

@ralfbrown

ralfbrown commented Jun 18, 2026

Copy link
Copy Markdown
Collaborator

Backward/forward-compatible serialization via blob-length detection rather than a schema-version bump, keeping .xmp sidecars and the library DB interoperable across versions.

What happens when a pre-PR copy of darktable tries to process a post-PR sidecar or database entry? Ignoring the new angle fields is fine (versioning skips the entire iop during processing if too new), while interpreting the new format as if it were the old one is not.

Let's see if I can link an old FR: fixes #14026.

@ralfbrown ralfbrown added feature: new new features to add scope: image processing correcting pixels labels Jun 18, 2026
Comment thread data/kernels/retouch.cl Outdated
Comment thread data/kernels/retouch.cl Outdated
Comment thread src/develop/masks/ellipse.c Outdated
Comment thread src/develop/masks/brush.c Outdated
Comment thread src/develop/masks/brush.c Outdated
Comment thread src/iop/retouch.c Outdated
@masterpiga

masterpiga commented Jun 19, 2026

Copy link
Copy Markdown
Collaborator Author

What happens when a pre-PR copy of darktable tries to process a post-PR sidecar or database entry? Ignoring the new angle fields is fine (versioning skips the entire iop during processing if too new), while interpreting the new format as if it were the old one is not.

Good point. It degrades gracefully. The only information that is lost is the independent rotation of source/target shapes in retouch. I.e., if a pre-PR darktable opens a post-PR edit in which you rotated the two independently, then the source shape will be at the same angle as the target one.

It seems an acceptable state of affairs to me, but I am happy to revisit if this is not ok.

@masterpiga

Copy link
Copy Markdown
Collaborator Author

Thanks for the review, @jenshannoschwalm, comments addressed

@masterpiga masterpiga added this to the 5.8 milestone Jun 19, 2026
Comment thread src/develop/masks/brush.c Outdated
Comment thread src/develop/masks/brush.c
Comment thread src/develop/masks/path.c Outdated
Comment thread src/develop/masks/path.c Outdated
Comment thread src/iop/retouch.c Outdated
- Uses CTRL+drag for all shape rotations (already used by ellipse rotation).
- Changes CTRL+click to add a node to a path or brush stroke to SHIFT+click (to prevent ambiguity w/ CTRL+drag).
- Allows independent rotation of source/target shapes in retouch (heal and clone) w/ CTRL+SHIFT+drag.
@masterpiga

Copy link
Copy Markdown
Collaborator Author

Thanks, Hanno, all done. I also deployed deg2radf and copysignf in a couple of places.

@masterpiga masterpiga added feature: enhancement current features to improve scope: UI user interface and interactions and removed feature: new new features to add labels Jun 19, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature: enhancement current features to improve scope: image processing correcting pixels scope: UI user interface and interactions

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants