Робота із зображеннями

Клас Nette\Utils\Image дає змогу легко маніпулювати зображеннями, наприклад, змінювати розмір, обрізати, підвищувати різкість, малювати або з'єднувати кілька зображень.

PHP має великий набір функцій для роботи із зображеннями. Але їхній API не дуже зручний. Це був би не Nette Framework, якби не придумали сексуальний API.

Встановлення:

composer require nette/utils

У всіх прикладах передбачається, що псевдонім уже створено:

use Nette\Utils\Image;
use Nette\Utils\ImageColor;
use Nette\Utils\ImageType;

Створення зображення

Створіть нове істинно кольорове зображення, наприклад, розміром 100×200:

$image = Image::fromBlank(100, 200);

За бажанням можна вказати колір фону (за замовчуванням чорний):

$image = Image::fromBlank(100, 200, ImageColor::rgb(125, 0, 0));

Або завантажте зображення з файлу:

$image = Image::fromFile('nette.jpg');

Збереження зображення

Зображення можна зберегти у файл:

$image->save('resampled.jpg');

Ми можемо вказати якість стиснення в діапазоні 0…100 для JPEG (за замовчуванням 85), WEBP (за замовчуванням 80) і AVIF (за замовчуванням 30) та 0…9 для PNG (за замовчуванням 9):

$image->save('resampled.jpg', 80); // JPEG, 80% якості

Якщо формат не очевидний з розширення файлу, його можна вказати за допомогою константи:

$image->save('resampled.tmp', null, ImageType::JPEG);

Зображення може бути записано не на диск, а у змінну:

$data = $image->toString(ImageType::JPEG, 80); // JPEG, 80% якості

або надсилається безпосередньо в браузер із відповідним HTTP-заголовком Content-Type:

// відправляє заголовок Content-Type: image/png
$image->send(ImageType::PNG);

Формати

Підтримуються формати JPEG, PNG, GIF, WebP, AVIF і BMP. Однак вони також повинні підтримуватися вашою версією PHP, що можна перевірити за допомогою функції isTypeSupported( ). Анімація не підтримується.

Формати представлені константами ImageType::JPEG, ImageType::PNG, ImageType::GIF, ImageType::WEBP, ImageType::AVIF і ImageType::BMP.

$supported = Image::isTypeSupported(ImageType::JPEG);

Потрібно визначити формат зображення при завантаженні? Метод повертає його у другому параметрі:

$image = Image::fromFile('nette.jpg', $type);

Власне визначення без завантаження зображення виконується за допомогою Image::detectTypeFromFile().

Змінити розмір

Частою операцією є зміна розміру зображення. Фактичні розміри повертаються методами getWidth() та getHeight().

Метод resize() використовується для зміни розміру зображення так, щоб воно не перевищувало 500×300 пікселів (або ширина буде рівно 500 px, або висота рівно 300 px, один із розмірів обчислюється для збереження співвідношення сторін):

$image->resize(500, 300);

Можна вказати тільки один вимір, а інший буде розраховано:

$image->resize(500, null); // ширина 500px, висота розраховується

$image->resize(null, 300); // ширина розрахована, висота 300px

Будь-який вимір може бути вказано у відсотках:

$image->resize('75%', 300); // 75 % × 300px

На поведінку resize можуть впливати такі симптоми. Усі, крім Image::Stretch, зберігають співвідношення сторін.

Прапор Опис
Image::OrSmaller (за замовчуванням) результуючі розміри будуть менші або дорівнювати запитуваним розмірам.
Image::OrBigger заповнює (і, можливо, перевищує в одному вимірі) цільову область
Image::Cover заповнює цільову область і обрізає те, що виходить за її межі.
Image::ShrinkOnly тільки зменшення (дозволяє уникнути розтягування маленького зображення)
Image::Stretch не зберігати співвідношення сторін

Прапори передаються як третій аргумент функції:

$image->resize(500, 300, Image::OrBigger);

Прапори можна комбінувати:

$image->resize(500, 300, Image::ShrinkOnly | Image::Stretch);

Зображення можна перевернути по вертикалі або горизонталі, вказавши один із розмірів (або обидва) як від'ємне число:

