Суть проблеми

Працюючи над новинним сайтом, я зіткнувся з проблемою зберігання картинок до
статтями у двох форматах – дрібний (90 на 90 для заголовних блоків) і великий
(218 на скільки є). Новини зберігалися в базі і додавалися дуже просто. З
картинками виходило складніше. Вихідні зображення з цифрової фотокамери (або
цифрового макету паперової газети) завантажувалися в PhotoShop і оброблялися
дизайнером під потрібний формат. Вже сам факт ручної роботи з графікою змусив
замислитися про автоматизацію. Крім того, доводилося локально зберігати дві
картинки, а потім закачувати їх через форму окремо, та щоб ще і не
переплутати, де яка! Виникає надмірність, а розмір займаного місця
ставав все більше і більше.


Перші ідеї


З самого початку роботи над проектом я знав, що існує така річ, як
ImageMagick для Perl. Запустити цей чудо-модуль мені так і не вдалося. Потім я
перейшов на PHP, і зрозумів, що Perl не зовсім пристосований для таких речей. На PHP
теж можна формувати картинки за допомогою модуля GD. Він хоч і простіше буде, але
функції масштабування в ньому є, що і було потрібно.


Постановка завдання – написати скрипт, який отримує шлях вихідного
зображення і виводив би дані image/jpeg – Картинку потрібного
формату (див. опис протоколу HTTP).


Скрипт масштабування resize.php


Вхідні параметри:



Подивимося початок скрипта:


resize.php

<?php 

/ / F – ім'я файлу
/ / Type – спосіб масштабування
/ / Q – якість стиснення
/ / Src – вихідне зображення
/ / Dest – результуюче зображення
/ / W – Ширнусь зображення
/ / Ratio – коефіцієнт пропорційності
/ / Str – текстовий рядок

/ / Тип перетворення, якщо не вказані розміри
if ($ type == 0) $ w = 70; / / квадратна 70×70
if ($ type == 1) $ w = 90; / / квадратна 90×90
if ($ type == 2) $ w = 218; / / пропорційна шириною 218

/ / Якість jpeg за замовчуванням
if (!isset($q)) $q = 100;


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

 / / Створюємо вихідне зображення на основі
/ / Вихідного файлу і опеределяем його розміри
$src = imagecreatefromjpeg($f);
$w_src = imagesx($src);
$h_src = imagesy($src);

header("Content-type: image/jpeg");


У цій частині програми ми завантажуємо вихідне зображення в змінну
$src. Функції imagesx і
imagesy визначають розміри вихідної картинки і записують їх у
відповідні змінні. Вони нам знадобляться для обчислення коефіцієнта
пропорційності. Тут же за допомогою функції header передаємо
заголовок
Contenttype:
image/jpeg в браузер
користувача. Після цього він очікує, що наступний потік даних буде
jpeg-файлом.


Наступна умова відповідає за розмір виведеної картинки і безпосередньо за
висновок. Можливо, що вихідна картинка вже потрібного розміру. Якщо це НЕ
так
, Опрацюємо її. Для цього поставимо така умова:

 / / Якщо розмір початкового зображення
