Сборка ICO файла с иконками в формате PNG при помощи FASM

Иногда я пишу небольшие программы на C++, и часто выходит так, что иконка программы «весит» больше, чем собственно сама программа. Так же вышло и при написании Sound Keeper: программа — 14КБ, иконка 16×16 + 32×32 + 48×48 пикселей — 15КБ. Какое расточительство! :) К счастью оказалось, что Windows (начиная с Vista) поддерживает PNG внутри ICO. Это как раз то, что нужно! Но почему-то не нашлось программы, которая бы позволила самому оптимизировать файлы PNG и собрать из них файл ICO. Поскольку у файлов ICO очень простой формат, соберём его при помощи FASM. Это нестандартное использование «плоского» ассемблера показывает, что его можно применять в самых неожиданных ситуациях, и это работает! :)Как это сделать?1. Создаём изображения для иконок в формате PNG. Например, сделаем два изображения с размерами 16×16 и 32×32 пикселей. Сохраним их в файлы icon16.png и icon32.png соответственно. Оптимизируем на свой вкус своими любимыми инструментами.2. Следуя документации, создаём файл icopng.asm с примерно таким содержимым:

dw 0; reserved, must be 0 dw 1; icon type, must be 1 dw 2; number of images in file

; 1st icon header db 32; width db 32; height db 0; no color palette db 0; reserved, must be 0 dw 1; planes dw 32; bits per pixel dd icon32_end-icon32_start; length dd icon32_start; offset

; 2nd icon header db 16; width db 16; height db 0; no color palette db 0; reserved, must be 0 dw 1; planes dw 32; bits per pixel dd icon16_end-icon16_start; length dd icon16_start; offset

; 1st icon body icon32_start: file 'icon32.png' icon32_end:

; 2nd icon body icon16_start: file 'icon16.png' icon16_end: Опытным путём было установлено, что лучше подключать изображения в обратном порядке, от больших к меньшим. При добавлении большего количества иконок не забывайте исправлять поле с общим количеством изображений в заголовке. Ошибка здесь может привести к самым неожиданным последствиям.3. Компилируем иконку командой:

fasm icopng.asm icopng.ico 4. В результате мы получили иконку с изображениями в формате PNG. У меня вместо 15КБ получился файл размером всего 3КБ.Поддержка PNG8 с альфа-каналом Согласно скудной документации, должны поддерживаться только изображения в формате PNG32. На практике система без проблем декодирует и изображения PNG8 даже с альфа-каналом. Разве что только просмотрщик картинок Windows не понимает PNG8 в иконках, видимо он не использует системные функции для декодирования и отрисовки иконки. Но вы же иконки делаете не для того, чтобы их в просмотрщике картинок смотреть, правда? :) Везде, где использутся стандартные функции Windows для загрузки и отображения иконки — PNG8 с альфа-каналом отображается так же, как и PNG32.Важное замечание: даже у изображений в формате PNG8 в заголовке должно быть указано отсутствие палитры, потому что декодер PNG на выходе даёт TrueColor изображение с альфа-каналом, где могут встречаться любые цвета. То есть в примере вам нужно будет изменять только поля width и height. Остальное трогать не нужно.

Проблема в системном декодере PNG Поддержка PNG в иконках в Windows Vista и новее по умолчанию используется для сохранения только больших иконок размером 256×256. Судя по всему, поддержку PNG внутри ICO тестировали плохо, поэтому есть кое-какие проблемы и обходные пути для них.Обнаружилась какая-то ошибка в системном декодере PNG, из-за чего конкретный файл иконки может отображаться неправильно в некоторых местах (например, в диалоге свойств файла). Причём минимальное изменение параметров сжатия легко решает проблему.

Я провёл небольшое исследование на файле иконки, которая неправильно отображалась в диалоге свойств файла. Системная функция DrawIconEx имеет 3 режима отрисовки иконки: DI_NORMAL (с альфой), DI_IMAGE (без альфы) и DI_MASK (только маска).

6bfd929539fd4436904e183ccfa8c0d3.png

На этом изображении видно как одна и та же иконка вывелась в разных режимах, а под названием RESULT приведено то, как выглядит эта иконка в свойствах файла. Видно, что в режимах DI_NORMAL и DI_IMAGE система отрисовывает иконку правильно. Однако, в режиме DI_MASK вычисленная с целью совместимости маска почему-то сдвинута на три пикселя вправо, а также в первом столбце пикселей появился какой-то мусор — вероятно, следствие неправильной работы с буфером. Иконка в свойствах файла, как видно, имеет серьёзные артефакты как раз по очертаниям некорректной маски. Было бы хорошо сообщить об этой проблеме разработчикам Microsoft, но к сожалению, я не нашёл какого-то открытого баг-трекера.

Советы по оптимизации PNG Рекомендую использовать программу Color Quantizer для преобразования файлов из PNG32 в PNG8 с альфа-каналом. Результирующий файл помимо прочего сжимается весьма эффективным алгоритмом, поэтому обычно дополнительная оптимизация не требуется.ae83a5f7dd65402e8490ea2e75f3946d.png

На скриншоте красным выделен блок с выбором количества цветов и коэффицентом допустимых ошибок квантования. При возникновении описанной выше проблемы с некорректным отображением иконки в диалоге свойств файла, просто поменяйте параметры квантования. Например, незначительно измените значение коэффицента допустимых ошибок квантования.

На этом всё Теперь и ваши компактные программы могут быть с красочными иконками всех необходимых размеров. Не нужно жертвовать прозрачностью или количеством вариантов иконки. Недостаток только один — пользователи Windows XP не увидят такую иконку. Но если учесть, что официальная поддержка Windows XP уже давно закончилась, и всё меньше компьютеров работает под управлением этой операционной системы, то не так всё плохо. Тем более, что авторы маленьких программ часто для экономии вообще не добавляют иконку.

© Habrahabr.ru