diff options
author | Christoph M. Becker <cmbecker69@gmx.de> | 2017-10-24 14:42:03 +0200 |
---|---|---|
committer | Christoph M. Becker <cmbecker69@gmx.de> | 2017-10-24 17:02:56 +0200 |
commit | 22c487616f132ee7ebfa838bce9d14c9243bd2d3 (patch) | |
tree | 6fc170720b23c488dfb6e2fd0df7bcef525f6126 /ext/gd | |
parent | 8e3260376c65b762a51eee7c30ab560dadefa1f6 (diff) | |
download | php-git-22c487616f132ee7ebfa838bce9d14c9243bd2d3.tar.gz |
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.
Diffstat (limited to 'ext/gd')
-rw-r--r-- | ext/gd/libgd/gd_interpolation.c | 87 | ||||
-rw-r--r-- | ext/gd/tests/bug65148.phpt | 179 | ||||
-rw-r--r-- | ext/gd/tests/bug73272.png | bin | 739 -> 741 bytes |
3 files changed, 244 insertions, 22 deletions
diff --git a/ext/gd/libgd/gd_interpolation.c b/ext/gd/libgd/gd_interpolation.c index 4446ec772b..07eefd6488 100644 --- a/ext/gd/libgd/gd_interpolation.c +++ b/ext/gd/libgd/gd_interpolation.c @@ -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 index 0000000000..9effae9714 --- /dev/null +++ b/ext/gd/tests/bug65148.phpt @@ -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=== diff --git a/ext/gd/tests/bug73272.png b/ext/gd/tests/bug73272.png Binary files differindex 97bfadf983..514e012a92 100644 --- a/ext/gd/tests/bug73272.png +++ b/ext/gd/tests/bug73272.png |