Сжатый несжатый GIF
Как многие знают, браузер нативно понимает сжатие gzip (алгоритм DEFLATE). Наконец-то браузеры научились с ним правильно работать, после стольких лет мучений — сжимать можно что угодно, достаточно просто указать правильный заголовок.
Графический файлы обычно так не сжимают — внутри у них свой алгоритм сжатия, например в ПНГ используется тот же DEFLATE. Но с ГИФом ситуация хуже — у него внутри более старый алгоритм LZW, к тому же нет никаких оптимизационных техник (например, ПНГ использует так называемые «фильтры», которые позволяют эффективно сжимать градиенты.
Вот я и подумал — нельзя ли как-то заменить алгоримт сжатия ГИФа с внутреннего LZW на внешний gzip? Если было бы нельзя, я бы эту заметку не писал.
Смотрите, слева у вас на экране ГИФ, сжатый при помощи gzip (так и грузится с сервера), справа — ПНГ.
В каждом из них равное количество уникальных цветов, ПНГ пропущен через все оптимизаторы, которые я только нашёл. При этом ПНГ занимает 1374 байта, а гзипованный ГИФ — 1347, на 27 байт меньше. Дело тут конечно, не в паре десятков байт, а в принципе — если бы я выбрал картинку побольше, выигрыш, возможно, был бы существеннее.
Почему же LZW внутри ГИФа, плюс DEFLATE снаружи сделали файл меньше, чем оптимизирующие просто DEFLATE, плюс фильтры в случае ПНГ? Обычно наложение одного сжимающего алгоритма на другой делает файл больше сравнению с применением одного, более удачного алгоритам из двоих.
Дело в том, что в ГИФ я использовал несжатый, он подготовлен специальным образом, так, что его собственный алгоритм сжатия не работает, а gzip работает над несжатым потоком данных.
Самый простой, по моему мнению, способ получить такой файл — пропустить его через утилиту gifinter из набора libungif — библиотеки и набора утилит для работы с несжатыми файлами ГИФ.
Утилиты эти отлично скомпилировались под мой «Мак» и обрабатывал я ГИФ следующим образом: для начала оптимизировал его при помощи giflite (эта старая утилита под DOS, которую я запускаю из-под dosbox, творит с ГИФами чудеса), потом пропустил через утилиту, вырезающую из ГИФа комментарии (их туда вставил giflite), только после этого пропустил файл через gifinter и сжал при помощи gzip:
gifinter optimized.gif > non-compressed.gif
gzip -9n non-compressed.gif
Дальше я переименовал файл в 2012.08.08.gif-gz и добавил в конфигурацию своего «Апача» следующие строки:
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
Если интересно как устроен несжатый ГИФ, об этом можно прочитать в Википедии, откуда я взял картинку, там интересная уличная магия, перепечатывать «Википедию» не очень-то интересно.
Готовой разгадки почему сжатый несжатый ГИФ обогнал ПНГ у меня нет, но есть одно возможное соображение — ГИФ чуть-чуть компактнее ПНГ, кроме того, ГИФ сжат целиком, тогда как в ПНГ сжата только картинка. Если моё предположение правда, то хорошего выигрыша ждать не сто́ит, но списывать формат ГИФ со счетов тоже не нужно.
По-настоящему опыт ценен, если удасться сделать несжатую ГИФ-анимацию (этого я ещё не пробовал) и сжать её при помощи gzip. Если удасться, это будет очень полезно, буду держать вас в курсе.
/tmp$ pngout 2012.08.08.png
In: 1374 bytes 2012.08.08.png /c3 /f0 /d8
Out: 1320 bytes 2012.08.08.png /c3 /f0 /d8, 102 colors
Chg: -54 bytes ( 96% of original)
:-)
Комментарий для alexeyten.ya.ru:
Забавно, ровно на 27 байт меньше gif-а. :-)
Но вообще идея интересная.
Комментарий для alexeyten.ya.ru:
Я пропускал через pngcrush, optipng, оптимизатор Yahoo и punypng. Значит надо было ещё и pngout использовать. Кошмар :)
В любом случае, ценность идеи совсем не в этом (см. последнее предложение в заметке).
Вот то, ради чего всё затевалось: http://bolknote.ru/all/3708
Я, кстати, думал, что для «Мака» pngout не существует. Оказывается, есть порт: http://www.jonof.id.au/kenutils
В IMGO ( https://github.com/imgo/imgo ) входит pngout.
Комментарий для greli.livejournal.com:
Спасибо, буду пробовать!
Комментарий для greli.livejournal.com:
Кстати, pngout там старше, чем тот, который я пришёл выше.
Комментарий для alexeyten.ya.ru:
Могу, кстати, этот ГИФ ещё на 18 байт уменьшить. Где бы ещё 11 байт найти? :)
Комментарий для alexeyten.ya.ru:
Т. е. 9, конечно, а не 11.
Комментарий для alexeyten.ya.ru:
$ ./compressgif test.gif test.gif-def
Это утилита из следующей статьи: http://bolknote.ru/all/3713
Если прогнать оба файла через zopfli, результат ещё больше впечатляет. PNG: 1339 байт, GIF.GZ: 1241.
Комментарий для subzey:
Ого, спасибо! Посмотрю!