-
Notifications
You must be signed in to change notification settings - Fork 3.5k
WIP: add image mask support to media edits #12327
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: trunk
Are you sure you want to change the base?
Changes from all commits
ea56933
0d28d3e
28a3a36
ba924ad
9205491
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -781,6 +781,14 @@ public function edit_media_item( $request ) { | |
| } | ||
| } | ||
|
|
||
| $has_mask_modifier = false; | ||
| foreach ( $modifiers as $modifier ) { | ||
| if ( 'mask' === ( $modifier['type'] ?? null ) ) { | ||
| $has_mask_modifier = true; | ||
| break; | ||
| } | ||
| } | ||
|
|
||
| /* | ||
| * If the file doesn't exist, attempt a URL fopen on the src link. | ||
| * This can occur with certain file replication plugins. | ||
|
|
@@ -791,9 +799,26 @@ public function edit_media_item( $request ) { | |
| $image_file_to_edit = _load_image_to_edit_path( $attachment_id ); | ||
| } | ||
|
|
||
| $image_editor = wp_get_image_editor( $image_file_to_edit ); | ||
| $image_editor_args = array(); | ||
| if ( $has_mask_modifier ) { | ||
| $image_editor_args = array( | ||
| 'methods' => array( 'mask' ), | ||
| 'mime_type' => $mime_type, | ||
| 'output_mime_type' => 'image/png', | ||
| ); | ||
| } | ||
|
|
||
| $image_editor = wp_get_image_editor( $image_file_to_edit, $image_editor_args ); | ||
|
|
||
| if ( is_wp_error( $image_editor ) ) { | ||
| if ( $has_mask_modifier && 'image_no_editor' === $image_editor->get_error_code() ) { | ||
| return new WP_Error( | ||
| 'rest_image_mask_unsupported', | ||
| __( 'Unable to mask this image.' ), | ||
| array( 'status' => 500 ) | ||
| ); | ||
| } | ||
|
Comment on lines
813
to
+820
|
||
|
|
||
| return new WP_Error( | ||
| 'rest_unknown_image_file_type', | ||
| __( 'Unable to edit this image.' ), | ||
|
|
@@ -859,6 +884,19 @@ public function edit_media_item( $request ) { | |
|
|
||
| break; | ||
|
|
||
| case 'mask': | ||
| $result = $image_editor->mask( $args ); | ||
|
|
||
| if ( is_wp_error( $result ) ) { | ||
| return new WP_Error( | ||
| 'rest_image_mask_failed', | ||
| __( 'Unable to mask this image.' ), | ||
| array( 'status' => 500 ) | ||
| ); | ||
| } | ||
|
|
||
| break; | ||
|
|
||
| } | ||
| } | ||
|
|
||
|
|
@@ -878,7 +916,9 @@ public function edit_media_item( $request ) { | |
| $image_name .= '-edited'; | ||
| } | ||
|
|
||
| $filename = "{$image_name}.{$image_ext}"; | ||
| $output_mime_type = $has_mask_modifier ? 'image/png' : null; | ||
| $output_ext = $has_mask_modifier ? 'png' : $image_ext; | ||
| $filename = "{$image_name}.{$output_ext}"; | ||
|
|
||
| // Create the uploads subdirectory if needed. | ||
| $uploads = wp_upload_dir(); | ||
|
|
@@ -887,7 +927,19 @@ public function edit_media_item( $request ) { | |
| $filename = wp_unique_filename( $uploads['path'], $filename ); | ||
|
|
||
| // Save to disk. | ||
| $saved = $image_editor->save( $uploads['path'] . "/$filename" ); | ||
| if ( $has_mask_modifier ) { | ||
| $disable_png_output_mapping = static function ( $output_format ) { | ||
| unset( $output_format['image/png'] ); | ||
| return $output_format; | ||
| }; | ||
| add_filter( 'image_editor_output_format', $disable_png_output_mapping, PHP_INT_MAX ); | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Masked circle crops must save as PNG to preserve transparent corners. WordPress allows plugins to globally remap image output formats, for example PNG to WebP. E.g., If that mapping runs here, the code could ask to save a .png mask but the editor might convert it to WebP during save(), which would break the strict “masked output is PNG” expectation. |
||
| } | ||
|
|
||
| $saved = $image_editor->save( $uploads['path'] . "/$filename", $output_mime_type ); | ||
|
|
||
| if ( $has_mask_modifier ) { | ||
| remove_filter( 'image_editor_output_format', $disable_png_output_mapping, PHP_INT_MAX ); | ||
| } | ||
|
|
||
| if ( is_wp_error( $saved ) ) { | ||
| return $saved; | ||
|
|
@@ -899,7 +951,7 @@ public function edit_media_item( $request ) { | |
| // Check request fields and assign default values. | ||
| $new_attachment_post = $this->prepare_item_for_database( $request ); | ||
| $new_attachment_post->post_mime_type = $saved['mime-type']; | ||
| $new_attachment_post->guid = $uploads['url'] . "/$filename"; | ||
| $new_attachment_post->guid = $uploads['url'] . '/' . $saved['file']; | ||
|
|
||
| // Unset ID so wp_insert_attachment generates a new ID. | ||
| unset( $new_attachment_post->ID ); | ||
|
|
@@ -1873,6 +1925,31 @@ protected function get_edit_media_item_args() { | |
| ), | ||
| ), | ||
| ), | ||
| array( | ||
| 'title' => __( 'Mask' ), | ||
| 'type' => 'object', | ||
| 'properties' => array( | ||
| 'type' => array( | ||
| 'description' => __( 'Mask type.' ), | ||
| 'type' => 'string', | ||
| 'enum' => array( 'mask' ), | ||
| ), | ||
| 'args' => array( | ||
| 'description' => __( 'Mask arguments.' ), | ||
| 'type' => 'object', | ||
| 'required' => array( | ||
| 'shape', | ||
| ), | ||
| 'properties' => array( | ||
| 'shape' => array( | ||
| 'description' => __( 'Mask shape.' ), | ||
| 'type' => 'string', | ||
| 'enum' => array( 'circle' ), | ||
| ), | ||
| ), | ||
| ), | ||
| ), | ||
| ), | ||
| ), | ||
| ), | ||
| ), | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here's my thinking:
WP_Image_Editordoes not need to addmask()if we treat masking as an optional editor capability.Adding an abstract
mask()there would force every third-party image editor subclass to implement it.That would be a hard backwards compat break, no?