Форум: Форум PHPФорум ApacheФорум Регулярные ВыраженияФорум MySQLHTML+CSS+JavaScriptФорум FlashРазное
Новые темы: 0000000
Самоучитель PHP 5 / 6 (3 издание). Авторы: Кузнецов М.В., Симдянов И.В. PHP 5. На примерах. Авторы: Кузнецов М.В., Симдянов И.В., Голышев С.В. PHP на примерах (2 издание). Авторы: Кузнецов М.В., Симдянов И.В. Самоучитель MySQL 5. Авторы: Кузнецов М.В., Симдянов И.В. MySQL 5. В подлиннике. Авторы: Кузнецов М.В., Симдянов И.В.
ВСЕ НАШИ КНИГИ
Консультационный центр SoftTime

Форум PHP

Выбрать другой форум

 

Здравствуйте, Посетитель!

вид форума:
Линейный форум Структурный форум

тема: GD заблуждения
 
 автор: sim5   (23.04.2010 в 20:45)   письмо автору
933 байт
 
 

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

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

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

1. Расточительность

Стоит ли при ресайзе изображения в угоду универсальности использовать функцию imagecreatetruecolor()? Простейшая грубая проверка расхода памяти даст ответ на этот вопрос:
<?
$m 
memory_get_usage();
//используем в качестве подложки полноцветный ресурс
$im imagecreatetruecolor(80,80);
echo 
'Memory for truecolor: ' . (memory_get_usage() - $m) . ' bytes<br>';
//это высвобождение ресурса (в данном примере) 
//косвенно показывает важность этого действия
//часто забываемое начинающими
imagedestroy($im);
//используем в качестве подложки индексированную палитру
$im imagecreate(80,80);
echo 
'Memory for palette based: ' . (memory_get_usage() - $m) . ' bytes';

Результат наглядный и в комментариях не нуждается.

2. Расточительность & «почему не правильно работает, и в какую сторону копать?»

Предположим, что у нас есть два GIF изображения, одинаковые палитрами, но с разницей только в наличии прозрачности. Эти изображения даны в прикреплении. Будем накладывать это изображение «универсальной» функцией, которая использует imagecreatetruecolor() при создании подложки конечного изображения. Так как мы вправе ожидать, что изображение может содержать прозрачность, то мы будем указывать цвет подложки как прозрачный.

Немного о подложке. Нет необходимости при использовании ее как прозрачную подложку, заливать ее цветом – применять imagefill(). Почему так? При создании ресурса, само битовое поле его будет содержать в своих значениях 0 (черный цвет), так как собственно мы его еще не «разукрасили». Следовательно, если подложка должна быть прозрачной, то нам достаточно установить этот цвет и указать его как прозрачный, если нужна прозрачная подложка. Собственно мы можем, даже не применять функцию imagecolorallocate() к подложке (в данном случае), а в функции imagecolortransparent() укажем явно черный цвет, его шестнадцатеричное значение – 0x0.

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

В примере будем просто копировать исходное изображение на подложку, так как процессы происходящие с ресурсами такие же, как и при изменении размеров изображения. В общем, это не принципиально.
<?
$im 
imagecreatetruecolor(80,80);
//указываем прозрачность подложке
imagecolortransparent($im0x0);
//накладываем на подложку gif-изображение не имеющего прозрачности
$ims imagecreatefromgif('notrsp.gif');
imagecopy($im$ims00008080);
header("Content-type: image/gif");
imagegif($im);
//высвобождаем ресурсы
imagedestroy($im);
imagedestroy($ims);

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

3. Куда копать

«Копать» нужно в сторону функций работы с индексированной палитрой. Если нам необходимо накладывать GIF изображения, то нужно использовать подложку, основанную на индексированной палитре, то есть воспользоваться функцией imagecreate().

Во-первых, это сэкономит память, а во-вторых, прозрачными будут только те участки изображения, которые есть в источнике, при этом цвет прозрачности подложки не будет затрагивать такой же цвет у источника. Это будет похоже на то, как например, в Photoshop отключить фон изображения, при этом не важно, какой он по цвету. Другими словами в данном случае подложка просто холст, а мы рисуем кистью, цвет которой определяет изображение источник. Но при работе с индексированной палитрой, нужно установить цвет подложки (в отличие от того, как мы поступали ранее).
<?
$im 
imagecreate(80,80);
//установим цвет подложки
$color imagecolorallocate($im000);
//указываем прозрачность подложке
imagecolortransparent($im$color);
//накладываем на подложку gif-изображение имеющее прозрачность
$ims imagecreatefromgif('trsp.gif');
imagecopy($im$ims00008080);
header("Content-type: image/gif");
imagegif($im);
//высвобождаем ресурсы
imagedestroy($im);
imagedestroy($ims);

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