$flipped = $image->resize(null, '-100%'); // перевернемо вертикально

$flipped = $image->resize('-100%', '-100%'); // повертаємо на 180

$flipped = $image->resize(-125, 500); // змінити розмір та перевернути по горизонталі

Після зменшення зображення можна поліпшити його зовнішній вигляд за допомогою тонкого налаштування різкості:

$image->sharpen();

Рослинництво

Для обробітку використовується метод crop():

$image->crop($left, $top, $width, $height);

Як і у випадку з resize(), усі значення можуть бути представлені у відсотках. Відсотки для $left і $top розраховуються з простору, що залишився, аналогічно властивості CSS background-position:

$image->crop('100%', '50%', '80%', '80%');

Зображення можна також автоматично обрізати, наприклад, обрізати чорні межі:

$image->cropAuto(IMG_CROP_BLACK);

Метод cropAuto() є об'єктною заміною функції imagecropauto(), більш детальну інформацію див. у документації до неї.

Кольори

Метод ImageColor::rgb() дозволяє визначити колір за допомогою значень червоного, зеленого та синього (RGB). За бажанням, ви також можете вказати значення прозорості в діапазоні від 0 (повністю прозорий) до 1 (повністю непрозорий), так само, як і в CSS.

$color = ImageColor::rgb(255, 0, 0); // Red
$transparentBlue = ImageColor::rgb(0, 0, 255, 0.5); // Semi-transparent blue

Метод ImageColor::hex() дозволяє визначити колір за допомогою шістнадцяткового формату, подібно до CSS. Він підтримує формати #rgb, #rrggbb, #rgba і #rrggbbaa:

$color = ImageColor::hex("#F00"); // Red
$transparentGreen = ImageColor::hex("#00FF0080"); // Semi-transparent green

Кольори можна використовувати і в інших методах, таких як ellipse(), fill() тощо.

Малювання та редагування

Ви можете малювати, можете писати, можете використовувати всі функції PHP для маніпулювання зображеннями, див. Огляд методів, але в об'єктно-орієнтованій обгортці:

$image->filledEllipse($centerX, $centerY, $width, $height, ImageColor::rgb(255, 0, 0));

Оскільки функції PHP для малювання прямокутників непрактичні через необхідність вказувати координати, клас Image пропонує їх заміну у вигляді функцій rectangleWH( ) і filledRectangleWH().

Об'єднання декількох зображень

Ви можете легко вставити інше зображення у фотографію:

$logo = Image::fromFile('logo.png');
$blank = Image::fromBlank(320, 240, ImageColor::rgb(52, 132, 210));

// координати можуть бути знову задані у відсотках
$blank->place($logo, '80%', '80%'); // вставте в правий нижній кут

Альфаканал дотримується під час вставки, і ми можемо впливати на прозорість вставленого зображення (ми створюємо водяний знак):

$blank->place($image, '80%', '80%', 25); // прозорість становить 25%

Цей API – справжнє задоволення від використання!

Огляд методів

static fromBlank(int $width, int $height, ImageColor $color=null)Image

Створює нове істинно кольорове зображення заданих розмірів. За замовчуванням використовується чорний колір.

static fromFile(string $file, int &$detectedFormat=null)Image

Читає зображення з файлу і повертає його тип в $detectedFormat.

static fromString(string $s, int &$detectedFormat=null)Image

Читає зображення з рядка і повертає його тип в $detectedFormat.

static rgb(int $red, int $green, int $blue, int $transparency=0)array

Ця функція була замінена на клас ImageColor, див. кольори.

static typeToExtension(int $type)string

Повертає розширення файлу для заданого типу.

static typeToMimeType(int $type)string

Повертає тип mime для заданого типу.

static extensionToType(string $extension)int

Повертає тип зображення відповідно до розширення файлу.

static detectTypeFromFile(string $file, int &$width=null, int &$height=null)?int

Повертає тип файлу зображення, а в параметрах $width та $height також його розміри.

static detectTypeFromString(string $s, int &$width=null, int &$height=null)?int

Повертає тип зображення з рядка, а в параметрах $width та $height також його розміри.

static isTypeSupported(int $type)bool

Визначає, чи підтримується даний тип зображення.

