Hướng dẫn dùng png crop trong PHP

Vietnamese (Tiếng Việt) translation by Dai Phong (you can also view the original English article)

Nội dung chính

  • Giới thiệu
  • Bước 1 Chuẩn bị
  • Bước 2 Gọi Đối tượng
  • Bước 3 Sườn của Lớp
  • Bước 4 Phương thức Xây dựng
  • Bước 5 Mở ảnh
  • Bước 6 Cách Thay đổi Kích thước
  • Bước 7 Thay đổi Kích thước. Hãy làm điều đó nào!
  • Bước 8 Cây Quyết định
  • Bước 9 Tối ưu hoá Kích thước
  • Bước 10 Cắt
  • Bước 11 Lưu Hình ảnh

Nội dung chính

  • Giới thiệu
  • Bước 1 Chuẩn bị
  • Bước 2 Gọi Đối tượng
  • Bước 3 Sườn của Lớp
  • Bước 4 Phương thức Xây dựng
  • Bước 5 Mở ảnh
  • Bước 6 Cách Thay đổi Kích thước
  • Bước 7 Thay đổi Kích thước. Hãy làm điều đó nào!
  • Bước 8 Cây Quyết định
  • Bước 9 Tối ưu hoá Kích thước
  • Bước 10 Cắt
  • Bước 11 Lưu Hình ảnh

Nội dung chính

  • Giới thiệu
  • Bước 1 Chuẩn bị
  • Bước 2 Gọi Đối tượng
  • Bước 3 Sườn của Lớp
  • Bước 4 Phương thức Xây dựng
  • Bước 5 Mở ảnh
  • Bước 6 Cách Thay đổi Kích thước
  • Bước 7 Thay đổi Kích thước. Hãy làm điều đó nào!
  • Bước 8 Cây Quyết định
  • Bước 9 Tối ưu hoá Kích thước
  • Bước 10 Cắt
  • Bước 11 Lưu Hình ảnh

Đã bao giờ bạn muốn có một phương pháp tất cả trong một, dễ sử dụng để thay đổi kích thước hình ảnh của bạn trong PHP chưa? Vâng đó là mục đích của các lớp PHP—các phần chức năng có thể sử dụng lại mà chúng ta gọi để thực hiện công việc nặng nhọc đằng sau hậu trường. Chúng ta sẽ học cách tạo ra lớp của riêng mình, sẽ được cấu trúc tốt cũng như có khả năng mở rộng. Việc thay đổi kích thước sẽ trở nên dễ dàng. Dễ thế nào? Trong ba bước thì sao!


Giới thiệu

Để cung cấp cho bạn một cái nhìn nhanh về những gì chúng ta đang cố gắng làm với lớp của chúng ta, lớp nên là:

  • Dễ dàng sử dụng
  • Định dạng độc lập. Tức là, mở, thay đổi kích thước, và lưu một số định dạng hình ảnh khác nhau.
  • Kích thước thông minh - Không biến dạng hình ảnh!

Lưu ý: Đây không phải là một hướng dẫn về cách tạo các lớp và đối tượng, và mặc dù vậy kỹ năng này có thể có ích, nhưng không nhất thiết cho hướng dẫn này.

Có rất nhiều thứ để tìm hiểu - Hãy bắt đầu nào.


Bước 1 Chuẩn bị

Chúng ta sẽ bắt đầu một cách nhẹ nhàng. Trong thư mục làm việc của bạn hãy tạo ra hai tập tin: một được gọi là index.php, một resize-class.php


Bước 2 Gọi Đối tượng

Để cho bạn một ý tưởng về những gì chúng ta đang cố gắng đạt được, chúng ta sẽ bắt đầu bằng cách viết code các cuộc gọi hàm mà chúng ta sẽ sử dụng để thay đổi kích thước hình ảnh. Mở tập tin index.php của bạn và thêm vào code sau đây.

Như bạn có thể thấy, có một logic thú vị cho những gì chúng ta đang làm. Chúng ta mở tập tin hình ảnh, thiết lập kích thước mà chúng ta muốn và kiểu thay đổi kích thước.
Sau đó chúng ta lưu hình ảnh, chọn định dạng và chất lượng hình ảnh mà chúng ta mong muốn. Lưu và đóng tập tin index.php của bạn.

  	// *** Include the class
		include("resize-class.php");

		// *** 1) Initialize / load image
		$resizeObj = new resize('sample.jpg');

		// *** 2) Resize image (options: exact, portrait, landscape, auto, crop)
		$resizeObj -> resizeImage(150, 100, 'crop');

		// *** 3) Save image
		$resizeObj -> saveImage('sample-resized.gif', 100);

