diff --git a/src/wp-includes/class-wp-image-editor-imagick.php b/src/wp-includes/class-wp-image-editor-imagick.php index 9abb631847544..edce8d2b7dcd9 100644 --- a/src/wp-includes/class-wp-image-editor-imagick.php +++ b/src/wp-includes/class-wp-image-editor-imagick.php @@ -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' ); + } + } + } } /* diff --git a/tests/phpunit/data/images/png-tests/Palette_icon-or8.png b/tests/phpunit/data/images/png-tests/Palette_icon-or8.png new file mode 100644 index 0000000000000..ead89766a3246 Binary files /dev/null and b/tests/phpunit/data/images/png-tests/Palette_icon-or8.png differ diff --git a/tests/phpunit/data/images/png-tests/cloudflare-status.png b/tests/phpunit/data/images/png-tests/cloudflare-status.png new file mode 100644 index 0000000000000..8517ff0b59fb9 Binary files /dev/null and b/tests/phpunit/data/images/png-tests/cloudflare-status.png differ diff --git a/tests/phpunit/data/images/png-tests/deskcat8.png b/tests/phpunit/data/images/png-tests/deskcat8.png new file mode 100644 index 0000000000000..89e62cd9e2b2c Binary files /dev/null and b/tests/phpunit/data/images/png-tests/deskcat8.png differ diff --git a/tests/phpunit/data/images/png-tests/rabbit-time-paletted-or8.png b/tests/phpunit/data/images/png-tests/rabbit-time-paletted-or8.png new file mode 100644 index 0000000000000..cff28ed3b2b3a Binary files /dev/null and b/tests/phpunit/data/images/png-tests/rabbit-time-paletted-or8.png differ diff --git a/tests/phpunit/data/images/png-tests/test8.png b/tests/phpunit/data/images/png-tests/test8.png new file mode 100644 index 0000000000000..49a513647147f Binary files /dev/null and b/tests/phpunit/data/images/png-tests/test8.png differ diff --git a/tests/phpunit/tests/image/editorImagick.php b/tests/phpunit/tests/image/editorImagick.php index cd424a8856cc5..eb71d9ff4715b 100644 --- a/tests/phpunit/tests/image/editorImagick.php +++ b/tests/phpunit/tests/image/editorImagick.php @@ -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', + ), + ); + } }