ProfiPHPProfiPHP
Категория: Полезное в PHP

Класс для работы с изображениями PHP

Введение в класс ImageResize

В этой статье опишем простой класс для работы с изображениями. Уже много лет лично его использую и он полностью себя оправдывает. Благодаря обработке, изображения становятся пригодными для использования на веб-ресурсах.

Свойства объекта класса Image

Как известно, объект - это совокупность данных (свойств) и функций (методов) для их обработки. Свойства и методы называются членами класса. Создадим класс ImageResize и объявим внутри следующие переменные:
private $image;
private $width;
private $height;
private $type;
Спецификатор Private (закрытый) запрещает доступ к атрибутам и методам класса за его пределами. В атрибуте объекта Image будет содержатся ресурс целевого изображения. В атрибуте Widht содержится ширина, в Height соответственно высота исходного изображения. Переменная Type будет хранить тип данного изображения.

Создаем конструктор класса

Далее мы инициализирует объект, создавая конструктор - специальный метод класса, который выполняется при создании объекта.
function __construct( $file ) {
$this->setType( $file );
$this->openImage( $file );
$this->setSize( );
}
Проверка существования и типа изображения должна происходить еще до инициализации класса, поэтому при создании конструктора мы должны быть точно уверенными, что файл существует и являет собой изображения.

В конструкторе мы определяем тип файла изображения:
$this->setType( $file );
Создаем новое изображение:
$this->openImage( $file );
Последняя операция является определение высоты и ширины изображения:
$this->setSize(  );
Определяем тип исходного изображения

Опишем частную функцию, которая записывает в поле Type тип исходного изображения:
private function setType( $file ) {
$pic = getimagesize( $file );
switch( $pic['mime'] ) {
case 'image/jpeg': $this->type = 'jpg'; break;
case 'image/png': $this->type = 'png'; break;
case 'image/gif': $this->type = 'gif'; break;
}
}
Функция создания нового изображения

Опишем частную функцию, которая создает новое изображения в зависимости от типа изображения.
private function openImage( $file ) {
switch( $this->type ) {
case 'jpg': $this->image = @imagecreatefromJpeg( $file ); break;
case 'png': $this->image = @imagecreatefromPng( $file ); break;
case 'gif': $this->image = @imagecreatefromGif( $file ); break;
}
}
В параметре $file мы как и в функции setType передаем путь к исходному файлу.

Функция определения размеров выходного изображения

Опишем частную функцию, которая записывает размеры исходного изображения:
private function setSize(  ) {
$this->width = imageSX( $this->image );
$this->height = imageSY( $this->image );
}
Эта функция определяет размеры изображения в зависимости от созданного нового изображения функции OpenImage.

Функция изменения атрибутов изображения в зависимости от параметров

Создаем функцию Resize, которая принимает параметры ширины и высоты изображения. Оба принятых параметра необязательны. Если в аргументах функции переданы и ширина и высота, изображение сожмется в рамки. Если передан только один параметр, изображение сжимается по ширине. Если без параметра, тогда изменение параметров изображения не происходит.
function resize( $width = false, $height = false )
{
if ( is_numeric( $width ) && is_numeric( $height ) && $width > 0 && $height > 0 ) {
$newSize = $this->getSizeByFramework( $width, $height );
}
elseif ( is_numeric( $width ) && $width > 0 ) {
$newSize = $this->getSizeByWidth( $width );
}
else {
$newSize = array( $this->width, $this->height );
}
$newImage = imagecreatetruecolor( $newSize[0], $newSize[1] );
if ( $this->type == 'gif' || $this->type == 'png' ) {
$white = imagecolorallocate( $newImage, 255, 255, 255 );
imagefill( $newImage, 0, 0, $white );
}
imagecopyresampled( $newImage, $this->image, 0, 0, 0, 0, $newSize[0], $newSize[1], $this->width, $this->height );
$this->image = $newImage;
$this->setSize( );
return $this;
}
Опишем данную функцию более подробно. Сначала идет проверка на существование введенной ширины и высоты изображения:
if ( is_numeric( $width ) && is_numeric( $height ) && $width > 0 && $height > 0 ) {
$newSize = $this->getSizeByFramework( $width, $height );
}
Если выполнение данного условия истинно, то есть существует введенное значений ширины и высоты, они числовые и больше нуля, тогда передаем управление функции getSizeByFramework, которая определяет размеры нового изображения при вписывания его в рамки.

