Работа с изображениями через PHP GD: полное руководство
Основы работы с расширением GD
Расширение GD (GD Graphics Library) предоставляет набор функций для динамического создания и обработки изображений. Основные возможности включают: создание изображений в форматах GIF, JPEG, PNG, WebP; рисование линий, фигур, текста; изменение размеров и наложение эффектов. Для работы необходимо убедиться, что расширение gd включено в конфигурации PHP.
Базовый сценарий: создание пустого изображения, выбор цветов, рисование и сохранение.
<?php
// Создание изображения 200x100
$im = imagecreatetruecolor(200, 100);
// Выделение цветов
$white = imagecolorallocate($im, 255, 255, 255);
$red = imagecolorallocate($im, 255, 0, 0);
// Заливка фона
imagefill($im, 0, 0, $white);
// Рисование красной линии
imageline($im, 0, 0, 200, 100, $red);
// Сохранение в формате PNG
imagepng($im, 'example.png');
imagedestroy($im);
?>
Php gd extension (расширение gd для php)
После выполнения скрипта создаётся файл example.png с диагональной линией на белом фоне. Важно использовать imagedestroy() для освобождения памяти.
Типичные проблемы при первом запуске:
- Ошибка Fatal error: Call to undefined function imagecreatetruecolor() – расширение GD не установлено или не активировано. Решение: установить пакет php-gd (на Ubuntu: sudo apt install php-gd).
- Изображение не сохраняется или отображается битый файл – проверка прав на запись в папку.
- Белый экран или пустой вывод – отсутствует буферизация вывода, если скрипт отправляет изображение напрямую в браузер без заголовков.
Как изменить размер изображения, сохранив пропорции?
Функция imagescale() позволяет масштабировать изображение. Можно указать новый размер, а пропорции сохраняются по умолчанию, если задать только ширину или высоту.
<?php
$src = imagecreatefromjpeg('photo.jpg');
$width = imagesx($src);
$height = imagesy($src);
// Уменьшаем до ширины 300 пикселей, высота автоматически
$new = imagescale($src, 300);
imagejpeg($new, 'thumb.jpg', 85);
imagedestroy($src);
imagedestroy($new);
?>
Неправильный результат: если изображение повёрнуто или имеет нестандартное соотношение сторон, может потребоваться более точный контроль через imagecopyresampled().
Добавление водяного знака на изображение
Для наложения полупрозрачной PNG-картинки на другую используется imagecopy() или imagecopymerge().
<?php
$main = imagecreatefromjpeg('background.jpg');
$watermark = imagecreatefrompng('logo.png');
// Получение размеров
$mW = imagesx($main);
$mH = imagesy($main);
$wW = imagesx($watermark);
$wH = imagesy($watermark);
// Размещение водяного знака в правом нижнем углу с отступами
$dst_x = $mW - $wW - 10;
$dst_y = $mH - $wH - 10;
imagecopy($main, $watermark, $dst_x, $dst_y, 0, 0, $wW, $wH);
imagepng($main, 'watermarked.png');
imagedestroy($main);
imagedestroy($watermark);
?>
Водяной знак может быть непрозрачным, если PNG-файл не содержит альфа-канала. Для смешивания используйте imagecopymerge() с параметром прозрачности (от 0 до 100).
Создание миниатюр из загруженных пользователем файлов
Необходимо проверять тип файла, создавать копию и сохранять. Используется getimagesize() и imagecreatefromstring().
<?php
$allowed = ['image/jpeg', 'image/png', 'image/gif'];
$file = $_FILES['photo']['tmp_name'];
$info = getimagesize($file);
if (!in_array($info['mime'], $allowed)) {
die('Неподдерживаемый формат');
}
$src = imagecreatefromstring(file_get_contents($file));
list($width, $height) = $info;
// Целевой размер миниатюры
$new_w = 150;
$new_h = round($height * $new_w / $width);
$thumb = imagecreatetruecolor($new_w, $new_h);
imagecopyresampled($thumb, $src, 0, 0, 0, 0, $new_w, $new_h, $width, $height);
imagejpeg($thumb, 'thumb_'.basename($_FILES['photo']['name']), 80);
imagedestroy($src);
imagedestroy($thumb);
?>
При загрузке больших файлов может не хватить памяти. Рекомендуется увеличить memory_limit в php.ini или обрабатывать изображения по частям.
Работа с прозрачностью в PNG и GIF
Для сохранения прозрачности нужно вызвать imagesavealpha() и imagealphablending().
<?php
$im = imagecreatetruecolor(100, 100);
// Включение поддержки альфа-канала
imagealphablending($im, false);
imagesavealpha($im, true);
$transparent = imagecolorallocatealpha($im, 0, 0, 0, 127);
imagefill($im, 0, 0, $transparent);
// Рисование красного полупрозрачного круга
$red = imagecolorallocatealpha($im, 255, 0, 0, 40);
imagefilledellipse($im, 50, 50, 80, 80, $red);
imagepng($im, 'transparent_circle.png');
imagedestroy($im);
?>
Если imagesavealpha() не вызвано, прозрачные участки станут чёрными. Также при конвертации в JPEG прозрачность теряется.
Углублённые примеры использования GD
<?php
/**
* Пример 1. Рисование сглаженной кривой Безье
* Используется алгоритм де Кастельжо для построения точек.
*/
function drawBezier($im, $p0, $p1, $p2, $p3, $color) {
$points = [];
for ($t = 0; $t <= 1; $t += 0.001) {
$x = (1-$t)^3*$p0[0] + 3*(1-$t)^2*$t*$p1[0] + 3*(1-$t)*$t^2*$p2[0] + $t^3*$p3[0];
$y = (1-$t)^3*$p0[1] + 3*(1-$t)^2*$t*$p1[1] + 3*(1-$t)*$t^2*$p2[1] + $t^3*$p3[1];
$points[] = [(int)$x, (int)$y];
}
for ($i = 0; $i < count($points)-1; $i++) {
imageline($im, $points[$i][0], $points[$i][1], $points[$i+1][0], $points[$i+1][1], $color);
}
}
$im = imagecreatetruecolor(400, 300);
$white = imagecolorallocate($im, 255, 255, 255);
$blue = imagecolorallocate($im, 0, 69, 255);
imagefill($im, 0, 0, $white);
drawBezier($im, [50,250], [100,50], [300,50], [350,250], $blue);
imagepng($im, 'bezier.png');
imagedestroy($im);
?>
Результат: файл 'bezier.png' сглаженная синяя кривая на белом фоне.
<?php
/**
* Пример 2. Поворот текста и создание водяного знака с прозрачностью
*/
$im = imagecreatetruecolor(300, 100);
$white = imagecolorallocate($im, 255, 255, 255);
$grey = imagecolorallocatealpha($im, 128, 128, 128, 40);
imagefill($im, 0, 0, $white);
// Поворот текста с помощью imagettftext
$font = 'arial.ttf'; // убедитесь, что файл шрифта доступен
$angle = -20;
$text = 'Водяной знак';
$color = imagecolorallocatealpha($im, 0, 0, 0, 40);
$bbox = imagettftext($im, 24, $angle, 10, 70, $color, $font, $text);
imagepng($im, 'watermark_text.png');
imagedestroy($im);
?>
Результат: изображение с повёрнутым полупрозрачным текстом.
<?php
/**
* Пример 3. Создание гистограммы из данных
*/
function drawHistogram($values, $filename) {
$width = 600;
$height = 400;
$margin = 50;
$barWidth = ($width - 2*$margin) / count($values);
$maxVal = max($values);
$im = imagecreatetruecolor($width, $height);
$white = imagecolorallocate($im, 255, 255, 255);
$blue = imagecolorallocate($im, 0, 102, 204);
$black = imagecolorallocate($im, 0, 0, 0);
imagefill($im, 0, 0, $white);
// Ось Y с подписями
imageline($im, $margin, 10, $margin, $height - $margin, $black);
// Ось X
imageline($im, $margin, $height - $margin, $width - 10, $height - $margin, $black);
foreach ($values as $i => $v) {
$barH = ($v / $maxVal) * ($height - 2*$margin);
$x1 = $margin + $i * $barWidth;
$y1 = $height - $margin - $barH;
$x2 = $x1 + $barWidth - 2;
$y2 = $height - $margin;
imagefilledrectangle($im, $x1, $y1, $x2, $y2, $blue);
}
imagepng($im, $filename);
imagedestroy($im);
}
$data = [12, 45, 78, 34, 90, 23, 67, 81];
drawHistogram($data, 'histogram.png');
?>
Результат: файл 'histogram.png' с синими столбцами, отображающими значения.
<?php
/**
* Пример 4. Применение маски (изображение в форме круга)
*/
$im = imagecreatefromjpeg('portrait.jpg');
$width = imagesx($im);
$height = imagesy($im);
// Создаём маску в виде круга
$mask = imagecreatetruecolor($width, $height);
imagealphablending($mask, false);
imagesavealpha($mask, true);
$transparent = imagecolorallocatealpha($mask, 0, 0, 0, 127);
imagefill($mask, 0, 0, $transparent);
$white = imagecolorallocatealpha($mask, 255, 255, 255, 0);
imagefilledellipse($mask, $width/2, $height/2, min($width, $height), min($width, $height), $white);
// Копирование с маской через альфа-канал
$result = imagecreatetruecolor($width, $height);
imagealphablending($result, false);
imagesavealpha($result, true);
imagefill($result, 0, 0, $transparent);
for ($x = 0; $x < $width; $x++) {
for ($y = 0; $y < $height; $y++) {
$color = imagecolorat($im, $x, $y);
$alpha = (imagecolorsforindex($mask, imagecolorat($mask, $x, $y))['alpha'] == 0) ? 0 : 127;
$colorIndex = imagecolorallocatealpha($result, ($color >> 16) & 0xFF, ($color >> 8) & 0xFF, $color & 0xFF, $alpha);
imagesetpixel($result, $x, $y, $colorIndex);
}
}
imagepng($result, 'portrait_circle.png');
imagedestroy($im);
imagedestroy($mask);
imagedestroy($result);
?>
Результат: портретное изображение в форме круга с прозрачной областью вокруг.
<?php
/**
* Пример 5. Анимация GIF из нескольких кадров (используя собственный класс)
*/
class GifAnimator {
private $frames = [];
private $delays = [];
public function addFrame($im, $delay = 100) {
ob_start();
imagegif($im);
$this->frames[] = ob_get_clean();
$this->delays[] = $delay;
}
public function save($filename) {
// Минимальная реализация через слияние GIF-кадров (требуется библиотека или ручной парсинг)
// Для простоты используем функцию imagegif с множественным выводом (не поддерживается нативно)
// Реальное решение: использовать библиотеку GIFEncoder или Imagick
die('Требуется полноценная библиотека для создания GIF-анимации на PHP');
}
}
// Демонстрация (не будет выполнена, только шаблон)
$anim = new GifAnimator();
$im = imagecreatetruecolor(100, 100);
$red = imagecolorallocate($im, 255, 0, 0);
imagefill($im, 0, 0, $red);
$anim->addFrame($im, 50);
// ... добавить другие кадры
// $anim->save('animation.gif');
?>
Результат: анимационный GIF (в данном примере только структура, для реального выполнения потребуется расширение Imagick).