Skip to content
Closed
Show file tree
Hide file tree
Changes from 4 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
67 changes: 56 additions & 11 deletions src/wp-includes/class-wp-image-editor-imagick.php
Original file line number Diff line number Diff line change
Expand Up @@ -444,6 +444,39 @@ protected function thumbnail_image( $dst_w, $dst_h, $filter_name = 'FILTER_TRIAN
}

try {
$is_png = false;
$is_indexed_png = false;
$is_indexed_png_with_alpha_channel = false;
$is_indexed_png_with_true_alpha_transparency = false;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we add a comment here indicating the meaning of true alpha channel (vs alpha channel)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've added a comment which explains the purpose of all these variables (and mentions what "true alpha" means here). Does that make things clearer?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, thanks. I will update the language slightly.


if ( 'image/png' === $this->mime_type ) {
$is_png = true;

if (
is_callable( array( $this->image, 'getImageProperty' ) )
&& '3' === $this->image->getImageProperty( 'png:IHDR.color-type-orig' )
) {
$is_indexed_png = true;

if (
is_callable( array( $this->image, 'getImageAlphaChannel' ) )
&& $this->image->getImageAlphaChannel()
) {
$is_indexed_png_with_alpha_channel = true;

if (
is_callable( array( $this->image, 'getImageChannelDepth' ) )
&& defined( 'Imagick::CHANNEL_ALPHA' )
) {
$alpha_channel_depth = $this->image->getImageChannelDepth( Imagick::CHANNEL_ALPHA );
if ( $alpha_channel_depth > 1 ) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this conditional can be included in the if block above

$is_indexed_png_with_true_alpha_transparency = true;
}
}
}
}
}

/*
* To be more efficient, resample large images to 5x the destination size before resizing
* whenever the output size is less that 1/3 of the original image size (1/3^2 ~= .111),
Expand Down Expand Up @@ -480,30 +513,42 @@ protected function thumbnail_image( $dst_w, $dst_h, $filter_name = 'FILTER_TRIAN
$this->image->setOption( 'jpeg:fancy-upsampling', 'off' );
}

if ( 'image/png' === $this->mime_type ) {
if ( $is_png ) {
$this->image->setOption( 'png:compression-filter', '5' );
$this->image->setOption( 'png:compression-level', '9' );
$this->image->setOption( 'png:compression-strategy', '1' );

// 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' )
) {
if ( $is_indexed_png ) {

// Check for an alpha channel.
if (
is_callable( array( $this->image, 'getImageAlphaChannel' ) )
&& $this->image->getImageAlphaChannel()
) {
if ( $is_indexed_png_with_alpha_channel ) {
$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' );

$this->image->quantizeImage( 256, $this->image->getColorspace(), 0, false, false );

/*
* If the colorspace is 'gray', use the png8 format to ensure it stays indexed.
* ImageMagick tends to save grayscale images as grayscale PNGs rather than indexed PNGs,
* even though grayscale PNGs usually have considerably larger file sizes.
* But we can force ImageMagick to save the image as an indexed PNG instead,
* by telling it to use png8 format.
*
* Note that we need to first call quantizeImage() before checking getImageColorspace(),
* because only after calling quantizeImage() will the colorspace be COLORSPACE_GRAY for grayscale images
* (and we have not found any other way to identify grayscale images).
*
* We need to avoid forcing indexed format for images with true alpha transparency,
* because ImageMagick does not support saving an image with true alpha transparency as an indexed PNG.
*/
if ( Imagick::COLORSPACE_GRAY === $this->image->getImageColorspace() && ! $is_indexed_png_with_true_alpha_transparency ) {
// 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.
Binary file added tests/phpunit/data/images/png-tests/oval-or8.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
38 changes: 38 additions & 0 deletions tests/phpunit/tests/image/editorImagick.php
Original file line number Diff line number Diff line change
Expand Up @@ -873,4 +873,42 @@ public static function data_png_color_type_after_resize() {
),
);
}

/**
* Tests that alpha transparency is preserved after resizing.
*
* @ticket 63448
* @dataProvider data_alpha_transparency_is_preserved_after_resize
*
* @param string $file_path Path to the image file.
*/
public function test_alpha_transparency_is_preserved_after_resize( $file_path ) {

$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 );
$alpha_channel_depth = $imagick->getImageChannelDepth( Imagick::CHANNEL_ALPHA );

unlink( $temp_file );

$this->assertGreaterThan( 1, $alpha_channel_depth, "Alpha transparency should be preserved after resize for {$file_path}." );
}

public static function data_alpha_transparency_is_preserved_after_resize() {
return array(
'oval-or8' => array(
DIR_TESTDATA . '/images/png-tests/oval-or8.png',
),
'oval-or8-grayscale-indexed' => array(
DIR_TESTDATA . '/images/png-tests/oval-or8-grayscale-indexed.png',
),
);
}
}
Loading