Основы использования GD (part 1) - предлагаю в FAQ

The employer

Новичок
Привет всем.

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

Вот что будет выложено за пару дней:

PHP:
Работа с цветом и прозрачностью для изображений с палитрой

    Общие принципы
        Описание цвета
        Описание прозрачности
        Поведение при экспорте в различные форматы
        Размер палитры
    Управление составом палитры при помощи стандартных средств GD
        Распределение ячейки
        Освобождение ячейки
        Использование "нераспределенных" ячеек палитры
    Управление содержимым палитры при помощи стандартных средств GD
        Получение цвета в ячейке
        Изменение цвета в ячейке
        Получение номера прозрачной ячейки
        Назначение прозрачной ячейки
    Поиск ячейки с определенным цветом
        Поиск точного соответствия
        Поиск ближайшего цвета
    Чего GD делать не дает, и как заставить ее вести себя прилично
        Получение палитры в бинарном виде
        Формат палитры
        Замена палитры изображения
    Недостающие функции библиотеки GD
        Функция, позволяющая отличить изображения с палитрой от всего прочего
        Функция получения палитры в виде массива
        Функция прямой записи палитры из массива в изображение
        Функция прямой установки количества цветов в палитре
        Функция прямой установки цвета в ячейке палитры
-~{}~ 15.07.09 11:40:


Работа с цветом и прозрачностью для изображений с палитрой


Общие принципы


Описание цвета

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

Цвет в ячейке палитры описывается тремя значениями для красной, зеленой
и синей компонент (RGB). Каждая компонента может иметь значение от 0 до 255,
отражающее яркость соответствующего цвета в данной точке изображения.
Нулевая яркость говорит о том, что этот цвет просто отсутствует.

Таким образом, набор значений RGB (0,0,0) соответствует черному цвету точки,
RGB (255,0,0) - красному, RGB (0,255,0) - зеленому, RGB (0,0,255) - синему,
RGB (255,255,255) - белому цвету точки.


Описание прозрачности

Прозрачность в палитре описывается сразу двумя способами.

Во-первых, в каждой ячейке хранится степень прозрачности для пикселей,
ссылающихся на эту ячейку. Значение 0 соответствует полной непрозрачности,
значение 127 соответствует полной прозрачности.

Во-вторых, в палитре фиксируется номер ячейки, которая назначена "прозрачной".
Все пиксели изображения, ссылающиеся на данную ячейку палитры,
будут полностью прозрачными - независимо от установленных значений RGB.

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

В палитре может быть только одна ячейка с признаком "прозрачный цвет".
При попытке сделать прозрачной еще одну ячейку - именно она станет прозрачной,
а предыдущая это свойство потеряет.


Поведение при экспорте в различные форматы

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

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

При экспорте в JPEG любая информация о прозрачности будет проигнорирована.


Размер палитры

Во внутреннем представлении GD, каждое изображение с палитрой всегда содержит
полную палитру размером 256 ячеек. При создании изображения все ячейки палитры
заполнены черным непрозрачным цветом.

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

Поэтому бИблиотека GD "притворяется", будто у нее палитра может иметь
разное число ячеек, и будто бы при создании нового изображения в его палитре
нет ни одной ячейки.

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

Получить текущее значение счетчика занятых ячеек можно при помощи функции
imagecolorstotal:

Код:
PHP:
$palette_size = imagecolorstotal( $img );
Кстати, для truecolor-изображений данная функция всегда возвращает ноль.



Управление составом палитры при помощи стандартных средств GD


Распределение ячейки

Чтобы добавить цвет в палитру, нужно воспользоваться функцией
imagecolorallocate либо функцией imagecolorallocatealpha. Первая функция
позволяет задать только компоненты RGB для новой ячейки палитры, вторая
помимо этого позволяет задать степень прозрачности.

Код:
PHP:
$background = imagecolorallocatealpha( $img, 0, 0, 0, 127 );
$foreground = imagecolorallocate( $img, 255, 0, 0 );
Этот код добавил в палитру два цвета - черный и при этом полностью прозрачный,
и полностью непрозрачный красный.

Вызов imagecolorallocate( $img, $r, $g, $b ) эквивалентен вызову
imagecolorallocatealpha( $img, $r, $g, $b, 0 ).

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

На случай, если хочется по-максимуму использовать существующие цвета палитры,
добавляя новые ячейки лишь при необходимости, в GD предусмотрены функции
imagecolorresolve и imagecolorresolvealpha. Эти функции пытаются отыскать
в палитре ячейку, содержащую те же самые значения RGB и альфа-канала,
которые переданы им в качестве параметров. Если удается отыскать такую ячейку,
функции возвращают ее номер; если такой ячейки не находится - функции добавляют
в палитру еще одну ячейку, устанавливают ей соответствующие параметры цвета
и прозрачности и возвращают номер добавленной ячейки.

Вызов imagecolorresolve( $img, $r, $g, $b ) эквиваелентен вызову
imagecolorresolvealpha( $img, $r, $g, $b, 0 ).

Код:
PHP:
$img = imagecreate( 100, 100 );
$p1 = imagecolorstotal( $img );

$background = imagecolorallocatealpha( $img, 0, 0, 0, 127 );
$foreground = imagecolorallocate( $img, 255, 0, 0 );
$p2 = imagecolorstotal( $img );

$new_foreground = imagecolorresolve( $img, 255, 0, 0 );
$new_background = imagecolorresolve( $img, 0, 0, 0 );
$p3 = imagecolorstotal( $img );
В результате выполнения этого кода переменная $p1 будет равна нулю,
переменная $p2 будет равна двум, $p3 - трем. Значение переменной
$new_foreground будет равно значению переменной $foreground,
в то же время значение переменной $new_background не будет равно
значению $background.


Освобождение ячейки

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

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

Код:
PHP:
$img = imagecreate( 100, 100 );
$background = imagecolorallocatealpha( $img, 0, 0, 0, 127 );
$foreground = imagecolorallocate( $img, 255, 0, 0 );
$p1 = imagecolorstotal( $img );

imagecolordeallocate( $img, $foreground );
$p2 = imagecolorstotal( $img );

$new_foreground = imagecolorallocate( $img, 255, 255, 255 );
$p3 = imagecolorstotal( $img );
В результате выполнения этого кода переменные $p1, $p1 и $p3 будут равны двум.
Значение переменной $new_foreground будет равно значению переменной $foreground,


Использование "нераспределенных" ячеек палитры

Как мы уже знаем, внутри GD всегда присутствует полная палитра на 256 ячеек,
все ячейки которой по умолчанию содержат черный непрозрачный цвет.

Библиотека ведет счетчик задействованных ячеек, причем предполагает
что пропусков в ряду занятых ячеек нет. То есть, если вы распределили 200 ячеек,
а затем 150 ячеек освободили - библиотека по прежнему будет считать что все
200 ячеек используются. Соответственно, вы можете спокойно использовать
в теле изображения пиксели, ссылающиеся на "освобожденные" ячейки.

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

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

findnext

Новичок
я вот тоже прочитал, для общего образования точно не повредит
 

weregod

unserializer
The employer, рекомендую RGB-компоненты цветов заменить на их шестнадцатеричное представление, HTML-related как ни как статья ;)
 

The employer

Новичок
Управление содержимым палитры при помощи стандартных средств GD


Получение цвета в ячейке

Функция imagecolorsforindex принимает в качестве параметра номер ячейки палитры,
и возвращает массив, содержащий описание цвета и степени прозрачности пикселей,
ссылающихся на данную ячейку.

Если попытаться получить описание ячейки с номером большим, чем значение
счетчика использованных ячеек, функция вернет false и сгенерирует
предупреждение уровня E_WARNING, которое можно подавить при помощи
error_reporting( E_ALL ^ E_WARNING ) или при помощи символа '@' перед
именем функции.

Код:
PHP:
$color = @imagecolorsforindex( $img, $index );
if( $color === false ) { /* обработка ошибки */ }
Если счетчик использованных ячеек палитры превышает значение в $index -
в переменной $color окажется массив с ключами "red", "green", "blue" и "alpha".
Значения под этими ключами будут соответствовать красной, зеленой и синей
компонентам цвета в ячейке, а под ключом "alpha" будет значение для степени
прозрачности (127 - полностью прозрачные пиксели).


Изменение цвета в ячейке

Изменить описание цвета в уже распределенной ячейке палитры позволяет функция
imagecolorset.

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

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

