A very special filter
The jpeg_quality
filter is a really special one: It gets used in three different cases and you’ve to use the second argument to determine, if you want to use the filter or not.
Don’t let it do everything
The main problem for such a special filter is, that it may fire for later actions, if you ain’t remove it – allow it to run after the first check. So we need to get another filter inside wp_save_image_file()
to check if we want to alter the compression or not. To disable it for another save process, we remove it right before altering the compression.
The kool kid
The really odd thing is, that WP uses a default compression of 90% (which is 10% reduced quality) for every save process. This means, that every time you upload/crop/edit an image, you reduce its quality… which is a pain for images that aren’t the best, when you upload them (test it with an image containing a lot of red with a high contrast background). But… The really neat thing is, that you can change this behavior. So you want to change the compression, but get increased quality – much better than core allows- at the same time.
/**
* Alter the image compression, depending on case
*
* @param int $compression
* @param string $case
* @return int $compression
*/
function wpse58600_custom_jpg_compression_cb( $compression, $case )
{
global $size_switch;
// Should only fire once - don't leave it in for later cases
remove_filter( current_filter(), __FUNCTION__ );
// Alter the compression, depending on the case
switch ( $case )
{
case( 'edit_image' ) :
// We only add the compression, if the switch triggered,
// which means, that the size is smaller, than set in the main function.
// 60 is the percentage value, that gets added as compression to the smaller images.
$compression = $size_switch ? 60 : 100;
break;
case( 'image_resize' ) :
// Better leave it on 100% for resize
$compression = 100;
break;
case( 'wp_crop_image' ) :
// Better leave it on 100% for crop
// We already compressed it on the camera, the desktop/handheld device
// and the server previously. That's enough so far.
$compression = 100;
break;
}
return $compression;
}
/**
* Alter the compression for JPEG mime type images
* Checks for a specific min size of the image, before altering it
*
* @param string $image
* @param int $post_id
* @return string $image
*/
function wpse58600_custom_jpg_compression( $image, $post_id )
{
global $size_switch;
$size_switch = false;
// Define the size, that stops adding a compression
$trigger_size = 641;
// Get the sizes
$size_x = imagesx( $image );
$size_y = imagesy( $image );
// Add the filter only in case
if ( $trigger_size < $size_x )
{
$size_switch = true;
}
add_filter( 'jpeg_quality', 'wpse58600_custom_jpg_compression_cb', 20, 2 );
return $image;
}
add_filter( 'image_save_pre', 'wpse58600_custom_jpg_compression', 20, 2 );
EDIT: After a short discussion with @toscho, he pointed out, that the whole callback could be reduced to the following:
function wpse58600_custom_jpg_compression_cb( $compression, $case )
{
// Should only fire once - don't leave it in for later cases
remove_filter( current_filter(), __FUNCTION__ );
return ( $GLOBALS['size_switch'] && 'edit_image' === $case ) ? 60 : 100;
}
As I pulled the code out of a plugin I’m currently working on, I needed the switch
to add settings in. I also have to note, that I don’t use the global
in my plugin, as it’s an OOP approach. The code you can read ↑ above, is mainly reduced and altered code from the plugin, that has some minor left overs and is meant to be explanatory for later readers and still works. If you want to use it as plugin, you can do some optimization depending on your personal use case.
Notes:
From some investigation on the different tasks, there are, I noticed, that multiple $case
s get triggered on the following steps:
- Rotate:
edit-image
»image-resize
(the later 1× for any any size you choose – Thumbnail, etc.) - Mirror:
edit-image
»image-resize
(–“–)
This means, that the filter callback for jpeq_quality
will trigger 2× for rotating/mirroring an image and +1× for any additional size you add. So if you got less than 100%, it will reduce the quality twice. I did a lot of research on this topic, but I’m still not completely sure, what exact functions cause this behavior.