Глава 19 – Растровые изображения

Сталкивались ли вы с ситуацией, когда «всё работает на вашем ПК», а у клиента внезапно не грузятся PNG/GIF/WEBP — и вы получаете странные баг-репорты вместо благодарности? Или когда одно неосторожное преобразование превращает картинку в «мыло», а попытка поправить пиксели делает код медленным и хрупким?

Эта глава раскроет, почему профессиональные Qt-разработчики разделяют работу с растром на два мира — и как это решение экономит часы отладки, ускоряет вывод и повышает качество графики. Здесь обнаружите, где лежит граница между точностью и скоростью, раскроем типичные ловушки форматов, и узнаете секрет, как избежать «невидимых» проблем деплоя.

Будут разобраны 2 ключевых класса (QImage vs QPixmap), несколько операций над пикселями (яркость, инверсия, масштабирование, отражение) и практичный кейс с scanLine(), который показывает, где реально рождается производительность. Плюс — нюансы форматов (PNG/JPEG/WEBP/GIF/XPM) и что именно нужно не забыть при развертывании через windeployqt/macdeployqt.

Если хочется перестать «лечить симптомы» и начать управлять изображениями в Qt уверенно — эту главу лучше не пропускать.

В этой главе вы найдёте готовые к использованию примеры кода.

Самопроверка по главе

В чём ключевое отличие контекстно-зависимого представления от контекстно-независимого при работе с растровыми изображениями?Ответ
Правильный ответ: Контекстно-зависимое (QPixmap) отображается быстрее, так как соответствует текущему графическому режиму видеокарты и не требует преобразований, но доступ к отдельным пикселам медленный. Контекстно-независимое (QImage) позволяет эффективно работать с каждым пикселом и сохранять данные в форматах, не поддерживаемых видеокартой.
Почему формат Format_ARGB32_Premultiplied является предпочтительным при использовании QImage как контекста рисования?Ответ
Правильный ответ: Этот формат оптимизирован для операций рисования в Qt, так как цветовые компоненты уже предумножены на значение альфа-канала, что ускоряет операции смешивания цветов при композитинге.
Зачем нужен класс QPixmapCache и в каких ситуациях его использование наиболее оправдано?Ответ
Правильный ответ: QPixmapCache кеширует часто используемые изображения в памяти, избегая повторных загрузок из файлов. Особенно полезен для иконок и элементов интерфейса, которые отображаются многократно.
Почему формат XPM считается неэкономичным по дисковому пространству, но всё же имел популярность в прошлом?Ответ
Правильный ответ: XPM хранит изображение как исходный код на C, что занимает больше места, чем бинарные форматы. Его использовали для возможности включения изображений прямо в код программы, но с появлением системы ресурсов Qt эта необходимость отпала.
В каких случаях следует выбрать QImage, а в каких QPixmap для работы с растровыми изображениями?Ответ
Правильный ответ: QImage используйте, когда нужна попиксельная обработка, сохранение/загрузка файлов или работа с форматами, не поддерживаемыми видеокартой. QPixmap — для быстрого отображения на экране без модификации пикселов.
Почему метод scanLine() класса QImage эффективнее прямого обращения к каждому пикселу через pixel()?Ответ
Правильный ответ: scanLine() возвращает прямой указатель на строку пикселов в памяти, позволяя последовательно обрабатывать их без повторных вызовов методов, что значительно ускоряет массовые операции над изображением.
Что произойдёт, если попытаться использовать метод pixelIndex() для изображения формата Format_RGB32?Ответ
Правильный ответ: Метод pixelIndex() работает только для формата Format_Indexed8, где пикселы хранят индексы цветовой палитры. Для других форматов, включая Format_RGB32, нужно использовать метод pixel(), возвращающий непосредственно RGB-значение.
Почему при передаче клиентам Qt-приложения с поддержкой графики необходимо включать плагины форматов изображений?Ответ
Правильный ответ: Qt использует систему плагинов для поддержки различных графических форматов. Без соответствующих DLL/SO файлов из каталога imageformats приложение не сможет загружать изображения некоторых форматов, даже если код написан правильно.
Как создать прозрачное окно нестандартной формы в Qt и какие проблемы это создаёт для пользовательского интерфейса?Ответ
Правильный ответ: Установите атрибут Qt::WA_TranslucentBackground и используйте изображение с альфа-каналом. Основная проблема — отсутствие стандартного заголовка окна, поэтому нужно самостоятельно реализовать перемещение окна через обработку событий мыши и добавить кнопку закрытия.
Зачем в функции brightness() из примера создаётся копия изображения вместо изменения оригинала?Ответ
Правильный ответ: Это следует принципу неизменяемости входных данных и позволяет использовать одно исходное изображение для создания нескольких вариантов с разной яркостью без повторной загрузки из файла.
Почему формат WEBP считается универсальным для веб-приложений?Ответ
Правильный ответ: WEBP поддерживает как сжатие с потерями (как JPEG), так и без потерь (как PNG), плюс прозрачность и анимацию (как GIF), при этом обеспечивая лучшее соотношение качества и размера файла.
Что означает проверка r > 255 ? 255 : r < 0 ? 0 : r в функции brightness() и почему она критически важна?Ответ
Правильный ответ: Это ограничение (клампинг) значения цветового компонента в диапазоне 0-255. Без этого арифметические операции могут привести к выходу за пределы допустимого диапазона, что вызовет переполнение и искажение цветов.
В чём разница между методами drawImage() и drawPixmap() класса QPainter с точки зрения производительности?Ответ
Правильный ответ: drawImage() перед отображением преобразует QImage в контекстно-зависимый формат, что медленнее. drawPixmap() отображает QPixmap напрямую, так как данные уже в нужном формате, что обеспечивает максимальную скорость вывода.