А теперь маленький трюк с определением прозрачности, что даст представление о том, что значит «выключить» подложку – переставим объявление прозрачного цвета, поместив его после функции копирования. Результат получим такой же, как и в случае применения для создания подложки функции imagecreatetruecolor() – то чего нам не надо.

4. Резюме

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

  Ответить  
 
 автор: neadekvat   (23.04.2010 в 21:37)   письмо автору
 
   для: sim5   (23.04.2010 в 20:45)
 

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

sim5, вы каким-то образом связаны с изображениями по роду деятельности или еще как-то? Давно заметил, что у вас в них познания достаточно глубокие =)

  Ответить  
 
 автор: sim5   (24.04.2010 в 07:19)   письмо автору
 
   для: neadekvat   (23.04.2010 в 21:37)
 

Если сказать, что я по образованию художник, то это не означает обязательных познаний цифровых изображений. Если сказать, что самым интрересным для меня занятием является мультимедиа, то это не означает обязательных познаний GD.
Мои познания в той или иной области глубоки на столько, на столько мне требуется углубиться в них, а на сколько это глубоко в действительности мне судить трудно.
Я никогда не пытаюсь познать все, меня интрересует только то, что требуется на данный момент времени, не более. Многое в РНР мне на данный момент и 300 лет не нужно, и я и пальцем не пошевелю, чтобы заинтересоваться этим, но если потребуется для работы, значит "зароюсь" по самое возможное.
Главное не знать функций, а понимать какая не просто подойдет (в РНР есть больное - избыток их), а является оптимальной. А это, в первую очередь, определит понимание задачи, правильная ее постановка, и это далеко от программирования как такового. Другое дело, знать, что лучше из двух:
4 / 2
или
4 >> 1
Если знать как будет в обеих случаях поступать процессор, то ответ будет очевиден. Но это уже тонкости, хотя они тоже могут помочь.

  Ответить  
 
 автор: Лена   (24.04.2010 в 00:07)   письмо автору
 
   для: sim5   (23.04.2010 в 20:45)
 

Хорошо рассказали. БОЛЬШОЕ СПАСИБО.
Теперь уже все понятно стало.
Вопросы:
По первому пункту.Почему разница в памяти только в два раза?
Палитра gif-изображения - менее 256 цветов, полноцветная палитра, если память не изменяет - 16 млн. цветов. Тогда разница должна быть в разы больше...

