--- a/wp/wp-includes/class-wp-image-editor-imagick.php Fri Sep 05 18:40:08 2025 +0200
+++ b/wp/wp-includes/class-wp-image-editor-imagick.php Fri Sep 05 18:52:52 2025 +0200
@@ -190,12 +190,14 @@
* Sets Image Compression quality on a 1-100% scale.
*
* @since 3.5.0
+ * @since 6.8.0 The `$dims` parameter was added.
*
- * @param int $quality Compression Quality. Range: [1,100]
+ * @param int $quality Compression Quality. Range: [1,100]
+ * @param array $dims Optional. Image dimensions array with 'width' and 'height' keys.
* @return true|WP_Error True if set successfully; WP_Error on failure.
*/
- public function set_quality( $quality = null ) {
- $quality_result = parent::set_quality( $quality );
+ public function set_quality( $quality = null, $dims = array() ) {
+ $quality_result = parent::set_quality( $quality, $dims );
if ( is_wp_error( $quality_result ) ) {
return $quality_result;
} else {
@@ -206,6 +208,7 @@
switch ( $this->mime_type ) {
case 'image/jpeg':
$this->image->setImageCompressionQuality( $quality );
+ $this->image->setCompressionQuality( $quality );
$this->image->setImageCompression( imagick::COMPRESSION_JPEG );
break;
case 'image/webp':
@@ -214,14 +217,23 @@
if ( 'lossless' === $webp_info['type'] ) {
// Use WebP lossless settings.
$this->image->setImageCompressionQuality( 100 );
+ $this->image->setCompressionQuality( 100 );
$this->image->setOption( 'webp:lossless', 'true' );
+ parent::set_quality( 100 );
} else {
$this->image->setImageCompressionQuality( $quality );
+ $this->image->setCompressionQuality( $quality );
}
break;
case 'image/avif':
+ // Set the AVIF encoder to work faster, with minimal impact on image size.
+ $this->image->setOption( 'heic:speed', 7 );
+ $this->image->setImageCompressionQuality( $quality );
+ $this->image->setCompressionQuality( $quality );
+ break;
default:
$this->image->setImageCompressionQuality( $quality );
+ $this->image->setCompressionQuality( $quality );
}
} catch ( Exception $e ) {
return new WP_Error( 'image_quality_error', $e->getMessage() );
@@ -258,10 +270,10 @@
}
/*
- * If we still don't have the image size, fall back to `wp_getimagesize`. This ensures AVIF images
+ * If we still don't have the image size, fall back to `wp_getimagesize`. This ensures AVIF and HEIC images
* are properly sized without affecting previous `getImageGeometry` behavior.
*/
- if ( ( ! $width || ! $height ) && 'image/avif' === $this->mime_type ) {
+ if ( ( ! $width || ! $height ) && ( 'image/avif' === $this->mime_type || wp_is_heic_image_mime_type( $this->mime_type ) ) ) {
$size = wp_getimagesize( $this->file );
$width = $size[0];
$height = $size[1];
@@ -336,7 +348,7 @@
* If true, image will be cropped to the specified dimensions using center positions.
* If an array, the image will be cropped using the array to specify the crop location:
*
- * @type string $0 The x crop position. Accepts 'left' 'center', or 'right'.
+ * @type string $0 The x crop position. Accepts 'left', 'center', or 'right'.
* @type string $1 The y crop position. Accepts 'top', 'center', or 'bottom'.
* }
* @return true|WP_Error
@@ -357,6 +369,14 @@
return $this->crop( $src_x, $src_y, $src_w, $src_h, $dst_w, $dst_h );
}
+ $this->set_quality(
+ null,
+ array(
+ 'width' => $dst_w,
+ 'height' => $dst_h,
+ )
+ );
+
// Execute the resize.
$thumb_result = $this->thumbnail_image( $dst_w, $dst_h );
if ( is_wp_error( $thumb_result ) ) {
@@ -464,7 +484,38 @@
$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' );
+ }
+ }
+ }
}
/*
@@ -483,11 +534,23 @@
}
}
- // Limit the bit depth of resized images to 8 bits per channel.
+ // Limit the bit depth of resized images.
if ( is_callable( array( $this->image, 'getImageDepth' ) ) && is_callable( array( $this->image, 'setImageDepth' ) ) ) {
- if ( 8 < $this->image->getImageDepth() ) {
- $this->image->setImageDepth( 8 );
- }
+ /**
+ * Filters the maximum bit depth of resized images.
+ *
+ * This filter only applies when resizing using the Imagick editor since GD
+ * does not support getting or setting bit depth.
+ *
+ * Use this to adjust the maximum bit depth of resized images.
+ *
+ * @since 6.8.0
+ *
+ * @param int $max_depth The maximum bit depth. Default is the input depth.
+ * @param int $image_depth The bit depth of the original image.
+ */
+ $max_depth = apply_filters( 'image_max_bit_depth', $this->image->getImageDepth(), $this->image->getImageDepth() );
+ $this->image->setImageDepth( $max_depth );
}
} catch ( Exception $e ) {
return new WP_Error( 'image_resize_error', $e->getMessage() );