Skip to content

Commit 5890761

Browse files
authored
Overhaul GD test helpers and affected tests (GH-17309)
* Use type declarations instead of doc-block annotations * Inline the terrible get_rgb() function * Always traverse pixels in Z order libgd stores the pixel as an array of rows, so we should use row-major- order traversal to improve caching. * Add assertions to avoid misuse of the functions The assertion regarding the image dimensions won't break any tests, and we had it already as a comment. However, asserting that the images are truecolor images is important for `calc_image_dissimilarity()` which otherwise would calculate nonsense, and not unreasonable for `test_image_equals_image()` which otherwise is overspecified (for our purposes, it doesn't matter which palette entry a pixel refers to, but rather whether the actual colors referred by a palette color match). Since the truecolor assertions break two tests, we fix these by converting to truecolor. That should likely be backported to lower branches. * Drop implicit conversion to truecolor Conversion to truecolor is a relatively expensive operation, and as such should not be implicit; instead test authors are encouraged to use truecolor images in the first place where possible, or to even find better ways to verify expectations than doing a full image comparison. * Merge similarity.inc into func.inc There is no particular reason to have a separate file for similarity comparisons. * Simplify bug43475.phpt and bug64641.phpt `calc_image_dissimilarity()` calculates the sum of the euclidean distance of the RGB channels of all pixels. The euclidean distance is either zero or greater than or equal to one (but never in ]0, 1[). The sum of these values also has this property, so it doesn't make sense to check for less than 1e-5. Thus we just call `test_image_equals_file()` instead. * Replace calc_image_dissimilarity() with the well-known mse() `calc_image_dissimilarity()` has the drawback that it did sum up the pixel differences, so for large images the result could be way larger than for small images. It also has the drawback that it likely is not as well understood as the mean squared error. Thus we replace it with the latter, and calculate the mean squared error of the individual RGB channels (to be precise). The result is always in range 0..255**2 what makes reasoning about thresholds easier.
1 parent f698c62 commit 5890761

24 files changed

+61
-118
lines changed

ext/gd/tests/avif_decode_encode.phpt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ gd
1414
--FILE--
1515
<?php
1616

17-
require_once __DIR__ . '/similarity.inc';
17+
require_once __DIR__ . '/func.inc';
1818

1919
$infile = __DIR__ . '/girl.avif';
2020
$outfile = __DIR__ . '/test.avif';
@@ -58,8 +58,8 @@ gd
5858

5959
// Note we could also forgive a certain number of pixel differences.
6060
// With the current test image, we just didn't need to.
61-
echo 'How many pixels are different in the two images? ';
62-
print_r(calc_image_dissimilarity($img, $img_from_avif));
61+
echo 'What is the mean squared error of the two images? ',
62+
mse($img, $img_from_avif);
6363

6464
unlink($outfile);
6565

@@ -79,4 +79,4 @@ Encoding AVIF with illegal quality: imageavif(): Argument #3 ($quality) must be
7979
Encoding AVIF with illegal speed: imageavif(): Argument #4 ($speed) must be between -1 and 10
8080
Encoding AVIF losslessly... ok
8181
Decoding the AVIF we just wrote...
82-
How many pixels are different in the two images? 0
82+
What is the mean squared error of the two images? 0

ext/gd/tests/bug43475.phpt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ gd
1313
?>
1414
--FILE--
1515
<?php
16-
require_once __DIR__ . '/similarity.inc';
16+
require_once __DIR__ . '/func.inc';
1717