Код:
PHP:
imagecolorset( $img, $index, $red, $green, $blue );
Значения компонент $red, $green и $blue должны быть в диапазоне от 0 до 255.
Если значение какой-либо из компонент выходит за эти пределы, то в ячейку
будет записан младший байт этого значения (как-бы остаток от деления
переданного значения на 256). То есть вызов
imagecolorset( $img, $index, 256, 512, 1024 ) эквивалентен вызову
imagecolorset( $img, $index, 0, 0, 0 ).


Получение номера прозрачной ячейки

Узнать, какая ячейка в палитре назначена "полностью прозрачной", позволяет
функция imagecolortransparent.

Код:
PHP:
$tr = imagecolortransparent( $img );
После выполнения в переменной $tr будет содержаться номер "прозрачной" ячейки
или минус единица, если ни одна ячейка не назначена прозрачной.


Назначение прозрачной ячейки

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


Поиск ячейки с определенным цветом

Поиск точного соответствия

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

В качестве параметров функция принимает ссылку на изображение, три цветовые
компоненты RGB и значение для альфа-канала.

В результате функция возвращает -1 если ячейку с таким цветом
найти не удалось - или номер ячейки, содержащей указанный цвет с указанной
степенью прозрачности.

При поиске не учитываются цвета в "нераспределенных" ячейках палитры.

Если в качестве параметров RGB и альфа задать значения вне диапазона 0-255,
никакая ячейка найдена не будет (функция вернет -1).

Код:
PHP:
$img = imagecreate( 100, 100 );
$c  = imagecolorallocatealpha( $img, 1, 2, 3, 4 );
$p = imagecolorexactalpha( $img, 1, 2, 3, 4 );
imagecolordeallocate( $img, $c );
$p1 = imagecolorexactalpha( $img, 1, 2, 3, 4 );
В результате выполнения этого кода значение $p будет равно значению $c,
$p1 будет равна минус единице.

В составе библиотеки есть также функция imagecolorexact. Вызов этой функции
эквивалентен вызову imagecolorexactalpha со значением 0 для альфа-канала.
Таким образом, imagecolorexact ищет соответствие только среди полностью
непрозрачных ячеек.


Поиск ближайшего цвета

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

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

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

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

Вот их перечень:
imagecolorclosest - поиск ячейки, содержащий цвет наиболее близкий указанному
в качестве параметра (в виде компонент RGB)
imagecolorclosestalpha - то же, что и предыдущая функция, но еще и с учетом
альфа-компоненты (степени прозрачности)
imagecolorclosesthwb - то что и imagecolorclosest, но цвета сравниваются
не по компонентам RGB, а по яркости, насыщенности и оттенку.

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

Небольшой пример, иллюстрирующий поведение:
PHP:
$img = imagecreate( 100, 100 );
$p1 = imagecolorclosest( $img, 0, 0, 0 );
$c  = imagecolorallocatealpha( $img, 255, 255, 255, 127 );
$p2 = imagecolorclosest( $img, 0, 0, 0 );
$p3 = imagecolorclosestalpha( $img, 0, 0, 0, 0 );
imagecolordeallocate( $img, $c );
$p4 = imagecolorclosestalpha( $img, 0, 0, 0, 0 );
После выполнения данного кода, в $p1 и $p4 будет минус единица, а $p2 и $p3
будут равны $c (таким образом, функция подобрала нам абсолютно белый цвет
в качестве "ближайшего" абсолютно черному - немного напоминает известный анекдот
о том, как Мойша продал ЦВЕТНОЙ телевизор).

-~{}~ 15.07.09 23:40:

Чего GD делать не дает, и как заставить ее вести себя прилично

Очевидно, что набор функций GD неполон. В ряду возможностей библиотеки зияют
заметные дыры. В части работы с палитрой можно назвать такие (отсутствующие)
функции как imagecolorsetalpha (изменение описания цвета ячейки вместе
с альфа-компонентой) и imagecolorstotalset (установка значения счетчика
использованных ячеек палитры). Еще иногда не хватает функции, аналогичной
imageistruecolor, но для изображений с палитрой - такой, которая могла бы
называться imageispalettebased.

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

Еще хотелось бы иметь простой способ установки цвета произвольной ячейки
палитры (без оглядки на счетчик использованных ячеек).

Конечно, самым радикальным методом было бы внесение изменений в GD-расширение
PHP. Однако, кое-что можно сделать и без привлечения тяжелой артиллерии.


Получение палитры в бинарном виде

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

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

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

Код:
PHP:
$img = imagecreatefromgif( $filename );
$pal = imagecreate( 1, 1 );
imagepalettecopy( $pal, $img );
imagecolortransparent( $pal, imagecolortransparent($img) );
ob_start();
imagegd2( $pal );
$raw = ob_get_clean();
imagedestroy( $pal );
В результате исполнения этого кода в переменной $raw будет содержаться строка
длиной 1050 символов, содержащая заголовок и палитру изображения, считанного
из файла $filename.


Формат палитры

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

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

Чтение количества использованных ячеек:
PHP:
$psize = array_pop( unpack( "n", substr( $raw, 19, 2 ) ) );
Установка количества использованных ячеек:
PHP:
$raw[19] = chr( (int) ($new_psize / 256) );
$raw[20] = chr( $new_psize % 256 );
Следующие четыре символа ($raw[21], $raw[22], $raw[23], $raw[24]) представляют
собой четыре байта длинного целого числа - номера ячейки, назначенной
прозрачной. И снова старший байт лежит первым, а младший - последним.

Код:
PHP:
$trans = array_pop( unpack( "N", substr( $raw, 21, 4 ) ) );
После выполнения кода в переменной $trans будет номер прозрачной ячейки либо
минус единица, если никакая ячейка не была назначена прозрачной.

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

C символа $raw[25] начинается собственно палитра - 256 ячеек, каждая ячейка
занимает четыре символа подряд, в первом символе лежит значение красной
компоненты цвета, во втором - зеленой, в третьем - синей, в четвертом -
значение альфа-канала (степени прозрачности).

Чтобы получить смещение нужной ячейки от начала строки, нужно номер ячейки
умножить на четыре и прибавить 25. Таким образом, вторая ячейка (с номером 1)
будет начинаться с символа $raw[29].


Замена палитры изображения

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

Не забываем о том, что imagepalettecopy не копирует данные о номере ячейки,
назначенной прозрачной.

Код:
PHP:
$new_image = imagecreatefromstring( $raw );
imagepalettecopy( $old_image, $new_image );
imagecolortransparent( $old_image, imagecolortransparent($new_image) );
-~{}~ 15.07.09 23:45:

Недостающие функции библиотеки GD

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


Функция, позволяющая отличить изображения с палитрой от всего прочего

PHP:
/**
 * Функция возвращает true, если ей передана ссылка на изображение с палитрой
 *
 * В случае, если функции передана сслыка на изображение с палитрой -
 * будет возвращено false.
 *
 * В случае, если переданный параметр вообще не является изображением -
 * функция возвращает false и генерирует ошибку уровня E_USER_WARNING
 * с сообщением о причинах ошибки.
 *
 * @param resource $img - ссылка на изображение во внутреннем формате GD
 * 
 * @return bool - возвращает true если передано изображение с палитрой,
 *                и false в случае ошибки или в случае если передано
 *                truecolor-изображение
 */
function imageispalettebased( $img ) {

    if( @imagesx($img) === false ) {
        trigger_error(
            "Invalid parameter \$img (probably it is not a GD image):\n"
          . var_export($img, true)."\n"
        );
        return false;
    }

    return !imageistruecolor($img);

} // function imageispalettebased()

Функция получения палитры в виде массива

PHP:
/**
 * Получение палитры изображения в виде ассоциативного массива
 *
 * Ключи полученного массива:
 * "colorstotal"  => <количество цветов в палитре>
 * "transparent"  => <номер прозрачной ячейки либо минус единица>
 * "palette" => array(
 *     <номер ячейки> => array(
 *         "R" => <красная компонента>
 *       , "G" => <зеленая компонента>
 *       , "B" => <синяя компонента>
 *       , "A" => <альфа-канал>
 *     )
 * )
 *
 * Ячейки нумеруются с нуля.
 *
 * Функция не предназначена для работы с truecolor-изображениями.
 * В ответ на попытку передать ссылку на truecolor-изображение,
 * функция вернет признак ошибки.
 *
 * В случае возникновения ошибки, функция возвращает false
 * и генерирует ошибку уровня E_USER_WARNING с подробным сообщением
 * о причинах ошибки.
 *
 * @param resource $img - ссылка на изображение во внутреннем формате GD
 *
 * @return array|bool - возвращает массив или false в случае ошибки
 */
