Збірка 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 (тільки маска).



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

Поради щодо оптимізації PNG
Рекомендую використовувати програму Color Quantizer для перетворення файлів з PNG32 в PNG8 з альфа-каналом. Результуючий файл крім іншого стискається досить ефективним алгоритмом, тому зазвичай додаткова оптимізація не потрібно.



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

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

Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.