From 0671dfd5b4888026874c12ccba9a6cb03f38aaf0 Mon Sep 17 00:00:00 2001 From: Adam Silverstein Date: Fri, 29 Nov 2024 23:46:50 +0000 Subject: [PATCH] Media: improve filter to enable setting output quality by image size. Add a new $size parameter to the wp_editor_set_quality filter. $size is an array with 'width' and 'height' keys. Developers can use this information to set image quality based on the image size. Props adamsilverstein, joemcgill, Mte90, codekraft, birgire, azaozz, sppramodh. Fixes #54648. git-svn-id: https://develop.svn.wordpress.org/trunk@59473 602fd350-edb4-49c9-b593-d223f7449a82 --- src/wp-includes/class-wp-image-editor-gd.php | 18 ++++++-- .../class-wp-image-editor-imagick.php | 16 +++++-- src/wp-includes/class-wp-image-editor.php | 16 +++++-- tests/phpunit/tests/media.php | 45 +++++++++++++++++++ 4 files changed, 85 insertions(+), 10 deletions(-) diff --git a/src/wp-includes/class-wp-image-editor-gd.php b/src/wp-includes/class-wp-image-editor-gd.php index e4f065537639d..47ba91a367d9f 100644 --- a/src/wp-includes/class-wp-image-editor-gd.php +++ b/src/wp-includes/class-wp-image-editor-gd.php @@ -221,6 +221,14 @@ protected function _resize( $max_w, $max_h, $crop = false ) { list( $dst_x, $dst_y, $src_x, $src_y, $dst_w, $dst_h, $src_w, $src_h ) = $dims; + $this->set_quality( + null, + array( + 'width' => $dst_w, + 'height' => $dst_h, + ) + ); + $resized = wp_imagecreatetruecolor( $dst_w, $dst_h ); imagecopyresampled( $resized, $this->image, $dst_x, $dst_y, $src_x, $src_y, $dst_w, $dst_h, $src_w, $src_h ); @@ -568,12 +576,14 @@ protected function _save( $image, $filename = null, $mime_type = null ) { * Sets Image Compression quality on a 1-100% scale. Handles WebP lossless images. * * @since 6.7.0 + * @since 6.8.0 The `$dims` parameter was added. * - * @param int $quality Compression Quality. Range: [1,100] + * @param int $quality Compression Quality. Range: [1,100] + * @param array $dims Optional. Image dimensions array with 'width' and 'height' keys. * @return true|WP_Error True if set successfully; WP_Error on failure. */ - public function set_quality( $quality = null ) { - $quality_result = parent::set_quality( $quality ); + public function set_quality( $quality = null, $dims = array() ) { + $quality_result = parent::set_quality( $quality, $dims ); if ( is_wp_error( $quality_result ) ) { return $quality_result; } else { @@ -586,7 +596,7 @@ public function set_quality( $quality = null ) { $webp_info = wp_get_webp_info( $this->file ); if ( ! empty( $webp_info['type'] ) && 'lossless' === $webp_info['type'] ) { $quality = IMG_WEBP_LOSSLESS; - parent::set_quality( $quality ); + parent::set_quality( $quality, $dims ); } } } catch ( Exception $e ) { diff --git a/src/wp-includes/class-wp-image-editor-imagick.php b/src/wp-includes/class-wp-image-editor-imagick.php index 45b0e3a5a48ef..dd8b9ad5191ee 100644 --- a/src/wp-includes/class-wp-image-editor-imagick.php +++ b/src/wp-includes/class-wp-image-editor-imagick.php @@ -190,12 +190,14 @@ public function load() { * Sets Image Compression quality on a 1-100% scale. * * @since 3.5.0 + * @since 6.8.0 The `$dims` parameter was added. * - * @param int $quality Compression Quality. Range: [1,100] + * @param int $quality Compression Quality. Range: [1,100] + * @param array $dims Optional. Image dimensions array with 'width' and 'height' keys. * @return true|WP_Error True if set successfully; WP_Error on failure. */ - public function set_quality( $quality = null ) { - $quality_result = parent::set_quality( $quality ); + public function set_quality( $quality = null, $dims = array() ) { + $quality_result = parent::set_quality( $quality, $dims ); if ( is_wp_error( $quality_result ) ) { return $quality_result; } else { @@ -367,6 +369,14 @@ public function resize( $max_w, $max_h, $crop = false ) { return $this->crop( $src_x, $src_y, $src_w, $src_h, $dst_w, $dst_h ); } + $this->set_quality( + null, + array( + 'width' => $dst_w, + 'height' => $dst_h, + ) + ); + // Execute the resize. $thumb_result = $this->thumbnail_image( $dst_w, $dst_h ); if ( is_wp_error( $thumb_result ) ) { diff --git a/src/wp-includes/class-wp-image-editor.php b/src/wp-includes/class-wp-image-editor.php index dc2420507800d..f9f58fcd50c6b 100644 --- a/src/wp-includes/class-wp-image-editor.php +++ b/src/wp-includes/class-wp-image-editor.php @@ -240,11 +240,14 @@ public function get_quality() { * Sets Image Compression quality on a 1-100% scale. * * @since 3.5.0 + * @since 6.8.0 The `$dims` parameter was added. * - * @param int $quality Compression Quality. Range: [1,100] + * @param int $quality Compression Quality. Range: [1,100] + * @param array $dims Optional. Image dimensions array with 'width' and 'height' keys. * @return true|WP_Error True if set successfully; WP_Error on failure. + */ - public function set_quality( $quality = null ) { + public function set_quality( $quality = null, $dims = array() ) { // 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. @@ -260,11 +263,18 @@ public function set_quality( $quality = null ) { * The WP_Image_Editor::set_quality() method has priority over the filter. * * @since 3.5.0 + * @since 6.8.0 Added the size parameter. * * @param int $quality Quality level between 1 (low) and 100 (high). * @param string $mime_type Image mime type. + * @param array $size { + * Dimensions of the image. + * + * @type int $width The image width. + * @type int $height The image height. + * } */ - $quality = apply_filters( 'wp_editor_set_quality', $default_quality, $mime_type ); + $quality = apply_filters( 'wp_editor_set_quality', $default_quality, $mime_type, $dims ? $dims : $this->size ); if ( 'image/jpeg' === $mime_type ) { /** diff --git a/tests/phpunit/tests/media.php b/tests/phpunit/tests/media.php index 41ae8ef216401..7b6dc5002f106 100644 --- a/tests/phpunit/tests/media.php +++ b/tests/phpunit/tests/media.php @@ -5433,6 +5433,51 @@ public function test_quality_with_avif_conversion_file_sizes() { } } + /** + * Test that the `wp_editor_set_quality` filter includes the dimensions in the `$dims` parameter. + * + * @ticket 54648 + */ + public function test_wp_editor_set_quality_includes_dimensions() { + // Before loading an image, set up the callback filter with the assertions. + add_filter( 'wp_editor_set_quality', array( $this, 'assert_dimensions_in_wp_editor_set_quality' ), 10, 3 ); + + $temp_dir = get_temp_dir(); + $file = $temp_dir . '/33772.jpg'; + copy( DIR_TESTDATA . '/images/33772.jpg', $file ); + + $editor = wp_get_image_editor( $file ); + + $attachment_id = self::factory()->attachment->create_object( + array( + 'post_mime_type' => 'image/jpeg', + 'file' => $file, + ) + ); + + // Generate all sizes. + wp_generate_attachment_metadata( $attachment_id, $file ); + + // Clean up the filter. + remove_filter( 'wp_editor_set_quality', array( $this, 'assert_dimensions_in_wp_editor_set_quality' ), 10, 3 ); + } + + /** + * Helper callback to assert that the dimensions are included in the `$dims` parameter. + * + * @param int $quality The quality level. + * @param array $dims The dimensions array. + */ + public function assert_dimensions_in_wp_editor_set_quality( $quality, $mime_type, $dims ) { + // Assert that the array has non empty width and height values. + $this->assertArrayHasKey( 'width', $dims ); + $this->assertArrayHasKey( 'height', $dims ); + $this->assertGreaterThan( 0, $dims['width'] ); + $this->assertGreaterThan( 0, $dims['height'] ); + + return $quality; + } + /** * Test that an image size isn't generated if it matches the original image size. *