Если указана только ширина изображения, тогда передаем управление частной функции getSizeByWidth:
elseif ( is_numeric( $width ) && $width > 0 ) {
$newSize = $this->getSizeByWidth( $width );
}
Если ни одно из условий не сработает, тогда возвращаем исходную ширину и высоту:
else {
$newSize = array( $this->width, $this->height );
}
Далее в этой функции идет операция создания пустого полноцветного изображения с явно указанной шириной и высотой с помощью функции Imagecreatetruecolor:
$newImage = imagecreatetruecolor( $newSize[0], $newSize[1] );
Далее создаем белый фон для прозрачных изображений:
if ( $this->type == 'gif' || $this->type == 'png' ) {
$white = imagecolorallocate( $newImage, 255, 255, 255 );
imagefill( $newImage, 0, 0, $white );
}
Далее происходит копирование и изменение размера изображения с ресемплированием с помощью функции Imagecopyresampled:
imagecopyresampled( $newImage, $this->image, 0, 0, 0, 0, $newSize[0], $newSize[1], $this->width, $this->height );
И последнее что делает функция Resize - это возвращает значение нового изображения а также новые параметры высоты и ширины:
$this->image = $newImage;
$this->setSize( );
return $this;
Далее разберем отдельно каждую функцию изменения размеров изображения.

Функция определения размеров нового изображения и вписывания его в рамки

Функции GetSizeByFramework определяет размеры нового изображения при вписывания его в рамки:
private function getSizeByFramework( $width, $height )
{
if ( $this->width <= $width && $this->height <= $height ) {
return array( $this->width, $this->height );
}
if ( $this->width / $width > $this->height / $height ) {
$newSize[0] = $width;
$newSize[1] = round( $this->height * $width / $this->width );
}
else {
$newSize[0] = round( $this->width * $height / $this->height );
$newSize[1] = $height;
}
return $newSize;
}
В первом условии проверяем, если исходная ширина меньше или равна введенной пользователем ширины, и соответственно такая ситуация и с высотой, тогда просто возвращаем исходную ширину и высоту:
if ( $this->width <= $width && $this->height <= $height ) {
return array( $this->width, $this->height );
}
Далее идут следующие условия:
if ( $this->width / $width > $this->height / $height ) {
$newSize[0] = $width;
$newSize[1] = round( $this->height * $width / $this->width );
}
Если исходная ширина разделенная на веденную пользователем ширину больше соответственно таким же условиям высоты, значит ширина остается неизменной, а высота будет равна округленному числу исходной высоты умноженной на ширину, которая делиться на исходную ширину.
else {
$newSize[0] = round( $this->width * $height / $this->height );
$newSize[1] = $height;
}
Если же наоборот, тогда вычисляем ширину, а высота остается прежней:

Для чего это все делается спросите Вы? Я по началу тоже задумался над этим. А все очень просто. Изменение размеров всегда будет происходить по одной стороне. То есть, если высота, допустим, 200 пикселей а ширина 500, соответственно изображение делиться по меньшей стороне, в нашем случае по высоте 200 пикселей. Сейчас нужно понять, что обрезка изображения всегда будет происходить по одной меньшей стороне. Делается это для сохранения пропорции изображения.