function imagepaletteasarray( $img ) {

    if( !imageispalettebased( $img ) ) {
        trigger_error( "Invalid parameter \$img:\n".var_export($img, true)."\n" );
        return false;
    }

    $pal = imagecreate( 1, 1 );
    imagepalettecopy( $pal, $img );
    imagecolortransparent( $pal, imagecolortransparent($img) );

    ob_start();
    imagegd2( $pal );
    $raw = ob_get_clean();
    imagedestroy( $pal );

    $result = unpack( "ncolorstotal/Ntransparent", substr( $raw, 19, 6 ) );

    $result["palette"] = array_map(
        create_function( '$v', 'return unpack( "CR/CG/CB/CA", $v );' )
      , str_split( substr($raw, 25, 256 * 4), 4 )
    );

    return $result;

} // function imagepaletteasarray()

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

PHP:
/**
 * Прямая запись палитры в изображение
 *
 * Палитра предоставляется в виде ассоциативного массива. Его ключи:
 * "colorstotal"  => <количество цветов в палитре>
 * "transparent"  => <номер прозрачной ячейки либо минус единица>
 * "palette" => array(
 *     <номер ячейки> => array(
 *         "R" => <красная компонента>
 *       , "G" => <зеленая компонента>
 *       , "B" => <синяя компонента>
 *       , "A" => <альфа-канал>
 *     )
 * )   
 *
 * Ячейки нумеруются с нуля.
 *
 * Количество цветов в палитре должно быть целым числом в диапазоне [0..256].
 * Если значение в соответствующем ключе не является целым числом
 * в указанном диапазоне - возвращаем признак ошибки.
 *
 * Номер прозрачной ячейки должен быть целым числом в диапазоне [-1..255].
 * Если значение в соответствующем ключе не является целым числом
 * в указанном диапазоне - возвращаем признак ошибки.
 *
 * Каждая цветовая компонента в описании ячейки должна быть целым числом
 * в диапазоне [0..256].
 * Если значение какой-либо компоненты не является целым числом
 * в указанном диапазоне - возвращаем признак ошибки.
 *
 * Альфа-канал в описании ячейки должен быть целым числом
 * в диапазоне [0..127].
 * Если значение альфа-канала не является целым числом
 * в указанном диапазоне - возвращаем признак ошибки.
 *
 * Общий формат массива и формат описания компонент должен соблюдаться.
 * При несоблюдении формата - возвращаем признак ошибки.
 *
 * Функция не предназначена для работы с truecolor-изображениями.
 * В ответ на попытку передать ссылку на truecolor-изображение,
 * функция вернет признак ошибки.
 *
 * В случае возникновения ошибки, функция возвращает false
 * и генерирует ошибку уровня E_USER_WARNING с подробным сообщением
 * о причинах ошибки.
 *
 * @param resource $img - ссылка на изображение во внутреннем формате GD
 * @param array $palette - массив, описывающий палитру
 *
 * @return bool - возвращает true в случае успеха и false в случае ошибки
 */
function imagepaletteapply( $img, $palette ) {

    if( !imageispalettebased( $img ) ) {
        trigger_error( "Invalid parameter \$img:\n".var_export($img, true)."\n" );
        return false;
    }

    if( !is_array( $palette ) 
     || count( $palette ) != 3
     || !isset( $palette["colorstotal"] )
     || !isset( $palette["transparent"] )
     || !isset( $palette["palette"] )
     || count( $palette["palette"] ) != 256
    ) {
        trigger_error(
            'Invalid parameter $palette '
          . "(probably it is not a valid palette array):\n"
          . var_export($palette, true)."\n"
        );
        return false;
    }

    $total = $palette["colorstotal"];
    if( !is_int($total) || $total < 0 || $total > 256 ) {
        trigger_error(
            'Invalid parameter $palette[colorstotal] '
          . "(should be an integer in range [0..256]):\n"
          . var_export($total, true)."\n"
        );
        return false;
    }
    $raw = pack( "n", $total );

    $trans = $palette["transparent"];
    if( !is_int($trans) || $trans < -1 || $trans > 255 ) {
        trigger_error(
            'Invalid parameter $palette[transparent] '
          . "(should be an integer in range [-1..255]):\n"
          . var_export($trans, true)."\n"
        );
        return false;
    }
    $raw .= pack( "N", $trans );

    for( $i = 0; $i < 256; $i++ ) {

        if( !isset($palette["palette"][$i]) ) {
            trigger_error( "Palette slot #$i is absent" );
            return false;
        }

        $v = $palette["palette"][$i];
        if( !is_array($v)
         || !isset($v["R"]) || !is_int($v["R"]) || $v["R"] < 0 || $v["R"] > 255
         || !isset($v["G"]) || !is_int($v["G"]) || $v["G"] < 0 || $v["G"] > 255
         || !isset($v["B"]) || !is_int($v["B"]) || $v["B"] < 0 || $v["B"] > 255
         || !isset($v["A"]) || !is_int($v["A"]) || $v["A"] < 0 || $v["A"] > 127
        ) {
            trigger_error(
                "Invalid description of palette slot #$i:\n"
              . var_export( $v, true )."\n"
            );
            return false;
        }

        //
        // Если установлен номер прозрачной ячейки -
        // устанавливаем альфа-канал этой ячейки в значение "полная прозрачность"
        // (именно так ведет себя функция imagecolortransparent)
        //
        if( $trans != -1 && $trans == $i ) $v["A"] = 127;

        $raw .= pack( 'CCCC', $v["R"], $v["G"], $v["B"], $v["A"] );

    } // for

    $raw =
        "gd2"
      . pack( 'CCCCCCCCCCCCCCCC',0,0,2,0,1,0,1,0,0x80,0,1,0,1,0,1,0 )
      . $raw
      . chr(0)
    ;

    $pal = imagecreatefromstring( $raw );
    imagepalettecopy( $img, $pal );
    imagecolortransparent( $img, imagecolortransparent($pal) );
    imagedestroy( $pal );

    return true;

} // function imagepaletteapply()

Функция прямой установки количества цветов в палитре

PHP:
/**
 * Установка количества задействованных цветов в палитре изображения
 *
 * Функция не предназначена для работы с truecolor-изображениями.
 * В ответ на попытку передать ссылку на truecolor-изображение,
 * функция вернет признак ошибки.
 *
 * В случае возникновения ошибки, функция возвращает false
 * и генерирует ошибку уровня E_USER_WARNING с подробным сообщением
 * о причинах ошибки.
 *
 * @param resource $img - ссылка на изображение во внутреннем формате GD
 * @param array $total - целое число в диапазоне [0..256]
 *
 * @return bool - возвращает true в случае успеха и false в случае ошибки
 */
function imagecolorstotalset( $img, $total ) {

    if( !imageispalettebased( $img ) ) {
        trigger_error( "Invalid parameter \$img:\n".var_export($img, true)."\n" );
        return false;
    }

    if( !is_int($total) || $total < 0 || $total > 256 ) {
        trigger_error( "Invalid parameter \$total:\n".var_export($total, true)."\n" );
        return false;
    }

    $pal = imagepaletteasarray( $img );
    if( $pal === false ) {
        trigger_error( "Can't get the palette of image" );
        return false;
    }

    $pal["colorstotal"] = $total;
    $res = imagepaletteapply( $img, $pal );
    if( $res === false ) {
        trigger_error( "Can't apply palette to the image" );
        return false;
    }

    return true;

} // function imagecolorstotalset()

Функция прямой установки цвета в ячейке палитры

PHP:
/**
 * Прямая установка описания цвета в ячейке палитры
 *
 * Функция не предназначена для работы с truecolor-изображениями.
 * В ответ на попытку передать ссылку на truecolor-изображение,
 * функция вернет признак ошибки.
 *
 * В случае возникновения ошибки, функция возвращает false
 * и генерирует ошибку уровня E_USER_WARNING с подробным сообщением
 * о причинах ошибки.
 *
 * @param resource $img - ссылка на изображение во внутреннем формате GD
 * @param int $color    - целое число в диапазоне [0..255]
 * @param int $red   - красная компонента цвета (целое число от 0 до 255)
 * @param int $green - зеленая компонента цвета (целое число от 0 до 255)
 * @param int $blue  - синяя компонента цвета (целое число от 0 до 255)
 * @param int $alpha - степень прозрачности (целое число от 0 до 127)
 *
 * @return bool - возвращает true в случае успеха и false в случае ошибки
 */
