From 1bbc5a4737f1c4b9c89503386231c8f3c1bdba17 Mon Sep 17 00:00:00 2001 From: Andrew Ozz Date: Tue, 31 Aug 2021 18:57:09 +0000 Subject: [PATCH] Media: apply the `wp_editor_set_quality` filter not only when loading an image in the editor but also when saving an converted image, after the mime-type of the output image has changed. Props mikeschroder, desrosj, azaozz. Fixes #53667. git-svn-id: https://develop.svn.wordpress.org/trunk@51704 602fd350-edb4-49c9-b593-d223f7449a82 --- src/wp-includes/class-wp-image-editor.php | 54 ++++++++++-- tests/phpunit/includes/mock-image-editor.php | 2 + tests/phpunit/tests/image/editor.php | 86 ++++++++++++++++++++ 3 files changed, 137 insertions(+), 5 deletions(-) diff --git a/src/wp-includes/class-wp-image-editor.php b/src/wp-includes/class-wp-image-editor.php index 0bc7ee6aee..3c2625d2f3 100644 --- a/src/wp-includes/class-wp-image-editor.php +++ b/src/wp-includes/class-wp-image-editor.php @@ -15,9 +15,12 @@ abstract class WP_Image_Editor { protected $file = null; protected $size = null; protected $mime_type = null; + protected $output_mime_type = null; protected $default_mime_type = 'image/jpeg'; protected $quality = false; - protected $default_quality = 82; + + // Deprecated since 5.8.1. See get_default_quality() below. + protected $default_quality = 82; /** * Each instance handles a single file. @@ -224,6 +227,11 @@ abstract class WP_Image_Editor { * @return true|WP_Error True if set successfully; WP_Error on failure. */ public function set_quality( $quality = null ) { + // Use the output mime type if present. If not, fall back to the input/initial mime type. + $mime_type = ! empty( $this->output_mime_type ) ? $this->output_mime_type : $this->mime_type; + // Get the default quality setting for the mime type. + $default_quality = $this->get_default_quality( $mime_type ); + if ( null === $quality ) { /** * Filters the default image compression quality setting. @@ -238,9 +246,9 @@ abstract class WP_Image_Editor { * @param int $quality Quality level between 1 (low) and 100 (high). * @param string $mime_type Image mime type. */ - $quality = apply_filters( 'wp_editor_set_quality', $this->default_quality, $this->mime_type ); + $quality = apply_filters( 'wp_editor_set_quality', $default_quality, $mime_type ); - if ( 'image/jpeg' === $this->mime_type ) { + if ( 'image/jpeg' === $mime_type ) { /** * Filters the JPEG compression quality for backward-compatibility. * @@ -261,7 +269,7 @@ abstract class WP_Image_Editor { } if ( $quality < 0 || $quality > 100 ) { - $quality = $this->default_quality; + $quality = $default_quality; } } @@ -278,6 +286,27 @@ abstract class WP_Image_Editor { } } + /** + * Returns the default compression quality setting for the mime type. + * + * @since 5.8.1 + * + * @param string $mime_type + * @return int The default quality setting for the mime type. + */ + protected function get_default_quality( $mime_type ) { + switch ( $mime_type ) { + case 'image/webp': + $quality = 86; + break; + case 'image/jpeg': + default: + $quality = $this->default_quality; + } + + return $quality; + } + /** * Returns preferred mime-type and extension based on provided * file's extension and mime, or current file's extension and mime. @@ -374,13 +403,28 @@ abstract class WP_Image_Editor { $new_ext = $this->get_extension( $mime_type ); } - if ( $filename ) { + // Ensure both $filename and $new_ext are not empty. + // $this->get_extension() returns false on error which would effectively remove the extension + // from $filename. That shouldn't happen, files without extensions are not supported. + if ( $filename && $new_ext ) { $dir = pathinfo( $filename, PATHINFO_DIRNAME ); $ext = pathinfo( $filename, PATHINFO_EXTENSION ); $filename = trailingslashit( $dir ) . wp_basename( $filename, ".$ext" ) . ".{$new_ext}"; } + if ( $mime_type && ( $mime_type !== $this->mime_type ) ) { + // The image will be converted when saving. Set the quality for the new mime-type if not already set. + if ( $mime_type !== $this->output_mime_type ) { + $this->output_mime_type = $mime_type; + $this->set_quality(); + } + } elseif ( ! empty( $this->output_mime_type ) ) { + // Reset output_mime_type and quality. + $this->output_mime_type = null; + $this->set_quality(); + } + return array( $filename, $new_ext, $mime_type ); } diff --git a/tests/phpunit/includes/mock-image-editor.php b/tests/phpunit/includes/mock-image-editor.php index 4cd252b1c3..bc08fbb290 100644 --- a/tests/phpunit/includes/mock-image-editor.php +++ b/tests/phpunit/includes/mock-image-editor.php @@ -56,6 +56,8 @@ if ( class_exists( 'WP_Image_Editor' ) ) : } } public function save( $destfilename = null, $mime_type = null ) { + // Set new mime-type and quality if converting the image. + $this->get_output_format( $destfilename, $mime_type ); return self::$save_return; } public function stream( $mime_type = null ) { diff --git a/tests/phpunit/tests/image/editor.php b/tests/phpunit/tests/image/editor.php index 7ed30ebdb0..a35f8af9f2 100644 --- a/tests/phpunit/tests/image/editor.php +++ b/tests/phpunit/tests/image/editor.php @@ -110,6 +110,92 @@ class Tests_Image_Editor extends WP_Image_UnitTestCase { remove_filter( 'wp_editor_set_quality', $func_100_percent ); } + /** + * Test test_quality when converting image + * + * @ticket 6821 + */ + public function test_set_quality_with_image_conversion() { + $editor = wp_get_image_editor( DIR_TESTDATA . '/images/test-image.png' ); + $editor->set_mime_type( 'image/png' ); // Ensure mime-specific filters act properly. + + // Set conversions for uploaded images. + add_filter( 'image_editor_output_format', array( $this, 'image_editor_output_formats' ) ); + + // Quality setting for the source image. For PNG the fallback default of 82 is used. + $this->assertSame( 82, $editor->get_quality(), 'Default quality setting is 82.' ); + + // Quality should change to the output format's value. + // A PNG image will be converted to WEBP whose quialty should be 86. + $editor->save(); + $this->assertSame( 86, $editor->get_quality(), 'Output image format is WEBP. Quality setting for it should be 86.' ); + + // Removing PNG to WEBP conversion on save. Quality setting should reset to the default. + remove_filter( 'image_editor_output_format', array( $this, 'image_editor_output_formats' ) ); + $editor->save(); + $this->assertSame( 82, $editor->get_quality(), 'After removing image conversion quality setting should reset to the default of 82.' ); + + unset( $editor ); + + // Set conversions for uploaded images. + add_filter( 'image_editor_output_format', array( $this, 'image_editor_output_formats' ) ); + // Change the quality values. + add_filter( 'wp_editor_set_quality', array( $this, 'image_editor_change_quality' ), 10, 2 ); + + // Get a new editor to clear quality state. + $editor = wp_get_image_editor( DIR_TESTDATA . '/images/test-image.jpg' ); + $editor->set_mime_type( 'image/jpeg' ); + + $this->assertSame( 56, $editor->get_quality(), 'Filtered default quality for JPEG is 56.' ); + + // Quality should change to the output format's value as filtered above. + // A JPEG image will be converted to WEBP whose quialty should be 42. + $editor->save(); + $this->assertSame( 42, $editor->get_quality(), 'Image conversion from JPEG to WEBP. Filtered WEBP quality shoild be 42.' ); + + // After removing the conversion the quality setting should reset to the filtered value for the original image type, JPEG. + remove_filter( 'image_editor_output_format', array( $this, 'image_editor_output_formats' ) ); + $editor->save(); + $this->assertSame( + 56, + $editor->get_quality(), + 'After removing image conversion the quality setting should reset to the filtered value for JPEG, 56.' + ); + + remove_filter( 'wp_editor_set_quality', array( $this, 'image_editor_change_quality' ) ); + } + + /** + * Changes the output format when editing images. PNG and JPEG files + * will be converted to WEBP (if the image editor in PHP supports it). + * + * @param array $formats + * + * @return array + */ + public function image_editor_output_formats( $formats ) { + $formats['image/png'] = 'image/webp'; + $formats['image/jpeg'] = 'image/webp'; + return $formats; + } + + /** + * Changes the quality according to the mime-type. + * + * @param int $quality Default quality. + * @param string $mime_type Image mime-type. + * @return int The changed quality. + */ + public function image_editor_change_quality( $quality, $mime_type ) { + if ( 'image/jpeg' === $mime_type ) { + return 56; + } elseif ( 'image/webp' === $mime_type ) { + return 42; + } else { + return 30; + } + } + /** * Test generate_filename *