static getSupportedTypes(): array

Повертає масив підтримуваних типів зображень (константи ImageType::XXX).

static calculateTextBox(string $text, string $fontFile, float $size, float $angle=0, array $options=[])array

Обчислює розміри прямокутника, який охоплює текст заданого шрифту та розміру. Повертає асоціативний масив, що містить ключі left, top, width, height. Ліве поле може бути від'ємним, якщо текст починається з лівого відступу.

affine(array $affine, array $clip=null)Image

Повертає зображення, що містить афінно-трансформоване зображення src з використанням необов'язкової області обрізання. (докладніше).

affineMatrixConcat(array $m1, array $m2)array

Повертає конкатенацію двох матриць афінного перетворення, що корисно, якщо до одного зображення необхідно застосувати одразу кілька перетворень. (докладніше)

affineMatrixGet(int $type, mixed $options=null)array

Повертає матрицю перетворення матриці. (докладніше)

alphaBlending(bool $on): void

Дозволяє використовувати два різні режими малювання в триколірних зображеннях. У режимі накладення компонент альфа-каналу кольору, який використовується у всіх функціях малювання, таких як setPixel(), визначає, якою мірою базовий колір повинен просвічувати. У результаті в цей момент наявний колір автоматично змішується з кольором малюнка, і результат зберігається в зображенні. У результаті піксель стає непрозорим. У режимі без змішування колір мультфільму копіюється дослівно з інформацією альфа-каналу і замінюється на цільовий піксель. Режим накладення недоступний під час малювання на зображеннях палітри. (докладніше)

antialias(bool $on): void

Активація малювання згладжених ліній і багатокутників. Не підтримує альфа-канали. Працює тільки з триколірними зображеннями.

Використання згладженого примітиву з прозорим кольором фону може призвести до несподіваних результатів. Метод змішування використовує колір фону як будь-який інший колір. (докладніше)

arc(int $centerX, int $centerY, int $width, int $height, int $startAngle, int $endAngle, ImageColor $color)void

Малює дугу кола з центром у заданих координатах. (докладніше)

colorAllocate(int $red, int $green, int $blue)int

Повертає ідентифікатор кольору, що представляє колір, який складається із заданих компонентів RGB. Повинен бути викликаний для створення кожного кольору, який буде використовуватися в зображенні. (докладніше)

colorAllocateAlpha(int $red, int $green, int $blue, int $alpha)int

Діє так само, як і colorAllocate(), з додаванням параметра прозорості $alpha. (докладніше)

colorAt(int $x, int $y)int

Повертає індекс кольору пікселя у вказаному місці зображення. Якщо зображення є truecolor, ця функція повертає значення RGB для даного пікселя у вигляді цілого числа. Використовуйте зсув бітів і бітову маску для доступу до окремих значень для червоного, зеленого і синього компонентів. (докладніше)

colorClosest(int $red, int $green, int $blue)int

Повертає індекс кольору в палітрі зображення, який “найближче” до вказаного значення RGB. Відстань" між бажаним кольором і кожним кольором у палітрі розраховується так, як якщо б значення RGB були точками в тривимірному просторі. (докладніше)

colorClosestAlpha(int $red, int $green, int $blue, int $alpha)int

Повертає індекс кольору в палітрі зображень, який “найближче” до вказаного значення RGB і рівня $alpha. (докладніше)

colorClosestHWB(int $red, int $green, int $blue)int

Отримати індекс кольору, який має відтінок, білий і чорний кольори, найближчі до заданого кольору. (докладніше)

colorDeallocate(int $color)void

Видаляє колір, раніше призначений за допомогою colorAllocate() або colorAllocateAlpha(). (докладніше)

colorExact(int $red, int $green, int $blue)int

Повертає індекс зазначеного кольору в палітрі зображення. (докладніше)

colorExactAlpha(int $red, int $green, int $blue, int $alpha)int

Повертає індекс зазначеного кольору + альфа в палітрі зображень. (докладніше)

colorMatch(Image $image2)void

Поєднує кольори палітри з кольорами іншої панелі. (докладніше)

colorResolve(int $red, int $green, int $blue)int

Повертає індекс кольору для бажаного кольору, або точний колір, або найближчий можливий альтернативний. (докладніше)

