gif
Обратная разработка GIFLITE.EXE ★
Вчера мучился бессонницей, нужно было
В случае GIF очень хорошо помогала известная утилита GIFLITE.EXE — коммерческая разработка компании «Вайт Ривер Софтвейр» (White River Software), которая, по всей видимости, давно исчезла, так как я про неё ни в какой другой связи никогда ничего не слышал.
Утилита была под DOS, но я тогда работал под «Виндой» и проблем это не создавало, но позже, когда я пересел на «Мак», пользоваться ей стало неудобно. Тем не менее я всё равно ей ещё долго пользовался через всякие эмуляторы.
Она всегда меня интриговала — было интересно что она вообще такое делает, как ей удаётся уменьшить размер гифа и чем отличаются её методы сжатия, которые задаются ключом -m.
В общем, раз уж не спалось, я провёл давно запланированный эксперимент, к которому всё никак не мог найти времени приступить — имея на руках только бинарный файл старой программы, воссоздать её на другой платформе. Довольно актуальная задача, кстати, я знаю, что у некоторых людей есть программы без исходного кода, которые они вынуждены запускать на давно устаревших компьютерах.
Забегая вперёд, хочу сказать, что эксперимент удался — программа создаёт в точности такие же GIF-файлы во всех режимах и я, кстати, получил ответ на свой вопрос — бо́льшая часть методов в ней объединяет близкие цвета, уменьшая файл за счёт этого.
А теперь, немного подробностей.
Opus 4.5 показался смелым исследователем, который идёт к цели, не пасует перед проблемами. Если
Grok Code — торопыга, который как будто считает себя чересчур способным, много фантазирует, принимая свои фантазии за реальность и вообще слишком некритично, без рассуждений всё воспринимающий.
Сначала я дизассемблировал код, но это поначалу мало помогло — видно было, что нейросети пытаются «нащупать» в получившемся коде границу функций, чтобы разобраться с ними по отдельности, но не могут. Но позже этого код пригодился — чтобы восстанавливать логику более точно.
Потом я установил radare2 с плагином r2ghidra и нарезал бинарный файл на сишные функции. Код восстановился плохо, с лакунами, поэтому я запустил отдельную сетку, которой буквально по шагам объяснял что делать. Основная идея — довести сишный код до читаемого состояния, разбираясь с функциями от самых коротких к более длинным.
Для этого мы сначала разобрались с таблицей виртуальных функций внутри ассемблера, после чего дело пошло веселей. Заняло это несколько часов интенсивной работы с нейросетями, по ощущениям выглядело как парное программирование.
Параллельно, в другом окне другой экземпляр нейросети писал основной костяк кода, попутно разбираясь с проблемами, туда я заглядывал редко, но без моего внимания то окно тоже не обходилось.
В полным соответствии с принципом Парето, 20% кода отняло у меня 80% времени. Тут очень хорошо показал себя Opus 4.5, — поскольку в коде разобраться было сложно, там 40 тысяч строк на ассемблере, он ставил очень много эксприментов, сравнивая два набора файлов между собой — тот, который получался при запуске оригинального GIFLITE.EXE с нашим.
Мы долго не могли добиться бинарной точности результирующих файлов — очень много нюансов, которые влияли на то, что получалось. Вплоть до реализации алгоритма сортировки цветов, функции хеширования и прочих мелких деталей. Восстанавливать детали помог ассемблерный код — в Си некоторые подробности не перенеслись, отчасти
Я очень доволен тем, как прошёл эксперимент. Очень интересный опыт и результаты воодушевляющие. Код, как обычно, можно увидеть на «Гитхабе».
Какие ещё графические форматы поддерживает ваш браузер?
Переложил на хостинг «Гитхаба» свой проект семилетней давности — «Какие графические форматы поддерживает ваш браузер?». Я его иногда обновляю и мне было бы проще это делать через репозиторий.
Из всего, что я там проверяю, сразу не сработала только проверка на AVIF — это новейший формат, у меня на компьютере его поддерживает пока только «Файерфокс», ему важно указание типа содержимого, а «Гитхаб» передаёт неправильное значение.
Я подумал, что наверняка есть
Пришлось идти другим путём — вместо того, чтобы загружать файл картинки, внедрил его внутрь при помощи протокола data:, это позволило обойти проблему. Можете протестировать на своём браузере.
Ссылка на проект: https://bolknote.github.io/detect-browser-graphics-formats/
Ссылка не репозиторий проекта: https://github.com/bolknote/detect-browser-graphics-formats
Картинка в терминале
Несколько лет назад программа «айТерм2» (iTerm2, терминал для «Мака») обзавелась поддержкой графики в командной строке. Сильной необходимости в ней нет, но иногда это удобно — можно посмотреть картинку на удалённом сервере, не выкачивая её себе.
Вообще уже очень давно существует формат «Сиксел», рождённый в незапамятные времена компанией «ДЭК» (DEC), его даже поддерживают некоторые терминалы, но он значительно сложнее придуманного авторами «айТерма2».
Я это знаю, так как
В общем виде тот и другой форматы представляют собой управляющие последовательности, после которых передаётся закодированная картинка.
Мне тут подумалось, что было бы интересно попробовать вставить внутрь настоящей картинки её закодированную копию, чтобы при попытке вывода её в консоль в виде текста, в терминал попадали управляющие коды и на экран выводился не бинарный мусор, а картинка.
Для этого я нарисовал небольшой ГИФ и вставил закодированную картинку после изображения следующей командой:
cat test.gif \
<(echo -ne "\033[3F\033[J\033]1337;File=inline=1:") \
<(base64 test.gif) \
<(printf "\a")\
> test2.gifЕстественно, так как бинарный код изображения идёт первым, при выводе на экран его так же будет видно, поэтому я добавил другие управляющие конструкции (большинство терминалов их поддерживают), возвращающие курсор на место и вычищающие бинарщину.
В данном случае я сначала двигаю курсор на три строки вверх (столько занимает бинарный мусор на экране), потом произвожу очистку от курсора до конца экрана.
Теперь буду пугать наших системных администраторов картинкой, которую видно сразу и в терминале, при выводе командой cat, и в графическом интерфейсе.
ICC-профили в GIF
Формат ГИФ
Например, считается, что анимация появилась только в версии 89a (неправда, она была уже в версии 87a, в 89а её возможности были расширены), что ГИФ может использовать не более 256 цветов (не совсем так — не более 256 цветов на кадр, кадры могут накладываться друг на друга, давая любое отображаемое компьютером количество цветов).
Другое распространённое заблуждение — что ГИФ не поддерживает цветовые профили (ICC).
В самом деле, если посмотреть спецификацию формата, то слово «ICC» там не найдётся, но не найдётся там и слова «loop» (здесь: количество повторов), тем не менее, вы видели анимации, где указано определённое количество повторений и браузеры
Дело в том, что формат ГИФ поддерживает расширения. Расширения в спецификацию формата не входят.
Упомянутое количество повторов анимации задаётся в расширении, которое было
Вот начало файла ГИФ с внедрённым профилем:
00000000 47 49 46 38 39 61 01 00 01 00 f0 00 00 fe 00 02 |GIF89a……….| |
Пока не все браузеры умеют поддерживать цветовые профили ГИФ (как и профили любого другого формата), так ФФ не научился делать это до сих пор, а «Опера» научится это делать в новой версии 12.50.
Проверить поддерживает ли браузер цветовые профили можно и через Джаваскрипт. Принцип такой: я загружаю
Сжатие GIF через gzip
Эксперимент мой сжатию несжатых ГИФ, по сути, провалился — если статические изображения иногда оказываются меньше (или сравнимы) с PNG, то с анимированными изображениями, ради которых всё и затевалось, всё плохо, я не сумел подобрать условия, при которых новоявленный метод обгоняет обычные соптимизированные ГИФы.
Зато я попутно выяснил, что ГИФ, вопреки моим ожиданиям, иногда жмётся gzip, на первой попавшейся анимации я получил выигрыш порядка 9%. Так что вот он метод ещё больше утоптать файлы ГИФ:
#!/bin/sh
which -s defluff
if [ $? -eq 0 ]; then
gifsicle -O3 "$1" | gzip -9nc | defluff 2>&- > "$2".temp
else
gifsicle -O3 "$1" | gzip -9nc > "$2".temp
fi
size=( $(ls -l "$2".temp) )
# срезаем заголовок gzip и контрольную сумму
let 'size=size[4]-10-8'
dd if="$2".temp of="$2" bs=1 skip=10 count=$size 2>&-
rm -f "$2".tempВ настройки Апача надо добавить новое расширение файла:
AddEncoding deflate .gif-def
AddType image/gif .gif-defДля браузера вместо gzip я указываю метод deflate — по факту это тот же gzip, но без заголовка и контрольной суммы, что позволяет сэкономить 18 байт, не бог весть сколько, конечно, но почему бы их не убрать, спрашивается.
Утилита defluff , которая у меня упоминается — это оптимизатор кодов Хаффмана для gzip, PNG и zip, иногда даёт выигрыш в размере.
Конвертор в несжатый GIF
Сделал я конвертор из обычного ГИФа в несжатый формат сжатый gzip, выложил на ГитХабе. Основное (разбор ГИФа) написано на Пайтоне, утилита, которая всё запускает — на Шеле. Для работы требуются ImageMagick (предпочтительнее) или gifsicle, gitinter (из libungif) и интерпретатор Пайтона.
Кажется, эксперимент провалился, я ещё буду вертеть различные ГИФы, но вряд ли
Из плюсов для себя: детально для себя разобрал формат GIF, освежил знания Пайтона (которые уже начинают тускнеть), Шела, поработал над интересной идеей.
Сжатый несжатый GIF
Как многие знают, браузер нативно понимает сжатие gzip (алгоритм DEFLATE).
Графический файлы обычно так не сжимают — внутри у них свой алгоритм сжатия, например в ПНГ используется тот же DEFLATE. Но с ГИФом ситуация хуже — у него внутри более старый алгоритм LZW, к тому же нет никаких оптимизационных техник (например, ПНГ использует так называемые «фильтры», которые позволяют эффективно сжимать градиенты.
Вот я и подумал — нельзя ли
Смотрите, слева у вас на экране ГИФ, сжатый при помощи gzip (так и грузится с сервера), справа — ПНГ.
![]()
![]()
В каждом из них равное количество уникальных цветов, ПНГ пропущен через все оптимизаторы, которые я только нашёл. При этом ПНГ занимает 1374 байта, а гзипованный ГИФ — 1347, на 27 байт меньше. Дело тут конечно, не в паре десятков байт, а в принципе — если бы я выбрал картинку побольше, выигрыш, возможно, был бы существеннее.
Почему же LZW внутри ГИФа, плюс DEFLATE снаружи сделали файл меньше, чем оптимизирующие просто DEFLATE, плюс фильтры в случае ПНГ? Обычно наложение одного сжимающего алгоритма на другой делает файл больше сравнению с применением одного, более удачного алгоритам из двоих.
Дело в том, что в ГИФ я использовал несжатый, он подготовлен специальным образом, так, что его собственный алгоритм сжатия не работает, а gzip работает над несжатым потоком данных.
Самый простой, по моему мнению, способ получить такой файл — пропустить его через утилиту gifinter из набора libungif — библиотеки и набора утилит для работы с несжатыми файлами ГИФ.
Утилиты эти отлично скомпилировались под мой «Мак» и обрабатывал я ГИФ следующим образом: для начала оптимизировал его при помощи giflite (эта старая утилита под DOS, которую я запускаю
gifinter optimized.gif > non-compressed.gif
gzip -9n non-compressed.gifДальше я переименовал файл
AddEncoding gzip .gif-gz
AddType image/gif .gif-gzТеперь при запросе сервер будет выдавать все необходимые заголовки:
bolk@Bolk ~ $ telnet bolknote.ru 80
Trying 91.230.61.15...
Connected to bolknote.ru.
Escape character is '^]'.
HEAD /imgs/2012.08.08.gif-gz HTTP/1.0
Host: bolknote.ru
Accept-encoding: gzip
HTTP/1.1 200 OK
Server: nginx
Date: Wed, 08 Aug 2012 06:12:14 GMT
Content-Type: image/gif
Content-Length: 1347
Connection: close
Accept-Ranges: bytes
Cache-Control: max-age=2592000
Expires: Fri, 07 Sep 2012 06:12:14 GMT
Content-Encoding: gzipЕсли интересно как устроен несжатый ГИФ, об этом можно прочитать в Википедии, откуда я взял картинку, там интересная уличная магия, перепечатывать «Википедию»
Готовой разгадки почему сжатый несжатый ГИФ обогнал ПНГ у меня нет, но есть одно возможное соображение — ГИФ
TrueColor GIF ★

Была у меня такая задумка — попробовать сделать полноцветную
Разбиение для начала выбрал простое — исходя из площади покрытия, думал потом взяться за оптимизацию (выбирать площадь, потом брать все цвета, которые в ней содержатся), но этого не понадобилось, оказалось, что если поставить задержку между кадрами ноль, то каждый браузер всё равно будет отрисовывать анимацию с очень заметной на глаз задержкой. Кажется в спецификации вообще не определено что означает нулевая задержка:
Delay Time — If not 0, this field specifies the number of hundredths (1/100) of a second to wait before continuing with the processing of the Data Stream. The clock starts ticking immediately after the graphic is rendered. This field may be used in conjunction with the User Input Flag field.
Но и если поставить задержку в 1/100 секунды, браузеры всё так же неторопливо отрисовывают картинку. Нарезанные кадры я собирал через утилиту gifsicle, она ещё и оптимизирует изображение — ненужные мне области я заливал прозрачным цветом, gifsicle умеет такие области отбрасывать, что уменьшает размер изображения:
gifsicle --delay 0 --crop-transparency -O3 out/*.gif > out.gifЖаль, что ничего не получилось.
Зато выяснились любопытные подробности. Не знаю как это выглядит в IE (негде попробовать), а вот FireFox и Chrome умеют, один раз отрисовав конечную анимацию, потом мгновенно доставать её из кеша на перезагрузке страницы. «Опера», как и следовало ожидать, постоянно перерисовает анимацию — стоит только вернуться на страницу с другого таба или прокрутить её обратно до анимированного изображения.
33000 и анимированный GIF

По всей видимости, «Опера» немного неправильно обрабатывает анимированные «гифы».
Анимированный GIF устроен очень просто — поверх первого кадра через заданное время лепится следующий. Задержка задаётся в сотых долях секунды и, согласно спецификации, поля для её хранения имеет размер беззнаковое слово (2 байт), то есть максимальное значение — 655,35 секунд или почти 11 минут.
Распространённая ошибка в Си и похожих языках — использовать вместо беззнакового число со знаком. Тогда максимальное значение по модулю сокращается в два раза.
Вот я и проверил — не содержат ли кодеки популярных браузеров эту ошибку. На картике — число «1», если оно через
У меня на ноуте на FF 5, Safari 5.0.5, Chrome 12.0.742.122 цифра меняется, а на «Опере» 11.50 — нет. Сейчас попробую найти ноут с Windows 7 и попробую там. Попробуйте тоже на своих браузерах.
Добавлено: меняются ещё цифры на Андроиде 2.3.4, на FF 4.0.1 (Windows), а так же под Internet Explorer 9.0 и 10PP2 (Windows 7).
Добавлено ещё позднее: всё в порядке у «Оперы», разъяснил один из читателей — «Опера» начинает анимацию заново, если GIF не было видно. Дожидаясь, я переключал табы, поэтому анимация стартовала всегда сначала и «2» не появлялась. Так что ложная тревога, но любопытный факт.
Как сэкономить один байт в GIF

Я понимаю, один байт — не бог весть что, но, возможно,
Файл в формате GIF должен заканчиваться символом с кодом 0x3B («;»), который называется «trailer». На самом деле, этот символ чаще всего не требуется декодерами (хотя обязателен в стандарте). Я проверил на сервисе скриншотов, все браузеры, которые там перечислены, кроме Konqueror 4.2 и выше, поддерживают файлы GIF без этого байта.
Я так же вывесил такой GIF у себя на сайте, обнаружилось, что его так же не поняли агенты, которые подписываются как Java/1.6.0_xx (думаю, это
Вывод: если вас это не смущает, то можете сократить свои GIFы на один байт. Или, напротив, проверьте имеют ли ваши GIF этот байт, если для вас
Internet Explorer показывает не все GIF
Нашёл в ЖЖ DiBR’а GIF, который не показывает Internet Explorer. Забавно, хотя в отличие от JPEG, который не показывает современная «Опера», этот GIF несколько некорректен.
Вот текст комментария из ЖЖ, который разъясняет принцип:
Согласно описанию формата, сразу за заголовком («GIF89a») идет Logical Screen Descriptor (в котором как раз и прописан размер 80×80), затем Global Color Table, а уж за ним — всякие Extensions и данные изображений. В обсуждаемом файле Global Color Table просто отсутствует (так что IE ведет себя вполне адекватно). Зато присутствует картинка размером 100×100, которую и видят менее привередливые рендереры.
GIF plain text extension
В формате GIF89 есть забавное поле — «plain text extension», заброшенное и никем не используемое. Настолько заброшенное, что для него делали специальное соответствующее поле в формате PNG, для совместимости, но потом объявили его «deprecated».
Как будет в точности выглядеть GIF с этим полем мне неизвестно, думаю, зависит от программы. По идее, оно должно выводить моноширинный семибитный (никакого Unicode) текст.
Насилу нашёл редактор, который умеет создавать GIF с таким полем, но, увы, ни один из браузеров не умеет правильно показывать содержимое такого GIF. Некоторые браузеры не умеют показывать его даже неправильно — выводят пиктограмму ошибочно загруженной картинки (например, IE, Opera 8.xx/9.xx, Safari и другие).
Понятно, что extension совершенно бесполезный,
P.S. Для тестирования браузеров я использовал сервис BrowserShots.
Добавлено 6 октября 2009 14:40: почти все браузеры показывают изображение в котором есть ещё хоть
Сжатие картинок
Вчера попробовал натравить инструменты для оптимизации картинок на свой каталог с картинками этого блога. Результат следующий: выигрыш составил около 10 процентов (больше всего у меня
PNG. Для сжатия PNG использовался известнейший pngcrush. Интересен в режиме «brute force» (перебора), где перебираются комбинации фильтров (фильтры — это специализированные методы сжатия, например, для градиента и т. д.). Я запускал сделующим образом: «pngcrush -brute -fix -e .png1 *.png». В итоге, все файлы png у меня в папке получают свою оптимизированную копию с расширением png1. Есть опасный момент — если у вас есть файлы APNG, pngcrush их превратит в файл без анимации. Экономия получилась порядка 7%.
JPEG. В JPEG’e особо оптимизировать нечего, формат сжатия с потерями, поэтому оптимизацию без участия человека не произвести — трудно просчитать насколько критично изменился рисунок. Оптимизировать тут нечего, но можно убрать мусор — многие программы записывают в файл комментарии, лишнюю информацию. Я использовал для чистки PureJPEG, после него прошёлся своим Photoshop Crap Remover, чтобы дочистить файл. Формат запуска PureJPEG следующий: «dir /B /S
GIF. Несмотря на то, что утилите GIFLite уже около 12 лет, мне она кажется лидером по переупаковке GIF. Утилита написана White River Software, её разработка начата ещё в 1992 году и она рабоатет под DOS, т. е. должна запускаться даже в Linux
Запускать её сложнее, чем предыдущие —
giflite.cmd — внутри по ссылке
@ECHO OFF
REM Написал Евгений «BOLK» Степанищев. 2007.
MKDIR GIFLITE.$$$ 2>nul
REM Основной цикл обработки файлов GIF
FOR /R %%N IN (*.gif) DO @CALL :method %%N
REM Удаляем весь мусор, который мог остаться
DEL /Q giflite.tmp 2>nul
RMDIR /S /Q GIFLITE.$$$
EXIT
:method
REM Перебираем методы
FOR %%M IN (0 1 2 3) DO @CALL :giflite %%M %%~s1 %1
REM Сортируем полученное по размеру и забираем последний (наименьший) файл
FOR /F "usebackq skip=3" %%R IN (`DIR /B /O-S GIFLITE.$$$`) DO @CALL :getresult %%R %1
GOTO :EOF
:getresult
REM Переписываем файл на место прежнего, удаляем мусор
MOVE /Y GIFLITE.$$$%1 %2
DEL /Q GIFLITE.$$$*.*
GOTO :EOF
:giflite
REM Запускаем преобразование
GIFLITE.EXE -t -h -m%1 -o %2 GIFLITE.$$$%1 >nul 2>nul
Анимация в вебе
Как известно, единственный формат анимированных картинок, который был доступен в вебе до недавнего времени — Animated GIF. Его недостатки общеизвестны — поддерживается только 256 цветов, нет полупрозрачности
В PNG, увы, нет анимации. Три новых формата, которые пока знакомы редкому
MNG

APNG (Animated PNG) — формат расширяющий PNG и совместимый сверху вниз. Если ваш браузер не поддерживает APNG, вы увидите просто статичную картинку. В настоящее время формат поддерживают Opera 9.50 и далёкий пока от релиза FireFox 3. Картинка выше этого абзаца — тест, поддерживает ли ваш браузер APNG. Если вы видите «Your browser does support Animated PNG», значит поддержка есть.
SVG (Scalable Vector Graphics) — относительно молодой векторный формат, который поддерживают достаточно много браузеров: Opera с восьмой версии, Safari 3 и выше, FireFox с версии 1.5, а так же все браузеры на движке Gecko, начиная с версии 1.8. Это XML формат, который для меньшего размера можно сжать алгоритмом GZip (формат SVGZ). Обычно, анимацию в SVG делают при помощи внедрённого туда EMCAScript, сменяя кадры из картинок в любом другом формате. Не совсем то, но поддерживается почти любым современным браузером, хотя в стандарте SVG есть собственные методы анимации, по этой теме можно почитать статью на несложном английском «Advanced SVG Animation Techniques».
Фокусы с GIF, прозрачность и hover
Самые внимательные, наверное, заметили, что в прошлых заметках у меня на картинке использовалась эмуляция эффекта наведения на ссылку — посмотрите, если навести на картинку мышку, «ссылка», выделенная синим, на картинке подчеркнётся. И это не JavaScript.
Объяснение очень простое — картинка сделана с прозрачностью, далее при помощи CSS под неё подставлен белый фон, а при наведении (селектор «:hover») фон меняется на голубой. Такой же эффект использован на картинке с пистолетом слева (наведите мышку). Ссылка на картинке обязательна — без неё в Internet Explorer (версии ниже
a.gun:hover img { background: black } /* стиль при наведении мышкой */
a.gun img { margin: 0px; background: white } /* убираем поля в Opera и подкладываем белый фон */HTML, соотвественно:
<a href="#" class="gun"><img src="gun.gif" /></a>Если вам не ясно, почему пистолетик «стреляет» (т. е. почему изображение движется, когда наводишь мышку), то тут всё просто — прозрачная область движется (это animated GIF), на белом фоне этого не видно, когда фон меняется, создаётся эффект движущейся пули.
Ещё интересные эффекты можно получить, если использовать