• I’ve done a lot of debugging and figured I’ve discovered the problem.

    It started when I was getting “false” returned from the function wp_get_attachment_image_srcset. wp_get_attachment_image_srcset calls the function wp_calculate_image_srcset, and that was where the problem was.

    The image I was using was 1080*1920 – and was just a placeholder image for while I got the site working. (if it matters the exact image, I got it from here, it was the one with the marinated steak being prodded with a fork on a barbeque, you’ll have to refresh until it comes up.)

    I tracked the problem down to this part of the source code:

    // If the new ratio differs by less than 0.002, use it.
    if ( abs( $image_ratio - $image_ratio_compare ) < 0.002 ) {
        // Add the URL, descriptor, and value to the sources array to be returned.
        $sources[ $image['width'] ] = array(
            'url'        => $image_baseurl . $image['file'],
            'descriptor' => 'w',
            'value'      => $image['width'],
        );
    }

    $image_ratio is calculated from the src image and if $image_ratio_compare isn’t within 0.002 of the src’s ratio that particular image won’t get added to the srcset. All of my images were filters out, except the original src. I don’t know why this is. It might be because the image was portrait in such a high aspect ratio? Either way, this seems like a bug to me – the value is so low that it’s filtering out legitimate srcset images.

    I’ve got around it by adding a filter that basically reuses all the code except changes the 0.002 value to 0.005, but it really was a bother tracking this down and I can’t imagine I’m the first to encounter this issue (though I think I am the first to post about it)

    So this is less of a request for help and more of a “if this helps anyone” and a potential bug report.

