Skip to content

Commit b47b888

Browse files
committed
Fix #77198: auto cropping has insufficient precision
We apply the upstream patch[1], and also fix the erroneous bailout at the end of `gdImageAutoCrop()`, since `crop.x` and `crop.y` may very well be zero. [1] <libgd/libgd@bda85aa>
1 parent ff02d50 commit b47b888

File tree

4 files changed

+114
-28
lines changed

4 files changed

+114
-28
lines changed

NEWS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ PHP NEWS
1111

1212
- GD:
1313
. Fixed bug #77195 (Incorrect error handling of imagecreatefromjpeg()). (cmb)
14+
. Fixed bug #77198 (auto cropping has insufficient precision). (cmb)
1415

1516
- Sockets:
1617
. Fixed bug #77136 (Unsupported IPV6_RECVPKTINFO constants on macOS).

ext/gd/libgd/gd_crop.c

Lines changed: 16 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -163,43 +163,38 @@ gdImagePtr gdImageCropAuto(gdImagePtr im, const unsigned int mode)
163163
}
164164
}
165165

166-
/* Nothing to do > bye
167-
* Duplicate the image?
168-
*/
169-
if (y == height - 1) {
166+
/* Whole image would be cropped > bye */
167+
if (match) {
170168
return NULL;
171169
}
172170

173-
crop.y = y -1;
171+
crop.y = y - 1;
172+
174173
match = 1;
175174
for (y = height - 1; match && y >= 0; y--) {
176175
for (x = 0; match && x < width; x++) {
177176
match = (color == gdImageGetPixel(im, x,y));
178177
}
179178
}
180-
181-
if (y == 0) {
182-
crop.height = height - crop.y + 1;
183-
} else {
184-
crop.height = y - crop.y + 2;
185-
}
179+
crop.height = y - crop.y + 2;
186180

187181
match = 1;
188182
for (x = 0; match && x < width; x++) {
189-
for (y = 0; match && y < crop.y + crop.height - 1; y++) {
183+
for (y = 0; match && y < crop.y + crop.height; y++) {
190184
match = (color == gdImageGetPixel(im, x,y));
191185
}
192186
}
193187
crop.x = x - 1;
194188

195189
match = 1;
196190
for (x = width - 1; match && x >= 0; x--) {
197-
for (y = 0; match && y < crop.y + crop.height - 1; y++) {
191+
for (y = 0; match && y < crop.y + crop.height; y++) {
198192
match = (color == gdImageGetPixel(im, x,y));
199193
}
200194
}
201195
crop.width = x - crop.x + 2;
202-
if (crop.x <= 0 || crop.y <= 0 || crop.width <= 0 || crop.height <= 0) {
196+
197+
if (crop.x < 0 || crop.y < 0 || crop.width <= 0 || crop.height <= 0) {
203198
return NULL;
204199
}
205200
return gdImageCrop(im, &crop);
@@ -258,39 +253,32 @@ gdImagePtr gdImageCropThreshold(gdImagePtr im, const unsigned int color, const f
258253
}
259254
}
260255

261-
/* Pierre
262-
* Nothing to do > bye
263-
* Duplicate the image?
264-
*/
265-
if (y == height - 1) {
256+
/* Whole image would be cropped > bye */
257+
if (match) {
266258
return NULL;
267259
}
268260

269-
crop.y = y -1;
261+
crop.y = y - 1;
262+
270263
match = 1;
271264
for (y = height - 1; match && y >= 0; y--) {
272265
for (x = 0; match && x < width; x++) {
273266
match = (gdColorMatch(im, color, gdImageGetPixel(im, x, y), threshold)) > 0;
274267
}
275268
}
276-
277-
if (y == 0) {
278-
crop.height = height - crop.y + 1;
279-
} else {
280-
crop.height = y - crop.y + 2;
281-
}
269+
crop.height = y - crop.y + 2;
282270

283271
match = 1;
284272
for (x = 0; match && x < width; x++) {
285-
for (y = 0; match && y < crop.y + crop.height - 1; y++) {
273+
for (y = 0; match && y < crop.y + crop.height; y++) {
286274
match = (gdColorMatch(im, color, gdImageGetPixel(im, x,y), threshold)) > 0;
287275
}
288276
}
289277
crop.x = x - 1;
290278

291279
match = 1;
292280
for (x = width - 1; match && x >= 0; x--) {
293-
for (y = 0; match && y < crop.y + crop.height - 1; y++) {
281+
for (y = 0; match && y < crop.y + crop.height; y++) {
294282
match = (gdColorMatch(im, color, gdImageGetPixel(im, x,y), threshold)) > 0;
295283
}
296284
}

ext/gd/tests/bug77198_auto.phpt

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
--TEST--
2+
Bug #77198 (auto cropping has insufficient precision)
3+
--SKIPIF--
4+
<?php
5+
if (!extension_loaded('gd')) die('skip gd extension not available');
6+
if (!GD_BUNDLED) die('upstream bugfix has not been released');
7+
?>
8+
--FILE--
9+
<?php
10+
11+
function createWhiteImageWithBlackPixelAt($x, $y)
12+
{
13+
$im = imagecreatetruecolor(8, 8);
14+
imagefilledrectangle($im, 0, 0, 7, 7, 0xffffff);
15+
imagesetpixel($im, $x, $y, 0x000000);
16+
return $im;
17+
}
18+
19+
for ($y = 0; $y < 8; $y++) {
20+
for ($x = 0; $x < 8; $x++) {
21+
if (($x == 0 && ($y == 0 || $y == 7)) || ($x == 7 && ($y == 0 || $y == 7))) {
22+
continue; // skip the corners
23+
}
24+
$orig = createWhiteImageWithBlackPixelAt($x, $y);
25+
$cropped = imagecropauto($orig, IMG_CROP_SIDES);
26+
if (!$cropped) {
27+
printf("Pixel at %d, %d: unexpected NULL crop\n", $x, $y);
28+
} else {
29+
$width = imagesx($cropped);
30+
if ($width !== 1) {
31+
printf("Pixel at %d, %d: unexpected width (%d)\n", $x, $y, $width);
32+
}
33+
$height = imagesy($cropped);
34+
if ($height !== 1) {
35+
printf("Pixel at %d, %d: unexpected height (%d)\n", $x, $y, $height);
36+
}
37+
$color = imagecolorat($cropped, 0, 0);
38+
if ($color !== 0x000000) {
39+
printf("Pixel at %d, %d: unexpected color (%d)\n", $x, $y, $color);
40+
}
41+
imagedestroy($cropped);
42+
}
43+
imagedestroy($orig);
44+
}
45+
}
46+
47+
?>
48+
===DONE===
49+
--EXPECT--
50+
===DONE===

ext/gd/tests/bug77198_threshold.phpt

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
--TEST--
2+
Bug #77198 (threshold cropping has insufficient precision)
3+
--SKIPIF--
4+
<?php
5+
if (!extension_loaded('gd')) die('skip gd extension not available');
6+
if (!GD_BUNDLED) die('upstream bugfix has not been released');
7+
?>
8+
--FILE--
9+
<?php
10+
11+
function createWhiteImageWithBlackPixelAt($x, $y)
12+
{
13+
$im = imagecreatetruecolor(8, 8);
14+
imagefilledrectangle($im, 0, 0, 7, 7, 0xffffff);
15+
imagesetpixel($im, $x, $y, 0x000000);
16+
return $im;
17+
}
18+
19+
for ($y = 0; $y < 8; $y++) {
20+
for ($x = 0; $x < 8; $x++) {
21+
$orig = createWhiteImageWithBlackPixelAt($x, $y);
22+
$cropped = imagecropauto($orig, IMG_CROP_THRESHOLD, 1, 0xffffff);
23+
if (!$cropped) {
24+
printf("Pixel at %d, %d: unexpected NULL crop\n", $x, $y);
25+
} else {
26+
$width = imagesx($cropped);
27+
if ($width !== 1) {
28+
printf("Pixel at %d, %d: unexpected width (%d)\n", $x, $y, $width);
29+
}
30+
$height = imagesy($cropped);
31+
if ($height !== 1) {
32+
printf("Pixel at %d, %d: unexpected height (%d)\n", $x, $y, $height);
33+
}
34+
$color = imagecolorat($cropped, 0, 0);
35+
if ($color !== 0x000000) {
36+
printf("Pixel at %d, %d: unexpected color (%d)\n", $x, $y, $color);
37+
}
38+
imagedestroy($cropped);
39+
}
40+
imagedestroy($orig);
41+
}
42+
}
43+
44+
?>
45+
===DONE===
46+
--EXPECT--
47+
===DONE===

0 commit comments

Comments
 (0)