colorResolveAlpha(int $red, int $green, int $blue, int $alpha)int

Повертає індекс кольору для бажаного кольору, або точний колір, або найближчий можливий альтернативний. (докладніше)

colorSet(int $index, int $red, int $green, int $blue)void

Встановлює вказаний індекс у палітрі на вказаний колір. (докладніше)

colorsForIndex(int $index)array

Отримує колір зазначеного індексу. (докладніше)

colorsTotal(): int

Повертає кількість кольорів у палітрі зображення. (докладніше)

colorTransparent(int $color=null)int

Отримує або встановлює прозорий колір зображення. (докладніше)

convolution(array $matrix, float $div, float $offset)void

Застосовує матрицю згортки до зображення, використовуючи заданий коефіцієнт і зміщення. (докладніше)

Вимагає наявності Bundled GD extension, тому може працювати не скрізь.

copy(Image $src, int $dstX, int $dstY, int $srcX, int $srcY, int $srcW, int $srcH)void

Копіює частину $src у зображення, що починається в координатах $srcX, $srcY з шириною $srcW і висотою $srcH. Певна частина буде скопійована в координати $dstX і $dstY. (докладніше)

copyMerge(Image $src, int $dstX, int $dstY, int $srcX, int $srcY, int $srcW, int $srcH, int $opacity)void

Копіює частину $src у зображення, що починається в координатах $srcX, $srcY з шириною $srcW і висотою $srcH. Певна частина буде скопійована в координати $dstX і $dstY. (докладніше)

copyMergeGray(Image $src, int $dstX, int $dstY, int $srcX, int $srcY, int $srcW, int $srcH, int $opacity)void

Копіює частину $src у зображення, що починається в координатах $srcX, $srcY з шириною $srcW і висотою $srcH. Певна частина буде скопійована в координати $dstX і $dstY.

Ця функція ідентична copyMerge(), за винятком того, що вона зберігає вихідний відтінок під час об'єднання, перетворюючи цільові пікселі у відтінки сірого перед операцією копіювання. (докладніше)

copyResampled(Image $src, int $dstX, int $dstY, int $srcX, int $srcY, int $dstW, int $dstH, int $srcW, int $srcH)void

Копіює прямокутну частину одного зображення в інше зображення, плавно інтерполюючи значення пікселів так, щоб при зменшенні розміру зображення зберігало високу чіткість.

Іншими словами, copyResampled() бере прямокутну область з $src шириною $srcW і висотою $srcH у позиції ($srcX, $srcY) і поміщає її в прямокутну область зображення шириною $dstW і висотою $dstH у позиції ($dstX, $dstY).

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

copyResized(Image $src, int $dstX, int $dstY, int $srcX, int $srcY, int $dstW, int $dstH, int $srcW, int $srcH)void

Копіює прямокутну частину одного зображення на інше зображення. Інакше кажучи, copyResized() отримує прямокутну область з $src шириною $srcW і висотою $srcH у позиції ($srcX, $srcY) і поміщає її в прямокутну область зображення шириною $dstW ] і висотою $dstH у позиції ($dstX, $dstY).

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

crop(int|string $left, int|string $top, int|string $width, int|string $height)Image

Обрізає зображення до заданої прямокутної області. Розміри можуть бути вказані як цілі числа в пікселях або рядки у відсотках (наприклад, '50%').

cropAuto(int $mode=-1, float $threshold=.5, ?ImageColor $color=null)Image

Автоматичне кадрування зображення відповідно до заданого $mode. (докладніше)

ellipse(int $centerX, int $centerY, int $width, int $height, ImageColor $color)void

Малює еліпс із центром у заданих координатах. (докладніше)

fill(int $x, int $y, ImageColor $color)void

Заповнює область, що починається в заданій координаті (зліва вгорі 0, 0), заданим $color. (докладніше)

filledArc(int $centerX, int $centerY, int $width, int $height, int $startAngle, int $endAngle, ImageColor $color, int $style)void

Малює неповну дугу з центром у заданих координатах. (докладніше)

filledEllipse(int $centerX, int $centerY, int $width, int $height, ImageColor $color)void

