Setting JPEG compression for custom image sizes doesn’t work in specific cases

I have a custom image size hello-image:

add_image_size( 'hello-image', 700, 300, true );

Whenever an image is uploaded, I compress the cropped hello-image sized image to 40% JPEG compression. It works great in all cases except…

The problem

If the width of the original image is greater than 700px it works just fine. But if the width of it is less than 700px, the compression goes to around 0%.

How to test it

Download the following two images:

BIG: 784px × 441px

SMALL: 670px × 377px

Then I have created a plugin which has all the relevant code:

<?php
/*
Plugin Name: WPSE: custom image size and JPEG compression
Description: custom image size and JPEG compression
Author: WPSE
Version: 1.0
*/

// Set JPEG compression quality
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'] . "https://wordpress.stackexchange.com/" . $pathinfo['dirname'];

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

                $image = $dir . "https://wordpress.stackexchange.com/" . $value['file'];
                $resource = imagecreatefromjpeg($image);

                if ($size == 'large') {

                    // set the jpeg quality for 'large' size
                    imagejpeg($resource, $image, 35);

                } elseif ($size == 'medium') {

                    // set the jpeg quality for the 'medium' size
                    imagejpeg($resource, $image, 35);

                } elseif ($size == 'hello-image') {

                    // set the jpeg quality for the 'hello-image' size
                    imagejpeg($resource, $image, 40);

                } else {

                    // set the jpeg quality for the rest of sizes
                    imagejpeg($resource, $image, 35); // 38
                }

                imagedestroy($resource);
            }
        }
    }
}


// add custom image sizes to media uploader
function my_insert_custom_image_sizes( $sizes ) {
    // get the custom image sizes
    global $_wp_additional_image_sizes;
    // if there are none, just return the built-in sizes
    if ( empty( $_wp_additional_image_sizes ) )
        return $sizes;

    // add all the custom sizes to the built-in sizes
    foreach ( $_wp_additional_image_sizes as $id => $data ) {
        // take the size ID (e.g., 'my-name'), replace hyphens with spaces,
        // and capitalise the first letter of each word
        if ( !isset($sizes[$id]) )
            $sizes[$id] = ucfirst( str_replace( '-', ' ', $id ) );
    }

    return $sizes;
}

// Which custom image size selected by default
function my_set_default_image_size () {
    return 'hello-image';
}

function custom_image_setup () {
    // custom image sizes
    add_image_size( 'medium', 300, 9999 ); //  medium
    add_image_size( 'hello-image', 700, 300, true ); // custom
    add_image_size( 'large', 700, 3000 ); //  large

    add_filter( 'image_size_names_choose', 'my_insert_custom_image_sizes' );
    add_filter( 'pre_option_image_default_size', 'my_set_default_image_size' );
}

add_action( 'after_setup_theme', 'custom_image_setup' );

Now, upload the big image and see how the hello-image image is compressed fine. Then upload the small image and see how the same image size is compressed terribly. So to clarify, this is the results:

700x300 = 40% compression
670x300 = 0% compression

Both of which are generated via the exact same custom image size. What is the cause of this and how can it be fixed?

I should highlight that my colleague posted a question here asking how to avoid creating this image size if the width is lower than 700px… but I’m a WordPress geek who would rather resolve the cause of the problem than add a hacky fix to it.

Finally, I started seeing this problem in a recent WordPress update, so it may be a bug in core, as opposed to an issue in my code.

Edit: Read the update.

2 s
2

Some suggestions:

If you want to try out the Image Editor API you can try to replace

imagejpeg( $resource, $image, 35 );

with:

$editor = wp_get_image_editor( $image );  
if ( ! is_wp_error( $editor ) )
{
    $editor->set_quality( 35 );
    $editor->save( $image  );
}
unset( $editor );

Also try to test e.g. these parts:

$resource = imagecreatefromjpeg( $image );
if( false !== $resource )
{
    imagejpeg( $resource, $image, 35 );
    imagedestroy( $resource );
}

in a standalone PHP script, to make sure it’s not a PHP or GD related issue.

PS: Here’s a different approach where we try to modify the image quality before the image size is generated.

Testing the plugin

Let’s check the plugin from the main question.

Test install

  • WordPress version 4.9-alpha-40917
  • PHP 7.0.19

gd
imagick

Here’s the generated hello-image (670×300) size, of the SMALL (670 × 377) image, from the question, where we set the quality to 40:

imagejpeg( $resource, $image, 40 );

q=40

Here’s the same but with the quality set to 0:

imagejpeg( $resource, $image, 0 );

q=0

So it looks like we don’t get the same behavior as described in the question, that when the quality is set to 40, it becomes 0, for the hello-image size of the 670×377 original image.

Leave a Comment