Skip to content

Commit

Permalink
Media: improve Imagick handling of colors and alpha channel for PNG i…
Browse files Browse the repository at this point in the history
…mage 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.



git-svn-id: https://develop.svn.wordpress.org/trunk@59589 602fd350-edb4-49c9-b593-d223f7449a82
  • Loading branch information
adamsilverstein committed Jan 7, 2025
1 parent 2bab211 commit 082f6e6
Show file tree
Hide file tree
Showing 7 changed files with 85 additions and 1 deletion.
33 changes: 32 additions & 1 deletion src/wp-includes/class-wp-image-editor-imagick.php
Original file line number Diff line number Diff line change
Expand Up @@ -484,7 +484,38 @@ protected function thumbnail_image( $dst_w, $dst_h, $filter_name = 'FILTER_TRIAN
$this->image->setOption( 'png:compression-filter', '5' );
$this->image->setOption( 'png:compression-level', '9' );
$this->image->setOption( 'png:compression-strategy', '1' );
$this->image->setOption( 'png:exclude-chunk', 'all' );
// Check to see if a PNG is indexed, and find the pixel depth.
if ( is_callable( array( $this->image, 'getImageDepth' ) ) ) {
$indexed_pixel_depth = $this->image->getImageDepth();

// Indexed PNG files get some additional handling.
if ( 0 < $indexed_pixel_depth && 8 >= $indexed_pixel_depth ) {
// Check for an alpha channel.
if (
is_callable( array( $this->image, 'getImageAlphaChannel' ) )
&& $this->image->getImageAlphaChannel()
) {
$this->image->setOption( 'png:include-chunk', 'tRNS' );
} else {
$this->image->setOption( 'png:exclude-chunk', 'all' );
}

// Reduce colors in the images to maximum needed, using the global colorspace.
$max_colors = pow( 2, $indexed_pixel_depth );
if ( is_callable( array( $this->image, 'getImageColors' ) ) ) {
$current_colors = $this->image->getImageColors();
$max_colors = min( $max_colors, $current_colors );
}
$this->image->quantizeImage( $max_colors, $this->image->getColorspace(), 0, false, false );

/**
* If the colorspace is 'gray', use the png8 format to ensure it stays indexed.
*/
if ( Imagick::COLORSPACE_GRAY === $this->image->getImageColorspace() ) {
$this->image->setOption( 'png:format', 'png8' );
}
}
}
}

/*
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added tests/phpunit/data/images/png-tests/deskcat8.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added tests/phpunit/data/images/png-tests/test8.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
53 changes: 53 additions & 0 deletions tests/phpunit/tests/image/editorImagick.php
Original file line number Diff line number Diff line change
Expand Up @@ -758,4 +758,57 @@ public function test_image_max_bit_depth() {
public function __return_eight() {
return 8;
}

/**
* Test that resizes are smaller for 16 bit PNG images.
*
* @ticket 36477
*
* @dataProvider data_resizes_are_small_for_16bit_images
*/
public function test_resizes_are_small_for_16bit_images( $file ) {

$temp_file = DIR_TESTDATA . '/images/test-temp.png';

$imagick_image_editor = new WP_Image_Editor_Imagick( $file );
$imagick_image_editor->load();
$size = $imagick_image_editor->get_size();

$org_filesize = filesize( $file );

$imagick_image_editor->resize( $size['width'] * .5, $size['height'] * .5 );

$saved = $imagick_image_editor->save( $temp_file );

$new_filesize = filesize( $temp_file );

unlink( $temp_file );

$this->assertLessThan( $org_filesize, $new_filesize, 'The resized image file size is not smaller than the original file size.' );
}

/**
* data_test_resizes_are_small_for_16bit
*
* @return array[]
*/
public static function data_resizes_are_small_for_16bit_images() {
return array(
'cloudflare-status' => array(
DIR_TESTDATA . '/images/png-tests/cloudflare-status.png',
),
'deskcat8' => array(
DIR_TESTDATA . '/images/png-tests/deskcat8.png',
),
'17-c3-duplicate-entries' => array(
DIR_TESTDATA . '/images/png-tests/Palette_icon-or8.png',
),
'rabbit-time-paletted' => array(
DIR_TESTDATA . '/images/png-tests/rabbit-time-paletted-or8.png',
),
'test8' => array(
DIR_TESTDATA . '/images/png-tests/test8.png',
),
);
}
}

0 comments on commit 082f6e6

Please sign in to comment.