function imagecolorsetalpha( $img, $color, $red, $green, $blue, $alpha ) {

    if( !imageispalettebased( $img ) ) {
        trigger_error( "Invalid parameter \$img:\n".var_export($img, true)."\n" );
        return false;
    }

    foreach( array( "color", "red", "green", "blue", "alpha" ) as $p ) {
        if( !is_int($$p) || $$p < 0 || $$p > ($p == "alpha" ? 127 : 255) ) {
            trigger_error( "Invalid parameter \$$p:\n".var_export($$p, true)."\n" );
            return false;
        }
    }

    $pal = imagepaletteasarray( $img );
    if( $pal === false ) {
        trigger_error( "Can't get the palette of image" );
        return false;
    }

    $pal["palette"][$color] = array(
        "R" => $red
      , "G" => $green
      , "B" => $blue
      , "A" => $alpha
    );
    $res = imagepaletteapply( $img, $pal );
    if( $res === false ) {
        trigger_error( "Can't apply palette to the image" );
        return false;
    }

    return true;

} // function imagecolorsetalpha()
 

exxbrain

Новичок
The employer
Eсть примеры использования описанных возможностей GD в реальной жизни? Можешь привести пару для полноты восприятия?
 

The employer

Новичок
Автор оригинала: exxbrain
The employer
Eсть примеры использования описанных возможностей GD в реальной жизни? Можешь привести пару для полноты восприятия?
Как обычно - автоматические типовые операции над изображениями, входящими в user-generated content. Кроп, ресайз, подписи, реколоринг, комбинирование разного рода (рамки, вотермарки и тому подобное), генерация превью. В общем, много всего.

Дальше я буду писать обо всем этом. Попозже.
 

exxbrain

Новичок
Как обычно - автоматические типовые операции над изображениями, входящими в user-generated content. Кроп, ресайз, подписи, реколоринг, комбинирование разного рода (рамки, вотермарки и тому подобное), генерация превью. В общем, много всего.
Вроде все довольно примитивные операции. А вот операции с палитрой зачем, к примеру, могут пригодиться в php? Имею ввиду главу "Управление содержимым палитры при помощи стандартных средств GD" в основном.
 

The employer

Новичок
Автор оригинала: exxbrain
Вроде все довольно примитивные операции. А вот операции с палитрой зачем, к примеру, могут пригодиться в php? Имею ввиду главу "Управление содержимым палитры при помощи стандартных средств GD" в основном.
Во-первых, изображения с палитрой очень удобно реколорить. Например, есть некий значок, который цепляется на фотку. Этакая метка. И цвет этой метки - некий индикатор. Метка лежит в palette-based. При наложении выставляется нужная палитра. Легко, просто, удобно.

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

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

Уж не знаю, помог ли тебе чем-то этот ответ :)
 

The employer

Новичок
Автор оригинала: SiMM
Автору курить синтаксис.
[m]language.types.array#language.types.array.foo-bar[/m]
и писать свой код под E_ALL
Спасибо, код исправил.

-~{}~ 21.07.09 06:19:

PHP:
Работа с цветом и прозрачностью для truecolor-изображений
    Описание цвета и прозрачности
    Коренное отличие от изображений с палитрой
    Работа с цветом пикселей
        Внутреннее представление цвета и прозрачности
        Два режима работы с прозрачностью (alpha blending)
        Подробнее об alpha blending - правило расчета компонент
        Еще один режим сложения цветов (overlay)
        Указание цвета для функций рисования
        Создание новых изображений и проблемы с альфа-каналом
    Поведение при экспорте в различные форматы
Работа с цветом и прозрачностью для truecolor-изображений

Описание цвета и прозрачности

Каждая точка truecolor-изображения состоит из четырех компонент - трех цветовых
(красной, зеленой и синей) и одной альфа-компоненты.

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

Значение альфа-компоненты может быть в диапазоне 0-127, что дает возможность
выбрать один из 128 уровней прозрачности для каждой точки изображения.
У полностью непрозрачных точек значение альфа-компоненты равно нулю.


Коренное отличие от изображений с палитрой

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

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

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


Работа с цветом пикселей

Внутреннее представление цвета и прозрачности

Во внутреннем формате GD описание пикселя представляет собой четыре байта -
первый байт описывает альфа-компоненту, второй - красную компоненту, третий -
зеленую и четвертый - синюю.

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

Самое интересное, что для получения такого числа из значений компонент -
можно воспользоваться функциями imagecolorallocate или imagecolorallocatealpha.
Когда им в качестве первого параметра передается ссылка на truecolor-изображение,
они как раз формируют соответствующее число (imagecolorallocate всегда формирует
число, соответствующее полностью непрозрачному пикселю).

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


Два режима работы с прозрачностью (alpha blending)

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

По умолчанию для truecolor-изображений включен режим alpha blending.

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

Это приводит к эффекту "просвечивания" прежнего изображения из-под наложенного
поверх него (в случае, если накладывалось полупрозрачное изображение).

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

Переключением режимов занимается функция imagealphablending.

Код:
PHP:
imagealphablending( $img, false ); // переход в режим прямого замещения


Подробнее об alpha blending: правило расчета компонент

Цветовые компоненты пикселей будем называть буквой N (расчет для них одинаковый),
компоненту прозрачности будем называть буквой A.

Таким образом, компоненты исходного пикселя будем называть Nb, Ab,
компоненты накладываемого пикселя будем называть Nf, Af,
компоненты результирующего пикселя будем называть Nd, Ad.

Формулы расчета:
PHP:
      Ab * Af
Ad = ---------
        127
                                                    

              Af     127 - Ab          127 - Af         127
Nd = ( Nb * ----- * ---------- + Nf * ---------- ) * ----------
             127       127               127          127 - Ad
Смысл первой формулы: при каждом сложении полупрозрачных пикселов - степень
прозрачности результата уменьшается. Это напоминает процесс складывания
куска полупрозрачной ткани - чем больше слоев лягут друг на друга, тем меньше
удастся рассмотреть на самом нижнем слое.

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

Все вместе обеспечивает эффект "просвечивания" одного цвета сквозь другой.

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


Еще один режим сложения цветов: (overlay)

На самом деле библиотека GD поддерживает не два, а три значения для второго
параметра функции gdAlphaBlending. Почему и зачем создателям расширения PHP
потребовалось менять эту логику и добавлять функцию imagelayereffect - загадка.

Но тем не менее. Функция imagelayereffect принимает те же два параметра,
что и imagealphablending, но у второго параметра возможны четыре значения,
два из которых по смыслу ничем не отличаются. Причем в мануале PHP смысл
некоторых параметров описан неверно.

Следует учесть, что в реальности вызовы imagealphablending и imagelayereffect
меняют один и тот же флаг внутри GD, поэтому вызов одной функции отменяет
режим, установленный при помощи другой функции.

Параметр IMG_EFFECT_REPLACE делает то же самое, что параметр false в вызове
imagealphablending - устанавливает для изображения режим замещения пикселей.

Параметры IMG_EFFECT_NORMAL и IMG_EFFECT_ALPHABLEND делают то же, что параметр
true в вызове imagealphablending - включают режим "просвечивания" фона сквозь
накладываемые пиксели с учетом альфа-канала.

Параметр IMG_EFFECT_OVERLAY включает режим сложения цвета пикселей без учета
альфа-канала.

Формулы преобразования для режима overlay:
PHP:
            (127 - Ab) * (127 - Af)
Ad = 127 - -------------------------
                      127

                                       2 * Nb * Nf
Если Nb  > 127:  Nd = 2 * (Nb + Nf) - ------------- - 256
                                           256

                       Nb * Nf
Если Nb <= 127:  Nd = --------- 
                         256
Смысл преобразования: яркие цвета фона сделают более яркими цвета
накладываемого изображения, тусклые цвета фона ослабят цвета накладываемого
изображения. При этом полупрозрачные пиксели при сложении будут становиться
все более прозрачными. Сложение полупрозрачного пикселя с непрозрачным -
степень прозрачности не изменит. Сложение любого пикселя с полностью прозрачным -
сделает результат сложения также полностью прозрачным.

Такое поведение дает несколько интересных возможностей.

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

А если в режиме overlay скопировать изображение поверх самого себя -
получится эффект повышения контрастности изображения:
PHP:
$img = imagecreatefromjpeg( $filename );
imagelayereffect( $img, IMG_EFFECT_OVERLAY );
imagecopy( $img, $img, 0, 0, 0, 0, imagesx($img), imagesy($img) );
imagejpeg( $img, "highcontrast.".$filename );

Указание цвета для функций рисования

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