Từ code ở trên bạn có thể thấy chúng ta đang mở một tập tin jpg nhưng lưu thành gif. Hãy nhớ rằng, nó xoay quanh tính linh hoạt.


Bước 3 Sườn của Lớp

Đó là Lập trình Hướng Đối tượng (OOP), phương pháp giúp giải quyết điều này một cách dễ dàng. Hãy nghĩ về lớp như là một mẫu; Bạn có thể đóng gói dữ liệu - một thuật ngữ khác có nghĩa thật sự là ẩn dữ liệu. Sau đó chúng ta có thể sử dụng lại lớp này nhiều lần mà không cần viết lại bất kỳ code thay đổi kích thước nào - bạn chỉ cần gọi các phương thức thích hợp như chúng ta đã làm trong bước hai. Một khi mẫu của chúng ta đã được tạo ra, chúng ta tạo các đối tượng của mẫu này.

"Hàm xây dựng, được gọi là một constructor, là một phương thức đặc biệt của lớp được gọi bởi lớp khi bạn tạo một đối tượng mới."

Hãy bắt đầu tạo lớp resize của chúng ta. Mở tập tin resize-class.php. Dưới đây là cấu trúc sườn thật sự cơ bản mà tôi đã đặt tên là 'resize'. Lưu ý dòng comment cho biến lớp; đây là nơi mà chúng ta sẽ bắt đầu thêm các biến quan trọng của lớp sau này.

Hàm xây dựng, là một phương thức đặc biệt của lớp (thuật ngữ "phương thức" cũng tương tự như hàm, tuy nhiên, khi nói về các lớp và các đối tượng thì thuật ngữ phương thức thường được sử dụng) được gọi bởi lớp khi bạn tạo một đối tượng mới. Nó thích hợp để chúng ta thực hiện một số khởi tạo - mà chúng ta sẽ làm trong bước tiếp theo.

		Class resize
		{
			// *** Class variables

			public function __construct()
			{

			}
		}

Lưu ý hai dấu gạch dưới cho phương thức xây dựng.


Bước 4 Phương thức Xây dựng

Chúng ta sẽ chỉnh sửa phương thức xây dựng ở trên. Trước tiên, chúng ta sẽ truyền vào tên tập tin (và đường dẫn) của hình ảnh cần thay đổi kích thước. Chúng ta sẽ gọi biến này là $fileName.

Chúng ta cần mở tập tin được truyền vào bằng PHP (cụ thể là thư viện PHP GD) để PHP có thể đọc được hình ảnh. Chúng ta làm việc này bằng phương thức 'openImage' tuỳ biến .
Tôi sẽ đi vào cách phương thức này làm việc trong một lát nữa, nhưng bây giờ, chúng ta cần phải lưu kết quả vào một biến của lớp. Một biến của lớp chỉ là một biến - nhưng nó cụ thể cho lớp đó. Bạn có nhớ biến của lớp mà tôi đã đề cập trước đây không? Thêm 'image' dưới dạng một biến private bằng cách gõ 'private $image;'. Bằng cách thiết lập biến là 'Private', bạn đang thiết lập phạm vi của biến đó để nó chỉ có thể được truy cập bởi lớp. Từ bây giờ trở đi chúng ta có thể gọi đến hình ảnh đã được mở của chúng ta, được xem như là một tài nguyên, mà chúng ta sẽ làm việc với nó sau này khi chúng ta thay đổi kích cỡ.

Khi chúng ta đang đến đó, hãy lưu trữ chiều cao và chiều rộng của hình ảnh. Tôi có một cảm nhận là những điều này sẽ hữu ích sau này.

Bây giờ bạn sẽ có những thứ sau đây.

		Class resize
		{
			// *** Class variables
			private $image;
			private $width;
			private $height;

			function __construct($fileName)
			{
			    // *** Open up the file
			    $this->image = $this->openImage($fileName);

			    // *** Get width and height
			    $this->width  = imagesx($this->image);
			    $this->height = imagesy($this->image);
			}
		}

Phương thức imagesx và imagesy là các hàm tích hợp của thư viện GD. Chúng lấy chiều rộng và chiều cao của hình ảnh tương ứng.


Bước 5 Mở ảnh

