Skip to content

WIP - Add circle crop mask support#79393

Draft
ramonjd wants to merge 7 commits into
trunkfrom
ramonjd/circle-crop-stencil
Draft

WIP - Add circle crop mask support#79393
ramonjd wants to merge 7 commits into
trunkfrom
ramonjd/circle-crop-stencil

Conversation

@ramonjd

@ramonjd ramonjd commented Jun 22, 2026

Copy link
Copy Markdown
Member

What?

Adds MVP support for circular crops in the media editor. At the moment its state is MVP/POC to guide direction.

Kapture.2026-06-26.at.14.26.45.mp4

Backport PR here: WordPress/wordpress-develop#12327

  • Adds circle crop mask handling to the attachments REST edit flow.
  • Adds Gutenberg image editor subclasses for applying image masks.
  • Adds Rectangle/Circle stencil selection in the crop panel.
  • Adds a mobile/footer toolbar crop-shape dropdown.
  • Uses edge handles for the circle stencil so resizing feels like dragging the circle edge.
  • Saves circle crops as PNG to preserve transparency.

Testing Instructions

  1. Open the media editor for an image.
  2. Go to the Crop panel.
  3. Confirm the Shape control shows Rectangle and Circle.
  4. Select Rectangle and confirm aspect-ratio controls are available.
  5. Select Circle and confirm aspect-ratio controls are hidden.
  6. Crop and save the image.
  7. Confirm the saved result is circular with transparent corners and is saved as PNG.
  8. On a narrow/mobile viewport, confirm the footer toolbar shows the crop-shape dropdown.
  9. Use the mobile dropdown to switch between Rectangle and Circle.
  10. Confirm circle resizing uses the middle edge handles instead of corner handles.

@ramonjd ramonjd changed the title Add circle crop mask support WIP - Add circle crop mask support Jun 22, 2026
@ramonjd ramonjd self-assigned this Jun 22, 2026
@ramonjd ramonjd added [Type] Experimental Experimental feature or API. No Core Sync Required Indicates that any changes do not need to be synced to WordPress Core labels Jun 22, 2026
@github-actions

github-actions Bot commented Jun 22, 2026

Copy link
Copy Markdown

Size Change: +1.07 kB (+0.01%)

Total Size: 7.61 MB

📦 View Changed
Filename Size Change
build/scripts/editor/index.min.js 490 kB +937 B (+0.19%)
build/styles/editor/style-rtl.css 31.2 kB +33 B (+0.11%)
build/styles/editor/style-rtl.min.css 26.3 kB +34 B (+0.13%)
build/styles/editor/style.css 31.2 kB +35 B (+0.11%)
build/styles/editor/style.min.css 26.3 kB +36 B (+0.14%)

compressed-size-action

@ramonjd ramonjd force-pushed the ramonjd/circle-crop-stencil branch from 3486e5e to a63aeb2 Compare June 22, 2026 07:29
@ramonjd ramonjd force-pushed the ramonjd/circle-crop-stencil branch 2 times, most recently from 509d976 to 236386f Compare June 23, 2026 06:46
@github-actions

github-actions Bot commented Jun 23, 2026

Copy link
Copy Markdown

Flaky tests detected in aa3ae8a.
Some tests passed with failed attempts. The failures may not be related to this commit but are still reported for visibility. See the documentation for more information.

🔍 Workflow run URL: https://github.com/WordPress/gutenberg/actions/runs/28421361712
📝 Reported issues:

@ramonjd ramonjd force-pushed the ramonjd/circle-crop-stencil branch 2 times, most recently from 21c22f7 to 792717d Compare June 26, 2026 03:54
Comment thread lib/compat/wordpress-7.1/class-gutenberg-image-editor-gd.php
Comment thread lib/compat/wordpress-7.1/class-gutenberg-image-editor-mask.php Outdated

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds MVP/POC support for circular image crops in the Media Editor, spanning UI/state updates in packages/media-editor and a WordPress REST /edit compatibility layer that can apply a circular mask and output PNG to preserve transparency.

Changes:

  • Introduces a cropShape option (rectangle/circle) with undo/history integration and “dirty” tracking.
  • Adds a circular stencil UI (edge handles + circular overlay styling) and exposes shape selection in the Crop panel and mobile/toolbar controls.
  • Extends the attachments /edit flow with an experimental mask modifier and registers mask-capable image editors (Imagick/GD) to produce circular PNG outputs.

Reviewed changes

