如果angle是旋转角度,那么旋转图像的宽度和高度,width′和height′,is given by:
宽度′ = 高度 * s + 宽度 * c
高度′ = 高度 * c + 宽度 * s
其中width 是源图像宽度,height 是源图像高度,并且:
s = abs(sin(角度))
c = abs(cos(角度))
请注意,由于三角恒等式 sin(-θ) = -sin(θ) 和 cos(-θ) = cos (θ),在上面的公式中,测量angle时,正方向是顺时针还是逆时针都没有关系。
您知道的一件事是源图像的中心点映射到旋转图像的中心。因此,如果width′ ≥ width 和height′ ≥ height,则旋转图像内左上角的坐标为:
x = 旋转宽度 / 2 - 宽度 / 2
y = 旋转高度 / 2 - 高度 / 2
所以,如果 width′ ≥ width 和 height′ ≥ height,下面的 PHP 代码将根据需要裁剪图像:
$cropped = imagecrop($rotated, array(
'x' => $rotated_width / 2 - $width / 2,
'y' => $rotated_height / 2 - $height / 2,
'width' => $width,
'height' => $height
));
但是,这只适用于width′ ≥ width 和height′ ≥ height。例如,如果源图像的尺寸是方形的,则这成立,因为:
长度′ = 长度 * (abs(sin(angle)) + abs(cos(angle)))
和abs(sin(angle)) + abs(cos(angle)) ≥ 1.
见"y = abs(sin(theta)) + abs(cos(theta)) minima" on WolframAlpha。
如果 width′ ≥ width 和 height′ ≥ height 不成立(例如,将 250×40 图像顺时针旋转 50°),则生成的图像将全黑(因为将无效的裁剪矩形传递给 imagecrop())。
这些问题可以通过以下代码修复:
$cropped = imagecrop($rotated, array(
'x' => max(0, $rotated_width / 2 - $width / 2),
'y' => max(0, $rotated_height / 2 - $height / 2),
'width' => min($width, $rotated_width),
'height'=> min($height, $rotated_height)
));
这段代码的结果是下图中的蓝色区域:
(有关 SVG 版本,请参阅 http://fiddle.jshell.net/5jf3wqn4/show/。)
在图中,半透明的红色矩形代表原始的 250×40 图像。红色矩形代表图像的旋转。虚线矩形表示 imagerotate() 创建的图像的边界。
总而言之,这里是旋转和裁剪图像的 PHP 代码:
$filename = 'http://placehold.it/250x40';
$degrees = -50;
$source = imagecreatefrompng($filename);
$width = imagesx($source);
$height = imagesy($source);
$rotated = imagerotate($source, $degrees, 0);
imagedestroy($source);
$rotated_width = imagesx($rotated);
$rotated_height = imagesy($rotated);
$cropped = imagecrop($rotated, array(
'x' => max(0, (int)(($rotated_width - $width) / 2)),
'y' => max(0, (int)(($rotated_height - $height) / 2)),
'width' => min($width, $rotated_width),
'height'=> min($height, $rotated_height)
));
imagedestroy($rotated);
imagepng($cropped);
编辑: 似乎有a bug in imagecrop() 在裁剪后的图像底部添加了一条 1 像素的黑线。请参阅imagecrop() alternative for PHP < 5.5 了解解决方法。
EDIT2:我发现imageaffine() 可以产生比imagerotate() 更好的质量。用户“abc at ed48 dot com”has commented 使用仿射变换逆时针旋转给定角度。
这是使用 imageaffine() 代替 imagerotate() 的代码:
// Crops the $source image, avoiding the black line bug in imagecrop()
// See:
// - https://bugs.php.net/bug.php?id=67447
// - https://stackoverflow.com/questions/26722811/imagecrop-alternative-for-php-5-5
function fixedcrop($source, array $rect)
{
$cropped = imagecreate($rect['width'], $rect['height']);
imagecopyresized(
$cropped,
$source,
0,
0,
$rect['x'],
$rect['y'],
$rect['width'],
$rect['height'],
$rect['width'],
$rect['height']
);
return $cropped;
}
$filename = 'http://placehold.it/250x40';
$degrees = -50;
$source = imagecreatefrompng($filename);
$width = imagesx($source);
$height = imagesy($source);
$radians = deg2rad($degrees);
$cos = cos($radians);
$sin = sin($radians);
$affine = [ $cos, -$sin, $sin, $cos, 0, 0 ];
$rotated = imageaffine($source, $affine);
imagedestroy($source);
$rotated_width = imagesx($rotated);
$rotated_height = imagesy($rotated);
$cropped = fixedcrop($rotated, array(
'x' => max(0, (int)(($rotated_width - $width) / 2)),
'y' => max(0, (int)(($rotated_height - $height) / 2)),
'width' => min($width, $rotated_width),
'height'=> min($height, $rotated_height)
));
imagedestroy($rotated);
imagepng($cropped);