Код:
PHP:
imagealphablending( $img, false );
imagesetpixel(
    $img, 0, 0
  , imagecolorallocatealpha( $img, $red, $green, $blue, $alpha )
);
Этот код поставит точку с указанными параметрами цвета и прозрачности
в левом верхнем углу изображения.


Создание новых изображений и проблемы с альфа-каналом

Новые изображения (создаваемые путем вызова imagecreatetruecolor, а не путем
импорта существующих изображений) заполнены черным непрозрачным цветом
и имеют включенный по умолчанию режим alpha blending. Это может сбить с толку
при попытке рисовать на таком изображении "полупрозрачными" цветами - результат
сложения будет всегда давать непрозрачный цвет.

Чтобы иметь возможность рисовать на изображении в точности тем цветом
(вместе с альфа-компонентой), каким вы хотите - следует сначала отключить
режим alpha blending:
PHP:
$img = imagecreatetruecolor( 100, 100 );
imagealphablending( $img, false );
imagefilledrectangle( $img, 0, 0, 99, 99, 0x7f000000 );
imagealphablending( $img, true );
В результате выполнения этого кода изображение $img будет заполнено
абсолютно прозрачным цветом, после чего на нем можно будет уже рисовать
каким угодно образом - прозрачность будет учитываться в соответствии
с приведенными выше формулами.


Поведение при экспорте в различные форматы

При экспорте truecolor-изображения, альфа-канал будет в любом случае
проигнорирован для изображений, альфа-канал не поддерживающих - таких как
GIF и JPEG. Чтобы сохранить альфа-канал для изображений PNG, нужно
вызвать функцию imagesavealpha с параметром true:
PHP:
imagesavealpha( $img, true );
Существует, однако, возможность создать GIF с прозрачным цветом на основе
truecolor-изображения. Для этого нужно установить прозрачный цвет при помощи
функции imagecolortransparent. Самое интересное, что если при этом ОТКЛЮЧИТЬ
возможность сохранения альфа-канала, то при экспорте в PNG этот цвет
также станет полностью прозрачным (другие цвета, понятное дело, станут
полностью непрозрачными).

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

Код:
PHP:
$img = imagecreatetruecolor( 100, 100 );
imagecolortransparent( $img, 0x7f111111 );
imagealphablending($img, false );
imagesetstyle( $img, array( 0xff0000, 0x7f111111, 0x7f111111 ) );
imagefilledrectangle( $img, 0, 0, 99, 99, IMG_COLOR_STYLED );
imagefilledrectangle( $img, 10, 10, 89, 89, 0x7f111111 );
imagesavealpha( $img, true );
imagepng( $img, "grid.png", 0 );
imagegif( $img, "grid.gif" );
Этот код создаст два совершенно одинаковых изображения с прозрачным фоном
в форматах GIF и PNG.

-~{}~ 21.07.09 06:22:

Чума. Форум объединяет сообщения от одного автора, идущие подряд - в добрый путь. Но при этом он берет дату первого сообщения и не дает редактировать только что опубликованное сообщение, потому что оно "прилипло" к слишком старому предыдущему.

Прикольный косяк.
 

The employer

Новичок
Комбинирование изображений

PHP:
Комбинирование изображений
    Копирование целого изображения или его части (imagecopy)
        Поведение при копировании изображений разного типа
        Пример использования функции imagecopy: градиент прозрачности
    Копирование с объединением (imagecopymerge, imagecopymergegray)
        Формулы расчета цветовых компонент
        Пример использования imagecopymergegray: отражение в темном стекле
        Особая роль цвета, назначенного прозрачным при помощи imagecolortransparent
    Копирование с изменением размеров (imagecopyresized, imagecopyresampled)
        Пример использования imagecopyresampled: придание перспективы
    Использование изображения как заполнителя
        Пример применения функций заполнения: фон для открытки ко Дню Победы

Комбинирование изображений

Копирование целого изображения или его части (imagecopy)

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

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

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

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

Нужно помнить, что все координаты в GD отсчитываются от нуля.


Поведение при копировании изображений разного типа

Поведение при копировании изображения любого типа в truecolor-изображение

При копировании в truecolor-изображение, функция imagecopy учитывает режим,
установленный для этого изображения функциями imagealphablending
и imagelayereffect (мы помним, что эти функции воздействуют на один и тот же
флаг внутри GD, поэтому их действия взаимоисключающие).


Поведение при копировании truecolor-изображения в изображение с палитрой

Для вычисления индекса цвета в палитре для каждого копируемого пикселя
используется функция imagecolorresolvealpha со всеми вытекающими последствиями
(см. раздел "Управление составом палитры при помощи стандартных средств GD").


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

Пиксели, ссылающиеся на "прозрачный" цвет, игнорируются при копировании (не
переносятся).

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

Мы помним, что imagecolorresolvealpha добавляет цвета в палитру,
если не находит точного соответствия. Таким образом, при копировании
изображений с палитрой, возможна ситуация когда палитра целевого изображения
будет быстро "переполнена", и функция подбора цвета начнет выдавать совершенно
произвольные результаты. Как итог - получившееся при копировании изображение
выглядит достаточно неожиданно.

Пример:
PHP:
$img1 = imagecreate( 256, 100 );
$img2 = imagecreate( 256, 100 );

for( $i = 0; $i < 256; $i++ ) {
    $c1 = imagecolorresolve( $img1, $i, 0, 0 );
    imageline( $img1, $i, 0, $i, 49, $c1 );
    $c2 = imagecolorresolve( $img2, 0, 0, $i );
    imageline( $img2, $i, 50, $i, 99, $c2 );
}

imagecopy( $img2, $img1, 0, 0, 0, 0, 256, 50 );
В примере формируется два изображения с палитрой, после чего в каждом
изображении рисуется градиент из 256 цветовых переходов, в первом - в красной
гамме, во втором - в синей гамме. После чего одно изображение копируется
в другое. В результате мы получаем изображение только с одной гаммой - либо
красной либо синей (в зависимости от того, какое изображение выступает целевым).
Там, где должен был бы быть градиент в другой гамме - черное поле.

Чтобы изображения с палитрой могли быть нормально скомбинированы, их палитры
должны в сумме представлять не более 256 различных цветов. Предыдущий пример
можно модифицировать таким образом, чтобы все-таки увидеть два градиента
на одном изображении. Для этого нужно "загрубить" переходы, ограничившись
128 ступенями для каждой гаммы:
PHP:
$img1 = imagecreate( 256, 100 );
$img2 = imagecreate( 256, 100 );

for( $i = 0; $i < 256; $i++ ) {
    $c1 = imagecolorresolve( $img1, ((int)($i/2)) * 2, 0, 0 );
    imageline( $img1, $i, 0, $i, 49, $c1 );
    $c2 = imagecolorresolve( $img2, 0, 0, 255 - ((int)($i/2)) * 2 );
    imageline( $img2, $i, 50, $i, 99, $c2 );
}

imagecopy( $img2, $img1, 0, 0, 0, 0, 256, 50 );

Пример использования функции imagecopy: градиент прозрачности

Постановка задачи

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

Решение

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

Затем установим для этого изображения режим IMG_EFFECT_OVERLAY,
и скопируем поверх него исходное изображение.

Код
PHP:
$img1 = imagecreatefromjpeg( $filename );
$w = imagesx($img1);
$h = imagesy($img1);
$img = imagecreatetruecolor( $w, $h );

imagealphablending( $img, false );
for( $i = 0; $i < $h; $i++ ) {
    imageline( $img2, 0, $i, $w - 1, $i
      , imagecolorallocatealpha( $img2, 128, 128, 128, $i * 128 / $h )
    );
}

imagelayereffect( $img, IMG_EFFECT_OVERLAY );
imagecopy( $img, $img1, 0, 0, 0, 0, $w, $h );
После выполнения этого кода в $img содержится ссылка на изображение с градиентом
прозрачности. Чтобы сохранить его в PNG, нужно не забыть установить режим
сохранения альфа-канала при помощи imagesavealpha.


Копирование с объединением (imagecopymerge, imagecopymergegray)

Функции копирования с объединением эмулируют полупрозрачность при копировании
прямоугольной области из одного изображения в другое.

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

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

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

Последний параметр - это степень прозрачности, заменяющая альфа-канал всех
копируемых пикселей. Отличие в том, что альфа-канал имеет диапазон значений
0-127, а этот параметр имеет диапазон 0-100.


Формулы расчета цветовых компонент

Функция imagecopymerge применяет для расчета цветовых компонент следующую
формулу (в которой pct - это последний параметр функции, задающий степень
прозрачности):
PHP:
Nd = Nf * pct / 100 + Nb * (100 - pct) / 100

