Skip to content

Commit 97ed384

Browse files
Media: fix degraded handling for 24 bit PNG uploads.
Add additional logic to correctly identify indexed PNG images so output PNGs correctly match the uploaded image depth. Fix a regression introduced in WordPress 6.8 [59589] where uploaded Truecolor PNG images (meaning > 8 bit) were mis-identified as indexed (8 bit) images, causing output images to be indexed instead of Truecolor resulting in a noticeable visual degradation. Props elvismdev, wildworks, SirLouen, siliconforks, joemcgill, iamshashank, nosilver4u. Fixes #63448. git-svn-id: https://develop.svn.wordpress.org/trunk@60246 602fd350-edb4-49c9-b593-d223f7449a82
1 parent 8c01157 commit 97ed384

File tree

4 files changed

+80
-32
lines changed

4 files changed

+80
-32
lines changed

src/wp-includes/class-wp-image-editor-imagick.php

Lines changed: 21 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -484,37 +484,28 @@ protected function thumbnail_image( $dst_w, $dst_h, $filter_name = 'FILTER_TRIAN
484484
$this->image->setOption( 'png:compression-filter', '5' );
485485
$this->image->setOption( 'png:compression-level', '9' );
486486
$this->image->setOption( 'png:compression-strategy', '1' );
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-
}
487+
488+
// Indexed PNG files get some additional handling.
489+
// See #63448 for details.
490+
if (
491+
is_callable( array( $this->image, 'getImageProperty' ) )
492+
&& '3' === $this->image->getImageProperty( 'png:IHDR.color-type-orig' )
493+
) {
494+
495+
// Check for an alpha channel.
496+
if (
497+
is_callable( array( $this->image, 'getImageAlphaChannel' ) )
498+
&& $this->image->getImageAlphaChannel()
499+
) {
500+
$this->image->setOption( 'png:include-chunk', 'tRNS' );
501+
} else {
502+
$this->image->setOption( 'png:exclude-chunk', 'all' );
517503
}
504+
// Set the image format to Indexed PNG.
505+
$this->image->setOption( 'png:format', 'png8' );
506+
507+
} else {
508+
$this->image->setOption( 'png:exclude-chunk', 'all' );
518509
}
519510
}
520511

8.64 KB
Loading
2.5 MB
Loading

tests/phpunit/tests/image/editorImagick.php

Lines changed: 59 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -770,6 +770,8 @@ public function __return_eight() {
770770
* @ticket 36477
771771
*
772772
* @dataProvider data_resizes_are_small_for_16bit_images
773+
*
774+
* @param string $file Path to the image file.
773775
*/
774776
public function test_resizes_are_small_for_16bit_images( $file ) {
775777

@@ -783,7 +785,7 @@ public function test_resizes_are_small_for_16bit_images( $file ) {
783785

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

786-
$saved = $imagick_image_editor->save( $temp_file );
788+
$imagick_image_editor->save( $temp_file );
787789

788790
$new_filesize = filesize( $temp_file );
789791

@@ -793,7 +795,7 @@ public function test_resizes_are_small_for_16bit_images( $file ) {
793795
}
794796

795797
/**
796-
* data_test_resizes_are_small_for_16bit
798+
* Data provider for test_resizes_are_small_for_16bit_images.
797799
*
798800
* @return array[]
799801
*/
@@ -816,4 +818,59 @@ public static function data_resizes_are_small_for_16bit_images() {
816818
),
817819
);
818820
}
821+
822+
/**
823+
* Tests that the 'png:IHDR.color-type-orig' property is preserved after resizing
824+
* Used to identify indexed PNG images, see https://www.w3.org/TR/PNG-Chunks.html#C.IHDR.
825+
*
826+
* @ticket 63448
827+
* @dataProvider data_png_color_type_after_resize
828+
*
829+
* @param string $file_path Path to the image file.
830+
* @param int $expected_color_type The expected original color type.
831+
*/
832+
public function test_png_color_type_is_preserved_after_resize( $file_path, $expected_color_type ) {
833+
834+
$temp_file = DIR_TESTDATA . '/images/test-temp.png';
835+
836+
$imagick_image_editor = new WP_Image_Editor_Imagick( $file_path );
837+
$imagick_image_editor->load();
838+
839+
$size = $imagick_image_editor->get_size();
840+
$imagick_image_editor->resize( $size['width'] * 0.5, $size['height'] * 0.5 );
841+
$imagick_image_editor->save( $temp_file );
842+
843+
$imagick = new Imagick( $temp_file );
844+
$actual_color_type = $imagick->getImageProperty( 'png:IHDR.color-type-orig' );
845+
846+
unlink( $temp_file );
847+
848+
$this->assertSame( (string) $expected_color_type, $actual_color_type, "The PNG original color type should be preserved after resize for {$file_path}." );
849+
}
850+
851+
/**
852+
* Data provider for test_png_color_type_is_preserved_after_resize.
853+
*
854+
* @return array[]
855+
*/
856+
public static function data_png_color_type_after_resize() {
857+
return array(
858+
'vivid-green-bird_color_type_6' => array(
859+
DIR_TESTDATA . '/images/png-tests/vivid-green-bird.png',
860+
6, // RGBA.
861+
),
862+
'grayscale-test-image_color_type_4' => array(
863+
DIR_TESTDATA . '/images/png-tests/grayscale-test-image.png',
864+
4, // Grayscale with Alpha.
865+
),
866+
'rabbit-time-paletted-or8_color_type_3' => array(
867+
DIR_TESTDATA . '/images/png-tests/rabbit-time-paletted-or8.png',
868+
3, // Paletted.
869+
),
870+
'test8_color_type_3' => array(
871+
DIR_TESTDATA . '/images/png-tests/test8.png',
872+
3, // Paletted.
873+
),
874+
);
875+
}
819876
}

0 commit comments

Comments
 (0)