Skip to content

Commit 22c4876

Browse files
committed
Fixed bug #65148 (imagerotate may alter image dimensions)
We apply the respective patches from external libgd, work around the still missing `gdImageClone()`, and fix the special cased rotation routines according to Pierre's patch (https://gist.github.com/pierrejoye/59d72385ed1888cf8894a7ed437235ae). We also cater to bug73272.phpt whose result obviously changes a bit.
1 parent 8e32603 commit 22c4876

File tree

4 files changed

+247
-22
lines changed

4 files changed

+247
-22
lines changed

NEWS

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,9 @@ PHP NEWS
1919
. Fixed bug #75301 (Exif extension has built in revision version). (Peter
2020
Kokot)
2121

22+
- GD:
23+
. Fixed bug #65148 (imagerotate may alter image dimensions). (cmb)
24+
2225
- intl:
2326
. Fixed bug #75317 (UConverter::setDestinationEncoding changes source instead
2427
of destination). (andrewnester)

ext/gd/libgd/gd_interpolation.c

Lines changed: 65 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1709,13 +1709,28 @@ gdImagePtr gdImageScale(const gdImagePtr src, const unsigned int new_width, cons
17091709
return im_scaled;
17101710
}
17111711

1712+
static int gdRotatedImageSize(gdImagePtr src, const float angle, gdRectPtr bbox)
1713+
{
1714+
gdRect src_area;
1715+
double m[6];
1716+
1717+
gdAffineRotate(m, angle);
1718+
src_area.x = 0;
1719+
src_area.y = 0;
1720+
src_area.width = gdImageSX(src);
1721+
src_area.height = gdImageSY(src);
1722+
if (gdTransformAffineBoundingBox(&src_area, m, bbox) != GD_TRUE) {
1723+
return GD_FALSE;
1724+
}
1725+
1726+
return GD_TRUE;
1727+
}
1728+
17121729
gdImagePtr gdImageRotateNearestNeighbour(gdImagePtr src, const float degrees, const int bgColor)
17131730
{
17141731
float _angle = ((float) (-degrees / 180.0f) * (float)M_PI);
17151732
const int src_w = gdImageSX(src);
17161733
const int src_h = gdImageSY(src);
1717-
const unsigned int new_width = (unsigned int)(abs((int)(src_w * cos(_angle))) + abs((int)(src_h * sin(_angle))) + 0.5f);
1718-
const unsigned int new_height = (unsigned int)(abs((int)(src_w * sin(_angle))) + abs((int)(src_h * cos(_angle))) + 0.5f);
17191734
const gdFixed f_0_5 = gd_ftofx(0.5f);
17201735
const gdFixed f_H = gd_itofx(src_h/2);
17211736
const gdFixed f_W = gd_itofx(src_w/2);
@@ -1726,6 +1741,12 @@ gdImagePtr gdImageRotateNearestNeighbour(gdImagePtr src, const float degrees, co
17261741
unsigned int dst_offset_y = 0;
17271742
unsigned int i;
17281743
gdImagePtr dst;
1744+
gdRect bbox;
1745+
int new_height, new_width;
1746+
1747+
gdRotatedImageSize(src, degrees, &bbox);
1748+
new_width = bbox.width;
1749+
new_height = bbox.height;
17291750

17301751
if (new_width == 0 || new_height == 0) {
17311752
return NULL;
@@ -1768,8 +1789,6 @@ gdImagePtr gdImageRotateGeneric(gdImagePtr src, const float degrees, const int b
17681789
const int angle_rounded = (int)floor(degrees * 100);
17691790
const int src_w = gdImageSX(src);
17701791
const int src_h = gdImageSY(src);
1771-
const unsigned int new_width = (unsigned int)(abs((int)(src_w * cos(_angle))) + abs((int)(src_h * sin(_angle))) + 0.5f);
1772-
const unsigned int new_height = (unsigned int)(abs((int)(src_w * sin(_angle))) + abs((int)(src_h * cos(_angle))) + 0.5f);
17731792
const gdFixed f_0_5 = gd_ftofx(0.5f);
17741793
const gdFixed f_H = gd_itofx(src_h/2);
17751794
const gdFixed f_W = gd_itofx(src_w/2);
@@ -1780,6 +1799,8 @@ gdImagePtr gdImageRotateGeneric(gdImagePtr src, const float degrees, const int b
17801799
unsigned int dst_offset_y = 0;
17811800
unsigned int i;
17821801
gdImagePtr dst;
1802+
int new_width, new_height;
1803+
gdRect bbox;
17831804

17841805
const gdFixed f_slop_y = f_sin;
17851806
const gdFixed f_slop_x = f_cos;
@@ -1792,6 +1813,10 @@ gdImagePtr gdImageRotateGeneric(gdImagePtr src, const float degrees, const int b
17921813
return NULL;
17931814
}
17941815

1816+
gdRotatedImageSize(src, degrees, &bbox);
1817+
new_width = bbox.width;
1818+
new_height = bbox.height;
1819+
17951820
dst = gdImageCreateTrueColor(new_width, new_height);
17961821
if (!dst) {
17971822
return NULL;
@@ -1831,8 +1856,7 @@ gdImagePtr gdImageRotateBilinear(gdImagePtr src, const float degrees, const int
18311856
float _angle = (float)((- degrees / 180.0f) * M_PI);
18321857
const unsigned int src_w = gdImageSX(src);
18331858
const unsigned int src_h = gdImageSY(src);
1834-
unsigned int new_width = abs((int)(src_w*cos(_angle))) + abs((int)(src_h*sin(_angle) + 0.5f));
1835-
unsigned int new_height = abs((int)(src_w*sin(_angle))) + abs((int)(src_h*cos(_angle) + 0.5f));
1859+
unsigned int new_width, new_height;
18361860
const gdFixed f_0_5 = gd_ftofx(0.5f);
18371861
const gdFixed f_H = gd_itofx(src_h/2);
18381862
const gdFixed f_W = gd_itofx(src_w/2);
@@ -1844,6 +1868,12 @@ gdImagePtr gdImageRotateBilinear(gdImagePtr src, const float degrees, const int
18441868
unsigned int dst_offset_y = 0;
18451869
unsigned int src_offset_x, src_offset_y;
18461870
gdImagePtr dst;
1871+
gdRect bbox;
1872+
1873+
gdRotatedImageSize(src, degrees, &bbox);
1874+
1875+
new_width = bbox.width;
1876+
new_height = bbox.height;
18471877

18481878
dst = gdImageCreateTrueColor(new_width, new_height);
18491879
if (dst == NULL) {
@@ -1863,19 +1893,14 @@ gdImagePtr gdImageRotateBilinear(gdImagePtr src, const float degrees, const int
18631893
const unsigned int m = gd_fxtoi(f_m);
18641894
const unsigned int n = gd_fxtoi(f_n);
18651895

1866-
if ((m > 0) && (m < src_h - 1) && (n > 0) && (n < src_w - 1)) {
1896+
if ((m >= 0) && (m < src_h - 1) && (n >= 0) && (n < src_w - 1)) {
18671897
const gdFixed f_f = f_m - gd_itofx(m);
18681898
const gdFixed f_g = f_n - gd_itofx(n);
18691899
const gdFixed f_w1 = gd_mulfx(f_1-f_f, f_1-f_g);
18701900
const gdFixed f_w2 = gd_mulfx(f_1-f_f, f_g);
18711901
const gdFixed f_w3 = gd_mulfx(f_f, f_1-f_g);
18721902
const gdFixed f_w4 = gd_mulfx(f_f, f_g);
18731903

1874-
if (n < src_w - 1) {
1875-
src_offset_x = n + 1;
1876-
src_offset_y = m;
1877-
}
1878-
18791904
if (m < src_h-1) {
18801905
src_offset_x = n;
18811906
src_offset_y = m + 1;
@@ -1890,13 +1915,13 @@ gdImagePtr gdImageRotateBilinear(gdImagePtr src, const float degrees, const int
18901915
register int pixel2, pixel3, pixel4;
18911916

18921917
if (src_offset_y + 1 >= src_h) {
1893-
pixel2 = bgColor;
1894-
pixel3 = bgColor;
1895-
pixel4 = bgColor;
1918+
pixel2 = pixel1;
1919+
pixel3 = pixel1;
1920+
pixel4 = pixel1;
18961921
} else if (src_offset_x + 1 >= src_w) {
1897-
pixel2 = bgColor;
1898-
pixel3 = bgColor;
1899-
pixel4 = bgColor;
1922+
pixel2 = pixel1;
1923+
pixel3 = pixel1;
1924+
pixel4 = pixel1;
19001925
} else {
19011926
pixel2 = src->tpixels[src_offset_y][src_offset_x + 1];
19021927
pixel3 = src->tpixels[src_offset_y + 1][src_offset_x];
@@ -1946,8 +1971,7 @@ gdImagePtr gdImageRotateBicubicFixed(gdImagePtr src, const float degrees, const
19461971
const float _angle = (float)((- degrees / 180.0f) * M_PI);
19471972
const int src_w = gdImageSX(src);
19481973
const int src_h = gdImageSY(src);
1949-
const unsigned int new_width = abs((int)(src_w*cos(_angle))) + abs((int)(src_h*sin(_angle) + 0.5f));
1950-
const unsigned int new_height = abs((int)(src_w*sin(_angle))) + abs((int)(src_h*cos(_angle) + 0.5f));
1974+
unsigned int new_width, new_height;
19511975
const gdFixed f_0_5 = gd_ftofx(0.5f);
19521976
const gdFixed f_H = gd_itofx(src_h/2);
19531977
const gdFixed f_W = gd_itofx(src_w/2);
@@ -1963,7 +1987,11 @@ gdImagePtr gdImageRotateBicubicFixed(gdImagePtr src, const float degrees, const
19631987
unsigned int dst_offset_y = 0;
19641988
unsigned int i;
19651989
gdImagePtr dst;
1990+
gdRect bbox;
19661991

1992+
gdRotatedImageSize(src, degrees, &bbox);
1993+
new_width = bbox.width;
1994+
new_height = bbox.height;
19671995
dst = gdImageCreateTrueColor(new_width, new_height);
19681996

19691997
if (dst == NULL) {
@@ -2206,8 +2234,11 @@ gdImagePtr gdImageRotateBicubicFixed(gdImagePtr src, const float degrees, const
22062234

22072235
gdImagePtr gdImageRotateInterpolated(const gdImagePtr src, const float angle, int bgcolor)
22082236
{
2209-
const int angle_rounded = (int)floor(angle * 100);
2210-
2237+
/* round to two decimals and keep the 100x multiplication to use it in the common square angles
2238+
case later. Keep the two decimal precisions so smaller rotation steps can be done, useful for
2239+
slow animations, f.e. */
2240+
const int angle_rounded = fmod((int) floorf(angle * 100), 360 * 100);
2241+
22112242
if (bgcolor < 0) {
22122243
return NULL;
22132244
}
@@ -2224,6 +2255,18 @@ gdImagePtr gdImageRotateInterpolated(const gdImagePtr src, const float angle, in
22242255

22252256
/* no interpolation needed here */
22262257
switch (angle_rounded) {
2258+
case 0: {
2259+
gdImagePtr dst = gdImageCreateTrueColor(src->sx, src->sy);
2260+
if (dst == NULL) {
2261+
return NULL;
2262+
}
2263+
dst->transparent = src->transparent;
2264+
dst->saveAlphaFlag = 1;
2265+
dst->alphaBlendingFlag = gdEffectReplace;
2266+
2267+
gdImageCopy(dst, src, 0,0,0,0,src->sx,src->sy);
2268+
return dst;
2269+
}
22272270
case -27000:
22282271
case 9000:
22292272
return gdImageRotate90(src, 0);

ext/gd/tests/bug65148.phpt

Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
1+
--TEST--
2+
Bug #65148 (imagerotate may alter image dimensions)
3+
--SKIPIF--
4+
<?php
5+
if (!extension_loaded('gd')) die('skip gd extension is not available');
6+
?>
7+
--FILE--
8+
<?php
9+
10+
$interpolations = array(
11+
'IMG_BELL' => IMG_BELL,
12+
'IMG_BESSEL' => IMG_BESSEL,
13+
'IMG_BICUBIC' => IMG_BICUBIC,
14+
'IMG_BICUBIC_FIXED' => IMG_BICUBIC_FIXED,
15+
'IMG_BILINEAR_FIXED' => IMG_BILINEAR_FIXED,
16+
'IMG_BLACKMAN' => IMG_BLACKMAN,
17+
'IMG_BOX' => IMG_BOX,
18+
'IMG_BSPLINE' => IMG_BSPLINE,
19+
'IMG_CATMULLROM' => IMG_CATMULLROM,
20+
'IMG_GAUSSIAN' => IMG_GAUSSIAN,
21+
'IMG_GENERALIZED_CUBIC' => IMG_GENERALIZED_CUBIC,
22+
'IMG_HERMITE' => IMG_HERMITE,
23+
'IMG_HAMMING' => IMG_HAMMING,
24+
'IMG_HANNING' => IMG_HANNING,
25+
'IMG_MITCHELL' => IMG_MITCHELL,
26+
'IMG_POWER' => IMG_POWER,
27+
'IMG_QUADRATIC' => IMG_QUADRATIC,
28+
'IMG_SINC' => IMG_SINC,
29+
'IMG_NEAREST_NEIGHBOUR' => IMG_NEAREST_NEIGHBOUR,
30+
'IMG_WEIGHTED4' => IMG_WEIGHTED4,
31+
'IMG_TRIANGLE' => IMG_TRIANGLE,
32+
);
33+
34+
$img = imagecreate(40, 20);
35+
$results = array();
36+
37+
foreach ($interpolations as $name => $interpolation) {
38+
imagesetinterpolation($img, $interpolation);
39+
$t = imagecolorallocatealpha($img, 0, 0, 0, 127);
40+
$imgr = imagerotate($img, -5, $t);
41+
$results[$name] = array('x' => imagesx($imgr), 'y' => imagesy($imgr));
42+
imagedestroy($imgr);
43+
}
44+
45+
imagedestroy($img);
46+
print_r($results);
47+
?>
48+
===DONE===
49+
--EXPECT--
50+
Array
51+
(
52+
[IMG_BELL] => Array
53+
(
54+
[x] => 40
55+
[y] => 23
56+
)
57+
58+
[IMG_BESSEL] => Array
59+
(
60+
[x] => 40
61+
[y] => 23
62+
)
63+
64+
[IMG_BICUBIC] => Array
65+
(
66+
[x] => 40
67+
[y] => 23
68+
)
69+
70+
[IMG_BICUBIC_FIXED] => Array
71+
(
72+
[x] => 40
73+
[y] => 23
74+
)
75+
76+
[IMG_BILINEAR_FIXED] => Array
77+
(
78+
[x] => 40
79+
[y] => 23
80+
)
81+
82+
[IMG_BLACKMAN] => Array
83+
(
84+
[x] => 40
85+
[y] => 23
86+
)
87+
88+
[IMG_BOX] => Array
89+
(
90+
[x] => 40
91+
[y] => 23
92+
)
93+
94+
[IMG_BSPLINE] => Array
95+
(
96+
[x] => 40
97+
[y] => 23
98+
)
99+
100+
[IMG_CATMULLROM] => Array
101+
(
102+
[x] => 40
103+
[y] => 23
104+
)
105+
106+
[IMG_GAUSSIAN] => Array
107+
(
108+
[x] => 40
109+
[y] => 23
110+
)
111+
112+
[IMG_GENERALIZED_CUBIC] => Array
113+
(
114+
[x] => 40
115+
[y] => 23
116+
)
117+
118+
[IMG_HERMITE] => Array
119+
(
120+
[x] => 40
121+
[y] => 23
122+
)
123+
124+
[IMG_HAMMING] => Array
125+
(
126+
[x] => 40
127+
[y] => 23
128+
)
129+
130+
[IMG_HANNING] => Array
131+
(
132+
[x] => 40
133+
[y] => 23
134+
)
135+
136+
[IMG_MITCHELL] => Array
137+
(
138+
[x] => 40
139+
[y] => 23
140+
)
141+
142+
[IMG_POWER] => Array
143+
(
144+
[x] => 40
145+
[y] => 23
146+
)
147+
148+
[IMG_QUADRATIC] => Array
149+
(
150+
[x] => 40
151+
[y] => 23
152+
)
153+
154+
[IMG_SINC] => Array
155+
(
156+
[x] => 40
157+
[y] => 23
158+
)
159+
160+
[IMG_NEAREST_NEIGHBOUR] => Array
161+
(
162+
[x] => 40
163+
[y] => 23
164+
)
165+
166+
[IMG_WEIGHTED4] => Array
167+
(
168+
[x] => 40
169+
[y] => 23
170+
)
171+
172+
[IMG_TRIANGLE] => Array
173+
(
174+
[x] => 40
175+
[y] => 23
176+
)
177+
178+
)
179+
===DONE===

ext/gd/tests/bug73272.png

2 Bytes
Loading

0 commit comments

Comments
 (0)