2 пункт недопоняла.
Берем подложку, делаем ее прозрачной. Сверху накладываем непрозрачное изображение. Значит, все цвета непрозрачного изображения должны остаться. Вот по каким законам черный цвет бордюра почему-то становится прозрачным? У нас получается как бы два изображения: подложка и накладываемый gif. Причем цвета в gif`е мы и пальцем не трогали. Или после наложения изображения одно на другое цвета смешиваются и это считается одним изображением, так что ли?
Меня интересует момент, почему черный цвет подложки цепляет черный цвет изображения?

3 пункт.
>Но при работе с индексированной палитрой, нужно установить цвет подложки

Почему нужно установить цвет? Почему он не черный? В индексированной палитре разве черного цвета нет?

И последний вопрос. Можно ли средствами GD до применения imagecreatetruecolor() или imagecreate() узнать, перед нами прозрачное изображение или нет? Если мы уже заботимся о памяти, давайте ее вообще не нагружать, если перед нами изображение "ненужной" прозрачности :)

Да, и про прозрачные png забыли написать. Сами о них говорили - смешение цветов и сохранение информации по альфа-каналу:

<?php
imagealphablending
($dest_imgfalse);
imagesavealpha($dest_imgtrue);
?>

  Ответить  
 
 автор: sim5   (24.04.2010 в 06:38)   письмо автору
 
   для: Лена   (24.04.2010 в 00:07)
 

Я ведь тоже хитрый и "козырей" не раскрываю. Нужно думать, и это освобождает меня от обязанностей прописывать все, от А до Я. :)

1.
А вы удалите из примера комментарии - расхода памяти стало меньше? Этот пример не показывает расхода памяти непосредственно занимаемой созданным gd-ресурсом, это расход памяти занимаемой процессом, а это не только открытый ресурс, но и память на объявленные переменные, обработка выражений... Сам вычислительный процесс будет выглядеть намного сложнее, нежели сами записи примера. Можно вычислить и память, отданную под ресурс, но столь ли важно это в данном случае? Уберите из примера функцию высвобождения открытого ресурса, посмотрите при этом расход памяти. Что это означает?

2 and blending 3.
Что такое РНР? Скорее это запорожское войско любящее вольность. Строгие языки программирования обяжут вас объявлять не только переменные, но и их тип, а также приучат определять объем памяти под них. И это правильно, это освобождает процессор от лишней рутины. Вольность же (процесс сам определит типы, зарезервирует память под них...) порождает некую расхлябанность. Привычка полностью полагаться на разум машины, зачастую приводит к не пониманию сути происходящих процессов.
Но и компьютер, какой бы он, ни был крутой, в сущности, тупая штуковина, по терминологии Задоронова - "тупой американец". :) Создавая gd-ресурс, процесс просто резервирует под него необходимый размер памяти. При этом "тупая машина" (gd-библиотека) интерпретирует эту память, как "пустую", занятую нулями во всех ее разрядах, но мы, то знаем, что пустое для изображения, это черное.
Мы это знаем, в отличие от машины, но кроме этого надо знать, что в полноцветном изображении (в созданном ресурсе) гарантированно будет присутствовать цвет с индексом 0 (черный) - диапазон цветов определен умолчанием. А вот в базовой палитре, совсем не обязательно, пока мы не определим этот цвет в палитре (при открытии gd-ресурса его палитра не определена - пустая). В этом можно убедиться простым способом:
<?
//создаем полноцветный ресурс
$im imagecreatetruecolor(80,80);
//получаем индекс цвета ресурса по первому его пикселю
$color imagecolorat($im00);
//посмотрим, какой цвет имеет этот пиксель
print_r(imagecolorsforindex($im$color));

Результат:
Array ( [red] => 0 [green] => 0 [blue] => 0 [alpha] => 0 )

Повторим тоже самое, но создав ресурс на базовой палитре (imagecreate). Попытка узнать цвет первого пикселя ресурса приведет к этому:

Warning: imagecolorsforindex() [function.imagecolorsforindex]: Color index 0 out of range in....

А если перед получением индекса цвета установить в палитре созданного ресурса черный цвет:
$color = imagecolorallocate($im, 0, 0, 0);
то получим результат аналогичный выше.

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

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

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

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

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

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

Можно лишь только добавить, что PNG, это не обязательно прозрачность и не обязательно наличие определяющего альфа-канала. Не надо забывать, что png-изображение может быть тоже основано на базовой палитре - PNG8. Что это означает, уже говорилось, ну а узнать каким является открытое изображение, в GD труда не составляет. Для этого можно воспользоваться двумя функциями:

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

imageistruecolor - возвращает истину, если открытое изображение является полноцветным (truecolor), в противном случае возвращает ложь.

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

Кстати, знание того, чем является базовая палитра, поможет не только в GD, но и при подготовке изображений для страницы в Photoshop или ином редакторе. Обратите внимание при сохранении изображения для веб в GIF и PNG8 форматах. Оптимизировать изображение можно, указав не только фиксированное число цветов в палитре таких изображений, выбираемого из списка, но и воспользовавшись пипеткой, выбрать и удалить дубликаты цветов одинаковые по индексу, либо очень близкие.

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

  Ответить  
 
 автор: Лена   (24.04.2010 в 16:03)   письмо автору
 
   для: sim5   (24.04.2010 в 06:38)
 

Все попробовала, спасибо, что объяснили.
>А вы удалите из примера комментарии - расхода памяти стало меньше?
И удаляла, и добавляла, расход памяти остался тем же.
А вот при удалении imagedestroy, понятно, памяти стало больше, мы же ее не освободили...

  Ответить  
Rambler's Top100
вверх

Rambler's Top100 Яндекс.Метрика Яндекс цитирования