1818
function setStyleAndThickness($im, $color, $thickness)
1919
{
@@ -59,8 +59,8 @@ imageline($im, 200, 100, 700, 100, IMG_COLOR_STYLED);
5959
imageline($im, 700, 100, 700, 600, IMG_COLOR_STYLED);
6060
imageline($im, 700, 600, 200, 100, IMG_COLOR_STYLED);
6161

62-
$ex = imagecreatefrompng(__DIR__ . '/bug43475.png');
63-
var_dump(calc_image_dissimilarity($ex, $im) < 1e-5);
62+
imagepalettetotruecolor($im);
63+
test_image_equals_file(__DIR__ . '/bug43475.png', $im);
6464
?>
6565
--EXPECT--
66-
bool(true)
66+
The images are equal.

ext/gd/tests/bug43475.png

6.58 KB
Loading

ext/gd/tests/bug52070.phpt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ imagedashedline($im, 800, 400, 500, 800, $color);
2020
imagedashedline($im, 800, 400, 600, 800, $color);
2121
imagedashedline($im, 800, 400, 700, 800, $color);
2222
imagedashedline($im, 800, 400, 800, 800, $color);
23+
imagepalettetotruecolor($im);
2324
include_once __DIR__ . '/func.inc';
2425
test_image_equals_file(__DIR__ . '/bug52070.png', $im);
2526
?>

ext/gd/tests/bug52070.png

6.35 KB
Loading

ext/gd/tests/bug64641.phpt

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ if (!(imagetypes() & IMG_PNG)) {
1313
?>
1414
--FILE--
1515
<?php
16-
require_once __DIR__ . '/similarity.inc';
16+
require_once __DIR__ . '/func.inc';
1717

1818
$im = imagecreatetruecolor(640, 480);
1919

@@ -31,15 +31,7 @@ $points = array(
3131
);
3232
imagefilledpolygon($im, $points, 0xFFFF00);
3333

34-
$ex = imagecreatefrompng(__DIR__ . '/bug64641.png');
35-
if (($diss = calc_image_dissimilarity($ex, $im)) < 1e-5) {
36-
echo "IDENTICAL";
37-
} else {
38-
echo "DISSIMILARITY: $diss";
39-
}
40-
imagedestroy($ex);
41-
42-
imagedestroy($im);
34+
test_image_equals_file(__DIR__ . '/bug64641.png', $im);
4335
?>
4436
--EXPECT--
45-
IDENTICAL
37+
The images are equal.

ext/gd/tests/bug72913.phpt

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,16 @@ imagesavealpha($dst, true);
2323

2424
imagecopy($dst, $src, 0,0, 0,0, 50,50);
2525

26-
include_once __DIR__ . '/func.inc';
27-
test_image_equals_file(__DIR__ . '/bug72913.png', $dst);
26+
var_dump(imagecolorsforindex($dst, imagecolorat($dst, 0, 0)));
2827
?>
2928
--EXPECT--
30-
The images are equal.
29+
array(4) {
30+
["red"]=>
31+
int(255)
32+
["green"]=>
33+
int(255)
34+
["blue"]=>
35+
int(255)
36+
["alpha"]=>
37+
int(127)
38+
}

ext/gd/tests/bug72913.png

-147 Bytes
Binary file not shown.

ext/gd/tests/func.inc

Lines changed: 22 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -78,9 +78,7 @@ function test_image_equals_file(string $filename, GdImage $actual): void
7878
save_actual_image($actual);
7979
return;
8080
}
81-
$actual = test_to_truecolor($actual);
8281
$expected = imagecreatefrompng($filename);
83-
$expected = test_to_truecolor($expected);
8482
test_image_equals_image($expected, $actual, true);
8583
}
8684

@@ -95,6 +93,7 @@ function test_image_equals_file(string $filename, GdImage $actual): void
9593
*/
9694
function test_image_equals_image(GdImage $expected, GdImage $actual, bool $save_actual = false): void
9795
{
96+
assert(imageistruecolor($expected) && imageistruecolor($actual));
9897
$exp_x = imagesx($expected);
9998
$exp_y = imagesy($expected);
10099
$act_x = imagesx($actual);
@@ -126,35 +125,13 @@ function test_image_equals_image(GdImage $expected, GdImage $actual, bool $save_
126125
}
127126
}
128127

