2011-11-06 91 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);