Особенности декодирования PNG

На днях провел самую нетривиальную сессию отладки за всю историю Dagon: по-тихому сломалась загрузка одной из текстур в сцене с лапшичной — 16-битный grayscale PNG, карта высот под parallax mapping. Я уже сталкивался с багом обработки 16-битных изображений в SDL_Image, но пофиксил эту проблему только для RGB — SDL_Image выдает формат SDL_PIXELFORMAT_RGB48, можно перехватывать такие кейсы и конвертировать в 8 бит на канал вручную вместо вызова SDL_ConvertSurface. Но в случае с grayscale чудит уже сам декодер, выдавая почему-то формат SDL_PIXELFORMAT_INDEX8, тогда как grayscale — это не индексированный режим, а отдельный пиксельный формат, поддерживающий разную битовую глубину. Я так и не понял, что с этим делать, и решил для загрузки PNG адаптировать мой старый декодер из dlib.

Сказано — сделано: я создал dagon.resource.png, заменил SuperImage на TextureBuffer и выпилил поддержку APNG, которая в играх все равно не нужна. Но после этого внезапно сломалась загрузка сцены Sponza — для всех текстур декодер просто выдавал мусор в чанках. Я долго ломал голову, искал ошибку в алгоритме разбора, пока не додадался на всякий случай проверить одну из текстур hex-редактором. Оказалось, что это вообще не PNG, а JPEG! SDL_Image такие картинки переваривал без проблем, поэтому я даже не замечал. Поэтому, чтобы вернуть старое поведение, пришлось городить детектор формата на основе сигнатур. В итоге, впрочем, загрузчик текстур стал работать даже чуть быстрее, чем прежний вариант.

Бенчмарк загрузки PNG

Не так давно один из участников сообщества D провел любопытный тест, сравнив скорости загрузки изображения в формате PNG с различными библиотеками. В сравнении участвовали D-библиотеки (dlib и imageformats), а также C#.

Использовалось RGB-изображение размером 2048х2048, компилятор DMD 2.0.67 c флагами -release -inline -O. Задачей была загрузка из PNG, отражение по горизонтали и сохранение в PNG. Результат получился следующий:

C#:
Загрузка — 90 мс
Отражение — 10 мс
Сохранение — 380 мс

D (dlib):
Загрузка — 500 мс
Отражение — 30 мс
Сохранение — 950 мс

D (imageformats):
Загрузка — 230 мс
Отражение — 30 мс
Сохранение — 1100 мс

Тред с обсуждением

На днях обязательно сделаю собственный тест — необходимо выяснить, что именно тормозит в декодере.

Сохранение PNG в dlib

В dlib теперь поддерживается сохранение изображений в формат PNG (Portable Network Graphics) — ранее было доступно только чтение файлов этого формата. За сохранение отвечает функция savePNG в модуле dlib.image.io.png.
Изменения доступны в ревизии r22 и выше.

http://code.google.com/p/dlib/