Trong bước trước, chúng ta gọi phương thức tùy biến openImage. Trong bước này chúng ta sẽ tạo ra phương thức đó. Chúng ta muốn script thực hiện những suy nghĩ của chúng ta, do đó, tùy thuộc vào kiểu tập tin được truyền vào, script sẽ xác định những gì mà Thư viện GD gọi để mở hình ảnh. Điều này dễ dàng đạt được bằng cách so sánh phần mở rộng của tập tin bằng câu lệnh switch.

Chúng ta truyền vào tập tin mà chúng ta muốn thay đổi kích thước và trả về tài nguyên của tập tin đó.

		private function openImage($file)
		{
		    // *** Get extension
		    $extension = strtolower(strrchr($file, '.'));

		    switch($extension)
		    {
		        case '.jpg':
		        case '.jpeg':
		            $img = @imagecreatefromjpeg($file);
		            break;
		        case '.gif':
		            $img = @imagecreatefromgif($file);
		            break;
		        case '.png':
		            $img = @imagecreatefrompng($file);
		            break;
		        default:
		            $img = false;
		            break;
		    }
		    return $img;
		}

Bước 6 Cách Thay đổi Kích thước

Đây là nơi điều kỳ diệu sẽ xảy ra. Bước này thực sự chỉ là một lời giải thích về những gì chúng ta sẽ làm - vì vậy không cần làm gì ở đây cả. Trong bước tiếp theo, chúng ta sẽ tạo ra một phương thức public mà chúng ta sẽ gọi để thực hiện việc thay đổi kích thước của chúng ta; do đó đương nhiên chúng ta truyền vào chiều rộng và chiều cao, cũng như thông tin về cách chúng ta muốn thay đổi kích thước của hình ảnh. Hãy nói về điều này trong một phút nữa. Sẽ có các trường hợp mà bạn muốn thay đổi kích thước một tấm hình thành một kích thước chính xác. Tuyệt vời, hãy thêm điều này. Nhưng cũng sẽ có những lúc bạn phải thay đổi kích thước của hàng trăm hình ảnh và mỗi hình ảnh có một tỉ lệ khác nhau - ví dụ những hình ảnh chân dung. Thay đổi kích thước của những tấm ảnh này theo kích thước chính xác sẽ gây ra sự biến dạng nghiêm trọng. Nếu chúng ta nhìn vào các tuỳ chọn để tránh sự biến dạng thì chúng ta có thể:

  1. Thay đổi kích thước hình ảnh gần nhất có thể với kích thước hình ảnh mới, trong khi vẫn giữ tỷ lệ khung hình.
  2. Thay đổi kích thước hình ảnh gần nhất có thể với kích thước hình ảnh mới và cắt bỏ phần còn lại.

Cả hai tùy chọn đều khả thi, tùy thuộc vào nhu cầu của bạn.

Vâng. chúng ta sẽ cố gắng xử lý tất cả các thứ bên trên. Để tóm gọn lại, chúng ta sẽ cung cấp các tùy chọn để:

  1. Thay đổi kích thước chính xác theo chiều rộng/chiều cao. (chính xác)
  2. Thay đổi kích thước theo chiều rộng - chiều rộng chính xác sẽ được cố định, chiều cao sẽ được điều chỉnh tuỳ theo tỷ lệ. (phong cảnh)
  3. Thay đổi kích thước theo chiều cao - giống Thay đổi kích thước theo chiều rộng, nhưng chiều cao sẽ được thiết lập và chiều rộng được điều chỉnh một cách tự động. (chân dung)
  4. Tự động xác định các tùy chọn 2 và 3. Nếu bạn đang lặp qua một thư mục với các hình ảnh có kích thước khác nhau, hãy để script xác định cách xử lý điều này. (tự động)
  5. Thay đổi kích thước, sau đó cắt. Đây là cách yêu thích của tôi. Kích thước chính xác, không có biến dạng. (cắt)

Bước 7 Thay đổi Kích thước. Hãy làm điều đó nào!

Có hai phần đối với phương thức thay đổi kích thước. Đầu tiên là lấy chiều cao và chiều cao tối ưu cho hình ảnh mới của chúng ta bằng cách tạo một số phương thức tùy biến - và tất nhiên truyền vào trong 'tuỳ chọn' thay đổi kích thước của chúng ta như mô tả ở trên. Chiều rộng và chiều cao được trả về như là một mảng và thiết lập thành các biến tương ứng của chúng. Đừng ngại 'truyền vào như là tham chiếu'- nhưng tôi không phải là một fan hâm mộ lớn của điều đó.