129-
/**
130-
* Returns the truecolor version of an image.
131-
*
132-
* @param resource $image
133-
* @return resource
134-
*/
135-
function test_to_truecolor($image)
136-
{
137-
if (imageistruecolor($image)) {
138-
return $image;
139-
} else {
140-
$width = imagesx($image);
141-
$height = imagesy($image);
142-
$result = imagecreatetruecolor($width, $height);
143-
imagecopy($result, $image, 0,0, 0,0, $width, $height);
144-
return $result;
145-
}
146-
}
147-
148128
/**
149129
* Saves an actual image to disk.
150130
*
151131
* The image is saved right beside the temporary .php test file with the
152132
* extension .out.png.
153-
*
154-
* @param resource $image
155-
* @return void
156133
*/
157-
function save_actual_image($image)
134+
function save_actual_image(GdImage $image): void
158135
{
159136
$pathinfo = pathinfo($_SERVER['SCRIPT_FILENAME']);
160137
$filename = "{$pathinfo['dirname']}/{$pathinfo['filename']}.out.png";
@@ -178,3 +155,23 @@ function trycatch_dump(...$tests) {
178155
}
179156
}
180157

158+
/**
159+
* Calculate the mean squared error (in range 0..255**2)
160+
*/
161+
function mse(GdImage $image1, GdImage $image2): float
162+
{
163+
assert(imageistruecolor($image1) && imageistruecolor($image2));
164+
assert(imagesx($image1) === imagesx($image2) && imagesy($image1) === imagesy($image2));
165+
$e = $count = 0;
166+
for ($j = 0, $m = imagesy($image1); $j < $m; $j++) {
167+
for ($i = 0, $n = imagesx($image1); $i < $n; $i++) {
168+
$c1 = imagecolorat($image1, $i, $j);
169+
$c2 = imagecolorat($image2, $i, $j);
170+
$e += ((($c1 >> 16) & 255) - (($c2 >> 16) & 255)) ** 2
171+
+ ((($c1 >> 8) & 255) - (($c2 >> 8) & 255)) ** 2
172+
+ ((($c1 >> 0) & 255) - (($c2 >> 0) & 255)) ** 2;
173+
$count += 3;
174+
}
175+
}
176+
return $e / $count;
177+
}

ext/gd/tests/gd276.phpt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ $filename = __DIR__ . "/gd276.bmp";
1515
imagebmp($orig, $filename, true);
1616
$saved = imagecreatefrombmp($filename);
1717
var_dump($saved !== false);
18+
imagepalettetotruecolor($orig);
19+
imagepalettetotruecolor($saved);
1820
test_image_equals_image($orig, $saved);
1921
?>
2022
--EXPECT--

ext/gd/tests/imagebmp_basic.phpt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ $im = imagecreate(100, 100);
1313
imagecolorallocate($im, 0, 0, 0);
1414
$white = imagecolorallocate($im, 255, 255, 255);
1515
imageline($im, 10,10, 89,89, $white);
16-
16+
imagepalettetotruecolor($im);
1717
require __DIR__ . "/func.inc";
1818
test_image_equals_file(__DIR__ . "/imagebmp_basic.png", $im);
1919
?>

ext/gd/tests/imagebmp_basic.png

443 Bytes
Loading

ext/gd/tests/imagecolorset_basic.phpt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ $bg = imagecolorat($im, 0, 0);
2525
// Set the background to be blue
2626
imagecolorset($im, $bg, 0, 0, 255);
2727

28+
imagepalettetotruecolor($im);
2829
include_once __DIR__ . '/func.inc';
2930
test_image_equals_file(__DIR__ . '/imagecolorset_basic.png', $im);
3031
imagedestroy($im);

ext/gd/tests/imagecolorset_basic.png

335 Bytes
Loading

ext/gd/tests/imagecreatefromstring_bmp.phpt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ $bmp = "\x42\x4D\x3E\x00\x00\x00\x00\x00\x00\x00\x3E\x00\x00\x00\x28\x00"
2222
. "\x01\x00\x05\x00\x00\x00\x02\x00\x01\x01\x01\x00\x06\x00\x00\x00"
2323
. "\x0A\x00\x00\x00\x0A\x00\x00\x00\x00\x01";
2424
$im = imagecreatefromstring($bmp);
25-
25+
imagepalettetotruecolor($im);
2626
include_once __DIR__ . '/func.inc';
2727
test_image_equals_file(__DIR__ . '/imagecreatefromstring_bmp.png', $im);
2828
?>
8 Bytes
Loading

ext/gd/tests/imagegammacorrect_variation1.phpt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ $half2 = imagefilledarc ( $image, 75, 75, 70, 70, 0, -180, $gray, IMG_ARC_PIE )
2626

