Skip to content
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

Fix wp_editor_set_quality to support setting quality for both loaded and saved image types #1622

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
53 changes: 48 additions & 5 deletions src/wp-includes/class-wp-image-editor.php
Expand Up @@ -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.
Expand Down Expand Up @@ -224,6 +227,11 @@ public function get_quality() {
* @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.
Expand All @@ -238,9 +246,9 @@ public function set_quality( $quality = null ) {
* @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.
*
Expand All @@ -261,7 +269,7 @@ public function set_quality( $quality = null ) {
}

if ( $quality < 0 || $quality > 100 ) {
$quality = $this->default_quality;
$quality = $default_quality;
}
}

Expand All @@ -278,6 +286,27 @@ public function set_quality( $quality = null ) {
}
}

/**
* 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.
Expand Down Expand Up @@ -374,13 +403,27 @@ protected function get_output_format( $filename = null, $mime_type = null ) {
$new_ext = $this->get_extension( $mime_type );
}

if ( $filename ) {
// $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 );
}

Expand Down
2 changes: 2 additions & 0 deletions tests/phpunit/includes/mock-image-editor.php
Expand Up @@ -56,6 +56,8 @@ public function flip( $horz, $vert ) {
}
}
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 ) {
Expand Down
86 changes: 86 additions & 0 deletions tests/phpunit/tests/image/editor.php
Expand Up @@ -110,6 +110,92 @@ public function test_set_quality() {
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
*
Expand Down