Phần thứ hai là những gì thực hiện việc thay đổi kích thước thật sự. Để làm cho hướng dẫn này trở nên gọn, tôi sẽ cho bạn đọc các hàm GD sau đây:

  • imagecreatetruecolor
  • imagecopyresampled.

Chúng ta cũng lưu kết quả của phương thức imagecenchetruecolor (một hình ảnh true color (màu sắc thực) mới như là một biến của lớp. Thêm 'private $imageResized;' cùng với các biến khác của lớp.

Việc thay đổi kích thước được thực hiện bởi một mô-đun PHP được gọi là Thư viện GD. Rất nhiều phương thức mà chúng ta sử dụng được cung cấp bởi thư viện này.

		// *** Add to class variables
		private $imageResized;
		public function resizeImage($newWidth, $newHeight, $option="auto")
		{

			// *** Get optimal width and height - based on $option
			$optionArray = $this->getDimensions($newWidth, $newHeight, strtolower($option));

			$optimalWidth  = $optionArray['optimalWidth'];
			$optimalHeight = $optionArray['optimalHeight'];

			// *** Resample - create image canvas of x, y size
			$this->imageResized = imagecreatetruecolor($optimalWidth, $optimalHeight);
			imagecopyresampled($this->imageResized, $this->image, 0, 0, 0, 0, $optimalWidth, $optimalHeight, $this->width, $this->height);

			// *** if option is 'crop', then crop too
			if ($option == 'crop') {
				$this->crop($optimalWidth, $optimalHeight, $newWidth, $newHeight);
			}
		}

Bước 8 Cây Quyết định

Công việc bạn làm bây giờ càng nhiều, thì càng ít phải làm gì khi bạn thay đổi kích thước. Phương thức này chọn lộ trình thực hiện, với mục tiêu đạt được chiều rộng và chiều cao thay đổi tối ưu dựa trên tùy chọn thay đổi kích thước của bạn. Nó sẽ gọi phương thức thích hợp, trong đó chúng ta sẽ tạo ra ở bước tiếp theo.

		private function getDimensions($newWidth, $newHeight, $option)
		{

		   switch ($option)
		    {
		        case 'exact':
		            $optimalWidth = $newWidth;
		            $optimalHeight= $newHeight;
		            break;
		        case 'portrait':
		            $optimalWidth = $this->getSizeByFixedHeight($newHeight);
		            $optimalHeight= $newHeight;
		            break;
		        case 'landscape':
		            $optimalWidth = $newWidth;
		            $optimalHeight= $this->getSizeByFixedWidth($newWidth);
		            break;
		        case 'auto':
		            $optionArray = $this->getSizeByAuto($newWidth, $newHeight);
					$optimalWidth = $optionArray['optimalWidth'];
					$optimalHeight = $optionArray['optimalHeight'];
		            break;
				case 'crop':
		            $optionArray = $this->getOptimalCrop($newWidth, $newHeight);
					$optimalWidth = $optionArray['optimalWidth'];
					$optimalHeight = $optionArray['optimalHeight'];
		            break;
		    }
			return array('optimalWidth' => $optimalWidth, 'optimalHeight' => $optimalHeight);
		}

Bước 9 Tối ưu hoá Kích thước

Chúng ta đã thảo luận những gì mà bốn phương thức này làm. Chúng chỉ là các công thức toán học cơ bản, thật vậy, chúng tính toán kích thước phù hợp nhất.

		private function getSizeByFixedHeight($newHeight)
		{
		    $ratio = $this->width / $this->height;
		    $newWidth = $newHeight * $ratio;
		    return $newWidth;
		}

		private function getSizeByFixedWidth($newWidth)
		{
		    $ratio = $this->height / $this->width;
		    $newHeight = $newWidth * $ratio;
		    return $newHeight;
		}

		private function getSizeByAuto($newWidth, $newHeight)
		{
		    if ($this->height < $this->width)
		    // *** Image to be resized is wider (landscape)
		    {
		        $optimalWidth = $newWidth;
		        $optimalHeight= $this->getSizeByFixedWidth($newWidth);
		    }
		    elseif ($this->height > $this->width)
		    // *** Image to be resized is taller (portrait)
		    {
		        $optimalWidth = $this->getSizeByFixedHeight($newHeight);
		        $optimalHeight= $newHeight;
		    }
			else
		    // *** Image to be resizerd is a square
		    {
				if ($newHeight < $newWidth) {
					$optimalWidth = $newWidth;
					$optimalHeight= $this->getSizeByFixedWidth($newWidth);
				} else if ($newHeight > $newWidth) {
					$optimalWidth = $this->getSizeByFixedHeight($newHeight);
				    $optimalHeight= $newHeight;
				} else {
					// *** Sqaure being resized to a square
					$optimalWidth = $newWidth;
					$optimalHeight= $newHeight;
				}
		    }

			return array('optimalWidth' => $optimalWidth, 'optimalHeight' => $optimalHeight);
		}

		private function getOptimalCrop($newWidth, $newHeight)
		{

			$heightRatio = $this->height / $newHeight;
			$widthRatio  = $this->width /  $newWidth;

			if ($heightRatio < $widthRatio) {
				$optimalRatio = $heightRatio;
			} else {
				$optimalRatio = $widthRatio;
			}

			$optimalHeight = $this->height / $optimalRatio;
			$optimalWidth  = $this->width  / $optimalRatio;

			return array('optimalWidth' => $optimalWidth, 'optimalHeight' => $optimalHeight);
		}

Bước 10 Cắt

Nếu bạn chọn cắt - nghĩa là bạn đã sử dụng tùy chọn cắt ảnh, sau đó bạn có thêm một bước nhỏ nữa .
Chúng ta sẽ cắt ảnh từ trung tâm. Cắt là một quá trình rất giống với thay đổi kích thước nhưng với một vài tham số thay đổi kích thước được truyền vào.

		private function crop($optimalWidth, $optimalHeight, $newWidth, $newHeight)
		{
			// *** Find center - this will be used for the crop
			$cropStartX = ( $optimalWidth / 2) - ( $newWidth /2 );
			$cropStartY = ( $optimalHeight/ 2) - ( $newHeight/2 );

			$crop = $this->imageResized;
			//imagedestroy($this->imageResized);

			// *** Now crop from center to exact requested size
			$this->imageResized = imagecreatetruecolor($newWidth , $newHeight);
			imagecopyresampled($this->imageResized, $crop , 0, 0, $cropStartX, $cropStartY, $newWidth, $newHeight , $newWidth, $newHeight);
		}

Bước 11 Lưu Hình ảnh

Chúng ta đang đến gần mục tiêu; tất cả gần như xong. Bây giờ là lúc để lưu hình ảnh. Chúng ta truyền vào đường dẫn, và chất lượng hình ảnh mà chúng ta muốn từ 0-100, 100 là tốt nhất, và gọi phương thức thích hợp. Một vài điều cần lưu ý về chất lượng hình ảnh: ảnh JPG sử dụng thang đo từ 0-100, 100 là tốt nhất. Hình ảnh GIF không có thiết lập chất lượng hình ảnh. PNG thì có, nhưng chúng sử dụng thang đo từ 0-9, 0 là tốt nhất. Điều này không tốt vì chúng ta không thể kỳ vọng là chúng ta nhớ điều này mỗi khi chúng ta muốn lưu một hình ảnh. Chúng ta thực hiện một chút ma thuật để chuẩn hóa tất cả mọi thứ.

		public function saveImage($savePath, $imageQuality="100")
		{
			// *** Get extension
        	$extension = strrchr($savePath, '.');
        	$extension = strtolower($extension);

			switch($extension)
			{
				case '.jpg':
				case '.jpeg':
					if (imagetypes() & IMG_JPG) {
						imagejpeg($this->imageResized, $savePath, $imageQuality);
					}
		            break;

				case '.gif':
					if (imagetypes() & IMG_GIF) {
						imagegif($this->imageResized, $savePath);
					}
					break;

				case '.png':
					// *** Scale quality from 0-100 to 0-9
					$scaleQuality = round(($imageQuality/100) * 9);

					// *** Invert quality setting as 0 is best, not 9
					$invertScaleQuality = 9 - $scaleQuality;

					if (imagetypes() & IMG_PNG) {
						imagepng($this->imageResized, $savePath, $invertScaleQuality);
					}
					break;

				// ... etc

				default:
					// *** No extension - No save.
					break;
			}

			imagedestroy($this->imageResized);
		}

Bây giờ cũng là lúc thích hợp để xoá hình ảnh nguồn của chúng ta để giải phóng một ít bộ nhớ. Nếu bạn sử dụng điều này trong sản phẩm được phát hành ra, thì bạn cũng nên nắm bắt và trả về kết quả hình ảnh đã lưu.


Tóm tắt

Vậy là chúng ta đã thực hiện xong. Cảm ơn bạn đã theo dõi hướng dẫn này, tôi hy vọng bạn thấy nó hữu ích. Tôi đánh giá cao những phản hồi của bạn, thông qua phần bình luận dưới đây.