Media: improve Imagick handling of indexed PNG images with transparency.
Fix an issue where certain transparent PNG images experienced noticeable quality degradation when resized by Imagick. Follow up to [60246]. Props elvismdev, SirLouen, siliconforks, nosilver4u, iamshashank. Fixes #63448. Built from https://develop.svn.wordpress.org/trunk@60667 git-svn-id: http://core.svn.wordpress.org/trunk@60003 1a063a9b-81f0-0310-95a4-ce76da25c4cd
This commit is contained in:
@@ -444,6 +444,53 @@ class WP_Image_Editor_Imagick extends WP_Image_Editor {
|
||||
}
|
||||
|
||||
try {
|
||||
/*
|
||||
* We need to perform some special handling for certain types of images:
|
||||
* 1. For PNG images, we need to specify compression settings and remove unneeded chunks.
|
||||
* 2. For indexed PNG images, the number of colors must not exceed 256.
|
||||
* 3. For indexed PNG images with an alpha channel, the tRNS chunk must be preserved.
|
||||
* 4. For indexed PNG images with true alpha transparency (an alpha channel > 1 bit),
|
||||
* we need to avoid saving the image using ImageMagick's 'png8' format,
|
||||
* because that supports only binary (1 bit) transparency.
|
||||
*
|
||||
* For #4 we want to check whether the image has a 1-bit alpha channel before resizing,
|
||||
* because resizing may cause the number of alpha values to multiply due to antialiasing.
|
||||
* (We're assuming that, if the original image had only a 1-bit alpha channel,
|
||||
* then a 1-bit alpha channel should be good enough for the resized images too.)
|
||||
* So we're going to perform all the necessary checks before resizing the image
|
||||
* and store the results in variables for later use.
|
||||
*/
|
||||
$is_png = false;
|
||||
$is_indexed_png = false;
|
||||
$is_indexed_png_with_alpha_channel = false;
|
||||
$is_indexed_png_with_true_alpha_transparency = false;
|
||||
|
||||
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' )
|
||||
&& 1 < $this->image->getImageChannelDepth( Imagick::CHANNEL_ALPHA )
|
||||
) {
|
||||
$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),
|
||||
@@ -480,30 +527,42 @@ class WP_Image_Editor_Imagick extends WP_Image_Editor {
|
||||
$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' );
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
*
|
||||
* @global string $wp_version
|
||||
*/
|
||||
$wp_version = '6.9-alpha-60666';
|
||||
$wp_version = '6.9-alpha-60667';
|
||||
|
||||
/**
|
||||
* Holds the WordPress DB revision, increments when changes are made to the WordPress DB schema.
|
||||
|
||||
Reference in New Issue
Block a user