Viewing 3 replies - 1 through 3 (of 3 total)
  • Thread Starter sammy.salter

    (@sammysalter)

    oh and if anyone else had this problem here is the filter I used

    function my_calculate_image_srcset( $sources, $size_array, $image_src, $image_meta, $attachment_id ) {
        /**
         * Let plugins pre-filter the image meta to be able to fix inconsistencies in the stored data.
         *
         * @param array  $image_meta    The image meta data as returned by 'wp_get_attachment_metadata()'.
         * @param array  $size_array    Array of width and height values in pixels (in that order).
         * @param string $image_src     The 'src' of the image.
         * @param int    $attachment_id The image attachment ID or 0 if not supplied.
         */
    
        $image_meta = apply_filters( 'wp_calculate_image_srcset_meta', $image_meta, $size_array, $image_src, $attachment_id );
    
        if ( empty( $image_meta['sizes'] ) ) {
          return false;
        }
    
        $image_sizes = $image_meta['sizes'];
    
        // Get the width and height of the image.
        $image_width = (int) $size_array[0];
        $image_height = (int) $size_array[1];
    
        // Bail early if error/no width.
        if ( $image_width < 1 ) {
            return false;
        }
    
        $image_basename = wp_basename( $image_meta['file'] );
    
        /*
         * WordPress flattens animated GIFs into one frame when generating intermediate sizes.
         * To avoid hiding animation in user content, if src is a full size GIF, a srcset attribute is not generated.
         * If src is an intermediate size GIF, the full size is excluded from srcset to keep a flattened GIF from becoming animated.
         */
        if ( ! isset( $image_sizes['thumbnail']['mime-type'] ) || 'image/gif' !== $image_sizes['thumbnail']['mime-type'] ) {
            $image_sizes['full'] = array(
                'width'  => $image_meta['width'],
                'height' => $image_meta['height'],
                'file'   => $image_basename,
            );
        } elseif ( strpos( $image_src, $image_meta['file'] ) ) {
            return false;
        }
    
        // Retrieve the uploads sub-directory from the full size image.
        $dirname = _wp_get_attachment_relative_path( $image_meta['file'] );
    
        if ( $dirname ) {
            $dirname = trailingslashit( $dirname );
        }
    
        $image_baseurl = _wp_upload_dir_baseurl();
        $image_baseurl = trailingslashit( $image_baseurl ) . $dirname;
    
        // Calculate the image aspect ratio.
        $image_ratio = $image_height / $image_width;
        /*
         * Images that have been edited in WordPress after being uploaded will
         * contain a unique hash. Look for that hash and use it later to filter
         * out images that are leftovers from previous versions.
         */
        $image_edited = preg_match( '/-e[0-9]{13}/', wp_basename( $image_src ), $image_edit_hash );
    
        /**
         * Filter the maximum image width to be included in a 'srcset' attribute.
         *
         * @since 4.4.0
         *
         * @param int   $max_width  The maximum image width to be included in the 'srcset'. Default '1600'.
         * @param array $size_array Array of width and height values in pixels (in that order).
         */
        $max_srcset_image_width = apply_filters( 'max_srcset_image_width', 1600, $size_array );
    
        // Array to hold URL candidates.
        $sources = array();
    
        /**
         * To make sure the ID matches our image src, we will check to see if any sizes in our attachment
         * meta match our $image_src. If no mathces are found we don't return a srcset to avoid serving
         * an incorrect image. See #35045.
         */
        $src_matched = false;
    
        /*
         * Loop through available images. Only use images that are resized
         * versions of the same edit.
         */
    
        foreach ( $image_sizes as $image ) {
    
            // If the file name is part of the <code>src</code>, we've confirmed a match.
            if ( ! $src_matched && false !== strpos( $image_src, $dirname . $image['file'] ) ) {
                $src_matched = true;
            }
    
            // Filter out images that are from previous edits.
            if ( $image_edited && ! strpos( $image['file'], $image_edit_hash[0] ) ) {
    
              continue;
    
            }
    
            /*
             * Filter out images that are wider than '$max_srcset_image_width' unless
             * that file is in the 'src' attribute.
             */
            if ( $max_srcset_image_width && $image['width'] > $max_srcset_image_width &&
                false === strpos( $image_src, $image['file'] ) ) {
    
              continue;
    
            }
    
            // Calculate the new image ratio.
            if ( $image['width'] ) {
                $image_ratio_compare = $image['height'] / $image['width'];
            } else {
                $image_ratio_compare = 0;
            }
    
            // If the new ratio differs by less than 0.005, use it.
            if ( abs( $image_ratio - $image_ratio_compare ) < 0.005 ) {
                // Add the URL, descriptor, and value to the sources array to be returned.
                $sources[ $image['width'] ] = array(
                    'url'        => $image_baseurl . $image['file'],
                    'descriptor' => 'w',
                    'value'      => $image['width'],
                );
    
            }
        }
    
        return $sources;
    }
    add_filter ('wp_calculate_image_srcset', 'my_calculate_image_srcset', null, 5);
    Thread Starter sammy.salter

    (@sammysalter)

    Another thought – it might be best to do some sort of “OR” statement to test portrait or landscape rather than increasing the value, making the test less sensitive.

    changing the line

    if ( abs( $image_ratio - $image_ratio_compare ) < 0.002 ) {

    to something like

    if ( abs( $image_ratio - $image_ratio_compare ) < 0.002 || abs( ( $image_ratio ** -1 ) - ( $image_ratio_compare ** -1 ) ) < 0.002) {

    I think that the issue really is caused by the fact that aspect ratios are not linear but the comparison is a linear approximation (one that works well for landscape images but is poor for portrait images). It might even be better to use a non-linear comparison, e.g.

    if ( abs( $image_ratio - $image_ratio_compare ) < 0.002 * $image_ratio) {

    But that would be harder to optimise. Either way a better solution is needed, as valid images are being filtered out by the function inappropriately.

    Thread Starter sammy.salter

    (@sammysalter)

    I’ve tested out this one:

    if ( abs( $image_ratio - $image_ratio_compare ) < 0.002 * $image_ratio) {

    and I think it’s the best one to use. It depends how WP came up with the 0.002 number. This fix assumes it was best optimised for a square image ratio but if it’s known whatever image ratio it was optimised for just multiply the 0.002 by it.

Viewing 3 replies - 1 through 3 (of 3 total)
  • The topic ‘Aspect ratio value in wp_calculate_image_srcset() breaking the function’ is closed to new replies.