base23 12

Создание графики

Для того чтобы определить области применения средств создания графики в сценариях для веб, рассмотрим приведенный ниже ряд возможных вариантов использования графики:

  • На HTML-страницу могут быть внедрены статические изображения, созданные самостоятельно или заимствованные где-либо.

  • Могут быть использованы формируемые программным путем изображения (HTML + CSS).

  • Можно использовать библиотеку gd, чтобы заранее создавать статические графические изображения для всех возможных ситуации, возникающих в ходе выполнения сценария, сохранять их в файлах и отображать по условию.

Вариант внедрения графики с использованием статических изображений мы рассматривать не будем ввиду его простоты, поэтому сначала рассмотрим простой вариант (графика HTML + CSS), а затем рассмотрим использование библиотеки gd в PHP.

Графика HTML + CSS

Посетителям веб-сайтов хорошо знакомы горизонтальные гистограммы с окрашенными сторонами, которые особенно часто используются для иллюстрации результатов опросов. На первый взгляд складывается впечатление, будто для создания таких диаграмм применяются какие-то графические средства, но в действительности для этого достаточно использовать несколько простых сценариев:

Код PHP
<?php
    // Простая обработка формы
    if (isset($_POST['orange'], $_POST['apple'], $_POST['banana'], $_POST['tomat'], $_POST['cucumber'], $_POST['potato']))
	{
		try {
			foreach ($_POST as $value)
			{
				if (is_numeric($value)) {
					$value = intval($value)/100;
				}
				else {
					throw new Exception('Вы ввели некорректные данные');
				}
			}
			extract($_POST);
			$e = 1;
		}
		catch (Exception $ex){
			echo '<b style="color:red">'.$ex->getMessage().'</b>';
		}
	}
            