Функция возвращает массив, содержащий размеры нового изображения.
return $newSize;
Также напишу, что если Вам, допустим, на сайте нужно обрезать фотографию в квадрат, то это не обязательно нужно делать скриптом, можно воспользоваться каскадными таблицами стилей CSS.

Для того, чтобы более понять вычисления ширины и высоты, посмотрим на картинку:

Здесь исходное изображение имеет атрибуты 300 пикселей ширина и 250 высота. Второе изображение, которое будет как результат работы данного скрипта, будет с атрибутами 200 пикселей ширина и 100 высота. Поскольку обрезание изображения будет происходить по меньшей стороне, нам нужно узнать длину второй стороны. Еще со школы помню, что если X1 сопоставляется значение Y1, а неизвестном X2 - Y2, то неизвестно X2 = X1 * Y2 / Y1.

Именно эту задачу и лежит на плечах функции GetSizeByFramework, определение ширины и высоты изображения. Функция возвращает массив, содержащий размеры нового изображения.

Функция изменений размера изображения по ширине

Если указана только ширина изображения, тогда передаем управление частной функции GetSizeByWidth. Опишем данную функцию:
private function getSizeByWidth( $width )
{
if ( $width >= $this->width ) {
return array( $this->width, $this->height );
}
$newSize[0] = $width;
$newSize[1] = round( $this->height * $width / $this->width );
return $newSize;
}
Если Вы внимательно читали о функции GetSizeByFramework, тогда сразу поймете работу данной функции. Сначала проверяем, не превышает размер ширины введенной пользователем размер выходного ширины, если превышает, возвращаем исходную ширину. Далее просто вычисляем высоту изображения.

Следующая функция выполняет обрезку с координатами, шириной и высотой:
function crop( $x0 = 0, $y0 = 0, $w = false, $h = false ) {
if ( ! is_numeric( $x0 ) || $x0 < 0 || $x0 >= $this->width ) $x0 = 0;
if ( ! is_numeric( $y0 ) || $y0 < 0 || $y0 >= $this->height ) $y0 = 0;
if ( ! is_numeric( $w ) || $w <= 0 || $w > $this->width - $x0 ) $w = $this->width - $x0;
if ( ! is_numeric( $h ) || $h <= 0 || $h > $this->height - $y0 ) $h = $this->height - $y0;
return $this->cropSave( $x0, $y0, $w, $h );
}
Функция добавления водяного знака:
function watermark(  ) {
$stamp = imagecreatefromPng( 'watermark.png' );
$marge_right = 4;
$marge_bottom = 5;
$sx = imageSX( $stamp );
$sy = imageSY( $stamp );
imagealphablending( $stamp, true );
imagealphablending( $this->image, true );
imagecopy( $this->image, $stamp, $this->width - $sx - $marge_right, $this->height - $sy - $marge_bottom, 0, 0, $sx, $sy );
return $this;
}
Функция, сохраняющая обрезанное изображение:
private function cropSave( $x0, $y0, $w, $h ) {
$newImage = imagecreatetruecolor( $w, $h );
imagecopyresampled( $newImage, $this->image, 0, 0, $x0, $y0, $w, $h, $w, $h );
$this->image = $newImage;
$this->setSize( );
return $this;
}
Далее следует функция добавления водяного знака. Обязательно должен существовать само изображение водяного знака, которое будет называться watermark.png.
function watermark(  ) {
$stamp = imagecreatefromPng( 'watermark.png' );
$marge_right = 4;
$marge_bottom = 5;
$sx = imageSX( $stamp );
$sy = imageSY( $stamp );
imagealphablending( $stamp, true );
imagealphablending( $this->image, true );
imagecopy( $this->image, $stamp, $this->width - $sx - $marge_right, $this->height - $sy - $marge_bottom, 0, 0, $sx, $sy );
return $this;
}
Иногда бывает необходимо вывести изображения на экран, но при этом не сохраняя его. Для этого существует следующая функция:
function save_base64(  ) {
ob_start( );
imagejpeg( $this->image );
$outputBuffer = ob_get_clean( );
$base64 = 'data:image/'.$this->type.';base64,'.base64_encode( $outputBuffer );
imagedestroy( $this->image );
return $base64;
}
Функция сохранения изображения