Практические задания

Простой уровень

Просмотрщик изображений с фильтрами яркости
Создайте приложение, которое загружает изображение из файла и отображает его вместе с двумя вариантами: затемнённым (яркость -50) и осветлённым (яркость +50). Расположите все три изображения в один ряд. Добавьте возможность сохранения отредактированных изображений в файлы.
Подсказки: Используйте QImage для загрузки и обработки изображения. Реализуйте функцию изменения яркости по аналогии с примером из главы, используя scanLine() для эффективного доступа к пикселам. Для отображения используйте QLabel с методом setPixmap(). Для компоновки трёх изображений примените QHBoxLayout. Метод QImage::save() позволит сохранить результат.

Средний уровень

Галерея эффектов изображения
Разработайте приложение, которое применяет к загруженному изображению различные эффекты и отображает их в виде сетки: оригинал, инвертированное, отражённое по горизонтали, отражённое по вертикали, уменьшенное в 2 раза, увеличенное в 1.5 раза. Добавьте возможность кеширования часто используемых изображений через QPixmapCache. Реализуйте кнопки для переключения между разными исходными изображениями.
Подсказки: Используйте методы QImage::flipped(), scaled() и invertPixels() для создания эффектов. Для компоновки примените QGridLayout (сетку 2×3 или 3×2). Создайте функцию применения всех эффектов, которая возвращает список QPixmap. Используйте QPixmapCache::insert() с уникальными ключами для кеширования. QFileDialog поможет выбрать файл изображения. Обратите внимание на параметры Qt::KeepAspectRatio при масштабировании.

Сложный уровень

Мини-редактор изображений с кастомными фильтрами
Создайте полноценный редактор растровых изображений с нестандартным прозрачным окном. Реализуйте: загрузку/сохранение файлов разных форматов (PNG, JPEG, WEBP, BMP), применение фильтров (яркость, контраст, насыщенность, размытие), возможность рисования на изображении с использованием QPainter, отмену/повтор операций (undo/redo), масштабирование и отражение. Окно приложения должно иметь прозрачные области и кастомное оформление без стандартного заголовка.
Подсказки: Используйте QImage как основной формат для хранения и обработки (Format_ARGB32_Premultiplied). Храните стек изменений для undo/redo функциональности. Для кастомного окна примените Qt::WA_TranslucentBackground и Qt::FramelessWindowHint, реализуйте перемещение через mousePressEvent() и mouseMoveEvent(). Создайте отдельный класс фильтров с методами для каждого эффекта. Для размытия используйте свёртку с ядром (kernel). Рисование реализуйте через QPainter на QImage с обработкой событий мыши. QFileDialog::getOpenFileName() и getSaveFileName() помогут с диалогами файлов. Используйте QToolBar или кастомные кнопки для интерфейса управления.

💬 Присоединяйтесь к обсуждению!

Разобрались с различиями между QImage и QPixmap? Возникли вопросы о форматах изображений или оптимизации работы с растровой графикой?

Может быть, вы нашли интересные способы применения контекстно-независимого представления или создали необычный фильтр для обработки изображений?

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

Leave a Reply

Your email address will not be published. Required fields are marked *