9

I use various custom image sizes (by add_image_size) and I have set my JPEG compression to 30% with this filter:

function jpeg_quality_callback($arg) {
   return (int)30;
}
add_filter('jpeg_quality', 'jpeg_quality_callback');

If I am not mistaken, the above code compresses all of my custom image sizes with 30%. Now, for two of my custom image sizes named splash1 and splash2, I want to set the compression to 80%. How is this possible?

Or alternatively, exclude those image sizes from the 30% compression filter.

1 Answer 1

9

The 'jpeg_quality' filter hook functions accept two arguments: $jpeg_quality and $function which is the function from within the filter hook is fired and can be either image_resize or wp_crop_image. So there is no way to selectively set the quality of .jpeg images according to image size from this filter hook function.

However, you still can hook to a later action hook in the process of uploading attachments and adjust the .jpeg image quality of uploaded images at that point according to thier specific size to suit your needs. First set the jpeg_quality to the maximum to preserve the original image quality, then hook to added_post_meta action hook (which is fired at the end of inserting attachment metadata`) to adjust the quality, as follow:

// set the quality to maximum
add_filter('jpeg_quality', create_function('$quality', 'return 100;'));

add_action('added_post_meta', 'ad_update_jpeg_quality', 10, 4);

function ad_update_jpeg_quality($meta_id, $attach_id, $meta_key, $attach_meta) {

    if ($meta_key == '_wp_attachment_metadata') {

        $post = get_post($attach_id);

        if ($post->post_mime_type == 'image/jpeg' && is_array($attach_meta['sizes'])) {

            $pathinfo = pathinfo($attach_meta['file']);
            $uploads = wp_upload_dir();
            $dir = $uploads['basedir'] . '/' . $pathinfo['dirname'];

            foreach ($attach_meta['sizes'] as $size => $value) {

                $image = $dir . '/' . $value['file'];
                $resource = imagecreatefromjpeg($image);

                if ($size == 'spalsh') {
                    // set the jpeg quality for 'spalsh' size
                    imagejpeg($resource, $image, 100);
                } elseif ($size == 'spalsh1') {
                    // set the jpeg quality for the 'splash1' size
                    imagejpeg($resource, $image, 30);
                } else {
                    // set the jpeg quality for the rest of sizes
                    imagejpeg($resource, $image, 10);
                }

                // or you can skip a paticular image size
                // and set the quality for the rest:
                // if ($size == 'splash') continue;

                imagedestroy($resource);
            }
        }
    }
}

The above code will affect the newly uploaded images only. If you want to update the quality of previously uploaded images, you can utilize register_activation_hook of plugins. Create a new php file in wp-content/plugins directory and name it whatever you like (update-jpeg-quality.php for example) and add the following code to it:

<?php
/*
Plugin Name: Update JPEG Quality
Plugin URI: http://wordpress.stackexchange.com/questions/74103/set-jpeg-compression-for-specific-custom-image-sizes
Description: This plugin will change the jpeg image quality according to its size.
Author: Ahmad M
Version: 1.0
Author URI: http://wordpress.stackexchange.com/users/12961/ahmad-m
*/

register_activation_hook(__FILE__, 'ad_modify_jpeg_quality');

function ad_modify_jpeg_quality() {

    $attachments = get_posts(array(
        'numberposts' => -1,
        'post_type' => 'attachment',
        'post_mime_type' => 'image/jpeg'
    ));

    if (empty($attachments)) return;

    $uploads = wp_upload_dir();

    foreach ($attachments as $attachment) {

        $attach_meta = wp_get_attachment_metadata($attachment->ID);
        if (!is_array($attach_meta['sizes'])) break;

        $pathinfo = pathinfo($attach_meta['file']);
        $dir = $uploads['basedir'] . '/' . $pathinfo['dirname'];

        foreach ($attach_meta['sizes'] as $size => $value) {

            $image = $dir . '/' . $value['file'];
            $resource = imagecreatefromjpeg($image);

            if ($size == 'spalsh') {
                // set the jpeg quality for 'spalsh' size
                imagejpeg($resource, $image, 100);
            } elseif ($size == 'spalsh1') {
                // set the jpeg quality for the 'splash1' size
                imagejpeg($resource, $image, 30);
            } else {
                // set the jpeg quality for the rest of sizes
                imagejpeg($resource, $image, 10);
            }

            imagedestroy($resource);
        }
    }
}
?>

Now visit your Plugins page and hit activate of the Update JPEG Quality plugin. This will loop through all your previously uploaded .jpeg images and adjust their quality according to the values and conditions you specify in the plugin. Then you can safely deactivate and delete this plugin. Please test on testing environment first before applying to production site.

12
  • This is a very interesting reply. How do I add both 'splash1' and 'splash2' to the IF statement?: if ($size == 'splash')... Commented Nov 29, 2012 at 15:55
  • 1
    @AmandaDuke: You can extend the if statement by elseif. See my update.
    – Ahmad M
    Commented Nov 29, 2012 at 16:11
  • 2
    @Ahmad there is a bug in your code. When uploading an image, if it is smaller than the dimensions of the smallest custom image size, then this error appears: Invalid argument supplied for foreach(). So basically, when no custom image is made, the error appears. Do you know how to fix this problem? Commented Dec 2, 2012 at 16:29
  • 1
    @AmandaDuke Thanks for pointing that out. This error happen because $attach_meta['sizes'] is expected to be an array while in this case it is not. In most cases it should and this can only happen if we dont have custom image sizes and we don't specify the default image sizes in Settings -> Media Settings. To be safe and make sure we have an array we can add a check before passing it to the foreach loop: if (is_array($attach_meta['sizes'])). Please see the updated code.
    – Ahmad M
    Commented Dec 2, 2012 at 20:18
  • 1
    @AmandaDuke: Both blocks of code have been updated. The first block was before: if ( $post->post_mime_type == 'image/jpeg' ) and now it is if ( $post->post_mime_type == 'image/jpeg' && is_array($attach_meta['sizes']) )
    – Ahmad M
    Commented Dec 3, 2012 at 13:41

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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