Далее следует важнейшая функция, без которой все ранее описанное просто не имеет смысла. Это функция сохранения результата:
function save( $path = '', $fileName, $type = false, $quality = 100 )
{
if ( trim( $fileName ) == '' || $this->image === false || ! is_dir( $path ) ) return false;
$type = strtolower( $type );
switch( $type )
{
case false:
$savePath = $path.trim( $fileName ).'.'.$this->type;
switch( $this->type )
{
case 'jpg':
if ( ! is_numeric( $quality ) || $quality < 0 || $quality > 100 ) $quality = 100;
imagejpeg( $this->image, $savePath, $quality );
return $savePath;
case 'png':
imagepng( $this->image, $savePath );
return $savePath;
case 'gif':
imagegif( $this->image, $savePath );
return $savePath;
default:
return false;
}
break;
case 'jpg':
$savePath = $path.trim( $fileName ).'.'.$type;
if ( ! is_numeric( $quality ) || $quality < 0 || $quality > 100 ) $quality = 100;
imagejpeg( $this->image, $savePath, $quality );
return $savePath;
case 'png':
$savePath = $path.trim( $fileName ).'.'.$type;
imagepng( $this->image, $savePath );
return $savePath;
case 'gif':
$savePath = $path.trim( $fileName ).".".$type;
imagegif( $this->image, $savePath );
return $savePath;
default:
return false;
}
imagedestroy( $this->image );
}
Опишем аргументы данной функции:

$Path - путь к папке сохранения;

$FileName - имя нового изображения;

$Type - тип изображения;

$Quality - качество изображения.

Если данный аргумент $Type имеет значение False, тогда расширение изображения останется прежним. Если данный аргумент явно указан (jpg, png, gif), тогда исходное изображение сохраняется именно в данном формате.

Смело могу вас заверить, что 70 процентов качества весьма хороший результат для веб-страниц, как и по качеству картинки, так и по размеру.

Примеры работы класса ImageResize

Ну вот и подошел к концу класс для работы с изображениями.

Теперь осталось привести наиболее употребительные приемы использования и управления классом.

Допустим, мы имеем изображение Desert.jpg в корне сайта.

Чтобы получить уменьшенную копию изображения необходимо воспользоваться следующим выражением:
$img = new ImageResize( $_SERVER['DOCUMENT_ROOT'].'/Desert.jpg' );
$img->resize( 200 )->save( $_SERVER['DOCUMENT_ROOT'].'/', 'images', false, 70 );
Теперь в корне сайта будет создано изображения, с максимальной шириной 200 пикселей и качеством 70, название которого Images. Расширение изображения будет JPG, поскольку атрибут типа указанный False.

Немного изменим данное выражение:
$img->resize( 200, 100 )->save( $_SERVER['DOCUMENT_ROOT'].'/', 'images', 'PNG', 70 );
Теперь создастся новое изображение в формате PNG, с максимальной из сторон 200 и 100 пикселей соответственно.

Чтобы вывести изображения на экран, достаточно воспользоваться следующим выражением:
$preview_thumbs = $img->resize( 100 )->crop( 0, 0, 100, 100 )->save_base64(  );
echo '<img src="'.$preview_thumbs.'" />';
Вот и все, теперь Вы сможете обрезать до нужного вам размера любые изображения. Скрипт написан для работы с веб сайтами, а здесь особое внимание нужно уделить оптимизации изображений. Хорошо если их несколько, а если сотни, а тысячи? Тогда нужно заботиться о размерах файлов. Благодаря этому скрипту Вы можете быть уверены в оптимизации Ваших изображений на сайте.

Добавить комментарий

Имя:
Текст комментария: