Оптимизация загружаемых картинок в PHP - Разработчик Павел Скляр

@pavelsklyar | 01 июл 2020 в 17:50

Оптимизация загружаемых картинок в PHP

При разработке любого веб-сайта, который подразумевает загрузку изображений, очень важно продумать оптимизацию их размера. Чтобы удержать посетителя, всё должно загружаться очень быстро. Люди не любят ждать, и даже ожидание в 2 секунды порой приводит к потере интереса к вашему контенту.

В поиске решения этой проблемы, я наткнулся на очень интересный класс. Вот он:

class SimpleImage {

   private $image;
   private $image_type;

   public function load($filename) 
   {
      $image_info = getimagesize($filename);
      $this->image_type = $image_info[2];

      if ($this->image_type == IMAGETYPE_JPEG) {
         $this->image = imagecreatefromjpeg($filename);
      } 
      elseif ($this->image_type == IMAGETYPE_GIF) {
         $this->image = imagecreatefromgif($filename);
      } 
      elseif ($this->image_type == IMAGETYPE_PNG) {
         $this->image = imagecreatefrompng($filename);
      }
   }

   public function save($filename, $image_type = IMAGETYPE_JPEG, $compression = 75, $permissions = null) 
   {
      if ($image_type == IMAGETYPE_JPEG) {
         imagejpeg($this->image, $filename, $compression);
      } 
      elseif ($image_type == IMAGETYPE_GIF) {
         imagegif($this->image, $filename);
      } 
      elseif ($image_type == IMAGETYPE_PNG) {
         imagepng($this->image, $filename);
      }

      if( $permissions != null) {
         chmod($filename, $permissions);
      }
   }

   public function output($image_type = IMAGETYPE_JPEG)
   {
      if ($image_type == IMAGETYPE_JPEG) {
         imagejpeg($this->image);
      } 
      elseif ($image_type == IMAGETYPE_GIF) {
         imagegif($this->image);
      } 
      elseif ($image_type == IMAGETYPE_PNG) {
         imagepng($this->image);
      }
   }

   public function getWidth() 
   {
      return imagesx($this->image);
   }

   public function getHeight() 
   {
      return imagesy($this->image);
   }

   public function resizeToHeight($height) 
   {
      $ratio = $height / $this->getHeight();
      $width = $this->getWidth() * $ratio;
      $this->resize($width, $height);
   }

   public function resizeToWidth($width)
   {
      $ratio = $width / $this->getWidth();
      $height = $this->getheight() * $ratio;
      $this->resize($width, $height);
   }

   public function scale($scale)
   {
      $width = $this->getWidth() * $scale / 100;
      $height = $this->getheight() * $scale / 100;
      $this->resize($width, $height);
   }

   public function resize($width, $height) 
   {
      $new_image = imagecreatetruecolor($width, $height);
      imagecopyresampled($new_image, $this->image, 0, 0, 0, 0, $width, $height, $this->getWidth(), $this->getHeight());
      $this->image = $new_image;
   }
}

В чём его особенность? Большинство реализаций, которые я находил, для изменения веса изображения используют простые функции аля imagejpeg() и параметр $quality. Он принимает значения в диапазоне от 0 (низкое качество, маленький размер файла) до 100 (высокое качество, большой размер файла. Если использовать этот способ, то изображение может быть совершенно нечитаемым, а то и битым, если ставить слишком низкие значения. В SimpleImage используется другой подход, который меня и привлёк. В чём он заключается?

Сначала мы вызываем метод load(), который в зависимости от типа изображения создаёт точно такое же, используя функции imagecreatefrom. Далее для изменения размеров изображения нужно вызвать метод resize(), передав туда нужные нам значения ширины и высоты. Как этот метод работает?

Сначала создаётся новое полноцветное изображение заданного размера с помощью функции imagecreatetruecolor(). Дальше в дело вступает очень интересная функция imagecopyresampled(). Что она вообще делает?

Она копирует область одного изображения на другое таким образом, что при уменьшении изображения не теряется чёткость. То есть это схоже с тем, что мы в фотошопе из картинки 6000x4000px выводим картинку 3000x2000px. При этом она не теряет своей чёткости и смотрится почти так же, как оригинал.

После этого мы просто сохраняем изображение с помощью метода save().

Примеры использования

Изменение изображения до определённых размеров. В данном случае, картинка будет размерами 400х200px:

$image = new SimpleImage();
$image->load('image.jpg');
$image->resize(400, 200);
$image->save('image1.jpg');

Если нам нужно изменить размеры картинки, основываясь только на одном параметре (например, на ширине), можно использовать метод resizeToWidth():

$image = new SimpleImage();
$image->load('image.jpg');
$image->resizeToWidth(250);
$image->save('image1.jpg');

Аналогичный метод есть и для высоты - resizeToHeight.

Если вам нужно изменить размер в процентном соотношении, поможет метод scale(), который принимает значения от 0 до 100:

$image = new SimpleImage();
$image->load('image.jpg');
$image->scale(50);
$image->save('image1.jpg');

Также учитываются и ситуации, когда нужно вывести изображение в браузер без сохранения. Для этого есть метод output():

$image = new SimpleImage();
$image->load('image.jpg');
$image->resizeToWidth(150);
$image->output();

Мои наблюдения

Когда я тестировал этот класс, заметил маленькую особенность. На входе у меня было изображение весом около 15 Мб. Для изменения размера я использовал метод scale() и передал туда 100, то есть сам размер изображения не меняется. Я хотел посмотреть, сколько будет весить файл на выходе, и результат меня очень сильно удивил. Полученное изображение весило около 300 Кб и при этом почти не потеряла в качестве.

Именно эту реализацию я использую на своём сайте и планирую использовать в будущем. Код класса я также выложил на Pastebin.

Теги: оптимизация изображений , программиирование , php , SimpleImage

80