Make WordPress Core

Changeset 59589


Ignore:
Timestamp:
01/07/2025 10:33:37 PM (20 months ago)
Author:
adamsilverstein
Message:

Media: improve Imagick handling of colors and alpha channel for PNG image uploads.

Fix an issue where index color (8 bit) PNG uploads were output as true color (24 bit) PNGs, significantly increasing their size. When using Imagick, PNG output images will now match the colors of the uploaded image.

Also, correct handling of PNG alpha channel information so it is preserved in output images.

Props adamsilverstein, pbearne, nosilver4u, peterdavehello, joemcgill, azaozz, codex-m, kirasong, justlevine, jokanane, sallyruchman, wpfed, tgsrvrs, antpb, tb1909.
Fixes #36477.

Location:
trunk
Files:
6 added
2 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/wp-includes/class-wp-image-editor-imagick.php

    r59588 r59589  
    485485                                $this->image->setOption( 'png:compression-level', '9' );
    486486                                $this->image->setOption( 'png:compression-strategy', '1' );
    487                                 $this->image->setOption( 'png:exclude-chunk', 'all' );
     487                                // Check to see if a PNG is indexed, and find the pixel depth.
     488                                if ( is_callable( array( $this->image, 'getImageDepth' ) ) ) {
     489                                        $indexed_pixel_depth = $this->image->getImageDepth();
     490
     491                                        // Indexed PNG files get some additional handling.
     492                                        if ( 0 < $indexed_pixel_depth && 8 >= $indexed_pixel_depth ) {
     493                                                // Check for an alpha channel.
     494                                                if (
     495                                                        is_callable( array( $this->image, 'getImageAlphaChannel' ) )
     496                                                        && $this->image->getImageAlphaChannel()
     497                                                ) {
     498                                                        $this->image->setOption( 'png:include-chunk', 'tRNS' );
     499                                                } else {
     500                                                        $this->image->setOption( 'png:exclude-chunk', 'all' );
     501                                                }
     502
     503                                                // Reduce colors in the images to maximum needed, using the global colorspace.
     504                                                $max_colors = pow( 2, $indexed_pixel_depth );
     505                                                if ( is_callable( array( $this->image, 'getImageColors' ) ) ) {
     506                                                        $current_colors = $this->image->getImageColors();
     507                                                        $max_colors = min( $max_colors, $current_colors );
     508                                                }
     509                                                $this->image->quantizeImage( $max_colors, $this->image->getColorspace(), 0, false, false );
     510
     511                                                /**
     512                                                 * If the colorspace is 'gray', use the png8 format to ensure it stays indexed.
     513                                                 */
     514                                                if ( Imagick::COLORSPACE_GRAY === $this->image->getImageColorspace() ) {
     515                                                        $this->image->setOption( 'png:format', 'png8' );
     516                                                }
     517                                        }
     518                                }
    488519                        }
    489520
  • trunk/tests/phpunit/tests/image/editorImagick.php

    r59588 r59589  
    759759                return 8;
    760760        }
     761
     762        /**
     763         * Test that resizes are smaller for 16 bit PNG images.
     764         *
     765         * @ticket 36477
     766         *
     767         * @dataProvider data_resizes_are_small_for_16bit_images
     768         */
     769        public function test_resizes_are_small_for_16bit_images( $file ) {
     770
     771                $temp_file = DIR_TESTDATA . '/images/test-temp.png';
     772
     773                $imagick_image_editor = new WP_Image_Editor_Imagick( $file );
     774                $imagick_image_editor->load();
     775                $size = $imagick_image_editor->get_size();
     776
     777                $org_filesize = filesize( $file );
     778
     779                $imagick_image_editor->resize( $size['width'] * .5, $size['height'] * .5 );
     780
     781                $saved = $imagick_image_editor->save( $temp_file );
     782
     783                $new_filesize = filesize( $temp_file );
     784
     785                unlink( $temp_file );
     786
     787                $this->assertLessThan( $org_filesize, $new_filesize, 'The resized image file size is not smaller than the original file size.' );
     788        }
     789
     790        /**
     791         * data_test_resizes_are_small_for_16bit
     792         *
     793         * @return array[]
     794         */
     795        public static function data_resizes_are_small_for_16bit_images() {
     796                return array(
     797                        'cloudflare-status'       => array(
     798                                DIR_TESTDATA . '/images/png-tests/cloudflare-status.png',
     799                        ),
     800                        'deskcat8'                => array(
     801                                DIR_TESTDATA . '/images/png-tests/deskcat8.png',
     802                        ),
     803                        '17-c3-duplicate-entries' => array(
     804                                DIR_TESTDATA . '/images/png-tests/Palette_icon-or8.png',
     805                        ),
     806                        'rabbit-time-paletted'    => array(
     807                                DIR_TESTDATA . '/images/png-tests/rabbit-time-paletted-or8.png',
     808                        ),
     809                        'test8'                   => array(
     810                                DIR_TESTDATA . '/images/png-tests/test8.png',
     811                        ),
     812                );
     813        }
    761814}
Note: See TracChangeset for help on using the changeset viewer.