Take the 2-minute tour ×
WordPress Development Stack Exchange is a question and answer site for WordPress developers and administrators. It's 100% free, no registration required.

Short. I want ''large' images compressed by 90%, and 'medium' to be by 60%. Has many af you know, sometimes larger images suffer from high compression, but other small images don't.

This function allows to resample all the jpg images

function custom_jpg_compression($args) {
    return 90;
}
add_filter('jpeg_quality', 'custom_jpg_compression');

How to filter by image size?

share|improve this question
add comment

1 Answer

up vote 10 down vote accepted

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 $cases 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.

share|improve this answer
    
Ups you got me, print_r/var_dump doesn't work. How can I output that? –  DarkGhostHunter Jul 16 '12 at 0:45
    
Use echo '<pre>'.var_export( $image, true ).'</pre>';. Put an exit; after it, so it doesn't get skipped with reloading the page, etc. –  kaiser Jul 16 '12 at 0:55
    
damn, lost in the woods - I can't see where it prints. I have that code before the return $image; –  DarkGhostHunter Jul 16 '12 at 1:05
    
Add it right at the beginning of the wpse58600_custom_jpg_compression() function. –  kaiser Jul 16 '12 at 10:20
1  
let us continue this discussion in chat –  DarkGhostHunter Jul 17 '12 at 0:36
show 5 more comments

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.