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

Класс 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 пикселей (либо ширина будет ровно 500px, либо высота ровно 300px; вторая величина вычисляется с сохранением пропорций):

$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); // Красный
$transparentBlue = ImageColor::rgb(0, 0, 255, 0.5); // Полупрозрачный синий

Метод ImageColor::hex() позволяет задать цвет в шестнадцатеричном формате, как в CSS. Поддерживаются форматы #rgb, #rrggbb, #rgba и #rrggbbaa:

$color = ImageColor::hex("#F00"); // Красный
$transparentGreen = ImageColor::hex("#00FF0080"); // Полупрозрачный зелёный

Цвета можно использовать в других методах, таких как 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, string &$warnings=null)Image

Читает изображение из файла и возвращает его тип в $detectedFormat. Если вы передадите $warnings, все нефатальные предупреждения декодера изображения сохранятся в нём вместо того, чтобы выдаваться как предупреждение PHP; при их отсутствии он остаётся равным null.

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

Читает изображение из строки и возвращает его тип в $detectedFormat. Если вы передадите $warnings, все нефатальные предупреждения декодера изображения сохранятся в нём вместо того, чтобы выдаваться как предупреждение PHP; при их отсутствии он остаётся равным null.

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

Возвращает массив поддерживаемых типов изображений.

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

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

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

Возвращает индекс цвета пикселя в указанном месте изображения. Если изображение полноцветное, эта функция возвращает значение 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, int $alpha=0)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=0, 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 $x, int $y, int $width, int $height, ImageColor $color)void

Создаёт на изображении прямоугольник, залитый цветом $color, начиная с точки ($x, $y), шириной $width и высотой $height. Точка (0, 0) – левый верхний угол изображения.

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

Выполняет заливку, граница которой задана цветом $borderColor. Начальная точка заливки – ($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(): GdImage

Возвращает лежащий в основе ресурс изображения GD.

getWidth(): int

Возвращает ширину изображения.

interlace (?int $interlace=null)int

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

isTrueColor(): bool

Проверяет, является ли изображение полноцветным. (подробнее)

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 $x, int $y, int $width, int $height, ImageColor $color)void

Создаёт прямоугольник в заданных координатах, используя ширину и высоту.

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

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

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

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

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

rotate (float $angle, ImageColor $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 полные сведения об альфа-канале (в отличие от одноцветной прозрачности).

Nette включает этот флаг в конструкторе Image, поэтому изображение, загруженное или созданное через fromFile(), fromString() или fromBlank(), сохраняет свой альфа-канал. Учтите, что при режиме смешивания по умолчанию альфа рисуемого цвета используется только для смешивания результата и не сохраняется, поэтому, если нарисованная прозрачность должна уцелеть, сначала вызовите 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(). (подробнее)

getInterpolation(): int

Возвращает текущий метод интерполяции, используемый методами 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

Преобразует полноцветное изображение в палитровое. (подробнее)

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

Пишет заданный текст на изображении. (подробнее)

версия: 4.x