Графические возможности PHP
Денис Колисниченко
При написании сценариев на PHP очень часто возникает потребность создания различных рисунков "на лету". Данная возможность оказывается очень полезной при написании различных счетчиков, формировании графиков или же просто для создания красивых картинок.
Для того чтобы рядовой Web-программист почувствовал себя еще и художником, нужна библиотека GD. Если вы используете Linux, то, скорее всего, данная библиотека уже будет установлена и PHP будет собран с ее поддержкой. Если же это не так, установите библиотеку gd и пересоберите PHP с поддержкой библиотеки GD. Как это сделать, описано в документации по PHP.
Если вы используете PHP для Windows, вам понадобится всего один файл - php_gd.dll. Этот файл нужно скопировать в каталог %WINDIR%\SYSTEM (обычно это C:\WINDOWS\SYSTEM или C:\WINNT\SYSTEM). Затем добавьте в ваш конфигурационный файл php.ini следующую строку
extension=php_gd.dll
Скорее всего, данная строка уже будет добавлена в разделе Dynamic Extensions, вам нужно лишь убрать комментарий.
После установки библиотеки проверим корректность ее работы. Создайте небольшой сценарий:
Листинг 1. Сценарий test.php
1. <? 2. $image = ImageCreateFromPng("image.png"); 3. Header("Content-type: image/png"); 4. ImagePng($image); 5. ImageDestroy($image); 6. ?>
Использовать данный сценарий очень просто - в нужном месте вашего HTML-документа добавьте тег
<img src=test.php>
Перед запуском сценария поместите в каталог, в котором расположен сценарий, файл image.png. Сценарий test.php не будет изменять наш рисунок image.png, он просто скопирует его в броузер. Вместо использования данного сценария мы могли бы просто использовать тег
<img src=image.png>
Сценарий test.php может использовать только в демонстрационных целях - он демонстрирует несколько важных моментов при работе с рисунками: создание, вывод формата и сохранение рисунка.
В строке 2 мы создаем рисунок $image из файла image.png. Точнее надо было бы написать "загружаем", а не "создаем". Затем, в строке 3, мы сообщаем броузеру тип содержимого - рисунок формата PNG (image/png). В строке 4 с помощью функции ImagePng мы передаем рисунок на стандартный вывод, то есть в броузер. Функция ImageDestroy освобождает память, которая была выделена для рисунка.
Нужно более подробно рассмотреть процессы создания и сохранения рисунков. В качестве прототипа вашего рисунка вы можете использовать файлы других форматов, например, JPEG или GIF, а не только PNG. Для этого вместо функции ImageCreateFromPng нужно использовать функции ImageCreateFromJpeg() или ImageCreateFromGif().
Если вы хотите нарисовать картинку "с ноля", не используя заготовку, используйте функцию
ImageCreate(int $x, int $y);
После вызова функции ImageCreate() будет создана пустая картинка размера X x Y. Дальнейшая работа с рисунком возможна черех его идентификатор.
После загрузки изображения с диска производится его распаковка и в дальнейшем изображение хранится в собственном растровом неупакованном формате (подобном формату BMP), что обеспечивает высокую скорость выполнения различных операций. Неупакованный формат требует намного больше памяти, чем сжатый, поэтому после завершения работы с картинкой обязательно освободите память с помощью функции ImageDestroy().
Следует отметить, что ваша библиотека может не поддерживать работу со всеми тремя форматами - PNG, JPEG и GIF. Поддержка формата PNG будет всегда, а вот поддержки формата GIF может не оказаться.
После того, как рисунок уже сформирован, его нужно отправить в стандартный вывод - в броузер. Перед сохранением нужно указать (желательно правильно) формат рисунка. Это можно сделать с помощью функции Header:
Header("Content-type: image/png"); // если рисунок в формате PNG Header("Content-type: image/gif"); // если рисунок в формате GIF Header("Content-type: image/jpeg"); // если рисунок в формате JPEG
Почему я написал "желательно правильно"? Потому что некоторые броузеры сами определяют тип картинки. Например, вы отправляете заголовок, в котором сказано, что картинка в формате PNG, а сами сохраняете картинку в формате GIF. Брозуер по первым байтам самостоятельно определит тип рисунка и правильно отобразит его. Однако не нужно надеяться, что все броузеры такие умные - многие просто надеются на вашу честность и верят содержимому заголовка.
Если вы убедились, что формат JPEG или GIF поддерживаются вашей библиотекой GD, можно немного модифицировать наш сценарий test.php.
Листинг 2. Сценарий convert.php
1. <? 2. $image = ImageCreateFromPng("image.png"); 3. Header("Content-type: image/jpeg"); 4. ImageJpeg($image); 5. ImageDestroy($image); 6. ?>
Этот сценарий преобразует картинку из формата PNG в формат JPEG. Как я уже отмечал, после загрузки, изображение хранится в собственном растровом формате, а информация о первоначальном формате нигде не хранится. Поэтому вы спокойно можете открыть изображение в одном формате, а сохранить - в другом.
После загрузки изображения полезно было бы знать его размеры (ширину и высоту). При создании пустой картинки размер изображения нам уже известен, а вот при загрузке рисунка его точный размер нам неизвестен. Для определения горизонтального размера можно воспользоваться функцией
int ImageSX(int $image);
Аналогично, для определения вертикального размера используется функция
int ImageSY(int $image);
Вам уже надоело рассматривать различные форматы и хочется что-нибудь нарисовать? Не спешите! Перед тем, как нарисовать какой-нибудь примитив (линию, эллипс или дугу) нам нужно выбрать цвет и создать его. Выбрать - это означает решить, каким цветом вы будете рисовать, а создать - это означает задать тройку RGB-значений. Тройка RGB (Red, Gree, Blue)-значений - это три числа (от 0 до 255), которые определяют содержание красной, зеленой и синей составляющих в создаваемом цвете. Число 0 означает минимальную яркость, а 255 - максимальную. Например, тройка (0,0,0) задает черный цвет, а (255,255,255) - белый.
Вы не можете работать с цветами напрямую, например, выбрать примитив и указать цвет, каким он должен быть нарисован - red, yellow, blue, white как это возможно в других языках. Вам нужно создать новый цвет в палитре рисунка или выбрать уже существующий цвет из палитры. Работать с цветом можно только через его идентификатор, аналогично тому, как мы работаем с рисунком. Палитра - это набор цветов, которые использованы в рисунке. Некоторые форматы, например GIF, не поддерживают любое количество цветов в рисунке. Формат GIF поддерживает только 256 цветов. Причем, чем меньше количество цветов, тем лучше сжатие рисунка. При небольшом размере палитры рисунок очень хорошо сжимается, а при переходе через степень двойки, эффективность сжатия снижается. Если ваш рисунок хорошо сжимался при 32 цветах, то если добавить хотя еще один цвет, размер файла рисунка будет заметно увеличен. Создать новый цвет в палитре можно с помощью функции
int ImageColorAllocate(int $image, int $red, int $gree, int $blue);
Каждый вызов функции ImageColorAllocate() увеличивает число цветов на единицу (в палитру добавляется новый цвет). Что же произойдет, если достигнут предел - 256 цветов? Тогда функция ImageColorAllocate() попытается найти в палитре цвет, который, по ее мнению, ближе всех к запрашиваемому цвету. функция ImageColorAllocate() действует примерно так: если есть свободные места в палитре, создается новый цвет и добавляется в палитру, а если свободных мест нет, производится поиск похожего цвета.
Функция
int ImageColorClosest(int $image, int $red, int $green, int $blue)
возвращает похожий цвет из уже имеющихся в палитре. При этом новый цвет в палитру не добавляется. Если вы не хотите, чтобы ваша палитра разрослась, для выбора цвета следует использовать именно эту функцию, а функцию ImageColorAllocate() нужно использовать только для добавления в палитру нового цвета.
Я рекомендую в самом начале сценария определить все нужные цвета. При этом желательно, чтобы идентификаторы цветов несли смысловую нагрузку, например $white, $black, а не $c0, $c1.
Узнать текущее количество цветов в палитре можно с помощью функции ImageColorsTotal(int $image).
Очень часто нам нужно задать прозрачный цвет для рисунка. Прозрачный цвет не будет отображен в броузере и вместо них будет отображены части фона документа HTML. Определить прозрачный цвет можно с помощью функции:
int ImageColorTransparent(int $image, int $color);
После вызова этой функции цвет $color в изображении $image будет считаться прозрачным. Например, у нас есть изображение на красном фоне и нам нужно сделать красный цвет прозрачным. Это можно сделать вывозов таких операторов:
// Мы используем функцию ImageColorClosest, потому что красный цвет уже есть в палитре $red = ImageColorClosest($image,255,0,0); ImageColorTransparent($image,$red);
Помните, что не все форматы поддерживают прозрачность, например, JPEG прозрачность не поддерживает.
Вот теперь мы можем приступить к рисованию графических примитивов.
Простейшим примитивом является точка. Нарисовать ее можно с помощью функции
int ImageSetPixel(int $image, int $x, $y, int $color);
Эта функция нарисует одну точку цвета $color с координатами (x,y). Если вам нужно узнать цвет какой-нибудь точки изображения, используйте функцию
int ImageColorAt(int $image, int $x, int $y);
Нарисовать прямоугольник можно, вызвав функцию
int ImageFilledRectangle(int $image, int $x1, int $y1, int $x2, int $y2, int $color);
Данная функция нарисует закрашенный прямоугольник в зображении $image. Размеры прямоугольника заданый параметрами ($x1,$y1) , ($x2,$y2). Первая пара определяет координаты верхнего левого угла, в вторая - правого нижнего. Цвет, которым будет закрышен прямоугольник, задается идентификатором $color.
Нарисовать линию можно функциями
int ImageLine(int $image, int $x1, int $y1, int $x2, int $y2, int $color); int ImageDashedLine(int $image, int $x1, int $y1, int $x2, int $y2, int $color);
Первая функция рисует обыкновенную линию, а вторая - пунктирную. Линия проходит через точки (x1,y1) и (x2,y2). Цвет, которым будет нарисована линия, определяется идентификатором $color.
Для изображения дуги сектора используйте функцию:
int ImageArc(int $image, int $x, int $y, int $width, int $height, int $s_a, int $e_a, int $col);
Функция ImageArc рисует дугу сектора эллипса от угла $s_a (start angle) до угла $e_a (end angle). Сам эллипс должен вписаться в прямоугольник, заданный параметрами $x, $y - координаты верхнего левого угла, $width - ширина, $height - высота. Фигура не закрашивается, а цветом $color обводится ее контур.
Закрасить произвольную одноцветную область, которая содержит точку ($x,$y), цветом $color можно функцией
int ImageFill(int $image, int $x, int $y, int $color);
Данная функция произведет сплошную заливку одноцветной области. Если вам нужно закрасить не только одноцветные точки, а любые, используйте функцию
int imageFillToBorder(int $image, int $x, $y, int $border, int $color);
Эта функция будет закрашивать все точки, пока не будет достигнута граница цвета $border.
С помощью функции
int ImagePolygon(int $image, list $dots, int $num, int $color);
Функция ImagePolygon() рисует многоугольник, заданый своими вершинами. Количество вершин задается параметром $num. Координаты вершин передаются в массиве $dots так:
$dots[0]=x0; $dots[1]=y0; $dots[2]=x1; $dots[3]=y1; и т.д.
Если вам нужно нарисовать закрашенный многоугольник, используйте функцию ImageFilledPolygon, которая имеет те же параметры.
При написании сценария-счетчика (да и других сценариев) нам нужно вывести какую-нибудь строку в изображение. Это можно сделать, используя функцию
int ImageString(int $image, int $font, int $x, int $y, string $s, int $color);
После вызова этой функции в изображение $image будет выведена строка $s. Будут использованы шрифт $font и цвет $color. Координаты верхнего левого угла задаются параметрами $x и $y.
Вы можете использовать один из встроенных шрифтов - их номера 1..5 или загрузить свой шрифт с помощью функции ImageLoadFont(string $file). В последнем случае номер шрифта будет больше 5.
Вывести строку в вертикальном направлении можно функцией ImageStringUp, которая обладает такими же параметрами.
Нам осталось рассмотреть очень мощную функцию, которая "умеет" копировать участки изображения и целые изображения, масштабировать участки изображения, копировать и масштабировать участки изображения в пределах одного рисунка.
int ImageCopyResized(int $dest, int $src, int $destX, int $destY, int $srcX, int $srcY, int $destW, int $destH, int $srcW, int $srcH);
Выглядит просто ужасающе. Однако все намного проще, когда разберешься что здесь и к чему. Параметры $dest и $src задают рисунок-результат и рисунок-источник. Эти параметры могут совпадать.
Параметры $srcX, $srcY, $srcW, $srcH определяют область исходного изображения, над которой будет произведена операция.
Параметры $destX, $destY, $destW, $destH задают область изображения-результата, в которую будет "вписан" прямоугольник, определенный предыдущими параметрами. Если ширина и высота прямоугольников не совпадают, то изображение-результат будет сжато или растянуто соответственно параметрам.
На этом я завершу этот обзор графических функций PHP. Следует заметить, что в статье рассмотрены далеко не все функции, но и их будет вполне достаточно для нашей задачи. Нам же нужно всего лишь построить какой-нибудь график или вывести информацию о посещениях нашего узла, а для создания более сложной графики разработано множество специальных пакетов - тот же GIMP. Все ваши вопросы, замечания и пожелания можете отправить по адресу dhsilabs@mail.ru.