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

From: Date: Sat, 16 Apr 2016 15:53:01 +0000
Subject: Bug #65148 [Asn]: imagerotate may alter image dimensions
References: 1  Groups: php.bugs 
Request: Send a blank email to php-bugs+get-200598@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
 Updated by:         pajoye@php.net
 Reported by:        imprec at gmail dot com
 Summary:            imagerotate may alter image dimensions
 Status:             Assigned
 Type:               Bug
 Package:            GD related
-PHP Version:        5.5.0
+PHP Version:        5.5+
 Assigned To:        pajoye
 Block user comment: N
 Private report:     N

 New Comment:

The cause was slightly different. The calculation of the transformed image dimensions were partially
wrong.

If you can try this patch, that should fix your issue:

https://gist.github.com/pierrejoye/59d72385ed1888cf8894a7ed437235ae


Previous Comments:
------------------------------------------------------------------------
[2016-04-11 05:38:08] pajoye@php.net

It seems to be only for the default interpolation method, IMG_BILINEAR_FIXED.
Adding:
imagesetinterpolation($image, IMG_BICUBIC);

before calling imagerotate shows the correct result. IMG_BICUBIC_FIXED for example works well too.

I have to check if other are affected but my feeling is that only the bilinear fixed point
implementation fails.

If you have time, you can also try it. The various interpolation methods are available here:
http://php.net/manual/en/function.imagesetinterpolation.php

------------------------------------------------------------------------
[2016-04-09 10:51:38] plunk at hushmail dot com

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>';
?>

------------------------------------------------------------------------
[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).

------------------------------------------------------------------------


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


Thread (13 messages)

« previous php.bugs (#200598) next »