6

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'] . '/' . $pathinfo['dirname'];

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

                $image = $dir . '/' . $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.

  • 1
    Given the complexity of this question, I will bounty it 50 points when it's eligible. – Christine Cooper Jun 24 '17 at 10:46
6
+50

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 (670x300) 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 670x377 original image.

  • Tried your approach with same results. Quite possibly the most bizarre issue. It can't be a PHP/GD issue because it compresses the image properly if the width is above 700px as noted in the question. Did you have a chance to test this on your local environment? Before I look into alternative solution (as the one you linked), I'm hoping to get to the bottom of why this doesn't work. Mainly to understand how to troubleshoot this type of issue in the future given that WordPress is my actual career now. =) Wouldn't be surprised if we discovered a WP bug. – Christine Cooper Jun 25 '17 at 10:46
  • I tested the plugin and updated the answer. It looks like I can't replicate the problem on my test install. – birgire Jun 25 '17 at 12:40
  • Thank you very much for the update. I am looking into the cause of this now that it may appear to be a local issue. I will post an update soon (may take some days). A 1+ meanwhile! – Christine Cooper Jun 25 '17 at 13:16
  • A question. Is the imagick module really required to have this working? – Christine Cooper Jun 25 '17 at 14:16
  • No, I just included both info tables as I tested both WP_Image_Editor_Imagick and WP_Image_Editor_GD via the wp_image_editors filter. – birgire Jun 25 '17 at 14:49
0

Interesting, I tried it and the small image gets displayed 670x300 and the big image 700x300. Seems to me like its is doing exactly what it needs to do, it crops the image if either the height or width are above the image type (or both).

I understand you only want to crop the image if the height is higher? Then using this would work:

  add_image_size( 'hello-world', 700, 0, true );

I re-upload the images after that and the small images gets displayed 670 by 377 and the big image 700 by 394. I uploaded another smaller version (516x290) and it does not get cropped at all.

Although when you upload an image like 690x4000 it also does not get cropped.

  • This does not actually resolve the problem. Please note that I am looking for the reason why the 670x300 is compressed to 0% and the 700x300 compressed to 40%. Both of which are generated via the exact same image size, so the compression should be set to 40% on both. I have updated my question a bit to clarify the question further. Please let me know if you need any further clarification. – Christine Cooper Jun 24 '17 at 14:04

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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