2727
$gamma = imagegammacorrect($image, 1, 5);
2828
var_dump((bool) $gamma);
29-
29+
imagepalettetotruecolor($image);
3030
include_once __DIR__ . '/func.inc';
3131
test_image_equals_file(__DIR__ . '/imagegammacorrect_variation1.png', $image);
3232
?>
487 Bytes
Loading

ext/gd/tests/imagegammacorrect_variation2.phpt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,9 @@ function test_gamma($in, $out, $constructor)
3333

3434
imagegammacorrect($im, $in, $out);
3535

36+
if ($constructor === "imagecreate") {
37+
imagepalettetotruecolor($im);
38+
}
3639
$filename = __DIR__ . DIRECTORY_SEPARATOR
3740
. "imagegammacorrect_variation2_{$in}_{$out}.png";
3841
$kind = $constructor === 'imagecreate' ? 'palette' : 'truecolor';

ext/gd/tests/imagetruecolortopalette_basic.phpt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ $half = imagefilledarc ( $image, 75, 75, 70, 70, 0, 180, $a, IMG_ARC_PIE );
2525
$half2 = imagefilledarc ( $image, 75, 55, 80, 70, 0, -180, $b, IMG_ARC_PIE );
2626

2727
var_dump(imagetruecolortopalette($image, true, 2));
28-
28+
imagepalettetotruecolor($image);
2929
include_once __DIR__ . '/func.inc';
3030
test_image_equals_file(__DIR__ . '/imagetruecolortopalette_basic.png', $image);
3131
?>
501 Bytes
Loading

ext/gd/tests/similarity.inc

Lines changed: 0 additions & 64 deletions
This file was deleted.

ext/gd/tests/test_image_equals_file_palette.phpt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,15 @@ $red = imagecolorallocate($im, 255, 0, 0);
1818
imagefilledrectangle($im, 3,3, 7,7, $red);
1919

2020
$filename = __DIR__ . DIRECTORY_SEPARATOR . 'test_image_equals_file_palette.png';
21+
imagepalettetotruecolor($im);
2122
imagepng($im, $filename);
2223

2324
$im = imagecreate(10, 10);
2425
imagecolorallocate($im, 255, 255, 255);
2526
$blue = imagecolorallocate($im, 0, 0, 255);
2627
imagefilledrectangle($im, 3,3, 7,7, $blue);
2728

29+
imagepalettetotruecolor($im);
2830
test_image_equals_file($filename, $im);
2931

3032
$im = imagecreate(10, 10);
@@ -33,6 +35,7 @@ imagecolorallocate($im, 0, 0, 0);
3335
$red = imagecolorallocate($im, 255, 0, 0);
3436
imagefilledrectangle($im, 3,3, 7,7, $red);
3537

38+
imagepalettetotruecolor($im);
3639
test_image_equals_file($filename, $im);
3740
?>
3841
--EXPECT--

ext/gd/tests/webp_basic.phpt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ if (!function_exists('imagewebp') || !function_exists('imagecreatefromwebp'))
1212
?>
1313
--FILE--
1414
<?php
15-
require_once __DIR__ . '/similarity.inc';
15+
require_once __DIR__ . '/func.inc';
1616

1717
$filename = __DIR__ . '/webp_basic.webp';
1818

@@ -30,12 +30,12 @@ imagewebp($im1, $filename);
3030
$im2 = imagecreatefromwebp($filename);
3131
imagewebp($im2, $filename);
3232
echo 'Is lossy conversion close enough? ';
33-
var_dump(calc_image_dissimilarity($im1, $im2) < 10e5);
33+
var_dump(mse($im1, $im2) < 500);
3434

3535
imagewebp($im1, $filename, IMG_WEBP_LOSSLESS);
3636
$im_lossless = imagecreatefromwebp($filename);
3737
echo 'Does lossless conversion work? ';
38-
var_dump(calc_image_dissimilarity($im1, $im_lossless) == 0);
38+
var_dump(mse($im1, $im_lossless) == 0);
3939

4040
try {
4141
imagewebp($im1, $filename, -10);

0 commit comments

Comments
 (0)