Make WordPress Core

Changeset 59588


Ignore:
Timestamp:
01/07/2025 09:04:35 PM (5 weeks ago)
Author:
adamsilverstein
Message:

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.

Location:
trunk
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/wp-includes/class-wp-image-editor-imagick.php

    r59473 r59588  
    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 ) {
  • trunk/tests/phpunit/tests/image/editorImagick.php

    r56559 r59588  
    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}
Note: See TracChangeset for help on using the changeset viewer.