Copilot reviewed 33 out of 33 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
packages/media-editor/src/state/use-media-editor-state.ts Adds setCropShape controller API and includes crop shape in dirty tracking.
packages/media-editor/src/state/types.ts Defines CropShape and adds cropShape to CropOptionsSlice + action types.
packages/media-editor/src/state/test/use-media-editor-state.ts Adds coverage for undo/dirty behavior when switching crop shape.
packages/media-editor/src/state/index.ts Re-exports CropShape type.
packages/media-editor/src/state/composite-reducer.ts Implements SET_CROP_SHAPE handling and includes crop shape in equality checks.
packages/media-editor/src/image-editor/react/components/stencils/test/circle-stencil.tsx Adds unit test ensuring circle stencil uses edge handles (not corners).
packages/media-editor/src/image-editor/react/components/stencils/rectangle-stencil.tsx Adds internal props to support circle styling and edge-handle locking.
packages/media-editor/src/image-editor/react/components/stencils/circle-stencil.tsx Implements circle stencil by composing RectangleStencil with square constraints.
packages/media-editor/src/image-editor/react/components/index.ts Exports CircleStencil.
packages/media-editor/src/image-editor/react/components/cropper.tsx Adds stencilShape support and enforces square constraints for circle crops.
packages/media-editor/src/image-editor/react/components/cropper.scss Adds circular overlay styling (grid/dimming/stencil border radius).
packages/media-editor/src/image-editor/index.ts Re-exports CircleStencil from the package surface.
packages/media-editor/src/image-editor/core/test/stencil-math.ts Adds test coverage for locked edge-handle resizing behavior.
packages/media-editor/src/image-editor/core/stencil-math.ts Adds computeLockedEdgeResizeRect and routes edge handles through it.
packages/media-editor/src/components/media-editor/use-save-media-editor.ts Appends mask modifier when cropShape is circle.
packages/media-editor/src/components/media-editor/use-crop-options.ts Exposes cropShape setters and resolves circle to a square aspect ratio.
packages/media-editor/src/components/media-editor/test/use-crop-options.tsx Adds coverage for circle resolved aspect ratio and reset behavior.
packages/media-editor/src/components/media-editor/style.scss Tweaks sidebar padding on small breakpoints.
packages/media-editor/src/components/media-editor/index.tsx Wires crop shape into Crop panel and hides aspect ratio controls for circles.
packages/media-editor/src/components/media-editor-modal/build-modifiers.ts Extends modifier union type to include mask.
packages/media-editor/src/components/media-editor-image-controls/test/index.tsx Adds test coverage for toolbar crop-shape dropdown and circle behavior.
packages/media-editor/src/components/media-editor-image-controls/style.scss Adjusts toolbar layout wrapping/centering for added controls.
packages/media-editor/src/components/media-editor-image-controls/index.tsx Adds crop-shape dropdown and hides aspect ratio dropdown for circle crops.
packages/media-editor/src/components/media-editor-crop-panel/test/index.tsx Adds test coverage for crop shape selection and circle-specific UI behavior.
packages/media-editor/src/components/media-editor-crop-panel/index.tsx Adds shape toggle group and conditionally renders aspect ratio selector.
packages/media-editor/src/components/media-editor-canvas/index.tsx Passes circle stencil + stencilShape to the Cropper when shape is circle.
lib/media/class-gutenberg-rest-attachments-controller.php Switches controller base to a mask-capable attachments controller.
lib/compat/wordpress-7.1/image-editor-mask.php Registers mask-capable Gutenberg image editors via wp_image_editors filter.
lib/compat/wordpress-7.1/image-editor-mask-validation.php Adds validation/normalization helper for mask arguments.
lib/compat/wordpress-7.1/class-gutenberg-rest-attachments-controller-with-mask.php Adds mask modifier schema and custom /edit flow to apply mask + PNG output.
lib/compat/wordpress-7.1/class-gutenberg-rest-attachments-controller-7-1.php Updates 7.1 controller to inherit mask-capable base.
lib/compat/wordpress-7.1/class-gutenberg-image-editor-imagick.php Adds Imagick editor subclass implementing mask() for circle cropping.
lib/compat/wordpress-7.1/class-gutenberg-image-editor-gd.php Adds GD editor subclass implementing mask() for circle cropping.

@ramonjd ramonjd force-pushed the ramonjd/circle-crop-stencil branch from 2c159e3 to c8cd436 Compare June 30, 2026 04:52
ramonjd added 3 commits June 30, 2026 14:57
- Scope the mask image-editor filter to the edit request instead of
  swapping the global editor site-wide; load its helper from load.php.
- Remove the dead legacy-modifier branch and inline the circle-mask
  request check.
- Use a scanline fill for the GD circle mask (O(height) instead of
  O(width*height) per-pixel writes).
- Return a status array from the Imagick mask error.
- Let Cropper own the circle 1:1 ratio; drop the redundant canvas branch.
- Clarify the circle crop help text.
- Add PHP tests for mask validation, edit schema, GD masking, and the
  mask/non-mask edit routing.
Instead of unconditionally forcing PNG, honor the site's
image_editor_output_format choice when it can hold an alpha channel
(WebP/AVIF) and fall back to PNG otherwise. The format is forced for the
save so a site filter cannot remap it back to an opaque format and
discard the mask.

- Move the format decision out of the editor mask() methods, which now
  mutate pixels only like the other transforms.
- Drop the redundant Imagick setImageFormat and its test() gate entry.
- Add a test covering the alpha-capable (WebP) negotiation path.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 35 out of 35 changed files in this pull request and generated 1 comment.

Comment on lines +42 to +66
$args['modifiers']['items']['oneOf'][] = array(
'title' => __( 'Mask', 'gutenberg' ),
'type' => 'object',
'properties' => array(
'type' => array(
'description' => __( 'Mask type.', 'gutenberg' ),
'type' => 'string',
'enum' => array( 'mask' ),
),
'args' => array(
'description' => __( 'Mask arguments.', 'gutenberg' ),
'type' => 'object',
'required' => array(
'shape',
),
'properties' => array(
'shape' => array(
'description' => __( 'Mask shape.', 'gutenberg' ),
'type' => 'string',
'enum' => array( 'circle' ),
),
),
),
),
);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

No Core Sync Required Indicates that any changes do not need to be synced to WordPress Core [Type] Experimental Experimental feature or API.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants