PNG — not GIF!

    Доброго времени суток!
    Вам когда-нибудь хотелось узнать как устроены файлы PNG? Нет? А я все равно расскажу.
    Формат PNG(Portable Network Graphics) был изобретен в 1995 году, чтобы стать заменой GIF, а уже в 1996, с выходом версии 1.0, он был рекомендован W3C, в качестве полноправного сетевого формата. На сегодняшний день PNG является одним из основных форматов веб-графики.

    Под катом вы найдете общее описание строения PNG-файла, некоторое количество картинок-схем, препарирование в hex-редакторе, и, конечно, ссылку на спецификацию.

    Общее строение


    Структура PNG в самом общем виде представлена на следующем рисунке.

    То есть файл состоит из подписи и некоторого количества блоков(чанков, chunks), каждый из которых несет в себе некоторую информацию (спасибо КО!). Но почему подпись нельзя считать одним из чанков? Давайте разберемся поподробнее.

    Подпись файла

    Подпись PNG-файла всегда одинакова, состоит из 8 байт, и представляет собой (в hex-записи)
    89 50 4E 47 0D 0A 1A 0A

    Что же это означает?
    • 89 — non-ASCII символ. Препятствует распознаванию PNG, как текстового файла, и наоборот.
    • 50 4E 47 — PNG в ASCII записи.
    • 0D 0A — CRLF (Carriage-return, Line-feed), DOS-style перевод строки.
    • 1A — останавливает вывод файла в DOS режиме (end-of-file), чтобы вам не вываливалось многокилобайтное изображение в текстовом виде.
    • 0A — LF, Unix-style перевод строки.


    Chunks

    Чанки — это блоки данных, из которых состоит файл. Каждый чанк состоит из 4 секций.

    Разберем эти секции по порядку.

    Длина

    Ну, с длиной вроде все ясно. Просто числовое значение длины блока данных.

    Тип (имя)

    С типом немного поинтересней. Тип представляет собой 4 чувствительных к регистру ASCII-символа. Регистры символов (пятый бит в числовой записи символа) в имени чанка различаются неспроста — это флаги, которые сообщают декодеру некоторую дополнительную информацию.
    • Регистр первого символа определяет является ли данный чанк критическим(верхний регистр) или вспомогательным(нижний регистр). Критические чанки должны распознаваться каждым декодером. Если декодер встречает критический чанк, тип которого не может распознать, он обязан завершить выполнение с ошибкой.
    • Регистр второго символа задает «публичность»(верхний регистр) или «приватность»(нижний регистр) чанка. «Публичные» чанки — официальные, задокументированные, распознаваемые большинством декодеров. Но если вдруг вам для каких-то своих нужд понадобится кодировать специфическую информацию, то просто в имени чанка сделайте второй символ маленьким.
    • Регистр третьего символа оставлен для будущих свершений. Предполагается, что он будет использоваться для дифференциации различных версий стандарта. Для версий 1.0 и 1.1 третий символ должен быть большим. Если он (внезапно!) оказался маленьким, все нынешние декодеры должны поступать с чанком, так же как и с любым другим не распознанным (то есть выходить с ошибкой если чанк критический, или пропускать в противном случае).
    • Регистр же четвертого символа означает возможность копирования данного чанка редакторами, которые не могут его распознать. Если регистр нижний, чанк может быть скопирован, вне зависимости от степени модификации файла, иначе (верхний регистр) он копируется только в случае, когда при модификации не были затронуты никакие критические чанки.

    Для лучшего понимания, давайте разберем флаги на примере чанка, содержащего текст.


    Ниже приведен список типов чанков с краткими пояснениями.
    Критические чанки
    • IHDR — заголовок файла, содержит основную информацию о изображении. Обязан быть первым чанком.
    • PLTE — палитра, список цветов.
    • IDAT — содержит, собственно, изображение. Рисунок можно разбить на несколько IDAT чанков, для потоковой передачи. В каждом файле должен быть хотя бы один IDAT чанк.
    • IEND — завершающий чанк, обязан быть последним в файле.


    Вспомогательные чанки
    • bKGD — этот чанк задает основной фоновый цвет.
    • cHRM используется для задания CIE 1931 цветового пространства.
    • gAMA — определяет гамму.
    • hIST — в этом чанке может храниться гистограмма или общее содержание каждого цвета в изображении.
    • iCCP — цветовой профиль ICC
    • iTXt — содержит текст в UTF-8, возможно сжатый, с необязательной языковой меткой. iTXt чанк с ключевым словом 'XML:com.adobe.xmp' может содержать Extensible Metadata Platform (XMP).
    • pHYs — содержит предполагаемый размер пикселя и/или отношение сторон изображения.
    • sBIT (significant bits) — определяет «цветовую точность» (color-accuracy) изображения (черно-белое, полный цвет, черно-белое с прозрачностью и т.д.), для более простого декодирования.
    • sPLT — предлагает палитру для использования, если полный спектр цветов недоступен.
    • sRGB — свидетельствует о использовании стандартной sRGB схемы.
    • sTER — индикатор стереоскопических изображений.
    • tEXt — может содержать текст в ISO/IEC 8859-1 формате, с одной name=value парой для каждого чанка.
    • tIME — хранит дату последнего изменения изображения.
    • tRNS — содержит информацию о прозрачности.
    • zTXt — сжатый текст, с теми же ограничениям, что и tEXt.

    Более подробную информацию можно найти в спецификации.

    CRC

    Контрольная сумма CRC-32. Кстати на днях был топик о ее подсчете в Windows.

    Минимальный PNG


    С общей структурой разобрались. Теперь разберем содержание обязательных чанков. Но какие из них обязательные (не критические, критические обязаны распознаваться декодером, а не присутствовать в каждом файле), и как выглядит минимальный PNG-файл? А вот как:


    IHDR

    Блок данных в IHDR содержит следующие поля:
    • Ширина, 4 байта
    • Высота, 4 байта
    • Битовая глубина (bit depth), определяет количество бит на каждый сэмпл(не пиксель), 1 байт
    • Тип цвета, состоит из 3 флагов 1 (используется палитра), 2 (используется цвет, не монохромное изображение), and 4 (присутствует альфа-канал), 1 байт
    • Метод сжатия. На данный момент доступно только значение 0 — сжатие по алгоритму deflate. Если значение отлично от 0, чанк считается нераспознанным, и декодер рапортует об ошибке. 1 байт
    • Метод фильтрации. Так же, как и в случае сжатия, на данный момент может быть только нулем. 1 байт
    • Interlace(переплетение) метод. Определяет порядок передачи данных. На данный момент доступно 2 значения: 0 (no interlace) и 1 (Adam7 interlace). 1 байт

    Adam7 interlacing прекрасно демонстрирует картинка из википедии (да-да, GIF в статье про PNG):
    image

    IEND

    Сигнализирует о конце файла, блок данных этого чанка не содержит ничего.

    IDAT

    Содержит данные, закодированные, в соответствии с полем метода сжатия в заголовке. Алгоритм декодирования выходит за рамки данной статьи (однако если будут желающие, может появиться в следующей), но в довольно хорошо (и по-русски) описан здесь.

    Таким образом, простейший PNG-файл (на примере ) выглядит следующим образом.


    Заключение


    При написании данной статьи я ставил своей задачей дать читателю общие знания о строении PNG-файла, для более глубокого понимания рекомендуется читать спецификации.

    Топик на хабре про строение JPEG: habrahabr.ru/blogs/algorithm/102521
    Топик на хабре про строение GIF: habrahabr.ru/blogs/algorithm/127083

    Спасибо за внимание, буду рад любой критике!

    Комментарии 78

      +1
      Вот только жаль, что иллюстрация к описанию PNG сделана в GIF. Если бы PNG анимацию поддерживал, то был бы заменой, а так — не, не круто.
        +9
        Есть APNG, но как-то жизнь у него не сложилась
          0
          Очень даже замечательно, что «не сложилась». Формат {был} настолько сырой, что местами проигрывал анимированному gif'у. А вот если бы его доработали и взяли за стандарт — было бы самое оно.
            –1
            Где-то я видел анимированный просто .PNG. Не помню что это было, возможно даже .GIF с .PNG расширением, но в голове отложилось.
              0
              Сдвиг background-image и даже jpeg начнем анимироваться :)
                0
                Он локальным файлом тоже работал.
            +15
            png как формат поддерживает анимацию, а вот софт и браузеры — не очень.
              +3
              PNG как стандарт НЕ поддерживает анимацию, зато поддерживает введение новых чанков, которые могут содержать любую информацию, в т.ч. кадры анимации, закодированные PNG-сжатием.
              +1
              Неофициально — поддерживает. APNG и гугл в помощь. Но разработчики PNG отказались официально признавать, хотя спецификация позволяет.
                +1
                Еще есть MNG для этой цели
                  0
                  Слишком сложен, через чур сложен! Огромный декодер. Разработчики MNG здорово «перегнули палку».
                  +82
                  > Если бы PNG анимацию поддерживал

                  Этот мячик в формате APNG, и он прыгает:
                  Прыгающий мячик

                  Пользователи веб-браузеров на движке Presto (Opera) или Gecko (Firefox) это видят.
                  А пользователи веб-браузеров на базе движка Trident (IE) и Webkit (Safari, Chrome, Chromium) анимацию в этом PNG не видят.
                    +1
                    Ого! Спасибо за интересную информацию.
                      –16
                      Ща в моем хроме WebGL докрутят, и этот мячик так запрыгает, что мало не покажется :)
                      • НЛО прилетело и опубликовало эту надпись здесь
                          +3
                          Боятся что будет тормозить :) Дай бог без WebCL обойдётся
                            0
                            При том, что APNG не признан авторами PNG и не утвержден ИСО. Следственно считать, что авторы оперы и фаерфокса хорошие, а остальные плохие, не стоит. И в подтверждение тому — WebGL, который продвигается гуглом (что требует от них больше усилий, чем потребовалось от мозиллы для модификации стандарта PNG).

                            То что в вебките нет APNG вполне закономерно (смотрите кто у нас разбирал и разбирает PNG).

                            P.S.
                            Старайтесь читать между строк. Даже если строка всего одна.
                          –6
                          В опере оно хоть и пыргает, но иногда подлагивает. С гифом такого не происходит.
                            +6
                            определение типа браузера без Javascript — мячик прыгает, значит Опера или Фокс.
                              +1
                              Вы это примерно так представляете?

                              «Если мячик прыгает, перейдите по этой ссылке, а если не прыгает, то по этой ссылке».
                                +4
                                можно сделать хитрую анимацию с коротким первым кадром. Тогда поставив её в тело ссылки мы сможем скрыть ссылку от наивных пользователей крутых браузеров. Все честные люди увидят только первый кадр с классными сиськами
                              0
                              Давайте я всё-таки черкну пару строк о том, чем плох APNG, и почему было бы скверно, если бы его приняли стандартом.

                              1). Палитры. В отличие от gif, для анимаций с палитрой НЕ поддерживается режим «у каждого фрейма своя палитра». Это здорово сужает спектр применения анимации, и приводит к смешному результату, когда некоторые анимированные gif-ы оказываются компактнее в оригинале, чем те же мультики, закодированные в apng и вынужденные применять 24-битный цвет. Почему авторы не учли эту почти очевидную вещь — загадка природы.

                              2). Зацикливание. {Не проверено!} Плохое управление зацикливанием анимации. Якобы, у gif-a и тут больше возможностей: в частности, у gif-ов есть забавнейший режим «прокрутить анимацию один раз и остановиться», а тут его нет. Кстати, напомню, комбинация из «у каждого фрейма свой цвет» + «прозрачность не затирает картинку, что была снизу» + «прокрутить анимацию только один раз» позволяет записывать многоцветные gif-ы, которые (сюрприз!) иногда оказываются даже компактней размерами, чем «однослойный» PNG.

                              3). Перспективы. Вы можете спросить: «зачем тебе такие экзотические свойства?» и вот что я вам отвечу: формат PNG изначально ущербен (я сейчас объясню, не торопитесь нервничать) — он абсолютно не рассчитан на эффективное сжатие больших по площади изображений. Видимо, его авторы просто не предполагали, что в этом возникнет необходимость, ведь дело было довольно давно.

                              Итак, из-за того, что «фильтры» PNG применяются к строкам целиком (всегда!), мы наблюдаем унылую картину сжатия; пример: берём большую картинку с разнообразным наполнением, режем её на несколько частей, и сжимаем «большую» картинку, а затем разрезанные части по отдельности, и с удивлением замечаем, что «разрезанные» картинки в сумме заняли меньше места, чем большая картинка.

                              Здесь и может помочь анимация — предположим, каждая из разрезанных частей хранится в отдельном фрейме, и сжимается по отдельности, а затем из них, как из мозаики, собирается исходное статическое изображение. Впрочем, это скорее тема для отдельной статьи.

                              И тут появляется вопрос — есть весьма перспективные идеи по доработке apng (так сказать, apng v2), включающие в себя как решение вопросов (1) и (2), так и другие «полезняшки»; так вот — к кому обращаться в этими идеями? Что за люди утверждают подобные стандарты?
                                0
                                Резюмирую — грамотная реализация стандарта, подобного APNG, может улучшить сжатие статических картинок (в некоторых случаях, по прикидкам, до 15% экономии); текущая реализация APNG этого не позволяет.
                                  +1
                                  UPD: Пункт (2) таки ошибочен, зацикливание в APNG есть, в удовлетворительном виде.
                                  А пункт (1) наоборот, требует более расширенной трактовки. Оказывается, среди всего прочего APNG не умеет чередовать кадры с разной глубиной цвета; реализация этой возможности ничего не стоила бы авторам формата, но позволила бы легко экономить размер при сжатии анимации.
                                  0
                                  А в 2017 году он прыгает и в хроме и в лисе
                                0
                                Спасибо за топик!
                                Скажите пожалуйста когда лучше использовать формат PNG а когда JPG с точки зрения сжатия картинки.
                                А то иногда PNG лучше сжимает, а иногда JPG.
                                  +3
                                  Не очень понял вопроса. JPEGом картинку можно сжать сколь-угодно сильно, вопрос только в качестве результата. А PNG использует сжатие без потерь.
                                    0
                                    сколь угодно*
                                    +3
                                    Интересно почитать про PNG и JPG (http://www.artlebedev.ru/tools/technogrette/img/) на сайте сами знаете кого. Тут можно найти ответ на вопросы выше.
                                    P.S. На ссылку карма плохая.
                                    +7
                                    > Скажите пожалуйста когда лучше использовать формат PNG а когда JPG с точки зрения сжатия картинки.

                                    Если картинка многоцветная, пёстрая, без монотонных цветовых областей и без регулярных линейных структур (типичный пример: некая фотография), то лучше будет JPEG.

                                    Если изображение с большими монотонно-залитыми областями и прочими геометрическими примитивами со сплошной цветовой заливкой и регулярными графическими структурами с чёткими границами или равномерным линейным градиентом (типичный пример: скриншот какого-то окошка приложения в ОС), то лучше будет PNG.
                                      0
                                      Выбирать формат по принципу: лучше качество изображения, меньше размер файла.
                                      Вообще лучше конечно PNG, так как без потерь, но учитывать принцип выше.
                                      А иногда не приходится выбирать, когда разговор идёт о прозрачности.
                                        0
                                        ^ скучает по PCX
                                      +23
                                      В заголовоке рекурсивный акроним, такойже как у GNU :)
                                      PNG — PNG Not Gif!
                                      GNU — GNU Not Unix!

                                        +11
                                        Рад, что вы заметили. =)
                                          +5
                                          WINE — Wine Is Not Emulator
                                            +6
                                            Linux — Linux Is Not UniX
                                              +1
                                              PHP — PHP: Hypertext Preprocessor
                                                +1
                                                PHP Home Pages
                                                  +1
                                                  Вообще-то, исторически он назывался Personal Home Page.
                                                –18
                                                PHP — PHP Is Not Language.
                                                  +2
                                                  Тогда уже PHP is Hot Programming
                                                    –2
                                                    Обоснуйте
                                                    +6
                                                    [растерянно]

                                                    Да я же шутейно!

                                                    [демонстрирует татуировку «PHP — навсегда!» на левой груди]
                                                  +7
                                                  VISA — Visa International Service Association
                                                    +3
                                                    GNU HURD — HIRD of Unix-Replacing Daemons, где HIRD — также акроним от англ. HURD of Interfaces Representing Depth.
                                                      0
                                                      LAME — Lame Ain't MP3 Encoder
                                                        –3
                                                        И предлагаю закрыть оффтопик с акронимами следующими:
                                                        SANE — Scanner Access Now Easy
                                                        TWAIN — Technology Without Any Interesting Name
                                                          +2
                                                          Ваши акронимы не рекурсивные, в отличие от GNU, PNG, LAME, WINE
                                                            0
                                                            Каюсь, недоглядел
                                                              0
                                                              PNG не рекурсивный. Portable Network Graphics же.
                                                                0
                                                                Изначальный неофициальный рекурсивный акроним: PNG's not GIF.
                                                                Последующий уже официальный: Portable Network Graphics
                                                          0
                                                          WINDOWS — windows is not do ws :)
                                                          0
                                                          Допустим я открыл или нарисовал картинку в простом редакторе и после этого хочу сохранить. При выборе «Сохранить как» есть возможность выбрать разные форматы png, jpg… (без выбора уровня сжатия) по дефолтным настройкам.
                                                          Так вот иногда бывает что картинка в PNG весит намного меньше чем jpg, хотя в большинстве случаев картинка сохраненная в JPG сохраняется в меньшем размере.
                                                            +2
                                                            Ну тут может быть много факторов. Первое что приходит на ум — количество цветов. Например картинка из примера весит 282 байта при размере 32x32.
                                                            Чтобы разобраться с вашим вопросом, нужно досконально разбираться с спецификациями алгоритмов сжатия, и я не готов сейчас на него ответить.
                                                              +1
                                                              Если совсем утрируя, то PNG хорошо жмет картинки, где не очень большое разнообразие цветов, большие однотонные области, а JPEG — фотографические изображения, с большим кол-вом градиентов. Ну и к тому же надо учесть, что JPEG жмет с потерями, а PNG — без.
                                                                +8
                                                                Неправильно. PNG прекрасно подходит для изображений с большим количеством градиентов, чередований и последовательных повторений участков. А JPEG при этом вообще не имеет никаких идей по поводу градиентов, ибо просто разбивает изображение на блоки 8x8 и выполняет Фурье-преобразование без оглядки на характер содержания блока.

                                                                Понятным языком описано здесь и в следующих 4-х частях статьи.
                                                              –1
                                                              Тег «мы все умрем» порадовал)))
                                                                0
                                                                Помнить, что я скоро умру, – великолепный инструмент, который помог мне принять все самые важные решения в жизни. Мысль о скорой смерти – лучший способ избавиться от иллюзии, что тебе есть что терять. Ты уже будто голенький, и нет причины не следовать за своим сердцем. Смерть – это лучшее изобретение жизни. ©
                                                                +3
                                                                Данные в PNG всегда сжимаются с помощью Deflate/Inflate (который используется, например, в zip), причём не построково. Как тогда вы собираетесь разбивать «обычно по строкам» то, что выплёвывает zlib, который, во-первых, сам буферизует (сжимает-то блоками), во-вторых, выплёвывает куски переменной длины? ;) Ну и тратить 4 байта на каждую строку — слишком расточительно :)

                                                                На самом деле «обычно» размер IDAT — это размер буфера для результатов zlib — сколько не жалко. В далёком 2000-м году, когда Java-апплеты ещё были в моде, но поддержки PNG в зоопарке установленных у пользователей версий JVM ещё не было, в самописной библиотеке для чтения и записи PNG я выделил для буфера целых 32Кб :) При том хитрая реализация чтения могла распаковывать в ограниченном буфере IDAT любой длины.
                                                                  +2
                                                                  Хороший псто, побольше бы таких по разным форматам и протоколам!
                                                                    +1
                                                                    Так вы предлагайте, будет время — напишу.
                                                                      –1
                                                                      Я бы почитал о протоколе HTTP. А еще лучше — HTTPS

                                                                      А из форматов, интересно mp3 и avi. И mht еще
                                                                        0
                                                                        А чем эта статья плоха?
                                                                          +3
                                                                          Я бы почитал о протоколе HTTP.

                                                                          О себе: Фрилансер (вёрстка, web-программирование)

                                                                          вёрстка, web-программирование

                                                                          Facepalm.png
                                                                          В гугле забанили? И в википедии тоже? Сочувствую.
                                                                      +2
                                                                      Я пользуюсь таким мнемоническим правилом:
                                                                      — если данные имеют аналоговую естественную природу (фотографии), то лучшее соотношение (объем)/(качество с точки зрения человека) даст JPG.
                                                                      — если данные имеют цифровую синтетическую природу (графики, скриншоты интерфейсов) с постоянными структурами, ограниченным спектром цветов и без градиентов — лучше сожмет PNG

                                                                      не забывая о многих исключениях из него и экспериментируя по факту.
                                                                        +13
                                                                        А правило точно мнемоническое? :)
                                                                        0
                                                                        Adobe Fireworks вроде бы умел (умеет?) сохранять в PNG слои, кривые и еще какую-то информацию, причем совместимо. Как это могли сделать?
                                                                          +3
                                                                          Возможно используя «приватные» чанки со своей реализацией.
                                                                            0
                                                                            Забавно. Т.е. формально формат не нарушен, совместимость обеспечена и функциональность расширена.
                                                                              +2
                                                                              Да, используя тэги zTXt и tEXt, можно положить в PNG любые данные (и не только текстовые). То есть PNG можно использовать как контейнер для своих данных. При этом любая программа, поддерживающая PNG будет без проблем открывать такие файлы.

                                                                              Посмотреть на содержимое PNG можно при помощи утилиты TweakPNG:

                                                                              image

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

                                                                              NI Vision под LabVIEW тоже умеет. Например, чтобы создать файл 256x256 и записать туда пару «MyData»=«TEST» надо примерно такой код набросать:

                                                                              image

                                                                              (как это выглядит в TweakPNG — см. выше)
                                                                                0
                                                                                Спасибо!

                                                                                Только используют не описанные здесь mkBF, mkBT, mkBS и prVW чанки.

                                                                                Все в отдельных слоях, оранжевая «окружность» — битмап, текст — текстом, смайлик — векторные «штрихи». Перед сохранением видимо сделал отдельный flatten слой, видимый всем, но спрятанный в в самом Fireworks.
                                                                        • НЛО прилетело и опубликовало эту надпись здесь
                                                                            0
                                                                            Ну вон в генте у libpng уже прикрутили use-флаг для него. Может и до других дистров дойдёт.

                                                                            А в оффтопике Firefox поставляется со своей версией libpng, уже собранной с apng патчем.
                                                                            • НЛО прилетело и опубликовало эту надпись здесь
                                                                            0
                                                                            Прочитав про возможность добавления почти произвольных блоков почему-то сразу подумал про ЭЦП, а погуглив понял, что опять не оригинален и dSIG уже придумали. Для всяких регистраторов наверно самое то для выгрузки фото.
                                                                              0
                                                                              Небольшой вопрос — почему числовые данные записаны в Биг Эндиан?

                                                                              Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

                                                                              Самое читаемое