13 заметок с тегом

gif

Картинка в терминале

Несколько лет назад программа «айТерм2» (iTerm2, терминал для «Мака») обзавелась поддержкой графики в командной строке. Сильной необходимости в ней нет, но иногда это удобно — можно посмотреть картинку на удалённом сервере, не выкачивая её себе.

Вообще уже очень давно существует формат «Сиксел», рождённый в незапамятные времена компанией «ДЭК» (DEC), его даже поддерживают некоторые терминалы, но он значительно сложнее придуманного авторами «айТерма2».

Я это знаю, так как когда-то разобрался в обоих форматах и даже побаловался с тем, что придумали в «айТерме2» — написал программу на «баше», где нарисованный глаз следил за курсором мыши в терминале.

Картинку с надписью «HELLO» видно сразу и в графической среде, и в терминале

В общем виде тот и другой форматы представляют собой управляющие последовательности, после которых передаётся закодированная картинка.

Мне тут подумалось, что было бы интересно попробовать вставить внутрь настоящей картинки её закодированную копию, чтобы при попытке вывода её в консоль в виде текста, в терминал попадали управляющие коды и на экран выводился не бинарный мусор, а картинка.

Для этого я нарисовал небольшой ГИФ и вставил закодированную картинку после изображения следующей командой:

cat test.gif \
    <(echo -ne "\033[3F\033[J\033]1337;File=inline=1:") \
    <(base64 test.gif) \
    <(printf "\a")\
> test2.gif

Естественно, так как бинарный код изображения идёт первым, при выводе на экран его так же будет видно, поэтому я добавил другие управляющие конструкции (большинство терминалов их поддерживают), возвращающие курсор на место и вычищающие бинарщину.

В данном случае я сначала двигаю курсор на три строки вверх (столько занимает бинарный мусор на экране), потом произвожу очистку от курсора до конца экрана.

Теперь буду пугать наших системных администраторов картинкой, которую видно сразу и в терминале, при выводе командой cat, и в графическом интерфейсе.

29 августа   gif   macos   программирование

ICC-профили в GIF

Формат ГИФ почему-то окружён каким-то нереальным количеством мифов, хотя до недавнего времени он был очень популярен, а для анимации до сих пор остаётся единственным универсальным форматом.

Например, считается, что анимация появилась только в версии 89a (неправда, она была уже в версии 87a, в 89а её возможности были расширены), что ГИФ может использовать не более 256 цветов (не совсем так — не более 256 цветов на кадр, кадры могут накладываться друг на друга, давая любое отображаемое компьютером количество цветов).

Другое распространённое заблуждение — что ГИФ не поддерживает цветовые профили (ICC).

В самом деле, если посмотреть спецификацию формата, то слово «ICC» там не найдётся, но не найдётся там и слова «loop» (здесь: количество повторов), тем не менее, вы видели анимации, где указано определённое количество повторений и браузеры как-то это понимают.

Дело в том, что формат ГИФ поддерживает расширения. Расширения в спецификацию формата не входят.

Упомянутое количество повторов анимации задаётся в расширении, которое было когда-то предложено фирмой «Нетскейп» и потому имеет заголовок «NETSCAPE2.0», у расширения, которое содержит цветовой профиль заголовок другой — «ICCRGBG1012». ГИФ всегда разбит на блоки, и блок расширения (любого) помечен специальным флагом; заголовок расширения позволяет просмотрщику (в нашем случае — браузеру) решить с каким из расширений формата он имеет дело.

Вот начало файла ГИФ с внедрённым профилем:

00000000 47 49 46 38 39 61 01 00 01 00 f0 00 00 fe 00 02 |GIF89a..........|
00000010 00 00 00 21 f9 04 00 00 00 00 00 21 ff 0b 49 43 |...!.......!..IC|
00000020 43 52 47 42 47 31 30 31 32 ff 00 00 03 f0 55 43 |CRGBG1012.....UC|
00000030 43 4d 02 40 00 00 6d 6e 74 72 52 47 42 20 58 59 |CM.@..mntrRGB XY| 

Пока не все браузеры умеют поддерживать цветовые профили ГИФ (как и профили любого другого формата), так ФФ не научился делать это до сих пор, а «Опера» научится это делать в новой версии 12.50.

Проверить поддерживает ли браузер цветовые профили можно и через Джаваскрипт. Принцип такой: я загружаю ГИФ-изображение 1×1 с цветовым профилем в тег CANVAS, потом проверяю цвет точки, он должен быть определённого цвета. Проверить поддерживает ли ваш браузер цветовой профиль четвёртой версии ГИФ можно у меня на сайте, в разделе «Храню».

