Changeset 65367
- Timestamp:
- 11/10/2026 01:33:13 AM (22 hours ago)
- Location:
- trunk
- Files:
-
- 4 edited
-
src/wp-admin/includes/image-edit.php (modified) (2 diffs)
-
src/wp-includes/class-wp-image-editor-gd.php (modified) (2 diffs)
-
tests/phpunit/tests/image/editorGd.php (modified) (1 diff)
-
tests/phpunit/tests/image/functions.php (modified) (1 diff)
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/wp-admin/includes/image-edit.php
r63002 r65367 566 566 * 567 567 * @since 2.9.0 568 * @since 7.2.0 Switched to using imageflip() instead of imagecopyresampled() with negative dimensions. 568 569 * @deprecated 3.5.0 Use WP_Image_Editor::flip() 569 570 * @see WP_Image_Editor::flip() … … 578 579 _deprecated_function( __FUNCTION__, '3.5.0', 'WP_Image_Editor::flip()' ); 579 580 580 $w = imagesx( $img ); 581 $h = imagesy( $img ); 582 $dst = wp_imagecreatetruecolor( $w, $h ); 583 584 if ( is_gd_image( $dst ) ) { 585 $sx = $vert ? ( $w - 1 ) : 0; 586 $sy = $horz ? ( $h - 1 ) : 0; 587 $sw = $vert ? -$w : $w; 588 $sh = $horz ? -$h : $h; 589 590 if ( imagecopyresampled( $dst, $img, 0, 0, $sx, $sy, $w, $h, $sw, $sh ) ) { 591 if ( PHP_VERSION_ID < 80000 ) { // imagedestroy() has no effect as of PHP 8.0. 592 imagedestroy( $img ); 593 } 594 595 $img = $dst; 596 } 597 } 581 if ( ! is_gd_image( $img ) ) { 582 return $img; 583 } 584 585 // Keep returning a truecolor image, like the previous implementation did. imagewebp() rejects palette images. 586 if ( ! imageistruecolor( $img ) && ! imagepalettetotruecolor( $img ) ) { 587 return $img; 588 } 589 590 // Match the alpha settings of the previous implementation's copy, including when no axis is flipped. 591 imagealphablending( $img, false ); 592 imagesavealpha( $img, true ); 593 594 /* 595 * imageflip() changes the image in place, so the image keeps its current resolution and interlace setting. 596 * _rotate_image_resource() and _crop_image_resource() copy into a new image, which resets them to GD's defaults: 597 * 96 DPI and no interlacing. Reset them here too, so a flip gives the same result as the other edits. 598 * 599 * @link https://core.trac.wordpress.org/ticket/66113 600 */ 601 imageresolution( $img, 96, 96 ); 602 imageinterlace( $img, 0 ); 603 604 if ( ! $horz && ! $vert ) { 605 return $img; 606 } 607 608 if ( $horz && $vert ) { 609 $mode = IMG_FLIP_BOTH; 610 } elseif ( $horz ) { 611 $mode = IMG_FLIP_VERTICAL; 612 } else { 613 $mode = IMG_FLIP_HORIZONTAL; 614 } 615 616 imageflip( $img, $mode ); 598 617 599 618 return $img; -
trunk/src/wp-includes/class-wp-image-editor-gd.php
r63501 r65367 433 433 * 434 434 * @since 3.5.0 435 * @since 7.2.0 Switched to using imageflip() instead of imagecopyresampled() with negative dimensions. 435 436 * 436 437 * @param bool $horz Flip along Horizontal Axis. … … 439 440 */ 440 441 public function flip( $horz, $vert ) { 441 $w = $this->size['width']; 442 $h = $this->size['height']; 443 $dst = wp_imagecreatetruecolor( $w, $h ); 444 445 if ( is_gd_image( $dst ) ) { 446 $sx = $vert ? ( $w - 1 ) : 0; 447 $sy = $horz ? ( $h - 1 ) : 0; 448 $sw = $vert ? -$w : $w; 449 $sh = $horz ? -$h : $h; 450 451 if ( imagecopyresampled( $dst, $this->image, 0, 0, $sx, $sy, $w, $h, $sw, $sh ) ) { 452 if ( PHP_VERSION_ID < 80000 ) { // imagedestroy() has no effect as of PHP 8.0. 453 imagedestroy( $this->image ); 454 } 455 456 $this->image = $dst; 457 458 return true; 459 } 442 if ( ! is_gd_image( $this->image ) ) { 443 return new WP_Error( 'image_flip_error', __( 'Image flip failed.' ), $this->file ); 444 } 445 446 /* 447 * imageflip() works in place and keeps palette images as palette images. 448 * The previous implementation always copied into a new truecolor image, 449 * and saving or streaming as WebP relies on that, since imagewebp() 450 * rejects palette images. Convert first to keep that behavior. 451 */ 452 if ( ! imageistruecolor( $this->image ) && ! imagepalettetotruecolor( $this->image ) ) { 453 return new WP_Error( 'image_flip_error', __( 'Image flip failed.' ), $this->file ); 454 } 455 456 /* 457 * imageflip() also keeps the current alpha settings, which rotate() leaves 458 * with blending enabled. Reset them to what the previous implementation 459 * always produced, including when no axis is flipped. 460 */ 461 imagealphablending( $this->image, false ); 462 imagesavealpha( $this->image, true ); 463 464 /* 465 * imageflip() changes the image in place, so the image keeps its current resolution and interlace setting. 466 * rotate(), crop() and resize() copy into a new image, which resets them to GD's defaults: 96 DPI and no 467 * interlacing. Reset them here too, so a flip gives the same result as the other edits. 468 * 469 * @link https://core.trac.wordpress.org/ticket/66113 470 */ 471 imageresolution( $this->image, 96, 96 ); 472 imageinterlace( $this->image, 0 ); 473 474 if ( ! $horz && ! $vert ) { 475 return true; 476 } 477 478 if ( $horz && $vert ) { 479 $mode = IMG_FLIP_BOTH; 480 } elseif ( $horz ) { 481 $mode = IMG_FLIP_VERTICAL; 482 } else { 483 $mode = IMG_FLIP_HORIZONTAL; 484 } 485 486 if ( imageflip( $this->image, $mode ) ) { 487 return true; 460 488 } 461 489 -
trunk/tests/phpunit/tests/image/editorGd.php
r65353 r65367 579 579 580 580 /** 581 * Tests that flip() moves every pixel to its mirrored position. 582 * 583 * A small non-square image with a distinct color per pixel is used so that 584 * mixed up axes, off-by-one shifts and duplicated edges are all detected. 585 * 586 * @ticket 66113 587 * 588 * @dataProvider data_flip_axes 589 * 590 * @covers WP_Image_Editor_GD::flip 591 * 592 * @param bool $horz Whether to flip along the horizontal axis. 593 * @param bool $vert Whether to flip along the vertical axis. 594 */ 595 public function test_flip_moves_every_pixel( $horz, $vert ) { 596 $w = 3; 597 $h = 2; 598 $color = static function ( $x, $y ) { 599 return ( ( $x * 40 + 10 ) << 16 ) | ( ( $y * 90 + 20 ) << 8 ) | 0x77; 600 }; 601 602 $image = imagecreatetruecolor( $w, $h ); 603 for ( $y = 0; $y < $h; $y++ ) { 604 for ( $x = 0; $x < $w; $x++ ) { 605 imagesetpixel( $image, $x, $y, $color( $x, $y ) ); 606 } 607 } 608 609 $gd_image_editor = new WP_Image_Editor_GD( DIR_TESTDATA . '/images/gradient-square.jpg' ); 610 $gd_image_editor->load(); 611 612 $this->set_property_value( $gd_image_editor, 'image', $image ); 613 614 // Keep the stored size in sync with the replaced image. 615 $this->set_property_value( 616 $gd_image_editor, 617 'size', 618 array( 619 'width' => $w, 620 'height' => $h, 621 ) 622 ); 623 624 $this->assertTrue( $gd_image_editor->flip( $horz, $vert ) ); 625 626 $flipped = $this->get_property_value( $gd_image_editor ); 627 628 $this->assertSame( $w, imagesx( $flipped ), 'The width should not change.' ); 629 $this->assertSame( $h, imagesy( $flipped ), 'The height should not change.' ); 630 631 for ( $y = 0; $y < $h; $y++ ) { 632 for ( $x = 0; $x < $w; $x++ ) { 633 $source_x = $vert ? $w - 1 - $x : $x; 634 $source_y = $horz ? $h - 1 - $y : $y; 635 636 $this->assertSame( 637 $color( $source_x, $source_y ), 638 imagecolorat( $flipped, $x, $y ), 639 "Pixel ($source_x,$source_y) did not move to ($x,$y)." 640 ); 641 } 642 } 643 } 644 645 /** 646 * Data provider with every combination of flip axes. 647 * 648 * @return array[] 649 */ 650 public static function data_flip_axes() { 651 return array( 652 'no flip' => array( false, false ), 653 'vertical flip' => array( true, false ), 654 'horizontal flip' => array( false, true ), 655 'both' => array( true, true ), 656 ); 657 } 658 659 /** 660 * Tests that flip() returns a WP_Error when no valid GD image is loaded. 661 * 662 * @ticket 66113 663 * 664 * @dataProvider data_flip_axes 665 * 666 * @covers WP_Image_Editor_GD::flip 667 * 668 * @param bool $horz Whether to flip along the horizontal axis. 669 * @param bool $vert Whether to flip along the vertical axis. 670 */ 671 public function test_flip_returns_error_for_invalid_image( $horz, $vert ) { 672 $gd_image_editor = new WP_Image_Editor_GD( DIR_TESTDATA . '/images/gradient-square.jpg' ); 673 $gd_image_editor->load(); 674 675 $this->set_property_value( $gd_image_editor, 'image', false ); 676 677 $result = $gd_image_editor->flip( $horz, $vert ); 678 679 $this->assertWPError( $result, 'flip() should return a WP_Error when no valid image is loaded.' ); 680 $this->assertSame( 'image_flip_error', $result->get_error_code(), 'The error code should be image_flip_error.' ); 681 } 682 683 /** 684 * Tests that maybe_exif_rotate() flips images whose EXIF orientation requires a flip. 685 * 686 * This is the most common way to reach flip(): uploading a mirrored or 687 * upside-down JPEG. The image is only flipped, so its size does not change 688 * and every pixel must end up at its mirrored position. 689 * 690 * @ticket 66113 691 * 692 * @dataProvider data_maybe_exif_rotate_flips_image 693 * 694 * @covers WP_Image_Editor::maybe_exif_rotate 695 * @covers WP_Image_Editor_GD::flip 696 * 697 * @param int $orientation EXIF orientation value. 698 * @param bool $mirror_x Whether the image should be mirrored left to right. 699 * @param bool $mirror_y Whether the image should be mirrored top to bottom. 700 */ 701 public function test_maybe_exif_rotate_flips_image( $orientation, $mirror_x, $mirror_y ) { 702 $file = DIR_TESTDATA . '/images/test-image-upside-down.jpg'; 703 704 // The fixture has orientation 3. Override it to cover the other flip-only orientations. 705 $filter = static function () use ( $orientation ) { 706 return $orientation; 707 }; 708 add_filter( 'wp_image_maybe_exif_rotate', $filter ); 709 710 $gd_image_editor = new WP_Image_Editor_GD( $file ); 711 $gd_image_editor->load(); 712 713 $original = $this->get_property_value( $gd_image_editor ); 714 $w = imagesx( $original ); 715 $h = imagesy( $original ); 716 717 // Snapshot a grid of pixels, since flip() modifies the image in place. 718 $points = array(); 719 foreach ( array( 0, (int) ( $w / 3 ), (int) ( $w / 2 ), $w - 1 ) as $x ) { 720 foreach ( array( 0, (int) ( $h / 3 ), (int) ( $h / 2 ), $h - 1 ) as $y ) { 721 $points[] = array( $x, $y, imagecolorat( $original, $x, $y ) ); 722 } 723 } 724 725 $result = $gd_image_editor->maybe_exif_rotate(); 726 727 remove_filter( 'wp_image_maybe_exif_rotate', $filter ); 728 729 $this->assertTrue( $result, 'maybe_exif_rotate() should return true.' ); 730 $this->assertSame( 731 array( 732 'width' => $w, 733 'height' => $h, 734 ), 735 $gd_image_editor->get_size(), 736 'Flipping should not change the size.' 737 ); 738 739 $flipped = $this->get_property_value( $gd_image_editor ); 740 741 foreach ( $points as list( $x, $y, $color ) ) { 742 $target_x = $mirror_x ? $w - 1 - $x : $x; 743 $target_y = $mirror_y ? $h - 1 - $y : $y; 744 745 $this->assertSame( 746 $color, 747 imagecolorat( $flipped, $target_x, $target_y ), 748 "Pixel ($x,$y) did not move to ($target_x,$target_y)." 749 ); 750 } 751 } 752 753 /** 754 * Data provider for test_maybe_exif_rotate_flips_image(). 755 * 756 * @return array[] 757 */ 758 public static function data_maybe_exif_rotate_flips_image() { 759 return array( 760 'orientation 2: mirrored horizontally' => array( 2, true, false ), 761 'orientation 3: upside down' => array( 3, true, true ), 762 'orientation 4: mirrored vertically' => array( 4, false, true ), 763 ); 764 } 765 766 /** 767 * Tests that flip() converts palette images to truecolor, like the previous implementation. 768 * 769 * The previous implementation always copied into a new truecolor image. 770 * Code that saves or streams the flipped image as WebP relies on that, 771 * since imagewebp() rejects palette images. 772 * 773 * @ticket 66113 774 * 775 * @dataProvider data_flip_axes 776 * 777 * @covers WP_Image_Editor_GD::flip 778 * 779 * @param bool $horz Whether to flip along the horizontal axis. 780 * @param bool $vert Whether to flip along the vertical axis. 781 */ 782 public function test_flip_converts_palette_image_to_truecolor( $horz, $vert ) { 783 $gd_image_editor = new WP_Image_Editor_GD( DIR_TESTDATA . '/images/test-image.gif' ); 784 $gd_image_editor->load(); 785 786 $image = $this->get_property_value( $gd_image_editor ); 787 $w = imagesx( $image ); 788 $h = imagesy( $image ); 789 790 $this->assertFalse( imageistruecolor( $image ), 'The fixture should be a palette image.' ); 791 792 // Snapshot every pixel as a truecolor value, since flip() modifies the image in place. 793 $colors = array(); 794 for ( $y = 0; $y < $h; $y++ ) { 795 for ( $x = 0; $x < $w; $x++ ) { 796 $rgba = imagecolorsforindex( $image, imagecolorat( $image, $x, $y ) ); 797 798 $colors[ $y ][ $x ] = ( $rgba['alpha'] << 24 ) | ( $rgba['red'] << 16 ) | ( $rgba['green'] << 8 ) | $rgba['blue']; 799 } 800 } 801 802 $this->assertTrue( $gd_image_editor->flip( $horz, $vert ) ); 803 804 $flipped = $this->get_property_value( $gd_image_editor ); 805 806 $this->assertTrue( imageistruecolor( $flipped ), 'The flipped image should be a truecolor image.' ); 807 808 for ( $y = 0; $y < $h; $y++ ) { 809 for ( $x = 0; $x < $w; $x++ ) { 810 $source_x = $vert ? $w - 1 - $x : $x; 811 $source_y = $horz ? $h - 1 - $y : $y; 812 813 $this->assertSame( 814 $colors[ $source_y ][ $source_x ], 815 imagecolorat( $flipped, $x, $y ), 816 "Pixel ($source_x,$source_y) did not move to ($x,$y)." 817 ); 818 } 819 } 820 } 821 822 /** 823 * Tests that a flipped palette image can be saved as WebP. 824 * 825 * imagewebp() rejects palette images with a warning and writes an empty file, 826 * so this fails unless flip() returns a truecolor image. 827 * 828 * @ticket 66113 829 * 830 * @covers WP_Image_Editor_GD::flip 831 * @covers WP_Image_Editor_GD::save 832 */ 833 public function test_flipped_palette_image_can_be_saved_as_webp() { 834 if ( ! WP_Image_Editor_GD::supports_mime_type( 'image/webp' ) ) { 835 $this->markTestSkipped( 'This GD build does not support WebP.' ); 836 } 837 838 $gd_image_editor = new WP_Image_Editor_GD( DIR_TESTDATA . '/images/test-image.gif' ); 839 $gd_image_editor->load(); 840 841 $this->assertTrue( $gd_image_editor->flip( true, false ) ); 842 843 $save_to_file = get_temp_dir() . wp_unique_filename( get_temp_dir(), 'flipped-palette.webp' ); 844 $saved = $gd_image_editor->save( $save_to_file, 'image/webp' ); 845 846 $this->assertNotWPError( $saved, 'Saving the flipped image as WebP should not fail.' ); 847 848 $size = wp_getimagesize( $saved['path'] ); 849 $filesize = filesize( $saved['path'] ); 850 851 unlink( $saved['path'] ); 852 853 $this->assertGreaterThan( 0, $filesize, 'The saved WebP file should not be empty.' ); 854 $this->assertIsArray( $size, 'The saved WebP file should be a valid image.' ); 855 $this->assertSame( 'image/webp', $size['mime'], 'The saved file should be a WebP image.' ); 856 $this->assertSame( array( 50, 50 ), array( $size[0], $size[1] ), 'The saved image size should not change.' ); 857 } 858 859 /** 860 * Tests that flip() keeps the alpha channel of every pixel, in memory and after saving as PNG. 861 * 862 * Fully transparent pixels are left out on purpose: their hidden RGB value 863 * is not visible and is not guaranteed to survive saving. 864 * 865 * @ticket 66113 866 * 867 * @dataProvider data_flip_axes 868 * 869 * @covers WP_Image_Editor_GD::flip 870 * 871 * @param bool $horz Whether to flip along the horizontal axis. 872 * @param bool $vert Whether to flip along the vertical axis. 873 */ 874 public function test_flip_keeps_alpha_channel( $horz, $vert ) { 875 $w = 3; 876 $h = 2; 877 $color = static function ( $x, $y ) { 878 // Alpha values from 0 (opaque) to 125, all distinct. 879 $alpha = ( $y * 3 + $x ) * 25; 880 881 return ( $alpha << 24 ) | ( ( $x * 40 + 10 ) << 16 ) | ( ( $y * 90 + 20 ) << 8 ) | 0x77; 882 }; 883 884 $image = imagecreatetruecolor( $w, $h ); 885 imagealphablending( $image, false ); 886 imagesavealpha( $image, true ); 887 for ( $y = 0; $y < $h; $y++ ) { 888 for ( $x = 0; $x < $w; $x++ ) { 889 imagesetpixel( $image, $x, $y, $color( $x, $y ) ); 890 } 891 } 892 893 $source_file = get_temp_dir() . wp_unique_filename( get_temp_dir(), 'flip-alpha.png' ); 894 imagepng( $image, $source_file ); 895 896 $gd_image_editor = new WP_Image_Editor_GD( $source_file ); 897 $loaded = $gd_image_editor->load(); 898 899 unlink( $source_file ); 900 901 $this->assertTrue( $loaded, 'The test image should load.' ); 902 $this->assertTrue( $gd_image_editor->flip( $horz, $vert ) ); 903 904 $flipped = $this->get_property_value( $gd_image_editor ); 905 906 $save_to_file = get_temp_dir() . wp_unique_filename( get_temp_dir(), 'flipped-alpha.png' ); 907 $saved = $gd_image_editor->save( $save_to_file, 'image/png' ); 908 909 $this->assertNotWPError( $saved, 'Saving the flipped image as PNG should not fail.' ); 910 911 $reloaded = imagecreatefrompng( $saved['path'] ); 912 unlink( $saved['path'] ); 913 914 for ( $y = 0; $y < $h; $y++ ) { 915 for ( $x = 0; $x < $w; $x++ ) { 916 $source_x = $vert ? $w - 1 - $x : $x; 917 $source_y = $horz ? $h - 1 - $y : $y; 918 $expected = $color( $source_x, $source_y ); 919 920 $this->assertSame( 921 $expected, 922 imagecolorat( $flipped, $x, $y ), 923 "Pixel ($source_x,$source_y) did not move to ($x,$y) with its alpha in memory." 924 ); 925 $this->assertSame( 926 $expected, 927 imagecolorat( $reloaded, $x, $y ), 928 "Pixel ($source_x,$source_y) did not move to ($x,$y) with its alpha after saving." 929 ); 930 } 931 } 932 } 933 934 /** 935 * Tests that flipping along both axes gives the same image as rotating by 180 degrees. 936 * 937 * @ticket 66113 938 * 939 * @covers WP_Image_Editor_GD::flip 940 */ 941 public function test_flip_both_axes_matches_rotate_180() { 942 $file = DIR_TESTDATA . '/images/canola.jpg'; 943 944 $flipped_editor = new WP_Image_Editor_GD( $file ); 945 $flipped_editor->load(); 946 $this->assertTrue( $flipped_editor->flip( true, true ) ); 947 948 $rotated_editor = new WP_Image_Editor_GD( $file ); 949 $rotated_editor->load(); 950 $this->assertTrue( $rotated_editor->rotate( 180 ) ); 951 952 $this->assertSame( $rotated_editor->get_size(), $flipped_editor->get_size(), 'The sizes should match.' ); 953 954 $flipped = $this->get_property_value( $flipped_editor ); 955 $rotated = $this->get_property_value( $rotated_editor ); 956 957 $w = imagesx( $flipped ); 958 $h = imagesy( $flipped ); 959 960 // Count mismatches instead of asserting each pixel, since the image is large. 961 $mismatches = 0; 962 for ( $y = 0; $y < $h; $y++ ) { 963 for ( $x = 0; $x < $w; $x++ ) { 964 if ( imagecolorat( $flipped, $x, $y ) !== imagecolorat( $rotated, $x, $y ) ) { 965 ++$mismatches; 966 } 967 } 968 } 969 970 $this->assertSame( 0, $mismatches, 'Every pixel should match the image rotated by 180 degrees.' ); 971 } 972 973 /** 974 * Tests that flip() disables alpha blending, including after rotate() enabled it. 975 * 976 * Alpha blending only affects drawing onto the image, so it is detected by 977 * drawing a fully transparent pixel: with blending disabled, it replaces the 978 * existing pixel instead of being blended into it. 979 * 980 * @ticket 66113 981 * 982 * @dataProvider data_flip_axes 983 * 984 * @covers WP_Image_Editor_GD::flip 985 * 986 * @param bool $horz Whether to flip along the horizontal axis. 987 * @param bool $vert Whether to flip along the vertical axis. 988 */ 989 public function test_flip_disables_alpha_blending_after_rotate( $horz, $vert ) { 990 $gd_image_editor = new WP_Image_Editor_GD( DIR_TESTDATA . '/images/gradient-square.jpg' ); 991 $gd_image_editor->load(); 992 993 $this->assertTrue( $gd_image_editor->rotate( 90 ) ); 994 $this->assertTrue( $gd_image_editor->flip( $horz, $vert ) ); 995 996 $image = $this->get_property_value( $gd_image_editor ); 997 998 imagesetpixel( $image, 0, 0, imagecolorallocatealpha( $image, 0, 0, 0, 127 ) ); 999 1000 $this->assertSame( 127, ( imagecolorat( $image, 0, 0 ) >> 24 ) & 0x7F, 'Alpha blending should be disabled after flipping.' ); 1001 } 1002 1003 /** 1004 * Tests that flip() resets the resolution and interlace setting to GD's defaults. 1005 * 1006 * The previous implementation always copied into a new image, which starts 1007 * at 96 DPI with interlacing disabled, like rotate(), crop() and resize(). 1008 * 1009 * @ticket 66113 1010 * 1011 * @dataProvider data_flip_axes 1012 * 1013 * @covers WP_Image_Editor_GD::flip 1014 * 1015 * @param bool $horz Whether to flip along the horizontal axis. 1016 * @param bool $vert Whether to flip along the vertical axis. 1017 */ 1018 public function test_flip_resets_resolution_and_interlace( $horz, $vert ) { 1019 $gd_image_editor = new WP_Image_Editor_GD( DIR_TESTDATA . '/images/gradient-square.jpg' ); 1020 $gd_image_editor->load(); 1021 1022 $image = $this->get_property_value( $gd_image_editor ); 1023 1024 imageresolution( $image, 300, 150 ); 1025 imageinterlace( $image, true ); 1026 1027 $this->assertTrue( $gd_image_editor->flip( $horz, $vert ) ); 1028 1029 $flipped = $this->get_property_value( $gd_image_editor ); 1030 1031 $this->assertSame( array( 96, 96 ), imageresolution( $flipped ), 'The resolution should be reset to 96 DPI.' ); 1032 $this->assertFalse( (bool) imageinterlace( $flipped ), 'Interlacing should be disabled.' ); 1033 } 1034 1035 /** 581 1036 * Tests that an image created with WP_Image_Editor_GD preserves alpha with no resizing. 582 1037 * -
trunk/tests/phpunit/tests/image/functions.php
r65074 r65367 1204 1204 ); 1205 1205 } 1206 1207 /** 1208 * Tests that _flip_image_resource() mirrors every pixel for each flip mode. 1209 * 1210 * A small non-square image with a distinct color per pixel is used so that 1211 * mixed up axes, off-by-one shifts and duplicated edges are all detected. 1212 * 1213 * @ticket 66113 1214 * 1215 * @requires extension gd 1216 * 1217 * @expectedDeprecated _flip_image_resource 1218 * 1219 * @dataProvider data_flip_image_resource 1220 * 1221 * @covers ::_flip_image_resource 1222 * 1223 * @param bool $horz Whether to flip along the horizontal axis. 1224 * @param bool $vert Whether to flip along the vertical axis. 1225 */ 1226 public function test_flip_image_resource( $horz, $vert ) { 1227 require_once ABSPATH . 'wp-admin/includes/image-edit.php'; 1228 1229 $w = 3; 1230 $h = 2; 1231 $color = static function ( $x, $y ) { 1232 return ( ( $x * 40 + 10 ) << 16 ) | ( ( $y * 90 + 20 ) << 8 ) | 0x77; 1233 }; 1234 1235 $image = imagecreatetruecolor( $w, $h ); 1236 for ( $y = 0; $y < $h; $y++ ) { 1237 for ( $x = 0; $x < $w; $x++ ) { 1238 imagesetpixel( $image, $x, $y, $color( $x, $y ) ); 1239 } 1240 } 1241 1242 $flipped = _flip_image_resource( $image, $horz, $vert ); 1243 1244 $this->assertTrue( is_gd_image( $flipped ), 'A GD image should be returned.' ); 1245 $this->assertSame( $w, imagesx( $flipped ), 'The width should not change.' ); 1246 $this->assertSame( $h, imagesy( $flipped ), 'The height should not change.' ); 1247 1248 for ( $y = 0; $y < $h; $y++ ) { 1249 for ( $x = 0; $x < $w; $x++ ) { 1250 $source_x = $vert ? $w - 1 - $x : $x; 1251 $source_y = $horz ? $h - 1 - $y : $y; 1252 1253 $this->assertSame( 1254 $color( $source_x, $source_y ), 1255 imagecolorat( $flipped, $x, $y ), 1256 "Pixel ($source_x,$source_y) did not move to ($x,$y)." 1257 ); 1258 } 1259 } 1260 } 1261 1262 /** 1263 * Data provider for test_flip_image_resource(). 1264 * 1265 * @return array[] 1266 */ 1267 public static function data_flip_image_resource() { 1268 return array( 1269 'no flip' => array( false, false ), 1270 'vertical flip' => array( true, false ), 1271 'horizontal flip' => array( false, true ), 1272 'both' => array( true, true ), 1273 ); 1274 } 1275 1276 /** 1277 * Tests that _flip_image_resource() returns a truecolor image for a palette image. 1278 * 1279 * The previous implementation always copied into a new truecolor image. 1280 * A small palette image with a distinct color per pixel is used, so every 1281 * pixel can be checked at its mirrored position. 1282 * 1283 * @ticket 66113 1284 * 1285 * @requires extension gd 1286 * 1287 * @expectedDeprecated _flip_image_resource 1288 * 1289 * @dataProvider data_flip_image_resource 1290 * 1291 * @covers ::_flip_image_resource 1292 * 1293 * @param bool $horz Whether to flip along the horizontal axis. 1294 * @param bool $vert Whether to flip along the vertical axis. 1295 */ 1296 public function test_flip_image_resource_converts_palette_image_to_truecolor( $horz, $vert ) { 1297 require_once ABSPATH . 'wp-admin/includes/image-edit.php'; 1298 1299 $w = 3; 1300 $h = 2; 1301 $color = static function ( $x, $y ) { 1302 return ( ( $x * 40 + 10 ) << 16 ) | ( ( $y * 90 + 20 ) << 8 ) | 0x77; 1303 }; 1304 1305 $image = imagecreate( $w, $h ); 1306 for ( $y = 0; $y < $h; $y++ ) { 1307 for ( $x = 0; $x < $w; $x++ ) { 1308 $rgb = $color( $x, $y ); 1309 imagesetpixel( $image, $x, $y, imagecolorallocate( $image, ( $rgb >> 16 ) & 0xFF, ( $rgb >> 8 ) & 0xFF, $rgb & 0xFF ) ); 1310 } 1311 } 1312 1313 $this->assertFalse( imageistruecolor( $image ), 'The test image should be a palette image.' ); 1314 1315 $flipped = _flip_image_resource( $image, $horz, $vert ); 1316 1317 $this->assertTrue( imageistruecolor( $flipped ), 'The flipped image should be a truecolor image.' ); 1318 $this->assertSame( $w, imagesx( $flipped ), 'The width should not change.' ); 1319 $this->assertSame( $h, imagesy( $flipped ), 'The height should not change.' ); 1320 1321 for ( $y = 0; $y < $h; $y++ ) { 1322 for ( $x = 0; $x < $w; $x++ ) { 1323 $source_x = $vert ? $w - 1 - $x : $x; 1324 $source_y = $horz ? $h - 1 - $y : $y; 1325 1326 $this->assertSame( 1327 $color( $source_x, $source_y ), 1328 imagecolorat( $flipped, $x, $y ), 1329 "Pixel ($source_x,$source_y) did not move to ($x,$y)." 1330 ); 1331 } 1332 } 1333 } 1334 1335 /** 1336 * Tests that _flip_image_resource() returns an image that keeps its alpha channel when saved as PNG. 1337 * 1338 * The previous implementation always returned a new image with alpha saving 1339 * enabled, even if the input had it disabled and no axis was flipped. 1340 * 1341 * @ticket 66113 1342 * 1343 * @requires extension gd 1344 * 1345 * @expectedDeprecated _flip_image_resource 1346 * 1347 * @dataProvider data_flip_image_resource 1348 * 1349 * @covers ::_flip_image_resource 1350 * 1351 * @param bool $horz Whether to flip along the horizontal axis. 1352 * @param bool $vert Whether to flip along the vertical axis. 1353 */ 1354 public function test_flip_image_resource_enables_alpha_saving( $horz, $vert ) { 1355 require_once ABSPATH . 'wp-admin/includes/image-edit.php'; 1356 1357 // A single half-transparent pixel, so its position does not change when flipped. 1358 $image = imagecreatetruecolor( 1, 1 ); 1359 imagealphablending( $image, false ); 1360 imagesavealpha( $image, false ); 1361 imagesetpixel( $image, 0, 0, imagecolorallocatealpha( $image, 200, 100, 50, 64 ) ); 1362 1363 $flipped = _flip_image_resource( $image, $horz, $vert ); 1364 1365 $file = get_temp_dir() . wp_unique_filename( get_temp_dir(), 'flip-image-resource-alpha.png' ); 1366 imagepng( $flipped, $file ); 1367 $reloaded = imagecreatefrompng( $file ); 1368 unlink( $file ); 1369 1370 $this->assertSame( 64, ( imagecolorat( $reloaded, 0, 0 ) >> 24 ) & 0x7F, 'The alpha channel should be kept when saving as PNG.' ); 1371 } 1372 1373 /** 1374 * Tests that _flip_image_resource() returns an image with alpha blending disabled. 1375 * 1376 * The previous implementation always returned a new image with alpha blending 1377 * disabled, even if the input had it enabled and no axis was flipped. 1378 * 1379 * Alpha blending only affects drawing onto the image, so it is detected by 1380 * drawing a fully transparent pixel: with blending disabled, it replaces the 1381 * existing pixel instead of being blended into it. 1382 * 1383 * @ticket 66113 1384 * 1385 * @requires extension gd 1386 * 1387 * @expectedDeprecated _flip_image_resource 1388 * 1389 * @dataProvider data_flip_image_resource 1390 * 1391 * @covers ::_flip_image_resource 1392 * 1393 * @param bool $horz Whether to flip along the horizontal axis. 1394 * @param bool $vert Whether to flip along the vertical axis. 1395 */ 1396 public function test_flip_image_resource_disables_alpha_blending( $horz, $vert ) { 1397 require_once ABSPATH . 'wp-admin/includes/image-edit.php'; 1398 1399 // New truecolor images have alpha blending enabled. 1400 $image = imagecreatetruecolor( 3, 2 ); 1401 1402 $flipped = _flip_image_resource( $image, $horz, $vert ); 1403 1404 imagesetpixel( $flipped, 0, 0, imagecolorallocatealpha( $flipped, 0, 0, 0, 127 ) ); 1405 1406 $this->assertSame( 127, ( imagecolorat( $flipped, 0, 0 ) >> 24 ) & 0x7F, 'Alpha blending should be disabled after flipping.' ); 1407 } 1408 1409 /** 1410 * Tests that _flip_image_resource() resets the resolution and interlace setting to GD's defaults. 1411 * 1412 * The previous implementation always copied into a new image, which starts 1413 * at 96 DPI with interlacing disabled, like _rotate_image_resource() and 1414 * _crop_image_resource(). 1415 * 1416 * @ticket 66113 1417 * 1418 * @requires extension gd 1419 * 1420 * @expectedDeprecated _flip_image_resource 1421 * 1422 * @dataProvider data_flip_image_resource 1423 * 1424 * @covers ::_flip_image_resource 1425 * 1426 * @param bool $horz Whether to flip along the horizontal axis. 1427 * @param bool $vert Whether to flip along the vertical axis. 1428 */ 1429 public function test_flip_image_resource_resets_resolution_and_interlace( $horz, $vert ) { 1430 require_once ABSPATH . 'wp-admin/includes/image-edit.php'; 1431 1432 $image = imagecreatetruecolor( 3, 2 ); 1433 imageresolution( $image, 300, 150 ); 1434 imageinterlace( $image, true ); 1435 1436 $flipped = _flip_image_resource( $image, $horz, $vert ); 1437 1438 $this->assertSame( array( 96, 96 ), imageresolution( $flipped ), 'The resolution should be reset to 96 DPI.' ); 1439 $this->assertFalse( (bool) imageinterlace( $flipped ), 'Interlacing should be disabled.' ); 1440 } 1206 1441 } 1207 1442
Note:
See TracChangeset
for help on using the changeset viewer.
![(please configure the [header_logo] section in trac.ini)](/chrome/site/your_project_logo.png)