Make WordPress Core

Changeset 59844


Ignore:
Timestamp:
02/20/2025 06:44:02 AM (3 months ago)
Author:
adamsilverstein
Message:

Media: fix full size image generation for PNG uploads.

Remove a limitation that prevented PNG uploads from generating the full sized image. Fixes a bug where using the image_editor_output_format filter would not generate full sized images as expected. The removed code was present to prevent overly large PNG image output, however this issue was resolved separately in #36477.

Props: adamsilverstein, pixlpirate, flixos90, mukesh27, azaozz.

Fixes #62900.

Location:
trunk
Files:
1 added
2 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/wp-admin/includes/image.php

    r59379 r59844  
    262262    }
    263263
    264     // Do not scale (large) PNG images. May result in sub-sizes that have greater file size than the original. See #48736.
    265     if ( 'image/png' !== $imagesize['mime'] ) {
    266 
    267         /**
    268          * Filters the "BIG image" threshold value.
    269          *
    270          * If the original image width or height is above the threshold, it will be scaled down. The threshold is
    271          * used as max width and max height. The scaled down image will be used as the largest available size, including
    272          * the `_wp_attached_file` post meta value.
    273          *
    274          * Returning `false` from the filter callback will disable the scaling.
    275          *
    276          * @since 5.3.0
    277          *
    278          * @param int    $threshold     The threshold value in pixels. Default 2560.
    279          * @param array  $imagesize     {
    280          *     Indexed array of the image width and height in pixels.
    281          *
    282          *     @type int $0 The image width.
    283          *     @type int $1 The image height.
    284          * }
    285          * @param string $file          Full path to the uploaded image file.
    286          * @param int    $attachment_id Attachment post ID.
    287          */
    288         $threshold = (int) apply_filters( 'big_image_size_threshold', 2560, $imagesize, $file, $attachment_id );
    289 
    290         /*
    291          * If the original image's dimensions are over the threshold,
    292          * scale the image and use it as the "full" size.
    293          */
    294         $scale_down = false;
    295         $convert    = false;
    296 
    297         if ( $threshold && ( $image_meta['width'] > $threshold || $image_meta['height'] > $threshold ) ) {
    298             // The image will be converted if needed on saving.
    299             $scale_down = true;
    300         } else {
    301             // The image may need to be converted regardless of its dimensions.
    302             $output_format = wp_get_image_editor_output_format( $file, $imagesize['mime'] );
    303 
    304             if (
    305                 is_array( $output_format ) &&
    306                 array_key_exists( $imagesize['mime'], $output_format ) &&
    307                 $output_format[ $imagesize['mime'] ] !== $imagesize['mime']
    308             ) {
    309                 $convert = true;
    310             }
    311         }
    312 
    313         if ( $scale_down || $convert ) {
    314             $editor = wp_get_image_editor( $file );
    315 
    316             if ( is_wp_error( $editor ) ) {
    317                 // This image cannot be edited.
    318                 return $image_meta;
    319             }
    320 
     264    /**
     265     * Filters the "BIG image" threshold value.
     266     *
     267     * If the original image width or height is above the threshold, it will be scaled down. The threshold is
     268     * used as max width and max height. The scaled down image will be used as the largest available size, including
     269     * the `_wp_attached_file` post meta value.
     270     *
     271     * Returning `false` from the filter callback will disable the scaling.
     272     *
     273     * @since 5.3.0
     274     *
     275     * @param int    $threshold     The threshold value in pixels. Default 2560.
     276     * @param array  $imagesize     {
     277     *     Indexed array of the image width and height in pixels.
     278     *
     279     *     @type int $0 The image width.
     280     *     @type int $1 The image height.
     281     * }
     282     * @param string $file          Full path to the uploaded image file.
     283     * @param int    $attachment_id Attachment post ID.
     284     */
     285    $threshold = (int) apply_filters( 'big_image_size_threshold', 2560, $imagesize, $file, $attachment_id );
     286
     287    /*
     288     * If the original image's dimensions are over the threshold,
     289     * scale the image and use it as the "full" size.
     290     */
     291    $scale_down = false;
     292    $convert    = false;
     293
     294    if ( $threshold && ( $image_meta['width'] > $threshold || $image_meta['height'] > $threshold ) ) {
     295        // The image will be converted if needed on saving.
     296        $scale_down = true;
     297    } else {
     298        // The image may need to be converted regardless of its dimensions.
     299        $output_format = wp_get_image_editor_output_format( $file, $imagesize['mime'] );
     300
     301        if (
     302            is_array( $output_format ) &&
     303            array_key_exists( $imagesize['mime'], $output_format ) &&
     304            $output_format[ $imagesize['mime'] ] !== $imagesize['mime']
     305        ) {
     306            $convert = true;
     307        }
     308    }
     309
     310    if ( $scale_down || $convert ) {
     311        $editor = wp_get_image_editor( $file );
     312
     313        if ( is_wp_error( $editor ) ) {
     314            // This image cannot be edited.
     315            return $image_meta;
     316        }
     317
     318        if ( $scale_down ) {
     319            // Resize the image. This will also convet it if needed.
     320            $resized = $editor->resize( $threshold, $threshold );
     321        } elseif ( $convert ) {
     322            // The image will be converted (if possible) when saved.
     323            $resized = true;
     324        }
     325
     326        $rotated = null;
     327
     328        // If there is EXIF data, rotate according to EXIF Orientation.
     329        if ( ! is_wp_error( $resized ) && is_array( $exif_meta ) ) {
     330            $resized = $editor->maybe_exif_rotate();
     331            $rotated = $resized; // bool true or WP_Error
     332        }
     333
     334        if ( ! is_wp_error( $resized ) ) {
     335            /*
     336             * Append "-scaled" to the image file name. It will look like "my_image-scaled.jpg".
     337             * This doesn't affect the sub-sizes names as they are generated from the original image (for best quality).
     338             */
    321339            if ( $scale_down ) {
    322                 // Resize the image. This will also convet it if needed.
    323                 $resized = $editor->resize( $threshold, $threshold );
     340                $saved = $editor->save( $editor->generate_filename( 'scaled' ) );
    324341            } elseif ( $convert ) {
    325                 // The image will be converted (if possible) when saved.
    326                 $resized = true;
    327             }
    328 
    329             $rotated = null;
    330 
    331             // If there is EXIF data, rotate according to EXIF Orientation.
    332             if ( ! is_wp_error( $resized ) && is_array( $exif_meta ) ) {
    333                 $resized = $editor->maybe_exif_rotate();
    334                 $rotated = $resized; // bool true or WP_Error
    335             }
    336 
    337             if ( ! is_wp_error( $resized ) ) {
    338342                /*
    339                  * Append "-scaled" to the image file name. It will look like "my_image-scaled.jpg".
    340                  * This doesn't affect the sub-sizes names as they are generated from the original image (for best quality).
     343                 * Generate a new file name for the converted image.
     344                 *
     345                 * As the image file name will be unique due to the changed file extension,
     346                 * it does not need a suffix to be unique. However, the generate_filename method
     347                 * does not allow for an empty suffix, so the "-converted" suffix is required to
     348                 * be added and subsequently removed.
    341349                 */
    342                 if ( $scale_down ) {
    343                     $saved = $editor->save( $editor->generate_filename( 'scaled' ) );
    344                 } elseif ( $convert ) {
    345                     /*
    346                      * Generate a new file name for the converted image.
    347                      *
    348                      * As the image file name will be unique due to the changed file extension,
    349                      * it does not need a suffix to be unique. However, the generate_filename method
    350                      * does not allow for an empty suffix, so the "-converted" suffix is required to
    351                      * be added and subsequently removed.
    352                      */
    353                     $converted_file_name = $editor->generate_filename( 'converted' );
    354                     $converted_file_name = preg_replace( '/(-converted\.)([a-z0-9]+)$/i', '.$2', $converted_file_name );
    355                     $saved               = $editor->save( $converted_file_name );
    356                 } else {
    357                     $saved = $editor->save();
    358                 }
    359 
    360                 if ( ! is_wp_error( $saved ) ) {
    361                     $image_meta = _wp_image_meta_replace_original( $saved, $file, $image_meta, $attachment_id );
    362 
    363                     // If the image was rotated update the stored EXIF data.
    364                     if ( true === $rotated && ! empty( $image_meta['image_meta']['orientation'] ) ) {
    365                         $image_meta['image_meta']['orientation'] = 1;
    366                     }
    367                 } else {
    368                     // TODO: Log errors.
     350                $converted_file_name = $editor->generate_filename( 'converted' );
     351                $converted_file_name = preg_replace( '/(-converted\.)([a-z0-9]+)$/i', '.$2', $converted_file_name );
     352                $saved               = $editor->save( $converted_file_name );
     353            } else {
     354                $saved = $editor->save();
     355            }
     356
     357            if ( ! is_wp_error( $saved ) ) {
     358                $image_meta = _wp_image_meta_replace_original( $saved, $file, $image_meta, $attachment_id );
     359
     360                // If the image was rotated update the stored EXIF data.
     361                if ( true === $rotated && ! empty( $image_meta['image_meta']['orientation'] ) ) {
     362                    $image_meta['image_meta']['orientation'] = 1;
    369363                }
    370364            } else {
    371365                // TODO: Log errors.
    372366            }
    373         } elseif ( ! empty( $exif_meta['orientation'] ) && 1 !== (int) $exif_meta['orientation'] ) {
     367        } else {
     368            // TODO: Log errors.
     369        }
     370    } elseif ( ! empty( $exif_meta['orientation'] ) && 1 !== (int) $exif_meta['orientation'] ) {
    374371            // Rotate the whole original image if there is EXIF data and "orientation" is not 1.
    375372
    376373            $editor = wp_get_image_editor( $file );
    377374
    378             if ( is_wp_error( $editor ) ) {
    379                 // This image cannot be edited.
    380                 return $image_meta;
    381             }
     375        if ( is_wp_error( $editor ) ) {
     376            // This image cannot be edited.
     377            return $image_meta;
     378        }
    382379
    383380            // Rotate the image.
    384381            $rotated = $editor->maybe_exif_rotate();
    385382
    386             if ( true === $rotated ) {
    387                 // Append `-rotated` to the image file name.
    388                 $saved = $editor->save( $editor->generate_filename( 'rotated' ) );
    389 
    390                 if ( ! is_wp_error( $saved ) ) {
    391                     $image_meta = _wp_image_meta_replace_original( $saved, $file, $image_meta, $attachment_id );
    392 
    393                     // Update the stored EXIF data.
    394                     if ( ! empty( $image_meta['image_meta']['orientation'] ) ) {
    395                         $image_meta['image_meta']['orientation'] = 1;
    396                     }
    397                 } else {
    398                     // TODO: Log errors.
     383        if ( true === $rotated ) {
     384            // Append `-rotated` to the image file name.
     385            $saved = $editor->save( $editor->generate_filename( 'rotated' ) );
     386
     387            if ( ! is_wp_error( $saved ) ) {
     388                $image_meta = _wp_image_meta_replace_original( $saved, $file, $image_meta, $attachment_id );
     389
     390                // Update the stored EXIF data.
     391                if ( ! empty( $image_meta['image_meta']['orientation'] ) ) {
     392                    $image_meta['image_meta']['orientation'] = 1;
    399393                }
     394            } else {
     395                // TODO: Log errors.
    400396            }
    401397        }
  • trunk/tests/phpunit/tests/media/wpGenerateAttachmentMetadata.php

    r56559 r59844  
    8787        $this->assertSame( wp_filesize( get_attached_file( $attachment ) ), $metadata['filesize'] );
    8888    }
     89
     90    /**
     91     * Checks that large PNG uploads generate PNG `-scaled` thumbnails.
     92     *
     93     * @ticket 62900
     94     */
     95    public function test_wp_generate_attachment_metadata_png_thumbnail_smaller_than_original() {
     96        // Use the test-image-large.png test file.
     97        $attachment = $this->factory->attachment->create_upload_object( DIR_TESTDATA . '/images/png-tests/test-image-large.png' );
     98
     99        $metadata = wp_get_attachment_metadata( $attachment );
     100
     101        // Check that the full sized image with `-scaled` is created for the PNG.
     102        $this->assertStringContainsString( '-scaled.png', basename( $metadata['file'] ) );
     103    }
    89104}
Note: See TracChangeset for help on using the changeset viewer.