Ad = 0
Получившиеся таким способом пиксели объединяются с пикселями
изображения-приемника в соответствии с режимом, установленным
при помощи imagealphablending или imagelayereffect.

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

Формула, применяемая функцией imagecopymergegray отличается тем, что в расчете
вместо компонент изображения-приемника подставляется уровень яркости в этой
точке:
PHP:
Brt = 0.299 * Rb + 0.587 * Gb + 0.114 * Bb

Nd = Nf * pct / 100 + Brt * (100 - pct) / 100

Ad = 0

Пример использования imagecopymergegray: отражение в темном стекле

Постановка задачи

Cоздадим впечатление, будто изображение отражается в темном стекле.

Решение

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

Код
PHP:
$img1 = imagecreatefromjpeg( $filename );
$w = imagesx($img1);
$h = imagesy($img1);
$nh = $h / 2;
$img2 = imagecreatetruecolor( $w, $h + $nh );
imagelayereffect( $img2, IMG_EFFECT_REPLACE );

imagecopy( $img2, $img1, 0, 0, 0, 0, $w, $h );

for( $i = 0; $i < $nh; $i++ ) {
    $gray = $i / $nh * 128;
    imageline( $img2, 0, $h + $i, $w - 1, $h + $i
      , imagecolorallocate( $img2, $gray, $gray, $gray )
    );
    imagecopymergegray( $img2, $img1, 0, $h + $i, 0, $h - $i, $w, 1, 25 );
}
В результате исполнения в переменной $img2 будет ссылка на изображение
с эффектом отражения в темном стекле.


Особая роль цвета, назначенного прозрачным при помощи imagecolortransparent

Во-первых, при копировании игнорируются (не копируются) пиксели, цвет которых
определен как полностью прозрачный не при помощи альфа-канала, а при помощи
функции imagecolortransparent. Причем не имеет значения какое изображение
выступает источником - truecolor или palette-based.

Такое поведение дает возможность получить для truecolor-изображений быстрый
способ замены одного цвета на другой:
PHP:
$img1 = imagecreatefromjpeg( $filename );
$w = imagesx($img1);
$h = imagesy($img1);
$img2 = imagecreatetruecolor( $w, $h );

imagelayereffect( $img2, IMG_EFFECT_REPLACE);
imagefilledrectangle( $img2, 0, 0, $w - 1, $h - 1, 0x7f000000 );

imagecolortransparent( $img1, 0xffffff );
imagecopymerge( $img2, $img1, 0, 0, 0, 0, $w, $h, 100 );
После выполнения этого кода в $img2 будет ссылка на изображение из файла
$filename, в котором все белые пиксели заменены полностью прозрачными.


Копирование с изменением размеров (imagecopyresized, imagecopyresampled)

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

Отличие в том, что imagecopyresized использует целочисленную арифметику для
интерполяции цвета пикселей, а imagecopyresampled использует арифметику с плавающей
точкой. В результате при увеличении масштаба цвет добавляемых пикселей
рассчитывается точнее, что дает более качественный результат.

Если изображение-приемник является изображением с палитрой - для копирования
используется imagecopyresized, даже если вы вызываете imagecopyresampled.


Пример использования imagecopyresampled: придание перспективы

Постановка задачи

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

Решение

Изображение должно постепенно и сжиматься по вертикали и по горизонтали,
по мере удаления от левого к правому краю.

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

Код
PHP:
$img1 = imagecreatefromjpeg( $filename );
$w = imagesx($img1);
$h = imagesy($img1);

$aspect = 0.6;
$nw = $w * $aspect;
$nh = $h - $h * $aspect;
$img2 = imagecreatetruecolor( $nw, $h );

imagelayereffect( $img1, IMG_EFFECT_REPLACE );
imagecopyresampled( $img1, $img1, 0, 0, 0, 0, $nw, $h, $w, $h );

imagelayereffect( $img2, IMG_EFFECT_REPLACE );
imagefilledrectangle( $img2, 0, 0, $nw - 1, $h - 1, 0x7f000000 );

for( $i = 0; $i < $nw; $i++ ) {
    imagecopyresampled( 
          $img2, $img1
        ,    $i, $i / $nw * $nh / 2
        ,    $i, 0
        ,     1, $h - $i / $nw * $nh
        ,     1, $h
    );
}
После исполнения данного кода в переменной Simg2 будет содержаться ссылка на
искомое изображение.


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

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

При этом традиционно даже для truecolor-изображений учитывается цвет,
назначенный прозрачным при помощи imagecolortransparent.

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

Затем при использовании функций рисования заполненных контуров нужно в качестве
цвета заполнения указать специальную константу IMG_COLOR_TILED.

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

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


Пример применения функций заполнения: фон для открытки ко Дню Победы

Постановка задачи

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

Исходную фотографию можно взять, например, отсюда:
http://img-fotki.yandex.ru/get/3409/varjag-2007.50/0_2d567_6cf2e871_XL.jpg

