Skip to content

Limit Quantization to Indexed images #8813

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 6 commits into
base: trunk
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 21 additions & 30 deletions src/wp-includes/class-wp-image-editor-imagick.php
Original file line number Diff line number Diff line change
Expand Up @@ -484,37 +484,28 @@ 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' );
// 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' );
}

// Indexed PNG files get some additional handling.
// See #63448 for details.
if (
is_callable( array( $this->image, 'getImageProperty' ) )
&& '3' === $this->image->getImageProperty( 'png:IHDR.color-type-orig' )
) {

// 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' );
}
// Set the image format to Indexed PNG.
$this->image->setOption( 'png:format', 'png8' );

} else {
$this->image->setOption( 'png:exclude-chunk', 'all' );
}
}

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.
62 changes: 59 additions & 3 deletions tests/phpunit/tests/image/editorImagick.php
Original file line number Diff line number Diff line change
Expand Up @@ -770,6 +770,8 @@ public function __return_eight() {
* @ticket 36477
*
* @dataProvider data_resizes_are_small_for_16bit_images
*
* @param string $file Path to the image file.
*/
public function test_resizes_are_small_for_16bit_images( $file ) {

Expand All @@ -783,17 +785,17 @@ public function test_resizes_are_small_for_16bit_images( $file ) {

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

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

$new_filesize = filesize( $temp_file );

unlink( $temp_file );
wp_delete_file( $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
* Data provider for test_resizes_are_small_for_16bit_images.
*
* @return array[]
*/
Expand All @@ -816,4 +818,58 @@ public static function data_resizes_are_small_for_16bit_images() {
),
);
}

/**
* Tests that the 'png:IHDR.color-type-orig' property is preserved after resizing.
*
* @ticket 63448
* @dataProvider data_png_color_type_after_resize
*
* @param string $file_path Path to the image file.
* @param int $expected_color_type The expected original color type.
*/
public function test_png_color_type_is_preserved_after_resize( $file_path, $expected_color_type ) {

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

$imagick_image_editor = new WP_Image_Editor_Imagick( $file_path );
$imagick_image_editor->load();

$size = $imagick_image_editor->get_size();
$imagick_image_editor->resize( $size['width'] * 0.5, $size['height'] * 0.5 );
$imagick_image_editor->save( $temp_file );

$imagick = new Imagick( $temp_file );
$actual_color_type = $imagick->getImageProperty( 'png:IHDR.color-type-orig' );

wp_delete_file( $temp_file );

$this->assertSame( (string) $expected_color_type, $actual_color_type, "The PNG original color type should be preserved after resize for {$file_path}." );
}

/**
* Data provider for test_png_color_type_is_preserved_after_resize.
*
* @return array[]
*/
public static function data_png_color_type_after_resize() {
return array(
'vivid-green-bird_color_type_6' => array(
DIR_TESTDATA . '/images/png-tests/vivid-green-bird.png',
6, // RGBA.
),
'grayscale-test-image_color_type_4' => array(
DIR_TESTDATA . '/images/png-tests/grayscale-test-image.png',
4, // Grayscale with Alpha.
),
'rabbit-time-paletted-or8_color_type_3' => array(
DIR_TESTDATA . '/images/png-tests/rabbit-time-paletted-or8.png',
3, // Paletted.
),
'test8_color_type_3' => array(
DIR_TESTDATA . '/images/png-tests/test8.png',
3, // Paletted.
),
);
}
}
Loading