Малює еліпс із центром у заданих координатах. (докладніше)

filledPolygon(array $points, ImageColor $color)void

Створює заповнений багатокутник на зображенні. (докладніше)

filledRectangle(int $x1, int $y1, int $x2, int $y2, ImageColor $color)void

Створює прямокутник, заповнений $color на зображенні, що починається з $x1 та $y1 і закінчується $x2 та $y2. Точка 0, 0 є верхнім лівим кутом зображення. (докладніше)

filledRectangleWH(int $left, int $top, int $width, int $height, ImageColor $color)void

Створює на зображенні прямокутник, заповнений $color, починаючи з точок $left та $top з шириною $width та висотою $height. Точка 0, 0 є верхнім лівим кутом зображення.

fillToBorder(int $x, int $y, int $border, ImageColor $color)void

Створює заливку, колір межі якої визначається $border. Початкова точка заливки – $x, $y (лівий верхній кут – 0, 0), а область заливається кольором $color. (докладніше)

filter(int $filtertype, int …$args)void

Застосовує заданий фільтр $filtertype до зображення. (докладніше)

flip(int $mode): void

Інвертує зображення за заданою адресою $mode. (докладніше)

ftText(float $size, float $angle, int $x, int $y, ImageColor $color, string $fontFile, string $text, array $options=[])array

Напишіть текст на картинці. (більше)

gammaCorrect(float $inputgamma, float $outputgamma)void

Застосувати гамма-корекцію до зображення відносно вхідної та вихідної гами. (докладніше)

getClip(): array

Повертає поточний обріз, тобто область, за межами якої не будуть малюватися пікселі. (докладніше)

getHeight(): int

Повертає висоту зображення.

getImageResource(): resource|GdImage

Повертає вихідний ресурс.

getWidth(): int

Повертає ширину зображення.

interlace(int $interlace=null)int

Увімкнення або вимкнення режиму черезрядкової розгортки. Якщо встановлено черезрядковий режим і зображення зберігається у форматі JPEG, воно буде збережено як прогресивний JPEG. (докладніше)

isTrueColor(): bool

Визначте, чи є зображення truecolor. (докладніше)

layerEffect(int $effect)void

Встановіть прапор альфа-змішання для використання ефектів нашарування. (докладніше)

line(int $x1, int $y1, int $x2, int $y2, ImageColor $color)void

Проводить лінію між двома заданими точками. (докладніше)

openPolygon(array $points, ImageColor $color)void

Малює відкритий багатокутник на зображенні. На відміну від polygon(), між останньою і першою точкою не проводиться лінія. (докладніше)

paletteCopy(Image $source)void

Копіює палітру з сайту $source у зображення. (докладніше)

paletteToTrueColor(): void

Перетворює зображення на основі палітри в повнокольорове зображення. (докладніше)

place(Image $image, int|string $left=0, int|string $top=0, int $opacity=100)Image

Копіює $image у зображення за координатами $left і $top. Координати можуть бути вказані як цілі числа в пікселях або рядки у відсотках (наприклад, '50%').

polygon(array $points, ImageColor $color)void

Створює багатокутник на зображенні. (докладніше)

rectangle(int $x1, int $y1, int $x2, int $y2, ImageColor $color)void

Створює прямокутник за заданими координатами. (докладніше)

rectangleWH(int $left, int $top, int $width, int $height, ImageColor $color)void

Створює прямокутник за заданими координатами.

resize(int|string $width, int|string $height, int $flags=Image::OrSmaller)Image

Зміна розмірів зображення, додаткова інформація. Розміри можуть бути вказані як цілі числа в пікселях або рядки у відсотках (наприклад, '50%').

resolution(int $resX=null, int $resY=null)mixed

Встановлює або повертає роздільну здатність зображення в DPI (точках на дюйм). Якщо жоден із додаткових параметрів не вказано, поточну роздільну здатність повертають у вигляді індексованого поля. Якщо вказано тільки $resX, то горизонтальна і вертикальна роздільна здатність встановлюється на це значення. Якщо вказано обидва додаткові параметри, горизонтальну та вертикальну роздільну здатність встановлюють на ці значення.