Сжатие 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, к тому же нет никаких оптимизационных техник (например, ПНГ использует так называемые «фильтры», которые позволяют эффективно сжимать градиенты.

Вот я и подумал — нельзя ли как-то заменить алгоримт сжатия ГИФа с внутреннего 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. Если удасться, это будет очень полезно, буду держать вас в курсе.

TrueColor GIF

TrueColor GIF (191.75КиБ)

Была у меня такая задумка — попробовать сделать полноцветную GIF-анимацию. Сразу скажу, идея провалилась, к сожалению.

Мысль-то простая — каждый полноцветный кадр должен представлять из себя полноцветный GIF. Общеизвестно, что формат GIF поддерживает только 256 цветов. Не страшно, разбиваем картинку так, чтобы каждый кадр содержал в себе не более 256 цветов и ляпаем их каждый поверх другого без задержки, в конце получится полноцветное изображение. Несколько таких изображений с задержкой между ними — и готова анимация.

Разбиение для начала выбрал простое — исходя из площади покрытия, думал потом взяться за оптимизацию (выбирать площадь, потом брать все цвета, которые в ней содержатся), но этого не понадобилось, оказалось, что если поставить задержку между кадрами ноль, то каждый браузер всё равно будет отрисовывать анимацию с очень заметной на глаз задержкой. Кажется в спецификации вообще не определено что означает нулевая задержка:

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

Жаль, что ничего не получилось. Всё-таки надо переходить на APNG.

Зато выяснились любопытные подробности. Не знаю как это выглядит в IE (негде попробовать), а вот FireFox и Chrome умеют, один раз отрисовав конечную анимацию, потом мгновенно доставать её из кеша на перезагрузке страницы. «Опера», как и следовало ожидать, постоянно перерисовает анимацию — стоит только вернуться на страницу с другого таба или прокрутить её обратно до анимированного изображения.

33000 и анимированный GIF

По всей видимости, «Опера» немного неправильно обрабатывает анимированные «гифы».

Анимированный GIF устроен очень просто — поверх первого кадра через заданное время лепится следующий. Задержка задаётся в сотых долях секунды и, согласно спецификации, поля для её хранения имеет размер беззнаковое слово (2 байт), то есть максимальное значение — 655,35 секунд или почти 11 минут.

Распространённая ошибка в Си и похожих языках — использовать вместо беззнакового число со знаком. Тогда максимальное значение по модулю сокращается в два раза.

Вот я и проверил — не содержат ли кодеки популярных браузеров эту ошибку. На картике — число «1», если оно через пять-шесть минут сменится на «2», значит ошибки нет. Я выставил задержку между кадрами в 33000 (330 секунд или 5,5 минут). Кодек с ошибкой будет трактовать это число как -233 (отрицательное), поэтому кадр не сменится.

У меня на ноуте на 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

Trailer (7.79КиБ)

Я понимаю, один байт — не бог весть что, но, возможно, кому-то и такая мелочь будет приятна. Например, полезно во всяких конкурсах, вроде JS1K и т. п.

Файл в формате GIF должен заканчиваться символом с кодом 0x3B («;»), который называется «trailer». На самом деле, этот символ чаще всего не требуется декодерами (хотя обязателен в стандарте). Я проверил на сервисе скриншотов, все браузеры, которые там перечислены, кроме Konqueror 4.2 и выше, поддерживают файлы GIF без этого байта.

Я так же вывесил такой GIF у себя на сайте, обнаружилось, что его так же не поняли агенты, которые подписываются как Java/1.6.0_xx (думаю, это какие-то браузеры на J2ME).

Вывод: если вас это не смущает, то можете сократить свои GIFы на один байт. Или, напротив, проверьте имеют ли ваши GIF этот байт, если для вас почему-то важны эти браузеры (основанные на J2ME и Konqueror 4.xx).

Internet Explorer показывает не все GIF

IE, не покажи этот GIF! (1.21КБ)Нашёл в ЖЖ DiBR’а GIF, который не показывает Internet Explorer. Забавно, хотя в отличие от JPEG, который не показывает современная «Опера», этот GIF несколько некорректен.

Вот текст комментария из ЖЖ, который разъясняет принцип:

Согласно описанию формата, сразу за заголовком («GIF89a») идет Logical Screen Descriptor (в котором как раз и прописан размер 80x80), затем 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: почти все браузеры показывают изображение в котором есть ещё хоть что-то, кроме такого текста. Если же изображение содержит только текст, то многие браузеры (как сказано выше) покажут ошибку загрузки.

Ранее Ctrl + ↓