/ / Відрізняється від необхідного розміру
if ($w_src != $w)
{

Наступна частина програми буде підганяти картинку під ширину 218 пікселів
(Пропорційне зменшення) для випадку
$type=1.

 / / Операції для отримання прямокутного файлу
if ($type==2)
{
/ / Обчислення пропорцій
$ratio = $w_src/$w;
$w_dest = round($w_src/$ratio);
$h_dest = round($h_src/$ratio);

/ / Створюємо порожню картинку
/ / Важливо саме truecolor!, Інакше будемо мати 8-бітний результат
$dest = imagecreatetruecolor($w_dest,$h_dest);
$str = "foxweb.net.ru";
imagecopyresized ($ dest, $ src, 0, 0, 0, 0, $ w_dest, $ h_dest, $ w_src, $ h_src);


Спочатку обчислюємо коефіцієнт $ratio, Щоб відразу обчислити
розміри результуючої картинки $w_dest і
$h_dest. Потім створюємо нову картинку $dest з
таким ж розмірами, це заготовка для масштабированной картинки. Найскладніша
частина алгоритму – функція imagecopyresized. У ній я вказав
результуючу і вихідну картинку, координати лівого верхнього кута для обох
картинок (0, 0, 0,
0) І координати правого нижнього кута для обох картинок
($w_dest, $h_dest, $w_src,
$h_src). Ось офіційна довідка на цю функцію:


imagecopyresized – Копіює і змінює розміри частини
зображення


int imagecopyresized ( resource dst_im, resource src_im, int
dstX, int dstY, int srcX, int srcY, int dstW, int dstH, int srcW, int srcH)


imagecopyresized() копіює прямокутну частину одного
зображення в інше зображення. Dst_im – Результуюче
зображення, src_im – Вихідне зображення. Якщо вихідні і
координати розташування і ширини і висот відрізняються, буде виконано розтяг
або стиск фрагмента зображення. Початок координат – верхній лівий кут. Ця
функція може бути використана, щоб копіювати області в межах того ж
зображення (якщо dst_im і src_im збігаються),
але якщо області перекривають результати бути непередбачуваним.


Замість нього можна використати imagecopyresampled()
ресемплірованіе зі згладжуванням. Зображення вийде більш якісною.
Детальніше про параметри функції можна дізнатися в документації на сайті http://php.net/imagecopyresized/.


Наступна частина коду абсолютно необов'язкова. Вона друкує довільну
напис на готовій картинці. Причому, контрастним кольором: на темному тлі напис
буде біла, на світлому – чорна. Спочатку визначимо координати виведення тексту (в
нашому прикладі це внизу праворуч) і три кольори – $white,
$black і $gray за допомогою функції
imagecolorallocate. Щоб визначити, яким кольором друкувати
напис, порівняємо колір точки (функція imagecolorat), Де буде
напис, з «середнім» кольором $gray. Якщо колір точки виявиться
світліше сірого, то напис буде чорного кольору ($color =
$black
). Якщо колір точки виявиться темніше сірого, то напис буде
білого кольору ($color = $white). Нарешті визначаємо рядок
$str і наносимо її функцією imagestring, Вказавши
розмір шрифту, координати, рядок і колір. Пам'ятайте, що ця функція виводить
тільки латинські символи одним шрифтом. Хоча ніхто не забороняє вам користуватися
TrueType шрифтами, благо такі функції є.

 / / Визначаємо координати виведення тексту
$ Size = 2; / / розмір шрифту
$ X_text = $ w_dest-imagefontwidth ($ size) * strlen ($ str) -3;
$y_text = $h_dest-imagefontheight($size)-3;

/ / Визначаємо яким кольором на якому тлі виводити текст
$white = imagecolorallocate($dest, 255, 255, 255);
$black = imagecolorallocate($dest, 0, 0, 0);
$gray = imagecolorallocate($dest, 127, 127, 127);
if (imagecolorat ($ dest, $ x_text, $ y_text)> $ gray) $ color = $ black;
if (imagecolorat ($ dest, $ x_text, $ y_text) <$ gray) $ color = $ white;

/ / Виводимо текст
imagestring ($ dest, $ size, $ x_text-1, $ y_text-1, $ str, $ white-$ color);
imagestring ($ dest, $ size, $ x_text +1, $ y_text +1, $ str, $ white-$ color);
imagestring ($ dest, $ size, $ x_text +1, $ y_text-1, $ str, $ white-$ color);
imagestring ($ dest, $ size, $ x_text-1, $ y_text +1, $ str, $ white-$ color);

imagestring ($ dest, $ size, $ x_text-1, $ y_text, $ str, $ white-$ color);
imagestring ($ dest, $ size, $ x_text +1, $ y_text, $ str, $ white-$ color);
imagestring ($ dest, $ size, $ x_text, $ y_text-1, $ str, $ white-$ color);
imagestring ($ dest, $ size, $ x_text, $ y_text +1, $ str, $ white-$ color);

imagestring ($ dest, $ size, $ x_text, $ y_text, $ str, $ color);
}


Дещо складніше виглядає частина коду для отримання квадратного
фрагмента.

 / / Операції для отримання квадратного файлу
if (($type==0)||($type==1))
{
/ / Створюємо порожню квадратну картинку
/ / Важливо саме truecolor!, Інакше будемо мати 8-бітний результат
$dest = imagecreatetruecolor($w,$w);

/ / Вирізаємо квадратну серединку по x, якщо фото горизонтальне
if ($w_src>$h_src)
imagecopyresized($dest, $src, 0, 0,
round ((max ($ w_src, $ h_src)-min ($ w_src, $ h_src)) / 2),
0, $ w, $ w, min ($ w_src, $ h_src), min ($ w_src, $ h_src));

/ / Вирізаємо квадратну верхівку по y,
/ / Якщо фото вертикальне (хоча можна теж серединку)
if ($w_src<$h_src)
imagecopyresized($dest, $src, 0, 0, 0, 0, $w, $w,
min ($ w_src, $ h_src), min ($ w_src, $ h_src));

/ / Квадратна картинка масштабується без вирізок
if ($w_src=”http://www.ishodniki.ru/=$h_src)
imagecopyresized ($ dest, $ src, 0, 0, 0, 0, $ w, $ w, $ w_src, $ w_src);
}

/ / Вивід картинки та очищення пам'яті
imagejpeg($dest,””,$q);
imagedestroy($dest);
imagedestroy($src);
}


Ми не визначаємо коефіцієнт масштабування, оскільки картинка буде
квадратної заздалегідь відомого розміру. За розміром картинка може бути трьох типів
– Горизонтальна, вертикальна і квадратна. У перших двох випадках нам доведеться
вирізати і масштабувати квадратну область з прямокутної картинки. У
третьому – просто масштабувати.


Трохи поясню конструкцію: round((max($w_src, $h_src) – min($w_src,
$h_src)) / 2)
. Це вираз обчислює координату x
або y верхнього лівого кута квадратного блоку (без різниці для
горизонтальної або вертикальної картинки). Це видно на малюнках.














Горизонтальне зображення



Вертикальне зображення



Зазвичай в горизонтальних картинках (особи, пейзажі, об'єкти) найбільш
інформативна центральна частина, а у вертикальних (найчастіше це стоячі люди) –
найбільш важлива верхня частина. Щоб змінити вирізаний фрагмент вертикальної
картинці, рядок:

imagecopyresized($dest, $src, 0, 0, 0, 0, $w,
$w, min($w_src,$h_src), min($w_src,$h_src));

можна замінити на

imagecopyresized($dest, $src, 0, 0, 0,
round ((max ($ w_src, $ h_src)-min ($ w_src, $ h_src)) / 2),
$ W, $ w, min ($ w_src, $ h_src), min ($ w_src, $ h_src));

Тоді у вертикальній картинці буде братися не верхня частина, а середня (це
залежить від розташування об'єктів на фотографії). Точно так само можна брати нижню
частина або праву / ліву частину горизонтальної картинки – поекспериментуйте з
координатами. Особливо цей метод стане в нагоді, якщо потрібно створити квадратні
«Прев'юшки» (thumbnails) в одній таблиці.


В кінці виводимо картинку imagejpeg() і очищаємо пам'ять
imagedestroy().


Демонстрація масштабування


Щоб вам не доводилося гарячково міняти вхідні змінні в процесі
налагодження на локальному сервері, пропоную демо-сторінку, яка завантажує чотири
картинки різних форматів. Тестові вихідні картинки повинні зберігатися в поточній
директорії під іменами image1.jpg і
image2.jpg. Тут же повинен знаходитися файл
resize.php, Який на виведенні видає
jpeg-зображення.


resize.htm

<html>
<head>
<title></title>
</head>
<body>
<h3 align="center"> Демонстраційний приклад виміряти зображення </ h3>
<table align="center">
<tr align="center" valign="top">
<td> 90 * 90 <br> <img src="http://www.ishodniki.ru/"resize1.php?f=av.jpg&t=1&w=90&s=0" border=0> </ td>
<td> 90 * 90 <br> <img src="resize.php?f=news114.jpg&type=1" border=0> </ td>
</tr>
<tr align="center" valign="top">
<td> 218 * x <br> <img src="resize.php?f=av.jpg&type=2" border=0> </ td>
<td> 218 * x <br> <img src="resize.php?f=news114.jpg&type=2" border=0> </ td>
</tr>
</table>
</body>
</html>”

Просмотровщик картинок


Цей скрипт досить простий, він усього лише формує сторінку, посилання,
здійснює всю навігацію по каталогу картинок. Вся робота по завантаженню картинок
проводиться через resize.php. Його можна взяти
за основу простого веб-альбому.


preview.php

<html>
<head>
<title></title>
</head>
<body>
<?php

/ / Читаємо список jpg-файлів в масив list
$list = array();
if ($ dir = opendir (".")) / / успішне відкриття поточної директорії
{
/ / Перебираємо іменя файлів, отримані readdir
while (false !== ($file = readdir($dir)))
{
/ / Відбираємо тільки. Jpg, відсікаємо посилання "." і ".."
if ($ file! = "." & & $ file! = ".." & & preg_match ("/. jpg / i", $ file))
{
$ List [] = $ file; / / пишемо ім'я файлу в масив
$ I + +; / / лічильник файлів, потрібний для визначення меж масиву
}
}
closedir ($ dir); / / закриваємо директорію
}

/ / Змінні за замовчуванням, отримані через GET
if ($ f == "") $ f = $ list [0]; / / якщо файл не вказаний, беремо перший із списку
if ($ num == "") $ num = 0; / / номер запитаного файлу
if ($ type == "") $ type = 1; / / тип за умовчанням "середній"

/ / Заголовочек – ім'я файлу
echo "<p>$list[$num]</p>
";

/ / Визначаємо посилання з картинки аналізуючи type
/ / Клацаючи по картинці файл буде перезавантажуватися з новим type
if ($ type == 0) $ link = "href ="? type = 1 & num = $ num "title =" показати 90 "";
if ($ type == 1) $ link = "href ="? type = 2 & num = $ num "title =" показати 218 "";
if ($ type == 2) $ link = "href ="? type = full & num = $ num "title =" показати весь "";
if ($ type == "full") $ link = "href ="? type = 0 & num = $ num "title =" показати 70 "";
echo "<a $link> <img src="http://www.ishodniki.ru/"resize.php?f=$list[$num]&type=$type" border=0> </ a>";

/ / Визначаємо, крайні чи елементи списку
$num1 = $num-1;
if ($ num1 <0) $ num1 = $ i-1; / / якщо num вийшов за 0, перекладаємо його на 0
$num2 = $num+1;
if ($ num2> $ i-1) $ num2 = 0; / / якщо num вийшов за i, перекладаємо його i-1

/ / Посилання навігації
echo "<br> <a href="?type=$type&num=$num1"> перед </ a>
<a href="?type=$type&num=$num2"> слід </ a> ";

?>
</body>
</html>


Думаю, що скрипт досить добре коментувати, тому поясню лише суть.
Спочатку відбувається завантаження імен jpg-файлів в поточному каталозі в масив
$list. Потім завантажуємо картинку з потрібними
параметрами з скрипта resize.php, Причому на
картинці «висить» посилання на її збільшену копію (змінюється параметр
$type). В кінці формуємо посилання на наступну і попередню
картинку. Ось і все. В якості тестування Накидайте в каталог скрипта два
десятка фотографій і ви отримаєте простеньку «Переглядач». Навігацію, звичайно,
можна ускладнити, але тоді скрипт вже буде «сложненькім».


Чому не працює GD?


Я налагоджував скрипти в Windows, тому поясню рішення проблеми саме для
цієї ОС. Швидше за все, причина непрацездатності скрипта у відсутності модуля
php_gd.dll (Або більш новий php_gd2.dll).
Відкрийте директорію з інтерпретатором PHP, створіть директорію extensions, якщо
її немає. Знайдіть вищевказаний модуль – він є в повних дистрибутивах PHP.
Пропишіть в php. ini (Лежить в c: windows)
рядок extension = php_gd2.dll (А заодно і
extension_dir = “c:phpextensions”, Якщо такої немає). Шляхи
можуть бути іншими. Після цього все повинно запрацювати. Не «змішуйте» модулі
php_gd.dll і php_gd2.dll – Інтерпретатор буде
лаятися на дублювання функцій у бібліотеках.


Висновок


Ось такі цікаві речі можна зробити за допомогою пари графічних функцій –
зате яка економія на виготовленні та зберіганні дублюючих картинок. Чи не
PhotoShop звичайно, але для масового застосування це саме те. Картинок на сайті
дуже багато, якісно їх виводити необов'язково, головне щоб швидко і
просто. Скрипт вийшов не дуже універсальний, але з моїм завданням він
справляється чудово. Слід відзначити тонку різницю між функціями
imagecopyresized і imagecopyresampled. Перша
просто змінює розмір картинки без згладжування, вона працює швидко, але
результат – великі спотворення. Друга функція – ресемплірованіе, зміна
розмірів зі згладжуванням. Працює вона набагато повільніше, але картинка виходить
чистої, як в професійних графічних пакетах. Про ресемплірованіе можна
почитати тут.
Вибирати спосіб слід виходячи з ваших потреб – швидше або якісніше.
Можливо, ви реалізуєте інші ідеї: задавати довільний розмір
x і y, Наявність або відсутність напису, формат
вихідного файлу, малювання рамочки, водяних знаків, підписи на картинці шрифтами
TrueType, висновок картинки-заглушки у разі помилки.


Якщо хто помітив якісь дурні або смішні місця в програмах, чекаю ваших
коментарів.

Демонстрацію фотогалереї на основі описаної методики можна побачити на http://foxweb.net.ru/photo/.

Сайт, для якого це все спочатку розроблялася – http://58region.ru/. Правда там
використовується ускладнений варіант – resize.php
працює з трьома типами файлів, виводить напис за бажанням і виводить
картинку-заглушку в разі відсутності вихідного файлу.

Перевірити працездатність скриптів в онлайні і завантажити вихідні коди обох
версій можна тут http://foxweb.net.ru/test/resize/.


Завжди чекаю ваших коментарів. Удачи!

Схожі статті:


Сподобалася стаття? Ви можете залишити відгук або підписатися на RSS , щоб автоматично отримувати інформацію про нові статтях.

Коментарів поки що немає.

Ваш отзыв

Поділ на параграфи відбувається автоматично, адреса електронної пошти ніколи не буде опублікований, допустимий HTML: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

*

*