Это фото из альбома "Великая отечественная_Вторая мировая" пользователя
varjag-2007 (http://fotki.yandex.ru/users/varjag-2007/view/185703/?page=21,
http://varjag-2007.livejournal.com/818227.html).

Решение

Осветлим фотографию путем копирования в новое изображение с объединением.
Затем для нового изображения объявим исходную фотографию как заполнитель.
Затем нарисуем область в виде пятиугольной звезды с заполнением из
исходной фотографии.

Код
PHP:
$img1 = imagecreatefromjpeg(
    'http://img-fotki.yandex.ru/get/3409/varjag-2007.50/0_2d567_6cf2e871_XL.jpg'
);
$w = imagesx($img1);
$h = imagesy($img1);
$img2 = imagecreatetruecolor( $w, $h );

imagelayereffect( $img2, IMG_EFFECT_REPLACE );
imagefilledrectangle( $img2, 0, 0, $w - 1, $h - 1
  , imagecolorallocate( $img2, 255, 255, 255 )
);
imagecopymergegray( $img2, $img1, 0, 0, 0, 0, $w, $h, 35 );

imagesettile( $img2, $img1 );

$r = $h / 4;
$x = $w - $r - $r / 5;
$y = $r + $r / 5;
foreach( range( 180, 180 + 359, 72 ) as $alpha ) {
    $a = deg2rad( $alpha );
    $b = deg2rad( $alpha + 36 );
    $poly[] = $r * sin( $a ) + $x;
    $poly[] = $r * cos( $a ) + $y;
    $poly[] = $r / 2 * sin( $b ) + $x;
    $poly[] = $r / 2 * cos( $b ) + $y;
}
imagefilledpolygon( $img2, $poly, count($poly) / 2, IMG_COLOR_TILED );
После выполнения этого кода в переменной $img2 находится ссылка на полученный
фон открытки ко Дню Победы.

-~{}~ 30.03.10 17:21:

Типовые операции с изображениями посредством GD (part 6)

PHP:
Преобразование одиночного изображения (часть 1)
    Общие соображения относительно расхода памяти
    Поворот на произвольный угол
    Отражение
        Метод с минимальным расходом памяти
        Метод с повышенным быстродействием
    Коррекция гаммы
        Пример использования - превью изображения с различными вариантами коррекции гаммы
Преобразование одиночного изображения (часть 1)

Общие соображения относительно расхода памяти

Встроенные в GD функции преобразования изображения (такие как imagerotate, imagefilter, imageconvolution) в отношении памяти действуют одинаковым образом: они создают новое изображение-"холст", которое заполняется в процессе преобразования исходного изображения.

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

Разумеется, такое поведение функций GD ведет к повышенному расходу памяти. Планируя выполнять в своем коде преобразование изображения, всегда следует помнить, что в этот момент потребление памяти резко возрастет - минимум в два раза, а для операции поворота изображения на углы, приблизительно кратные 45 градусам (45, 135, 225, 315 градусов), расход памяти может увеличиться более чем втрое по сравнению с объемом, необходимым для хранения исходного изображения.


Поворот на произвольный угол

Функция imagerotate поворачивает изображение вокруг его центра на указанный угол. Угол поворота должен быть задан в градусах (поддерживаются дробные значения угла поворота).

Исходное изображение и угол поворота - это первые два параметра функции imagerotate.

Следует учесть, что imagerotate не меняет исходное изображение - вместо этого она создает новое изображение в формате truecolor, которое будет содержать исходное изображение, повернутое на указанный угол. Размеры нового изображения будут совпадать с размерами исходного, если поворот происходил точно на угол, кратный 90 градусам. В противном случае новое изображение будет иметь размер больше исходного - чтобы не обрезать углы повернутой картинки.

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

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

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


Отражение

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

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

Мы напишем два варианта функции отражения изображения - один, использующий минимум памяти, и еще один - с максимальным быстродействием, но повышенными требованиями к свободному объему памяти.

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

Следует учитывать, что метод с минимальным расходом памяти меняет параметр, отвечающий за режим наложения пикселей (тот самый, на который воздействуют функции imagealphablending и imagelayereffect). После выполнения операции отражения этот параметр принимает значение IMG_EFFECT_REPLACE.


Метод с минимальным расходом памяти (пример для отражения в вертикальной плоскости):
PHP:
/**
 * Фунция отражения изображение в вертикальной плоскости
 *
 * Побочный эффект - для изображения будет установлен режим alphablending - false.
 *
 * @param mixed $img - ссылка на изображение, которое следует отразить
 */
function imagereflect_v_inplace( $img ) {

    if( imagesy($img) === false ) {
        throw new Exception( "Incorrect value for '$img'" );
    }

    $w = imagesx( $img );
    $h = imagesy( $img );

    $buffer = imagecreatetruecolor( 1, $h );
    if( !$buffer ) {
        throw new Exception( "Cannot create row buffer" );
    }
    imagelayereffect( $buffer, IMG_EFFECT_REPLACE );
    imagelayereffect( $img, IMG_EFFECT_REPLACE );

    $nit = floor( $w / 2 );
    for( $i = 0; $i < $nit; ++$i ) {
        imagecopy( $buffer, $img, 0, 0, $i, 0, 1, $h );
        imagecopy( $img, $img, $i, 0, $w - $i, 0, 1, $h );
        imagecopy( $img, $buffer, $w - $i, 0, 0, 0, 1, $h );
    }

    imagedestroy( $buffer );

}  // function imagereflect_v_inplace()
Метод с повышенным быстродействием (пример для отражения в горизонтальной плоскости):
PHP:
/**
 * Фунция отражения изображение в горизонтальной плоскости
 *
 * Побочный эффект - для изображения будет установлен режим alphablending - false.
 *
 * @param mixed $img - ссылка на изображение, которое следует отразить
 */
function imagereflect_h_canvas( $img ) {

    if( imagesy($img) === false ) {
        throw new Exception( "Incorrect value for '$img'" );
    }

    $w = imagesx( $img );
    $h = imagesy( $img );

    $buffer = imagecreatetruecolor( $w, $h );
    if( !$buffer ) {
        throw new Exception( "Cannot create row buffer" );
    }
    imagelayereffect( $buffer, IMG_EFFECT_REPLACE );
    imagelayereffect( $img, IMG_EFFECT_REPLACE );

    for( $i = 0; $i < $h; ++$i ) {
        imagecopy( $buffer, $img, 0, $i, 0, $h - $i, $w, 1 );
    }

    return $buffer;

} // function imagereflect_h_canvas( $img )

Коррекция гаммы

Коэффициент гаммы показывает, насколько график яркости пикселов отличается от прямой линии. Если коэффициент равен единице, то пиксел с цветом (200, 200, 200) ровно вдвое ярче пиксела с цветом (100, 100, 100).

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

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

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

Библиотека GD не предоставляет функцию коррекции гаммы. Функция imagegammacorrect реализована только в расширении PHP. Она имеет три параметра - изображение, для которого нужно провести коррекцию гаммы, исходный коэффициент гаммы, желаемый коэффициент гаммы.

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

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


Пример использования - превью изображения с различными вариантами коррекции гаммы

Общая схема действий: берем исходное изображение, вычисляем коэффициент масштабирования, необходимый для того, чтобы ширина масштабированного изображения равнялась ста пикселям. Далее создаем изображение-холст для превью, шириной в 730 пикселей, и на этом холсте размещаем семь уменьшенных вариантов исходного изображения, с коэффициентами коррекции гаммы (слева направо) 2.0, 1.6, 1.25, 1.0, 0.8, 0.625, 0.5. Оставляем по пять пикселей между превьюшками.

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

Код:
PHP:
$img = imagecreatefromjpeg( $imagename );
$w = imagesx($img);
$h = imagesy($img);

$w_preview = 100; // Ширина одной превьюшки
$d_preview = 5;   // Расстояние между превьюшками
$g_preview = array( 2.0, 1.6, 1.25, 1.0, 0.8, 0.625, 0.5 ); // Коэффициенты для каждого превью

$ratio = $w_preview / $w;
$h_preview = intval($h * $ratio);

$pre = imagecreatetruecolor( ($w_preview + $d_preview) * count($g_preview) - $d_preview, $h_preview );
imagealphablending( $pre, false );

$dst = imagecreatetruecolor( $w_preview, $h_preview );
imagealphablending( $dst, false );

$pre_x = 0;
foreach( $g_preview as $gamma ) {
    imagecopyresampled( $dst, $img, 0, 0, 0, 0, $w_preview, $h_preview, $w, $h ); 
    imagegammacorrect( $dst, 1, $gamma );
	imagemaphist( $dst );
    imagecopy( $pre, $dst, $pre_x, 0, 0, 0, $w_preview, $h_preview );
    $pre_x += $w_preview + $d_preview;
}

imagedestroy( $dst );
imagedestroy( $img );

/**
 * Фунция добавления гистограммы на изображение
 *
 * Побочный эффект - для изображения будет установлен режим alphablending - false.
 *
 * @param mixed $img - ссылка на изображение, к которому надо добавить гистограмму
 */
function imagemaphist( $img ) {

    $w = imagesx($img);
    if( $w === false ) {
        throw new Exception( "Incorrect value for '$img'" );
    }
    $h = imagesy($img);

    $maxrange = $w / 4;
    $gheight = $h / 4;

    $gx = $w / 4 * 2.5;
    $gy = $h / 4 * 2.5;

    $range = array_fill( 1, $maxrange, 0 );
    for( $x = 0; $x < $w; ++$x ) {
        for( $y = 0; $y < $h; ++$y ) {
            $p = imagecolorsforindex( $img, imagecolorat( $img, $x, $y ) );
            $b = 0.299 * $p["red"] + 0.587 * $p["green"] + 0.114 * $p["blue"];
            ++$range[ $b / 255 * $maxrange ];
        }
    }

    asort( $range );
    $max = end( $range );

    imagealphablending( $img, false );
    $c1 = imagecolorallocatealpha( $img, 255, 255, 255, 0 );
    $c2 = imagecolorallocatealpha( $img, 0, 0, 0, 0 );

    for( $i = 0; $i < $maxrange; ++$i ) {
        $hgt = $range[ $i ] / $max * $gheight;
        imageline( $img, $gx + $i, $gy, $gx + $i, $gy + $gheight - $hgt, $c2 );
        imageline( $img, $gx + $i, $gy + $gheight - $hgt, $gx + $i, $gy + $gheight, $c1 );
    }

} // function imagemaphist()
В результате выполнения этого кода в переменной $pre будет содержаться изображение, представляющее собой полосу из семи уменьшенных вариантов исходной картинки, с различными гамма-коэффициентами.
 

The employer

Новичок
Типовые операции с изображениями посредством GD (part 7)

PHP:
Преобразование одиночного изображения (часть 2)
    Матричное преобразование (convolution)
	    Особенности реализации в GD
        Матрицы для популярных трансформаций
            Размытие (blur) / Повышение четкости (sharpen)
            Яркость (brightness) / Контраст (contrast)
            Подчеркивание границ (edge detect) / выдавливание (embossing)
            Комбинирование эффектов
        Пример кода для экспериментов
Преобразование одиночного изображения (часть 2)

Матричное преобразование (convolution)

Матричным преобразованием занимается функция imageconvolution. Ей требуется четыре параметра: преобразуемое изображение, матрица преобразования, коэффициент нормализации (делитель), смещение.

Матрица преобразования должна быть двумерным массивом размерностью 3x3, каждый элемент массива представляет собой число с плавающей точкой. Делитель и смещение также представлены числами с плавающей точкой.

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

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

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


Особенности реализации в GD

Особенностей реализации в GD ровно три.

Во-первых, для преобразования библиотека неявным образом создает копию исходного изображения. Таким образом, потребность в памяти при использовании imageconvolution удваивается.

Во-вторых, при выполнении матричного преобразования из-за ошибки в GD полностью теряется альфа-канал исходного изображения. После преобразования изображение станет полностью непрозрачным (за исключением цвета, назначенного прозрачным при помощи imagecolortransparent).

В-третьих, при обсчете точек на краях изображения - в качестве точек окружения "за пределами" изображения берутся точки на краях.

Например, при обсчете точки с координатами (0, 0), в качестве окружения будут взяты точки с координатами (0, 0), (0, 0), (1, 0), (0, 0), (0, 0), (1, 0), (0, 1), (0, 1), (1, 1). Таким образом, точка (0, 0) поучаствует в качестве соседки самой себя трижды, а точки (1, 0) и (0, 1) поучаствуют дважды.


Матрицы для популярных трансформаций

Размытие (blur) / Повышение четкости (sharpen)

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

Простейшее размытие имеет вид:
PHP:
1 1 1
1 1 1 делитель 9, смещение 0
1 1 1
Усилить размытие можно следующим образом:
PHP:
1 1 1
1 0 1 делитель 8, смещение 0
1 1 1
Грубое приближение гауссового размытия будет таким:
PHP:
1 2 1
2 4 2 делитель 16, смещение 0
1 2 1
Можно ослабить размытие, исключив из него какие-то точки окружения, или придав центральной точке больше "веса":
PHP:
0 1 0
1 4 1 делитель 8, смещение 0
0 1 0
Повышение четкости - это операция обратная размытию. Чтобы добиться нужного эффекта, каждую точку изображения следует сделать чуть более отличающейся от своих соседей. Чтобы добиться этого, нужно инвертировать соседей и затем добавить эту разницу к цвету центральной точки.
PHP:
+0 -1  0
-1  5 -1 делитель 1, смещение 0
 0 -1  0
Усилить повышение четкости можно путем вычитания из центральной точки большего количества соседей.
PHP:
-1 -1 -1
-1  5 -1 делитель 1, смещение 0
-1 -1 -1
Другой способ повысить четкость - повысить весовой коэффициент соседей по отношению к центральной точке.
PHP:
+0 -2  0
-2  9 -2 делитель 1, смещение 0
 0 -2  0
Интересно, что ослабляя центральную точку - мы вместо повышения четкости получаем довольно мощное размытие. Что, впрочем, естественно.
PHP:
+0 -1  0
-1  2 -1 делитель -2, смещение 0
 0 -1  0
Если нужно более "мягкое" повышение четкости - нужно повышать весовой коэффициент центральной точки.
PHP:
+0 -1  0
-1  8 -1 делитель 4, смещение 0
 0 -1  0

Яркость (brightness) / Контраст (contrast)

Яркость определяет насколько цвет близок к белому. Контраст показывает, насколько отличаются самые темные и самые светлые участки изображения.

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

Чтобы повысить или понизить яркость изображения на некую величину, нужно установить соответствующее значение смещения (положительное или отрицательное).
PHP:
0  0  0
0  1  0 делитель 1, смещение -20
0  0  0
Чтобы повысить контрастность изображения, следует увеличить коэффициент центральной точки матрицы и одновременно дать некоторое отрицательное смещение.

Увеличение коэффициента способствует увеличению яркости более ярких точек, отрицательное смещение дает уменьшение яркости менее ярких точек (на них не так сильно действует множитель, поэтому смещения хватает для того чтобы увести их яркость в "ноль").
PHP:
0  0   0
0  1.4 0 делитель 1, смещение -40
0  0   0
Понижается контраст, соответственно, матрицей с уменьшенным коэффициентом центральной точки и небольшим положительным смещением.
PHP:
0  0   0
0  0.6 0 делитель 1, смещение 40
0  0   0

Подчеркивание границ (edge detect) / выдавливание (embossing)

Подчеркивание границ основано на тех же принципах, что и повышение четкости - пиксели, отличающиеся от своего окружения, будут подчеркнуты.

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

Выбирая, какие точки окружения учитывать, а какие нет - мы выбираем, какие границы на изображении (вертикальные, горизонтальные, или наклонные) будут подчеркнуты лучше.

Вот матрица, хорошо подчеркивающая все линии:
PHP:
-1 -1 -1
-1  8 -1 делитель 1, смещение 0
-1 -1 -1
Вот такая матрица лучше подчеркнет линии, близкие к вертикальным:
PHP:
+0  0  0
-1  1  0 делитель 1, смещение 0
 0  0  0
Интересно, что центральной точке можно вовсе не участвовать в формировании изображения - вот такая матрица подчеркнет вертикальные линии не хуже предыдущей:
PHP:
+0  0  0
-1  0  1 делитель 1, смещение 0
 0  0  0
Увеличивая "амплитуду" между крайними значениями положительных и отрицательных коэффициентов, мы добьемся более яркой подсветки границ:
PHP:
-2 -2  0
-2  0  2 делитель 1, смещение 0
 0  2  2
Увеличивая смещение, мы добьемся того, что наша картинка будет напоминать чеканку:
PHP:
-2 -2  0
-2  0  2 делитель 1, смещение 100
 0  2  2
Теперь, если мы составим матрицу так, чтобы сумма коэффициентов была равна единице - вместо темной картинки, на которой остались лишь контуры, мы получим исходное изображение с "подчеркнутыми" границами объектов. Это и есть эффект "выдавливания".
PHP:
-2 -2  0
-2  1  2 делитель 1, смещение 0
 0  2  2
Для усиления эффекта можно увеличивать амплитуду между суммой положительных и суммой отрицательных коэффициентов.

Выбор точек матрицы для регулирования коэффициентов определяет "направление освещенности" картинки, а размах амплитуды определяет еще и количество деталей, которые будут подчеркнуты этим эффектом.
PHP:
-8 -2  0
-2  1  2 делитель 1, смещение 0
 0  2  8

Комбинирование эффектов

Многие эффекты можно комбинировать в одной матрице трансформации. Вот пример матрицы для одновременного повышения четкости и контрастности:
PHP:
+0 -1  0
-1  6 -1 делитель 2, смещение -20
 0 -1  0

Пример кода для экспериментов

Для экспериментов с собственными матрицами трансформации можно воспользоваться следующим кодом:

PHP:
$img = imagecreatefromjpeg( $imagename );
$w = imagesx($img);
$h = imagesy($img);

// Параметры превьюшек
$w_preview = 300; // ширина каждого превью
$d_preview = 10;  // зазор между превьюшками на полосе

// Набор матриц трансформации в удобном для восприятия виде
$conv = <<<XXX
 0  0  0    -8 -2  0    -2 -2  0     0 -1  0    -1 -1 -1
 0  1  0    -2  1  2    -2  0  2    -1  6 -1    -1  8 -1
 0  0  0     0  2  8     0  2  2     0 -1  0    -1 -1 -1
    1           1           1           2           1
    0           0         100         -20           0     
XXX;

// Импорт матриц трансформации
preg_match_all( "/-?\d+(\.\d+)?/", $conv, $v );
$v = $v[0];
$npreview = count( $v ) / 11;

$conv   = array_chunk(
    array_chunk( array_splice( $v, 0, $npreview * 9 ), 3 )
  , $npreview
);
$div    = array_splice( $v, 0, $npreview );
$offset = array_splice( $v, 0, $npreview );

for( $i = 0; $i < $npreview; ++$i ) {
    $convmatrix[$i][0] = $conv[0][$i];
    $convmatrix[$i][1] = $conv[1][$i];
    $convmatrix[$i][2] = $conv[2][$i];
}

// Подготовка временных изображений для формирования превью
$ratio = $w_preview / $w;
$h_preview = intval($h * $ratio);

$pre = imagecreatetruecolor( ($w_preview + $d_preview) * $npreview - $d_preview, $h_preview );
imagealphablending( $pre, false );

$dst = imagecreatetruecolor( $w_preview, $h_preview );
imagealphablending( $dst, false );

// Формирование набора превью и сборка единого изображения
$pre_x = 0;
for( $i = 0; $i < $npreview; ++$i ) {
    imagecopyresampled( $dst, $img, 0, 0, 0, 0, $w_preview, $h_preview, $w, $h ); 
    imageconvolution( $dst, $convmatrix[$i], $div[$i], $offset[$i] );
    imagecopy( $pre, $dst, $pre_x, 0, 0, 0, $w_preview, $h_preview );
    $pre_x += $w_preview + $d_preview;
}

imagedestroy( $dst );
imagedestroy( $img );
В результате выполнения этого кода в переменной $pre будет содержаться изображение, представляющее собой полосу из пяти уменьшенных вариантов исходной картинки, к каждому из которых была применена своя матрица трансформации.
 

PlayTime

Новичок
Довольно не плохой FAQ. Я например не все примеры применения GD смог найти на русском. А так. Как говорят. С миру по нитке и голому сорочка.
 
Сверху