2011-11-06 96 views
2

使用PHP的GD库,您可以使用imagerotate函数旋转图像。这个函数的缺点是它不会剪裁边缘,这正是我需要的。PHP使用修剪边缘旋转图像

下面是一个例子形象,显示了我的问题:

Photoshop vs GD rotate

正如你所看到的,在Photoshop边缘裁剪。在PHP中,由于旋转,图像的大小刚刚增加。我真的想得到和我在Photoshop中一样的结果。任何想法如何在PHP中做到这一点?

(我只能访问GD库)。

+2

这只是一个简单的数学的事情:你需要切出一个矩形的原始图像的大小。矩形的中心将是新图像的中心 –

+0

您是否考虑过使用浏览器的CSS旋转? (是的,它可以在所有浏览器中完成,甚至是旧版本的IE) – Spudley

+0

使用CSS进行旋转;将图像放在容器内;给容器一个固定的宽度,并将其CSS属性“溢出”设置为“隐藏”;那会照顾到这个问题。 –

回答

6

如果你懒得计算旋转图像的新大小,只需使用支持这些计算开箱的GD基于图像库。

一个这样的库是Wideimage

$image = WideImage::load('big.png'); 
$width = $image->getWidth(); 
$height = $image->getHeight(); 
$image->rotate(120)->crop("center", "middle", $width, $height); 
  • Rotate online demo
  • :你加载你的原始图像,得到它的宽度和高度,然后旋转它,然后用所谓的 智能坐标centermiddle并与原始图像的宽度和高度裁剪
  • Crop online Demo
1

目前的答案仅作为一个办法让周围的问题。它不讨论计算新盒子大小所需的数学。

旋转后需要用imagecrop裁剪图像。为此,您可以使用以下公式来保持居中。

rotated_dimension * (1 - source_dimension/rotated_dimension) * 0.5 

下面是一个工作示例。 placehold.it URL可以用本地文件路径替换。

<?php 
$filename = 'http://placehold.it/200x200'; 
$degrees = -45; 

header('Content-type: image/png'); 

$source = imagecreatefrompng($filename); 
$sw = imagesx($source); 
$sh = imagesy($source); 

$rotate = imagerotate($source, $degrees, 0); 
$rw = imagesx($rotate); 
$rh = imagesy($rotate); 

$crop = imagecrop($rotate, array(
    'x' => $rw * (1 - $sw/$rw) * 0.5, 
    'y' => $rh * (1 - $sh/$rh) * 0.5, 
    'width' => $sw, 
    'height'=> $sh 
)); 

imagepng($crop); 
2

如果angle是旋转角度,则宽度和旋转的图像的高度,width′height′is given by

 
width′ = height * s + width * c 
height′ = height * c + width * s 

其中width是源图像的宽度,height是源图像身高和:

 
s = abs(sin(angle)) 
c = abs(cos(angle)) 

请注意,由于trigono度量标识罪( -   θ)= -   SIN(θ)和cos( -   θ)= COS(θ),它不会在上式中,如果测量angle时无所谓,正方向是顺时针或逆时针。

你知道的一件事是源图像的中心点被映射到旋转图像的中心。因此,如果width′ ≥ widthheight′ ≥ height,你有左上点的旋转图像中的坐标是:

 
x = rotated_width/2 - width/2 
y = rotated_height/2 - height/2 

所以,如果width′ ≥ widthheight′ ≥ height,下面的PHP代码将根据需要裁剪图像:

$cropped = imagecrop($rotated, array(
    'x' => $rotated_width/2 - $width/2, 
    'y' => $rotated_height/2 - $height/2, 
    'width' => $width, 
    'height' => $height 
)); 

但是,这只适用于width′ ≥ widthheight′ ≥ height。例如,如果源图像的尺寸是正方形,则这适用,因为:

 
length′ = length * (abs(sin(angle)) + abs(cos(angle))) 

abs(sin(angle)) + abs(cos(angle)) ≥ 1
参见"y = abs(sin(theta)) + abs(cos(theta)) minima" on WolframAlpha

如果width′ ≥ widthheight′ ≥ 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) 
)); 

此代码的结果是蓝色着色的区域中的下图中:

diagram depicting cropping area

(对于参见http://fiddle.jshell.net/5jf3wqn4/show/一个SVG版本。)
在该图中,半透明的红色矩形代表原始图像。红色矩形表示图像的旋转。虚线矩形表示由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); 

编辑:那地方1px的黑线被添加到裁剪图像的底部似乎a bug in imagecrop() 。有关解决方法,请参见imagecrop() alternative for PHP < 5.5

编辑2:我发现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);