]> granicus.if.org Git - php/commitdiff
Fixed bug #65148 (imagerotate may alter image dimensions)
authorChristoph M. Becker <cmbecker69@gmx.de>
Tue, 24 Oct 2017 12:42:03 +0000 (14:42 +0200)
committerChristoph M. Becker <cmbecker69@gmx.de>
Tue, 24 Oct 2017 15:02:56 +0000 (17:02 +0200)
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.

NEWS
ext/gd/libgd/gd_interpolation.c
ext/gd/tests/bug65148.phpt [new file with mode: 0644]
ext/gd/tests/bug73272.png

diff --git a/NEWS b/NEWS
index a2ac9e5c34423779f0f3fe9c30f2c5ad73b674d8..8f89c9fd626385ffd3a09b9121d029da6b243579 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -19,6 +19,9 @@ PHP                                                                        NEWS
   . Fixed bug #75301 (Exif extension has built in revision version). (Peter
     Kokot)
 
+- GD:
+  . Fixed bug #65148 (imagerotate may alter image dimensions). (cmb)
+
 - intl:
   . Fixed bug #75317 (UConverter::setDestinationEncoding changes source instead 
     of destination). (andrewnester)
index 4446ec772bef6e884c71aafc939985da0cfc7b64..07eefd64886b554560142798da74c4352a19299f 100644 (file)
@@ -1709,13 +1709,28 @@ gdImagePtr gdImageScale(const gdImagePtr src, const unsigned int new_width, cons
        return im_scaled;
 }
 
