Skip to content

Commit 2bab211

Browse files
Media: enable high bit depth resized image output with Imagick.
Fix an issue where uploaded HDR images were resized and output as SDR and thus significantly degraded from the original. When using Imagick, output images will now match the bit depth of the uploaded image. Add a new filter ‘image_max_bit_depth’ which developers can use to control the maximum bit depth for resized images. Props adamsilverstein, kirasong, gregbenz, apermo. Fixes #62285. git-svn-id: https://develop.svn.wordpress.org/trunk@59588 602fd350-edb4-49c9-b593-d223f7449a82
1 parent 4cd96f7 commit 2bab211

File tree

2 files changed

+83
-4
lines changed

2 files changed

+83
-4
lines changed

src/wp-includes/class-wp-image-editor-imagick.php

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -503,11 +503,23 @@ protected function thumbnail_image( $dst_w, $dst_h, $filter_name = 'FILTER_TRIAN
503503
}
504504
}
505505

506-
// Limit the bit depth of resized images to 8 bits per channel.
506+
// Limit the bit depth of resized images.
507507
if ( is_callable( array( $this->image, 'getImageDepth' ) ) && is_callable( array( $this->image, 'setImageDepth' ) ) ) {
508-
if ( 8 < $this->image->getImageDepth() ) {
509-
$this->image->setImageDepth( 8 );
510-
}
508+
/**
509+
* Filters the maximum bit depth of resized images.
510+
*
511+
* This filter only applies when resizing using the Imagick editor since GD
512+
* does not support getting or setting bit depth.
513+
*
514+
* Use this to adjust the maximum bit depth of resized images.
515+
*
516+
* @since 6.8.0
517+
*
518+
* @param int $max_depth The maximum bit depth. Default is the input depth.
519+
* @param int $image_depth The bit depth of the original image.
520+
*/
521+
$max_depth = apply_filters( 'image_max_bit_depth', $this->image->getImageDepth(), $this->image->getImageDepth() );
522+
$this->image->setImageDepth( $max_depth );
511523
}
512524
} catch ( Exception $e ) {
513525
return new WP_Error( 'image_resize_error', $e->getMessage() );

tests/phpunit/tests/image/editorImagick.php

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -691,4 +691,71 @@ static function ( $value ) {
691691
$imagick->destroy();
692692
$this->assertSame( $expected, $output, 'The image color of the generated thumb does not match expected opaque background.' ); // Allow for floating point equivalence.
693693
}
694+
695+
/**
696+
* Test filter `image_max_bit_depth` correctly sets the maximum bit depth of resized images.
697+
*
698+
* @ticket 62285
699+
*/
700+
public function test_image_max_bit_depth() {
701+
$file = DIR_TESTDATA . '/images/colors_hdr_p3.avif';
702+
$imagick_image_editor = new WP_Image_Editor_Imagick( $file );
703+
704+
// Skip if AVIF not supported.
705+
if ( ! $imagick_image_editor->supports_mime_type( 'image/avif' ) ) {
706+
$this->markTestSkipped( 'The image editor does not support the AVIF mime type.' );
707+
}
708+
709+
// Skip if depth methods not available.
710+
if ( ! method_exists( 'Imagick', 'getImageDepth' ) || ! method_exists( 'Imagick', 'setImageDepth' ) ) {
711+
$this->markTestSkipped( 'The image editor does not support get or setImageDepth.' );
712+
}
713+
714+
// Verify source image has 10-bit depth.
715+
$imagick = new Imagick( $file );
716+
$this->assertSame( 10, $imagick->getImageDepth() );
717+
718+
// Test ability to save 10-bit image.
719+
$imagick->setImageDepth( 10 );
720+
$test_file = tempnam( get_temp_dir(), '' ) . 'test10.avif';
721+
$imagick->writeImage( $test_file );
722+
$im = new Imagick( $test_file );
723+
724+
if ( $im->getImageDepth() !== 10 ) {
725+
$this->markTestSkipped( 'Imagick is unable to save a 10 bit image.' );
726+
}
727+
$im->destroy();
728+
unlink( $test_file );
729+
730+
// Test default behavior preserves 10-bit depth.
731+
$imagick_image_editor->load();
732+
$imagick_image_editor->resize( 100, 50 );
733+
$test_file = tempnam( get_temp_dir(), '' ) . 'test1.avif';
734+
$imagick_image_editor->save( $test_file );
735+
$im = new Imagick( $test_file );
736+
$this->assertSame( 10, $im->getImageDepth() );
737+
unlink( $test_file );
738+
$im->destroy();
739+
740+
// Test filter can set 8-bit depth
741+
add_filter( 'image_max_bit_depth', array( $this, '__return_eight' ) );
742+
$imagick_image_editor = new WP_Image_Editor_Imagick( $file );
743+
$imagick_image_editor->load();
744+
$imagick_image_editor->resize( 100, 50 );
745+
$test_file = tempnam( get_temp_dir(), '' ) . 'test2.avif';
746+
$imagick_image_editor->save( $test_file );
747+
$im = new Imagick( $test_file );
748+
$this->assertSame( 8, $im->getImageDepth() );
749+
unlink( $test_file );
750+
$im->destroy();
751+
}
752+
753+
/**
754+
* Helper function to return 8 for the `image_max_bit_depth` filter.
755+
*
756+
* @return int
757+
*/
758+
public function __return_eight() {
759+
return 8;
760+
}
694761
}

0 commit comments

Comments
 (0)