Роздільна здатність використовується як метаінформація тільки під час читання і запису зображень у формати, що підтримують таку інформацію (наразі це PNG і JPEG). Це не впливає ні на які операції малювання. Роздільна здатність нових зображень за замовчуванням становить 96 DPI. (докладніше)

rotate(float $angle, int $backgroundColor)Image

Повертає зображення на вказане значення $angle у градусах. Центром обертання є центр зображення, і повернуте зображення може мати розміри, відмінні від розмірів вихідного зображення. (докладніше)

Вимагає наявності Bundled GD extension, тому може працювати не скрізь.

save(string $file, int $quality=null, int $type=null)void

Зберігає зображення у файл.

Якість стиснення знаходиться в діапазоні 0…100 для JPEG (за замовчуванням 85), WEBP (за замовчуванням 80) і AVIF (за замовчуванням 30) та 0…9 для PNG (за замовчуванням 9). Якщо тип не очевидний з розширення файлу, ви можете вказати його за допомогою однієї з констант ImageType.

saveAlpha(bool $saveflag)void

Встановлює прапор збереження повної інформації альфа-каналу (на відміну від монохромної прозорості) під час збереження зображень PNG.

Для збереження альфа-каналу альфа-квантування має бути вимкнено (alphaBlending(false)). (докладніше)

scale(int $newWidth, int $newHeight=-1, int $mode=IMG_BILINEAR_FIXED)Image

Масштабування зображення з використанням заданого алгоритму інтерполяції. (докладніше)

send(int $type=ImageType::JPEG, int $quality=null)void

Виводить зображення в браузер.

Якість стиснення знаходиться в діапазоні 0…100 для JPEG (за замовчуванням 85), WEBP (за замовчуванням 80) і AVIF (за замовчуванням 30) та 0…9 для PNG (за замовчуванням 9).

setBrush(Image $brush)void

Встановлює зображення пензля, яке буде використовуватися у всіх функціях малювання ліній (наприклад, line() і polygon()) під час малювання спеціальними кольорами IMG_COLOR_BRUSHED або IMG_COLOR_STYLEDBRUSHED. (докладніше)

setClip(int $x1, int $y1, int $x2, int $y2)void

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

setInterpolation(int $method=IMG_BILINEAR_FIXED)void

Задає метод інтерполяції, який впливає на методи rotate() і affine(). (докладніше)

setPixel(int $x, int $y, ImageColor $color)void

Малює піксель у вказаній координаті. (докладніше)

setStyle(array $style)void

Задає стиль, який буде використовуватися всіма функціями малювання ліній (наприклад, line() і polygon()) під час малювання спеціальним кольором IMG_COLOR_STYLED або ліній зображення кольором IMG_COLOR_STYLEDBRUSHED (докладніше).

setThickness(int $thickness)void

Встановлює товщину ліній під час малювання прямокутників, багатокутників, дуг тощо. На сайті $thickness пікселів. (докладніше)

setTile(Image $tile)void

Встановлює зображення плитки, яке буде використовуватися у всіх функціях заповнення регіону (наприклад, fill() і filledPolygon()) при заповненні спеціальним кольором IMG_COLOR_TILED.

Плитка – це зображення, що використовується для заповнення області повторюваним малюнком. Як плитку можна використовувати будь-яке зображення, а задавши індекс прозорого кольору зображення плитки за допомогою colorTransparent(), можна створити плитку, в якій будуть просвічувати певні частини нижчого регіону. (докладніше)

sharpen(): Image

Підвищує різкість зображення.

Вимагає наявності Bundled GD extension, тому може працювати не скрізь.

toString(int $type=ImageType::JPEG, int $quality=null)string

Зберігає зображення в рядку.

Якість стиснення знаходиться в діапазоні 0…100 для JPEG (за замовчуванням 85), WEBP (за замовчуванням 80) і AVIF (за замовчуванням 30) та 0…9 для PNG (за замовчуванням 9).

trueColorToPalette(bool $dither, int $ncolors)void

Перетворює truecolor зображення в палітру. (докладніше)

ttfText(float $size, float $angle, int $x, int $y, ImageColor $color, string $fontFile, string $text, array $options=[])array

Записує заданий текст у зображення. (докладніше)

версію: 4.0