+static int gdRotatedImageSize(gdImagePtr src, const float angle, gdRectPtr bbox)
+{
+    gdRect src_area;
+    double m[6];
+
+    gdAffineRotate(m, angle);
+    src_area.x = 0;
+    src_area.y = 0;
+    src_area.width = gdImageSX(src);
+    src_area.height = gdImageSY(src);
+    if (gdTransformAffineBoundingBox(&src_area, m, bbox) != GD_TRUE) {
+        return GD_FALSE;
+    }
+
+    return GD_TRUE;
+}
+
 gdImagePtr gdImageRotateNearestNeighbour(gdImagePtr src, const float degrees, const int bgColor)
 {
        float _angle = ((float) (-degrees / 180.0f) * (float)M_PI);
        const int src_w  = gdImageSX(src);
        const int src_h = gdImageSY(src);
-       const unsigned int new_width = (unsigned int)(abs((int)(src_w * cos(_angle))) + abs((int)(src_h * sin(_angle))) + 0.5f);
-       const unsigned int new_height = (unsigned int)(abs((int)(src_w * sin(_angle))) + abs((int)(src_h * cos(_angle))) + 0.5f);
        const gdFixed f_0_5 = gd_ftofx(0.5f);
        const gdFixed f_H = gd_itofx(src_h/2);
        const gdFixed f_W = gd_itofx(src_w/2);
@@ -1726,6 +1741,12 @@ gdImagePtr gdImageRotateNearestNeighbour(gdImagePtr src, const float degrees, co
        unsigned int dst_offset_y = 0;
        unsigned int i;
        gdImagePtr dst;
+       gdRect bbox;
+       int new_height, new_width;
+
+    gdRotatedImageSize(src, degrees, &bbox);
+    new_width = bbox.width;
+    new_height = bbox.height;
 
        if (new_width == 0 || new_height == 0) {
                return NULL;
@@ -1768,8 +1789,6 @@ gdImagePtr gdImageRotateGeneric(gdImagePtr src, const float degrees, const int b
        const int angle_rounded = (int)floor(degrees * 100);
        const int src_w  = gdImageSX(src);
        const int src_h = gdImageSY(src);
-       const unsigned int new_width = (unsigned int)(abs((int)(src_w * cos(_angle))) + abs((int)(src_h * sin(_angle))) + 0.5f);
-       const unsigned int new_height = (unsigned int)(abs((int)(src_w * sin(_angle))) + abs((int)(src_h * cos(_angle))) + 0.5f);
        const gdFixed f_0_5 = gd_ftofx(0.5f);
        const gdFixed f_H = gd_itofx(src_h/2);
        const gdFixed f_W = gd_itofx(src_w/2);
@@ -1780,6 +1799,8 @@ gdImagePtr gdImageRotateGeneric(gdImagePtr src, const float degrees, const int b
        unsigned int dst_offset_y = 0;
        unsigned int i;
        gdImagePtr dst;
+       int new_width, new_height;
+       gdRect bbox;
 
        const gdFixed f_slop_y = f_sin;
        const gdFixed f_slop_x = f_cos;
@@ -1792,6 +1813,10 @@ gdImagePtr gdImageRotateGeneric(gdImagePtr src, const float degrees, const int b
                return NULL;
        }
 
+    gdRotatedImageSize(src, degrees, &bbox);
+    new_width = bbox.width;
+    new_height = bbox.height;
+
        dst = gdImageCreateTrueColor(new_width, new_height);
        if (!dst) {
                return NULL;
@@ -1831,8 +1856,7 @@ gdImagePtr gdImageRotateBilinear(gdImagePtr src, const float degrees, const int
        float _angle = (float)((- degrees / 180.0f) * M_PI);
        const unsigned int src_w = gdImageSX(src);
        const unsigned int src_h = gdImageSY(src);
-       unsigned int new_width = abs((int)(src_w*cos(_angle))) + abs((int)(src_h*sin(_angle) + 0.5f));
-       unsigned int new_height = abs((int)(src_w*sin(_angle))) + abs((int)(src_h*cos(_angle) + 0.5f));
+       unsigned int new_width, new_height;
        const gdFixed f_0_5 = gd_ftofx(0.5f);
        const gdFixed f_H = gd_itofx(src_h/2);
        const gdFixed f_W = gd_itofx(src_w/2);
@@ -1844,6 +1868,12 @@ gdImagePtr gdImageRotateBilinear(gdImagePtr src, const float degrees, const int
        unsigned int dst_offset_y = 0;
        unsigned int src_offset_x, src_offset_y;
        gdImagePtr dst;
+       gdRect bbox;
+
+       gdRotatedImageSize(src, degrees, &bbox);
+
+       new_width = bbox.width;
+       new_height = bbox.height;
 
        dst = gdImageCreateTrueColor(new_width, new_height);
        if (dst == NULL) {
@@ -1863,7 +1893,7 @@ gdImagePtr gdImageRotateBilinear(gdImagePtr src, const float degrees, const int
                        const unsigned int m = gd_fxtoi(f_m);
                        const unsigned int n = gd_fxtoi(f_n);
 
-                       if ((m > 0) && (m < src_h - 1) && (n > 0) && (n < src_w - 1)) {
+                       if ((m >= 0) && (m < src_h - 1) && (n >= 0) && (n < src_w - 1)) {
                                const gdFixed f_f = f_m - gd_itofx(m);
                                const gdFixed f_g = f_n - gd_itofx(n);
                                const gdFixed f_w1 = gd_mulfx(f_1-f_f, f_1-f_g);
@@ -1871,11 +1901,6 @@ gdImagePtr gdImageRotateBilinear(gdImagePtr src, const float degrees, const int
                                const gdFixed f_w3 = gd_mulfx(f_f, f_1-f_g);
                                const gdFixed f_w4 = gd_mulfx(f_f, f_g);
 
-                               if (n < src_w - 1) {
-                                       src_offset_x = n + 1;
-                                       src_offset_y = m;
-                               }
-
                                if (m < src_h-1) {
                                        src_offset_x = n;
                                        src_offset_y = m + 1;
@@ -1890,13 +1915,13 @@ gdImagePtr gdImageRotateBilinear(gdImagePtr src, const float degrees, const int
                                        register int pixel2, pixel3, pixel4;
 
                                        if (src_offset_y + 1 >= src_h) {
-                                               pixel2 = bgColor;
-                                               pixel3 = bgColor;
-                                               pixel4 = bgColor;
+                                               pixel2 = pixel1;
+                                               pixel3 = pixel1;
+                                               pixel4 = pixel1;
                                        } else if (src_offset_x + 1 >= src_w) {
-                                               pixel2 = bgColor;
-                                               pixel3 = bgColor;
-                                               pixel4 = bgColor;
+                                               pixel2 = pixel1;
+                                               pixel3 = pixel1;
+                                               pixel4 = pixel1;
                                        } else {
                                            pixel2 = src->tpixels[src_offset_y][src_offset_x + 1];
                                                pixel3 = src->tpixels[src_offset_y + 1][src_offset_x];
@@ -1946,8 +1971,7 @@ gdImagePtr gdImageRotateBicubicFixed(gdImagePtr src, const float degrees, const
        const float _angle = (float)((- degrees / 180.0f) * M_PI);
        const int src_w = gdImageSX(src);
        const int src_h = gdImageSY(src);
-       const unsigned int new_width = abs((int)(src_w*cos(_angle))) + abs((int)(src_h*sin(_angle) + 0.5f));
-       const unsigned int new_height = abs((int)(src_w*sin(_angle))) + abs((int)(src_h*cos(_angle) + 0.5f));
+       unsigned int new_width, new_height;
        const gdFixed f_0_5 = gd_ftofx(0.5f);
        const gdFixed f_H = gd_itofx(src_h/2);
        const gdFixed f_W = gd_itofx(src_w/2);
@@ -1963,7 +1987,11 @@ gdImagePtr gdImageRotateBicubicFixed(gdImagePtr src, const float degrees, const
        unsigned int dst_offset_y = 0;
        unsigned int i;
        gdImagePtr dst;
+       gdRect bbox;
 
+       gdRotatedImageSize(src, degrees, &bbox);
+       new_width = bbox.width;
+       new_height = bbox.height;
        dst = gdImageCreateTrueColor(new_width, new_height);
 
        if (dst == NULL) {
@@ -2206,8 +2234,11 @@ gdImagePtr gdImageRotateBicubicFixed(gdImagePtr src, const float degrees, const
 
 gdImagePtr gdImageRotateInterpolated(const gdImagePtr src, const float angle, int bgcolor)
 {
-       const int angle_rounded = (int)floor(angle * 100);
-
+       /* round to two decimals and keep the 100x multiplication to use it in the common square angles 
+          case later. Keep the two decimal precisions so smaller rotation steps can be done, useful for
+          slow animations, f.e. */
+       const int angle_rounded = fmod((int) floorf(angle * 100), 360 * 100);
+          
        if (bgcolor < 0) {
                return NULL;
        }
@@ -2224,6 +2255,18 @@ gdImagePtr gdImageRotateInterpolated(const gdImagePtr src, const float angle, in
 
        /* no interpolation needed here */
        switch (angle_rounded) {
+               case    0: {
+                       gdImagePtr dst = gdImageCreateTrueColor(src->sx, src->sy);
+                       if (dst == NULL) {
+                               return NULL;
+                       }
+                       dst->transparent = src->transparent;
+                       dst->saveAlphaFlag = 1;
+                       dst->alphaBlendingFlag = gdEffectReplace;
+
+                       gdImageCopy(dst, src, 0,0,0,0,src->sx,src->sy);
+                       return dst;
+               }
                case -27000:
                case   9000:
                        return gdImageRotate90(src, 0);
diff --git a/ext/gd/tests/bug65148.phpt b/ext/gd/tests/bug65148.phpt
new file mode 100644 (file)
index 0000000..9effae9
--- /dev/null
@@ -0,0 +1,179 @@
+--TEST--
+Bug #65148 (imagerotate may alter image dimensions)
+--SKIPIF--
+<?php
+if (!extension_loaded('gd')) die('skip gd extension is not available');
+?>
+--FILE--
+<?php
+
+$interpolations = array(
+    'IMG_BELL' => IMG_BELL,
+    'IMG_BESSEL' => IMG_BESSEL,
+    'IMG_BICUBIC' => IMG_BICUBIC,
+    'IMG_BICUBIC_FIXED' => IMG_BICUBIC_FIXED,
+    'IMG_BILINEAR_FIXED' => IMG_BILINEAR_FIXED,
+    'IMG_BLACKMAN' => IMG_BLACKMAN,
+    'IMG_BOX' => IMG_BOX,
+    'IMG_BSPLINE' => IMG_BSPLINE,
+    'IMG_CATMULLROM' => IMG_CATMULLROM,
+    'IMG_GAUSSIAN' => IMG_GAUSSIAN,
+    'IMG_GENERALIZED_CUBIC' => IMG_GENERALIZED_CUBIC,
+    'IMG_HERMITE' => IMG_HERMITE,
+    'IMG_HAMMING' => IMG_HAMMING,
+    'IMG_HANNING' => IMG_HANNING,
+    'IMG_MITCHELL' => IMG_MITCHELL,
+    'IMG_POWER' => IMG_POWER,
+    'IMG_QUADRATIC' => IMG_QUADRATIC,
+    'IMG_SINC' => IMG_SINC,
+    'IMG_NEAREST_NEIGHBOUR' => IMG_NEAREST_NEIGHBOUR,
+    'IMG_WEIGHTED4' => IMG_WEIGHTED4,
+    'IMG_TRIANGLE' => IMG_TRIANGLE,
+);
+
+$img = imagecreate(40, 20);
+$results = array();
+
+foreach ($interpolations as $name => $interpolation) {
+  imagesetinterpolation($img, $interpolation);
+  $t = imagecolorallocatealpha($img, 0, 0, 0, 127);
+  $imgr = imagerotate($img, -5, $t);
+  $results[$name] = array('x' => imagesx($imgr), 'y' => imagesy($imgr));
+  imagedestroy($imgr);
+}
+
+imagedestroy($img);
+print_r($results);
+?>
+===DONE===
+--EXPECT--
+Array
+(
+    [IMG_BELL] => Array
+        (
+            [x] => 40
+            [y] => 23
+        )
+
+    [IMG_BESSEL] => Array
+        (
+            [x] => 40
+            [y] => 23
+        )
+
+    [IMG_BICUBIC] => Array
+        (
+            [x] => 40
+            [y] => 23
+        )
+
+    [IMG_BICUBIC_FIXED] => Array
+        (
+            [x] => 40
+            [y] => 23
+        )
+
+    [IMG_BILINEAR_FIXED] => Array
+        (
+            [x] => 40
+            [y] => 23
+        )
+
+    [IMG_BLACKMAN] => Array
+        (
+            [x] => 40
+            [y] => 23
+        )
+
+    [IMG_BOX] => Array
+        (
+            [x] => 40
+            [y] => 23
+        )
+
+    [IMG_BSPLINE] => Array
+        (
+            [x] => 40
+            [y] => 23
+        )
+
+    [IMG_CATMULLROM] => Array
+        (
+            [x] => 40
+            [y] => 23
+        )
+
+    [IMG_GAUSSIAN] => Array
+        (
+            [x] => 40
+            [y] => 23
+        )
+
+    [IMG_GENERALIZED_CUBIC] => Array
+        (
+            [x] => 40
+            [y] => 23
+        )
+
+    [IMG_HERMITE] => Array
+        (
+            [x] => 40
+            [y] => 23
+        )
+
+    [IMG_HAMMING] => Array
+        (
+            [x] => 40
+            [y] => 23
+        )
+
+    [IMG_HANNING] => Array
+        (
+            [x] => 40
+            [y] => 23
+        )
+
+    [IMG_MITCHELL] => Array
+        (
+            [x] => 40
+            [y] => 23
+        )
+
+    [IMG_POWER] => Array
+        (
+            [x] => 40
+            [y] => 23
+        )
+
+    [IMG_QUADRATIC] => Array
+        (
+            [x] => 40
+            [y] => 23
+        )
+
+    [IMG_SINC] => Array
+        (
+            [x] => 40
+            [y] => 23
+        )
+
+    [IMG_NEAREST_NEIGHBOUR] => Array
+        (
+            [x] => 40
+            [y] => 23
+        )
+
+    [IMG_WEIGHTED4] => Array
+        (
+            [x] => 40
+            [y] => 23
+        )
+
+    [IMG_TRIANGLE] => Array
+        (
+            [x] => 40
+            [y] => 23
+        )
+
+)
+===DONE===
index 97bfadf983e7cf9f96600b2a0bc09a105cb083f4..514e012a92983a86dd87692666e16ead7d3b8cd3 100644 (file)
Binary files a/ext/gd/tests/bug73272.png and b/ext/gd/tests/bug73272.png differ