Bug #65148 [Com]: imagerotate may alter image dimensions

From: Date: Sat, 09 Apr 2016 10:51:40 +0000
Subject: Bug #65148 [Com]: imagerotate may alter image dimensions
References: 1  Groups: php.bugs 
Request: Send a blank email to php-bugs+get-200452@lists.php.net to get a copy of this message
Edit report at https://bugs.php.net/bug.php?id=65148&edit=1 ID: 65148 Comment by: plunk at hushmail dot com Reported by: imprec at gmail dot com Summary: imagerotate may alter image dimensions Status: Open Type: Bug Package: GD related Operating System: Linux PHP Version: 5.5.0 Block user comment: N Private report: N New Comment: The top and left of the image is actually clipped. This problem appears at all rotations: <?php $image = imagecreatetruecolor(500,250); $bg = imagecolorallocatealpha($image, 255, 255, 127, 127); $black = imagecolorallocatealpha($image, 0, 0, 0, 0); $blue = imagecolorallocatealpha($image, 0, 0, 255, 0); $red = imagecolorallocatealpha($image, 255, 0, 0, 0); imagesavealpha($image, true); imagealphablending($image, false); ImageFill($image, 0, 0 , $bg); imagerectangle($image,0,0,imagesx($image)-1,imagesy($image)-1,$black); imagerectangle($image,1,1,imagesx($image)-2,imagesy($image)-2,$blue); imagerectangle($image,2,2,imagesx($image)-3,imagesy($image)-3,$red); imagepng($image,'dump1.png'); echo '<img src="dump1.png" /><p>'.(imagesx($image).' x '.imagesy($image)).'</p>'; $image = imagerotate($image,0.1,$bg); imagepng($image,'dump2.png'); echo '<img src="dump2.png" /><p>'.(imagesx($image).' x '.imagesy($image)).'</p>'; ?> Previous Comments: ------------------------------------------------------------------------ [2015-07-12 21:30:14] cmb@php.net Related To: Bug #66882 ------------------------------------------------------------------------ [2014-07-30 12:24:23] erwin at burorader dot com I dove a bit further into the code of both PHP 5.4 and 5.5 and found the following differences: 1) Special cases (i.e multiples of 90 degrees): - PHP5.4 does normalize angles first to be between 0 and 360 and then checks for equality on 0, 90, 180 and 270 - PHP5.5 does not normalize but checks on angles 0, 90 to 90.01, 180 to 180.01, and 270 to 270.01. The check on not being exactly equal can, considering the imprecision of float arithmetic, be considered a good feature. 2) Computation - PHP5.4 uses arithmetic that comes down to (PHP syntax and after normalizing to an angle between -45 and +45): $rad = deg2rad($degrees); $cos = abs(cos($rad)); // always >= 0 for angles between -45 and +45; $sin = abs(sin($rad)); // Shear 1: no intermediate results to store. // Shear 2: the width remains the same as after shear 1. $newHeight = (int) ($width * $sin + $height * $cos) + 1; // Shear 3: the height remains the same as after shear 2, the width is // recalculated based on the original dimensions. $newWidth = (int) ($height * $sin + $width * $cos) + 1; - PHP 5.5 use arithmetic that comes down to (PHP syntax): $rad = deg2rad($degrees); $cos = cos($rad); $sin = sin($rad); // This is how GD on PHP5.5 calculates the new dimensions. $newWidth = abs((int) ($width * $cos)) + abs((int)($height * $sin + 0.5)); $newHeight = abs((int) ($width * $sin)) + abs((int)($height * $cos + 0.5)); Errors in PHP 5.5 code: ad1) - PHP5.5 misses negative angles like -90, -180 and -270, - It misses angles just below such a multiple like 89.995. Why is this a bug? For reasons of symmetry I would expect both cases to be treated as an 'exact multiple' of 90 degrees and therefore being treated specially. ad 2) - PHP5.4 is always rounding up, which is what I would expect. OTOH, PHP5.5 is basically rounding down, though is asymmetrically and only partly compensating for that. It should at least be symmetrical, i.e. the 0.5 should be used in all parts of the calculation. - The compensation (the + 0.5) is making the situation even worse (i.e. worse than just rounding down) in cases where the (co)sine is negative. So the 0.5 should have the same sign as the accompanying sine or cosine. Why are these bugs? For reasons of symmetry I would expect - The same dimensions if I rotate an image over d or -d degrees. - The same dimensions for rotating an image of WxH over d degrees and rotating an image of HxW over 90+d degrees, as the latter can be seen as rotating over 90 degrees (swapping W and H) and subsequently rotating over d degrees. Both symmetries are satisfied by PHP5.4, both aren't by PHP5.5. Proposal: - Reintroduce the 'normalization' of the angle to better decide if it is a special case. - Return to the old PHP 5.4 calculations to compute the new dimensions. I do consider these to be "correct". ------------------------------------------------------------------------ [2014-07-18 13:09:56] erwin at burorader dot com Correction to previous comment: on PHP5.4 it used to be 42 x 24 (and not 42 x 23). ------------------------------------------------------------------------ [2014-07-18 13:05:33] erwin at burorader dot com Same on Zend Server CE on Windows with PHP Version 5.5.7 (GD Version: bundled (2.1.0 compatible)). I used this as test script: <?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 = imagecreatefrompng('image-test.png'); $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); } print_r($results); imagedestroy($img); ?> Results: Array ( [IMG_BELL] => Array ( [x] => 40 [y] => 22 ) [IMG_BESSEL] => Array ( [x] => 40 [y] => 22 ) [IMG_BICUBIC] => Array ( [x] => 40 [y] => 22 ) [IMG_BICUBIC_FIXED] => Array ( [x] => 41 [y] => 23 ) [IMG_BILINEAR_FIXED] => Array ( [x] => 41 [y] => 23 ) [IMG_BLACKMAN] => Array ( [x] => 40 [y] => 22 ) [IMG_BOX] => Array ( [x] => 40 [y] => 22 ) [IMG_BSPLINE] => Array ( [x] => 40 [y] => 22 ) [IMG_CATMULLROM] => Array ( [x] => 40 [y] => 22 ) [IMG_GAUSSIAN] => Array ( [x] => 40 [y] => 22 ) [IMG_GENERALIZED_CUBIC] => Array ( [x] => 40 [y] => 22 ) [IMG_HERMITE] => Array ( [x] => 40 [y] => 22 ) [IMG_HAMMING] => Array ( [x] => 40 [y] => 22 ) [IMG_HANNING] => Array ( [x] => 40 [y] => 22 ) [IMG_MITCHELL] => Array ( [x] => 40 [y] => 22 ) [IMG_POWER] => Array ( [x] => 40 [y] => 22 ) [IMG_QUADRATIC] => Array ( [x] => 40 [y] => 22 ) [IMG_SINC] => Array ( [x] => 40 [y] => 22 ) [IMG_NEAREST_NEIGHBOUR] => Array ( [x] => 40 [y] => 22 ) [IMG_WEIGHTED4] => Array ( [x] => 40 [y] => 22 ) [IMG_TRIANGLE] => Array ( [x] => 40 [y] => 22 ) ) Notes: 1) On PHP 5.4 it used to be 42 x 23 (it is a test case for Drupal: https://www.drupal.org/node/2215369). 2) The size can even be 2 off, depending on the interpolation setting. 3) I found that positive multiples of 90 degrees work correct, negative multiples are off by 1. ------------------------------------------------------------------------ [2013-06-28 08:54:03] imprec at gmail dot com I tested and reproduced the issue with PHP55-201306280830 ------------------------------------------------------------------------ The remainder of the comments for this report are too long. To view the rest of the comments, please view the bug report online at https://bugs.php.net/bug.php?id=65148 -- Edit this bug report at https://bugs.php.net/bug.php?id=65148&edit=1

« previous php.bugs (#200452) next »