?>
<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<title>Основы PHP</title>
<style>
   /* CSS-стили, формирующие оформление */
   body div { height:1em; display:inline-block; vertical-align:middle }
   span { display:inline-block; width:120px }
   .orange { background:orange }
   .apple { background:#33CC66 }
   .banana { background:yellow }
   .tomat { background:red }
   .cucumber { background:green }
   .potato {background:gray }
   
</style>
</head>
<body>
<h1>Форма заказа</h1>
<form method="post">
    <fieldset>
    <legend>Фрукты</legend>
        <label>Апельсины</label>
        <input name="orange" type="text" maxlength="2" placeholder="кол-во">
        <label>Яблоки</label>
        <input name="apple" type="text" maxlength="2" placeholder="кол-во">
        <label>Бананы</label>
        <input name="banana" type="text" maxlength="2" placeholder="кол-во">
   </fieldset>    
   <fieldset>
    <legend>Овощи</legend>
        <label>Помидоры</label>
        <input name="tomat" type="text" maxlength="2" placeholder="кол-во">
        <label>Огурцы</label>
        <input name="cucumber" type="text" maxlength="2" placeholder="кол-во">
        <label>Картошка</label>
        <input name="potato" type="text" maxlength="2" placeholder="кол-во">
   </fieldset>  
    <input type="submit" value="Заказать">
</form>
<?php
// Вывести результаты заказа
if (isset($e))
echo <<<EOT
   <h2>Вы заказали: </h2>
   <span>Апельсины: </span><div class="orange" style="width:{$orange}%"></div><br>
   <span>Яблоки: </span><div class="apple" style="width:{$apple}%"></div><br>
   <span>Бананы: </span><div class="banana" style="width:{$banana}%"></div><br>
   <span>Помидоры: </span><div class="tomat" style="width:{$tomat}%"></div><br>
   <span>Огурцы: </span><div class="cucumber" style="width:{$cucumber}%"></div><br>
   <span>Картошка: </span><div class="potato" style="width:{$potato}%"></div><br>
EOT;
?>
</body>
</html>

Данный пример не демонстрирует каких-то новых возможностей PHP, а просто показывает простой способ создания графики с помощью HTML и CSS (в данном случае гистограмм):

Обработка формы и вывод графики с использованием HTML и CSS

Библиотека gd

Графические средства, описанные в предыдущем разделе, практически исчерпывают возможности стандартного кода HTML (правда стоит отметить, что мы не рассмотрели достаточно мощное средство создания графики HTML5 Canvas). Теперь перейдем к описанию методов создания действительно произвольной графики с использованием библиотеки gd.

Общее описание библиотеки gd

Вообще говоря, комплект инструментальных средств gd представляет собой библиотеку кода на языке C, предназначенную для создания изображений и манипулирования ими. Эта библиотека была первоначально разработана и предоставлена во всеобщее пользование талантливыми и великодушными сотрудниками компании Boutell.com.

Библиотека gd сама по себе не является графической программой или программой рисования, а также не представляет собой автономное приложение или графический интерфейс пользователя. Вместо этого библиотека gd предоставляет функции, которые могут быть вызваны на выполнение в любой программе для осуществления требуемых манипуляций с изображениями. Это означает, что с библиотекой gd можно связать любую программу на языке C, в которой необходимо использовать код данной библиотеки. Именно такую задачу решили разработчики системы PHP.

В действительности для этой цели был написан набор интерфейсных функций, который позволяет легко вызывать процедуры gd из сценария на языке PHP. Но в самой библиотеке gd нет какого-либо кода, относящегося только к PHP, и разработаны интерфейсы, позволяющие обращаться к этой библиотеке из нескольких других языков программирования и вариантов программной среды, включая Perl, Pascal, Haskell и REXX.

Библиотека gd позволяет вызывать функции для создания исходных изображений (первоначально пустых, напоминающих чистый лист бумаги), чертить и рисовать внутри этих исходных изображений с применением различных способов и в конечном итоге преобразовывать изображение из внутреннего формата изображения gd в стандартный формат изображения, а затем отправлять в окончательное место назначение (выводить в окно браузера либо сохранять в файле или в базе данных). А поскольку все эти операции выполняются под управлением программы, а не осуществляются вручную, то созданные изображения могут становиться сколь угодно сложными и зависеть от любых ситуаций в ходе выполнения программы, от которых желательно сделать их зависимыми.

Форматы изображений

Библиотека gd в принципе позволяет импортировать и экспортировать изображения с использованием широкой разновидности форматов. Наиболее популярными форматами изображений являются GIF, JPEG и PNG, хотя в основном в рассматриваемых примерах применяется последний из этих форматов.

Форматы GIF и PNG предназначены для описания сетки из цветных элементов, соответствующих пикселям, с учетом некоторых дополнений. Первое дополнение состоит в том, что ячейки могут содержать действительные номера цветов или индексы в таблице с номерами цветов. (Первый вариант позволяет создавать более выразительные изображения, поскольку дает возможность предусматривать любое количество различных цветов, а последний вариант способствует созданию более компактных изображений.)

Второе дополнение заключается в следующем. Безусловно, концептуальное представление форматов GIF и PNG является довольно простым, но на практике чтение, запись и передача изображений в этих форматах всегда осуществляется в сжатой форме. Сжатие необходимо, поскольку для хранения данных, представленных в виде сетки ячеек, требуется большой объем памяти. Простое изображение, состоящее из 500x400 пикселей, включает 200 000 пикселей, причем если для задания каждого пикселя требуются три байта, то объем необходимой памяти уже превышает половину мегабайта.

Тема сжатия является обширной и непростой, но большинство алгоритмов сжатия основано на сокращении избыточности изображения в целях уменьшения его объема. (Например, потребуется меньше места в памяти, если будет указано, что каждый пиксель изображения имеет зеленый цвет, чем задавать значение зеленого цвета отдельно для каждого пикселя.) К сожалению, работа алгоритмов сжатия не сводится к использованию указанных свойств, поскольку при этом приходится решать такие сложные задачи, что способы решения этих задач, реализованные в алгоритме сжатия, применяемого для формирования изображений в формате GIF, были даже запатентованы.

Установка библиотеки

Откровенно говоря, задача инсталляции библиотеки gd и обеспечения ее успешной работы в сочетании с системой PHP является довольно сложной. Это обусловлено не какими-либо недостатками программного обеспечения PHP или gd, а полностью определяется проблемами настройки конфигурации; в частности, необходимо разобраться в том, где должны находиться и фактически находятся библиотеки gd, а также проследить за тем, чтобы этапы компиляции и связывания всех исполняемых файлов были выполнены должным образом. Поэтому наилучшей из всех возможных является такая ситуация, когда пользователь обнаруживает, что библиотека gd уже установлена, а в системе PHP уже разрешена поддержка gd (либо благодаря любезности компании, предоставляющей веб-хостинг, либо в связи с тем, что эта библиотека включена в состав инсталлированной системы PHP).

Поэтому инсталляция библиотеки gd должна начинаться с нулевого этапа — с проверки того, не была ли эта библиотека уже установлена. Прежде всего введите в файл сценария приведенный ниже код и рассмотрите результаты его выполнения в браузере, независимо от того, эксплуатируются ли ваши сценарии при поддержке компании веб-хостинга или для этого полностью применяется ваша собственная инсталляция:

Код PHP
<?php
   phpinfo();     
?>

После вывода этой страницы на экран достаточно выполнить поиск текстовой строки "gd" в окне браузера. В результате должен быть обнаружен подраздел с описанием того, в какой степени в используемой инсталляции PHP разрешена поддержка библиотеки gd. Если вы намереваетесь лишь подготавливать изображения некоторых типов (например, в формате PNG), а результаты выполнения функции phpinfo() говорят о том, что поддержка изображения этого типа разрешена, то можете сразу же приступать к работе. А если данные о версии gd включают слово "bundled", то применяется библиотека gd, которая входит в комплект поставки PHP.

Проверка того подключена ли библиотека gd или нет

Если же попытка найти упоминание о библиотеке gd оканчивается неудачей, а инсталляцией PHP занимаетесь вы сами, то можете выполнить инсталляцию и настройку конфигурации библиотеки gd. (С другой стороны, если инсталляцией PHP занимается компания, предоставляющая вам услуги хостинга, то вам остается лишь попросить, чтобы эта компания обеспечила поддержку библиотеки gd, или прибегнуть к услугам другой компании веб-хостинга.)

Использование библиотеки gd, входящий в комплект поставки PHP, позволяет избавиться от многих затруднений, связанных с инсталляцией gd, но не от всех. Дело в том, что применение самой версии, входящей в комплект поставки, позволяет получить библиотеку gd, но не обязательно все библиотеки, необходимые для работы gd. Библиотека gd сама зависит от нескольких других библиотек: libpng (служит для манипулирования изображениями PNG), zlib (используется при сжатии) и jpeg-6b или более поздней версии (предназначена для манипулирования изображениями JPEG, если в этом есть необходимость). Указанные библиотеки уже присутствуют во многих инсталляциях Linux, и в таком случае может оказаться достаточным включить в состав параметров требуемый параметр with (такой как --with-zlib), не указывая инсталляционный каталог. Если настройку конфигурации PHP выполняете вы сами, то достаточно добавить параметр --with-gd, чтобы обеспечить включение в исполняемый файл версии gd, входящей в комплект поставки. А если вы хотите указать на другую версию, то воспользуйтесь вместо этого параметром --with-gd=path.

Если вы обнаружите отсутствие одной или нескольких необходимых библиотек, это будет означать, что эти библиотеки придется сформировать отдельно. Для ознакомления с информацией о том, где найти текущие версии, можно начать с изучения документации, находящейся по адресу www.libgd.org.

Основные принципы работы с библиотекой gd

При создании изображения или манипулирования им с помощью инструментария gd это изображение представлено в специальном формате gd, не имеющим соответствия ни с одним обычным типом изображения. Теоретически возможен экспорт изображений в этом формате gd, но подобная операция применяется редко, поскольку результирующее изображение не является сжатым и не может быть отображено в браузере или в простой графической программе.

Изображение, обрабатываемое с помощью инструментария gd, характеризуется информацией о ширине, высоте и цвете всех пикселей, количество которых равно произведению ширины на высоту. Обычно программа начинает свое взаимодействие с библиотекой gd, либо создавая новое пустое изображение (на котором будет осуществляться черчение и рисование), либо импортируя изображение из файла. На следующих этапах обычно выполняются такие действия: во-первых, распределение цветов в изображении, во-вторых, черчение и рисование или манипулирование изображением с помощью каких-то других операций, в-третьих, преобразование изображения в общепринятый формат (например, PNG или JPEG) и передача его по месту назначения.

Представление цветов

Для представления цветов в изображениях gd применяются два способа: представление на основе палитры, которое ограничивается 256 цветами, и представление с реалистичными цветами, позволяющее задавать произвольное количество различных номеров цветов RBG. В версии gd 1.x единственная возможность состояла в использовании цветов на основе палитры, а в библиотеке gd 2.x и в версии этой библиотеки, входящей в комплект поставки PHP, поддерживается возможность создавать и изображения на основе палитры, и изображения в реалистичных цветах. Необходимо учитывать, что каждое конкретное изображение gd должно быть либо основанном на палитре, либо иметь реалистичные цвета (RGB); это означает, что возможность ввести реалистичные цвета в изображения на основе палитры отсутствует.

Чтобы получить исходное пустое изображение на основе палитры, необходимо вызвать функцию ImageCreate(), а дня получения изображения в реалистичных цветах используется функция ImageCreateTrueColor().

Изображения на основе палитры

Цвета задаются в формате "красный-зеленый-синий" (Red-Green-Blue — RGB), с помощью трех чисел от 0 до 255. Например, цвет, заданный числами (255, 0, 0) является ярко-красным, цвет (0, 255, 0) — зеленый, цвет (0, 0, 255) — синий, цвет (0, 0, 0) — черный, цвет (255, 255, 255) — белый и цвет (127, 127, 127) — серый. Создавая все новые и новые цвета, можно выбирать значения трех цветовых компонентов произвольно.

Любое рисование на изображении должно осуществляться с помощью какого-то конкретного цвета, а цвета должны быть распределены в изображении еще до их использования. Кроме того, первый цвет, распределенный в изображении, автоматически становится цветом фона. Итак, ни в коем случае нельзя считать, что можно обойтись без задания цветов, и обычно операция распределения цветов становится первой операцией после создания нового пустого изображения.

Цвета в изображениях, основанных на палитре, создаются с помощью функции imagecolorallocate(), которая принимает в качестве параметров изображение (созданное ранее) и три целых числа, задающих долю красного, зеленого и синего цветов. Возвращаемым значением является целое число, которое задает индекс нового цвета во внутренней палитре изображения. Это возвращаемое значение необходимо присвоить переменной, поскольку указанное значение индекса необходимо для выполнения всех будущих операций с помощью данного цвета.

Изображения на основе палитры могут иметь максимальное количество цветов, равное 256. (Читателя может интересовать или не интересовать, на чем фактически основаны такие изображения, но каждый пиксель в изображении на основе палитры в действительности представляет собой единственный байт, который хранит индекс одного элемента в палитре из 256 цветов.)

Обратите внимание на то, что индекс, возвращаемый при распределении одного из цветов в некотором изображении, имеет смысл только для этого изображения. Например, если в сценарии PHP переменной $black присваивается цвет, распределенный в одном изображении, то нет смысла использовать эту переменную в качестве входных данных о цвете для команды рисования, вызываемой для обработки другого изображения.

Изображения с реалистичными цветами

В библиотеке gd 2.0 и в более поздних версиях предусмотрена также возможность создавать изображения, не основанные на палитре, в которых каждый пиксель хранит произвольный номер цвета RGB. В этом так называемом формате с реалистичными цветами количество возможных цветов чрезвычайно велико. Указанная возможность не только беспредельно расширяет рамки художественного самовыражения, но и позволяет достоверно воспроизводить изображения PNG и JPEG с реалистичными цветами, загруженные в память с помощью инструментария gd.

Если не считать того, что для создания первоначального изображения применяется другая функция и отсутствуют ограничения по выбору различных цветов, принципы работы с изображениями с реалистичными цветами аналогичны таковым для изображений на основе палитры.

В частности, для создания новых цветов по-прежнему можно вызывать функцию ImageColorAllocate() и присваивать переменной возвращаемое значение для последующего использования в командах рисования. Единственное отличие состоит в том, что возвращаемым значением является номер цвета RGB, а не индекс элемента в палитре. Кроме того, в изображениях с реалистичными цветами не существует понятия цвета фона, создаваемого в качестве побочного эффекта применения функции ImageColorAllocate(); всем пикселям в результате инициализации присваивается обозначение черного цвета (0, 0, 0).

Прозрачность

В версиях gd 2.x поддерживается понятие прозрачности. Для этого используется альфа-канал (задаваемый дополнительно к значениям красного, зеленого и синего цветов), который указывает, насколько прозрачным является данный цвет. Это позволяет, например, накладывать одну форму на другую таким образом, чтобы первая форма оставалась частично видна, а не перекрывалась полностью второй.

В языке PHP многие функции для работы с изображениями имеют аналог, содержащий в имени слово "alpha", которое указывает, что в этих функциях цвет представлен четырьмя значениями (R,G,B,A). Например, функция imageColorAllocate() принимает три параметра, а при вызове функции ImageColorAllocateAlpha() необходимо задать четвертый параметр со значением от 0 до 127. Значение, равное нулю, указывает, что цвет полностью непрозрачен, а значение 127 говорит о том, что цвет полностью прозрачен.

Координаты и команды рисования

После создания изображения с помощью инструментария gd неявно образуется система координат, позволяющая задавать в ее рамках команды рисования. Предельные значения координат в этой системе определяются заданными параметрами ширины и высоты изображения.

Начало координат в данной системе, соответствующее координатам (0, 0), находится в верхнем левом углу изображения. Положительным направлением для значений X является направление слева направо, а для значений Y — сверху вниз. (В системах координат компьютерной графики такое местонахождение начала координат встречается часто, но те, кто изучал аналитическую геометрию, по-видимому, привыкли к тому, что начало координат находится в левом нижнем углу диаграммы.)

Количество команд рисования очень велико. Эти команды включают, но не ограничиваются командами черчения отрезков линии, прямоугольников и дуг, а также командами задания значений конкретных пикселей. Но следует учитывать, что конечным результатом всех этих команд рисования и черчения становится задание значений пикселей. После выполнения команд, изменяющих значения пикселей, в памяти не остается каких-либо следов (кроме самих изменившихся значений), поэтому отсутствует способ, позволяющий отменять действие команд рисования и черчения или раздельно представлять результаты различных команд.

Ничто не препятствует применению команд рисования, действие которых выходит за пределы указанного изображения, но такое рисование не оказывает видимого эффекта. Например, на изображении не будет виден прямоугольник, все значения координат которого являются отрицательными.

Преобразование форматов

Все операции рисования и манипулированием изображением выполняются над изображением, представленным во внутреннем формате gd. А после завершения этих операций в сценарии может быть вызвана одна из команд преобразования и вывода (imagepng, imagetjpeg и т.д.) для преобразования этого изображения в требуемый графический формат и вывода его в окно браузера пользователя (или в файл).

Освобождение ресурсов

После того как результат преобразования законченного изображения gd передан пользователю, можно считать, что работа с внутренней версией закончена. Это означает, что данная версия должна быть уничтожена. Правильный способ выполнения подобной операции состоит в вызове функции imagedestroy() с указанием изображения в качестве параметра.

Функции библиотеки gd

Мы не собираемся отдельно перечислять и описывать в данной статье все функции, предусмотренные в интерфейсе gd интерпретатора PHP. Для ознакомления с этой информацией рекомендуем воспользоваться разделом "Обработка и генерация изображений" руководства php.net. Большинство из функции gd относятся к одной из категорий, приведенных в таблице ниже. Обратите внимание на то, что в именах функций, перечисленных в этой таблице, для удобства чтения первая буква каждого слова представлена в верхнем регистре, но это условие в примерах кода не всегда соблюдается, поскольку имена функций PHP не чувствительны к регистру:

Классификация функций gd
Тип Пример Примечание
Функции создания изображения ImageCreate(), ImageCreateTruecolor(), ImageCreateFromGd(), ImageCreateFromJpeg() Возвращают новое изображение gd. Функция ImageCreate() принимает в качестве параметров значения ширины и высоты изображения, а параметрами других функций являются путь к файлу, URL или строка, содержащая ранее созданное изображение, которое должно быть загружено и преобразовано в формат gd
Функции, выполняющие операции распределения цветов ImageColorAllocate(), ImageColorAllocateAlpha(), ImageColorDeallocate() Функция ImageColorAllocate() принимает в качестве параметров дескриптор изображения и требуемые значения красного, зеленого и синего цветов, после чего возвращает номер цвета, предназначенный в дальнейшем для использования в операциях рисования и черчения. Функция ImageColorAllocateAlpha принимает дополнительный параметр—коэффициент прозрачности (0-127)
Функции, выполняющие операции согласования цветов ImageColorClosest(), ImageColorClosestAlpha(), ImageColorExact(), ImageColorExactAlpha() Возвращают индекс согласующегося цвета в изображении с палитрой. Функции, содержащие в своем имени слово "Closest", возвращают наиболее точно согласующийся цвет (точность согласования измеряется как расстояние между точками в пространстве значений RGB); функции с обозначением "Exact" возвращают номер цвета, только если он идентичен искомому, в противном случае возвращают значение -1, функции с именем "Alpha" оперируют цветами, для определения которых применяются четыре значения (с прозрачными цветами)
Функции вычерчивания линий ImageLine(), ImageDashedLine(), ImageRectangle(), ImagePolygon(), ImageEllipse(), ImageArc() Применяются для вычерчивания отрезков прямых или кривых указанной формы. Обычно первым параметром каждой из этих функций является изображение, последним параметром — цвет, промежуточными параметрами — координаты X и Y
Функции установки пера для вычерчивания линий ImageSetStyle(), ImageSetThickness() Изменяют параметры настройки, которые влияют на характеристики линий, создаваемых последующими командами вычерчивания линий (некоторые из этих функций применимы только в версии gd 2.0.1 или более поздних версиях)
Функции рисования и заливки ImageFilledRectangle(), ImageFilledEllipse(), ImageFilledPolygon(), ImageFilledArc(), ImageFill() Как правило, аналогичны соответствующим функциям вычерчивания линий, но предусматривают не только вычерчивание контуров областей, а заполнение создаваемых областей цветом. Специальная функция ImageFill() выполняет операцию заполнения заливкой, для который применяется указанный цвет заливки. Заливка осуществляется во всех направлениях, начиная с указанной точки с координатами XY (для выполнения некоторых из этих функций требуется версия gd 2.0.1 или более поздняя)
Функции для работы с текстом ImageString(), ImageLoadFont() Функция ImageString принимает в качестве параметров дескриптор изображения, номер шрифта, координаты X и Y, строку текста и цвет. Если номер шрифта находится в пределах между 1 и 5, то для вывода строки в данном цвете используется один из пяти встроенных шрифтов. С другой стороны, номер шрифта больше 5 указывает на результат загрузки специализированного шрифта с помощью функции ImageLoadFont()
Функции экспорта ImagePng(), ImageJpeg() Преобразовывают внутреннее изображение gd в изображение соответствующего формата, а затем отправляют это изображение в выходной поток. Если задан только один параметр (дескриптор изображения), то выполняется эхо-повтор изображения для пользователя, а при использовании дополнительного параметра, представляющего собой путь к файлу, адресом назначения выходного потока становится файл
Функция уничтожения изображения ImageDestroy() Принимает в качестве параметра дескриптор изображения и освобождает все ресурсы, связанные с изображением

Поддержка средств передачи изображений в протоколе HTTP

Для того чтобы изображение могло быть представлено должным образом в браузере пользователя, необходимо указать, откуда должно поступить изображение и каков его формат. Поэтому, к сожалению, невозможно просто внедрить, например, вызов функции imageToPng() в создаваемый файл HTML и тем самым решить задачу вывода изображения. По сути, приходится чередовать код вывода изображений с кодом HTML, формируемым в сценарии PHP, и для этого можно воспользоваться одним из трех описанных ниже вариантов.

Создание полностраничных изображений

В виде изображения может быть оформлена вся формируемая страница. В этом случае необходимо передать заголовок HTTP, предшествующий данным изображения, и тем самым объявить, что дальше следует изображение определенного типа. Например, в конце сценария могут быть предусмотрены такие строки:

Код PHP
// ... код, в котором создается изображение и присваивается 
// переменной $image
header("Content-type: image/png"); // Вывести заголовок в браузер 
imagepng($image); // Передать данные самого изображения, преобразованного в формат PNG 
imagedestroy($image); // Освободить ресурсы

Преимущество данного подхода состоит в том, что для передачи указаний о составе будущего изображения можно применять любую информацию, включая параметры POST. А его недостаток заключается в том, что результирующая страница не может содержать какого-либо обычного кода HTML. В действительности приходится даже следить за тем, чтобы перед заголовком и изображением в сценариях не передавался какой-либо текстовый вывод, поскольку такая ситуация равносильна преждевременной отправке информационного наполнения. В этом случае появляется сообщение об ошибке "Headers already sent...".

Внедрение изображений, хранящихся в файлах

Прежде всего отметим, что в языке HTML поддерживается дескриптор <img>, который позволяет внедрить изображение, указывая путь к файлу изображения или URL, как в следующем примере:

HTML
<img src="my_png.png">

Данная конструкция применяется к статическим файлам изображения, но нет причин, по которым нельзя было бы обеспечить с ее помощью внедрение вновь созданного изображения. Поэтому можно подготовить такой сценарий, в котором вначале создается изображение, затем данные изображения записываются в локальный файл, после чего вырабатывается код HTML с соответствующим дескриптором <img>, указывающим на только что созданный файл.

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

Но в определенной ситуации данный подход является идеальным. Речь идет о создании и кэшировании изображений, представляющих конечное множество вариантов выбора. В этом случае предусматривается определенный способ связывания той или иной ситуации с именем файла изображения. При возникновении некоторой из подобных ситуаций, требующей вывода изображения, проверяется, имеется ли уже соответствующий файл. В случае положительного ответа просто применяется ссылка на этот файл, оформленная в виде дескриптора <img>, а если ответ отрицателен, то создается изображение, записывается в файл, а затем опять-таки применяется ссылка на это изображение. В конечном итоге формирование новых файлов просто не потребуется.

Внедрение изображений, формируемых в сценариях

Наконец, нет таких причин, по которым нельзя было бы предусмотреть автономный сценарий формирования изображения, а затем внедрять полученное изображение в динамическую страницу, создаваемую с помощью другого сценария, задавая в дескрипторе <img> URL этого сценария. При этом единственная сложность состоит в выборе способа передачи необходимых данных на зависимую страницу. Например, дескриптор с указанием внедренного изображения может иметь такой вид:

HTML
<img src="ballpage.php?ball_color=green&ball_position=5">

В этом случае сценарий ballpage.php возвращает изображения PNG цветных шаров, находящихся в различных позициях рисунка.

Но при использовании указанного подхода могут возникать нарушения в работе, поскольку веб-серверы и браузеры иногда проверяют суффиксы обрабатываемых файлов и, получив результаты проверки, реагируют на них по-разному. Например, сценарию формирования изображения ballpage может потребоваться присвоить расширение .php для того, чтобы сервер Apache мог определить, что данный серверный код должен интерпретироваться как код PHP (хотя необходимый режим обработки может быть задан и с помощью файлов конфигурации).

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

Ниже рассматриваются примеры применения библиотеки gd для создания изображений.

Пример использования библиотеки gd: создание простых фигур

В следующем примере мы покажем как с использованием библиотеки gd сгенерировать рисунок содержащий простые геометрические фигуры:

Код PHP
<?php
// Создаем холст 800x800 px
$image = imagecreate(800, 800) or die('Невозможно создать рисунок');

// Цвет фона
$background = imagecolorallocate($image, 223, 243, 255);

// Добавить линию
$lineColor = imagecolorallocate($image, 111, 258, 88);  // Цвет линии
imagesetthickness($image, 16);  // Толщина линии
imageline($image, 20, 25, 350, 45, $lineColor);

// Рисуем окружность
$circleColor = imagecolorallocate($image, 111, 251, 88);
$circleBgColor = imagecolorallocate($image, 254, 92, 21);
imagearc($image, 200, 210, 200, 200,  0, 360, $circleColor);

// Прямоугольник
$rectBackColor = imagecolorallocate($image, 254, 174, 21);
$rectBorderColor = imagecolorallocate($image, 251, 21, 254);
imagefilledrectangle($image, 285, 100, 530, 200, $rectBackColor);  // Фон
imagesetthickness($image, 5);
imagerectangle($image, 285, 100, 530, 200, $rectBorderColor);

// Текст
$bg = imagecolorallocate($image, 255, 255, 255);
$textcolor = imagecolorallocate($image, 0, 0, 255);
imagestring($image, 5, 150, 200, 'Hello world!', $textcolor);

// Отобразить рисунок, а затем удалить из памяти
header("Content-type: image/png");
imagepng($image);
imagedestroy($image); 
?>
Рисование простых линий

Пример использования библиотеки gd: фрактальные изображения

Уже сложилась замечательная традиция украшать изложение темы вычерчивания линий, которая в принципе может показаться неинтересной, используя в качестве примера фракталы. Рассматриваемый ниже пример не только показывает, как создать сложное изображение программным путем, но и служит хорошей демонстрацией возможностей языка PHP. Дело в том, что массивы и нестрого заданные типы данных этого языка позволяют очень легко создавать сложные структуры данных, соответствующие фрактальным изображениям, не применяя большого количества объявлений.

Фрактал представляет собой геометрическую форму, подобную самой себе. Это означает, что части фрактала имеют форму, подобную всему фракталу, части этих частей также имеют подобную форму, и т.д.

Теоретически к идеальному фракталу можно применять операцию увеличения неограниченное количество раз и создавать все меньшие и меньшие части, продолжая обнаруживать повторение одних и тех же шаблонов. Но на практике фракталы, создаваемые с помощью компьютера, после использования некоторого ограниченного количества операций формирования частей вырождаются в нефрактальные формы, такие как обычные отрезки кривых и прямых.

Пример изображения такого типа, которое мы попытаемся создать, приведен на рисунке ниже:

Создание картинки фрактала с использованием PHP

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

Наша задача состоит в том, чтобы вычислить координаты конечных точек всех этих отрезков прямых, а затем представить все отрезки должным образом в виде изображения PNG. Мы могли бы просто собрать один фрагмент фрактального изображения, но решили поставить перед собой более сложную задачу — не только обеспечить вывод изображения фрактала, но и сформировать небольшую инфраструктуру, позволяющую легко варьировать параметры фрактала и создавать изображения новых типов.

Для начала подготовим определенные структуры данных, позволяющие представлять отображаемые сложные формы. Такие структуры данных должны обеспечивать не только проведение промежуточных вычислений, но и вычерчивание отрезков, положение и длина которых определяются полученными конечными результатами. Прежде всего примем некоторые достаточно произвольные предположения:

  • точка в системе координат задается парой чисел;

  • путь представляется в виде списка точек.

В конечном итоге для вычерчивания ломаной линии необходимо вычертить отрезки прямых между всеми точками линии. Если бы нужно было вычертить отдельный отрезок прямой, то эту задачу можно было представить как вычерчивание ломаной линии, которая проходит через две точки, а вычерчивание прямоугольника можно было представить как создание изображения ломаной линии, проходящей через пять точек (с учетом того, что начальная точка встречается в ломаной линии дважды и при повторном появлении замыкает прямоугольник). (В данном случае можно было бы в качестве примитивного объекта рассматривать отрезок прямой, но, по-видимому, при решении задачи создания фрактальных изображений представление в виде ломаной линии является более кратким.)

Теперь рассмотрим вопрос о том, как должны быть представлены точки и ломаные линии. Проще всего списки объектов можно представить в языке PHP с использованием массивов. Поэтому возьмем за основу то, что точка — это массив, который включает два числа, а ломаная линия — массив, который включает ряд точек. В результате реализации этого подхода должны быть созданы структуры, представляющие собой многомерные массивы PHP, но если нам удастся определить конструктор и функции доступа с легко запоминающимися именами, то можно забыть о том, насколько сложны эти структуры, и просто разрабатывать код, используя такие операции, как если бы эти структуры представляли собой настоящие типы данных.

Код создания изображения, показанного на рисунке выше, приведен ниже. В нем типы данных определяются в терминах функций их создания (имена которых начинаются с префикса make_), функций, предназначенных для доступа к компонентам структур данных, и функций, обеспечивающих вывод изображения на основе данных (имена этих функции начинаются с префикса display_). При использовании этого подхода вывод точек на экран не предусматривается, поэтому для них не предусмотрены функции вывода, а ломаные линии вычерчиваются путем вычерчивания отрезков прямых между следующими друг за другом парами точек.

Функция transform_path() принимает в качестве первого параметра исходное определение ломаной линии, а в качестве второго параметра — имя функции, которая, в свою очередь, принимает в качестве параметра определение ломаной линии и возвращает в качестве результата определение трансформированной ломаной линии. Третьим параметром функции transform_path() является значение кратности последовательного применения этой функции преобразования одной ломаной линии в другую для создания новой ломаной линии. Причина, по которой оказалось целесообразным введение подобной функции второго порядка, состоит в том, что в противном случае могло бы потребоваться создавать все новую и новую функцию циклической обработки каждый раз, когда потребовалось бы сформировать новый фрактал. Использование такого подхода позволяет увязывать различные части кода формирования фрактала в отдельную функцию, которую можно передать в функцию transform_path() и избежать дублирования работы.

Функция spike принимает в качестве параметра определение ломаной линии и возвращает другое определение ломаной линии, в котором каждый отрезок прямой, проходящий через две точки, заменяется ломаной линией, проходящей через пять точек, в середине которых изображен пик. А функция top-hat выполняет примерно аналогичное действие, за исключением того, что результирующая ломаная линия проходит через шесть точек и формируемый пик имеет прямоугольную форму. Кроме того, в этот пример включено еще несколько функций для создания ломаных линий со стандартными размерами, в которых углы между отрезками являются прямыми; эти функции предназначены для использования в качестве типичных образцов.

После загрузки определений всех функций из включаемых файлов в этом коде создается изображение gd с заданной высотой и шириной, а затем в этом изображении распределяются цвета. (Фон имеет белый цвет, а отрезки прямых — черный.)

Выполнение кода формирования фрактального изображения начинается с создания стандартной замкнутой ломаной линии прямоугольной формы, проходящей через пять точек и поэтому включающей четыре (неявно заданных) отрезка прямых. Затем это определение ломаной линии передается в функцию transform_path() в виде вызова этой функции с параметрами, которые указывают, что должно быть возвращено определение ломаной линии, ставшей результатом четырехкратного применения функции spike() к заданному прямоугольнику. Трансформация замкнутой ломаной линии прямоугольной формы начинается с обработки четырех отрезков прямых, и каждый отрезок заменяется четырьмя отрезками. Поэтому в результате четырех последовательных итераций формируются соответственно 16, 64, 256 и 1024 отрезка.

После этого остается только вывести сформированную сложную замкнутую ломаную линию на экран. Для вычерчивания всех отрезков прямых на изображении вызывается определенная в этом коде функция display_path(), передается заголовок HTTP, указывающий, что далее следует изображение PNG, вызывается функция imagepng() для преобразования и вывода, а затем внутреннее определение изображения gd уничтожается:

Код PHP
<?php
// --- Точки ----
// Точка обозначается парой числовых координат
function make_point ($x, $y) {
	return(array($x, $y));
}

function point_x ($point) {
	return($point[0]); 
}

function point_y ($point) {
	return($point[1]);
}

// --- Кривые ---
// Кривая линия представлена как список точек
function make_path () {
	return array();
}

function add_point_to_path ($path, $point) {
	$path[] = $point;
	return($path);
}

function display_path ($image, $path, $color) {
	static $line_count = 0;
	$prev_point = NULL;
	
	foreach ($path as $point) 
	{
		if ($point && $prev_point) {
			$line_count++;
			imageline($image, point_x($prev_point), point_y($prev_point), 
				point_x($point), point_y($point), $color);
		}
		$prev_point = $point;
	}
}

function transform_path ($path_input, $function_name, $iterations)
{
	// Входными параметрами являются точки кривой линии, функция 
	// преобразования одной кривой линии в другую и кратность
	// применения функции преобразования.
	// Возвращает измененную кривую
	$path_to_return = $path_input;
	
	for ($i = 0; $i < $iterations; $i++)
	{
		$path_to_return = $function_name($path_to_return);
	}
	
	return($path_to_return);
} 

function spike ($path) {
	// Принимает координаты кривой и модифицирует их
	$path_to_return = make_path();
	$prev_point = NULL;
	
	foreach ($path as $point)
	{
		if ($point && $prev_point) {
			$path_to_return = add_point_to_path($path_to_return, $prev_point);
			$path_to_return = add_point_to_path($path_to_return, 
				point_along_segment($prev_point, $point, 0.25));
			$path_to_return = add_point_to_path($path_to_return, 
				point_off_segment($prev_point, $point, 0.5, 0.23));
			$path_to_return = add_point_to_path($path_to_return, 
				point_along_segment($prev_point, $point, 0.75));
			$path_to_return = add_point_to_path($path_to_return, $point);
		}
		$prev_point = $point;
	}
	
	return($path_to_return);
}

function top_hat ($path) {
	// Принимает координаты кривой и модифицирует их
	$path_to_return = make_path();
	$prev_point = NULL; 
	
	foreach ($path as $point) {
		if ($point && $prev_point) {
			$path_to_return = add_point_to_path($path_to_return, $prev_point);
			$path_to_return = add_point_to_path($path_to_return, 
				point_along_segment($prev_point, $point, 0.35));
			$path_to_return = add_point_to_path($path_to_return, 
				point_off_segment($prev_point, $point, 0.35, 0.24));
			$path_to_return = add_point_to_path($path_to_return, 
				point_off_segment($prev_point, $point, 0.65, 0.24));
			$path_to_return = add_point_to_path($path_to_return, 
				point_along_segment($prev_point, $point, 0.65));
			$path_to_return = add_point_to_path($path_to_return, $point);
		}
		$prev_point = $point;
	}
	
	return($path_to_return);
}

function point_along_segment ($first_point, $second_point, $proportion) 
{
	$delta_x = (point_x($second_point) - point_x($first_point));
	$delta_y = (point_y($second_point) - point_y($first_point));
	
	return(make_point(point_x($first_point) + $proportion * $delta_x,
		point_y($first_point) + $proportion * $delta_y));
}

function point_off_segment ($first_point, $second_point, $proportion, $proportional_distance) 
{
	$delta_x = (point_x($second_point) - point_x($first_point));
	$delta_y = (point_y($second_point) - point_y($first_point));
	
	return(make_point(point_x($first_point) + $proportion * $delta_x - $proportional_distance * $delta_y,
		point_y($first_point) + $proportion * $delta_y + $proportional_distance * $delta_x)); 
}

function make_small_rectangle () {
	$path = make_path();
	$path = add_point_to_path ($path, make_point(75, 275));
	$path = add_point_to_path ($path, make_point(375, 275));
	$path = add_point_to_path ($path, make_point(375, 125));
	$path = add_point_to_path ($path, make_point(75, 125));
	$path = add_point_to_path ($path, make_point(75, 275));
	return($path);
}

function make_large_rectangle () {
	$path = make_path();
	$path = add_point_to_path ($path, make_point(5, 5));
	$path = add_point_to_path ($path, make_point(495, 5));
	$path = add_point_to_path ($path, make_point(495, 395));
	$path = add_point_to_path ($path, make_point(5, 395));
	$path = add_point_to_path ($path, make_point(5, 5));
	return($path);
}

$IMAGE_WIDTH = 500;
$IMAGE_HEIGHT = 400;

$image = ImageCreate($IMAGE_WIDTH, $IMAGE_HEIGHT) or die("Невозможно создать рисунок");

$background_color = ImageColorAllocate($image, 255, 255, 255);
$drawing_color = ImageColorAllocate($image, 0, 0, 0);

$path = make_small_rectangle();
$path = transform_path($path, 'spike', 4);
display_path($image, $path, $drawing_color);

header("Content-type: image/png");
imagepng($image);
imagedestroy($image); 

?> 

Для формирования и вывода этих изображений может потребоваться много времени, причем затраты времени увеличиваются по мере возрастания количества создаваемых отрезков прямых, поэтому веб-сервер может завершить работу сценария по тайм-ауту в то время, как формирование изображение еще не будет закончено. Чтобы выйти из этой ситуации, можно либо уменьшить количество этапов формирования в коде создания фрактального изображения, либо увеличить значения тайм-аутов в файлах конфигурации веб-сервера или интерпретатора PHP.

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

Отправка электронной почты

Комментарии (0)

Результаты поиска по запросу

Система Orphus