Как устроен файл png
На настоящий момент формат PNG, расшифровывающийся как Portable Network Graphics – один из популярнейших растровых форматов в сфере веб-дизайна. Решение о разработке было принято в 1995 году на конференции, где обсуждалась необходимость создания аналога GIF-формата. Так как последний подразумевал оплату соответствующей лицензии, предполагалось, что новый формат станет более доступным. В результате появился непатентованный PNG, ставший прославленному GIF достойным конкурентом. (О других графических форматах читайте здесь )
PNG: плюсы и минусы
Формат в основном используется для работы в интернете и в графических редакторах.
К его преимуществам относятся:
- Возможность сжатия файла с незначительными потерями или вовсе без них. Рисунок сохраняет исходное качество вне зависимости от интенсивности сжатия;
- Возможность сколько угодно сохранять объект в процессе работы, а потом видоизменять снова. Изначальное качество не меняется;
- Цветовой диапазон формата шире, нежели у GIF. PNG-8 работает с 256 цветами, PNG-24 – с 16,7 млн. цветов;
- Поддержка альфа-канала. Эта функция позволяет плавно сочетать изображение с фоном за счет уменьшаемой прозрачности. Уровни прозрачности по количеству соответствуют доступным цветам и могут варьироваться от предельной контрастности до полного растворения;
- Возможность создания и комбинирования слоев;
- Доступность включения в материал метаданных (при необходимости сохранения авторской информации).
- Компактный размер изображений.
В числе недостатков можно выделить:
- Отсутствие режима анимации;
- Затруднения при работе с полноцветными типами изображений;
- Невозможность сохранять в одном файле сразу несколько рисунков (попытки добавить формату эту функцию предпринимались, но пока не получили всеобщего признания).
Из списка преимуществ становится ясно, чем PNG в первую очередь привлекает интернет-дизайнеров. Его «изюминка» – возможность сохранить картинку на вырезанном ( прозрачном ) фоне. Такая функция очень важна при работе с сайтами, дизайном продукции. В частности, большинство логотипов создаются именно в формате PNG, поскольку являют собой фигурное изображение без фона. (Подробнее про виды логотипов читайте в нашей статье )
Сфера применения
Логотипы – лишь частный пример того, как может использоваться формат. Элементы с прозрачным фоном необходимы для оформления веб-страниц, создания эффектного текста, объемных и рельефных изображений, при различных техниках печати и гравировки. Фактически этот формат применяется во всех сферах дизайна, где необходимо наличие прозрачного фона и возможность трансформировать рисунок без потери качества.
Привет! В этой статье я познакомлю вас с устройством изображений в формате PNG, а так же с механизмом работы алгоритма Deflate. Мы на примере рассмотрим все аспекты этой темы. Надеюсь, вам будет интересно.
Дисклеймер
Пример изображения
Долой длинные вступления. Начинаем!
Структура PNG
Не буду грузить читателя описанием формата, историей его появления и прочими вещами, которые никаким образом не относятся к теме статьи, а сразу же без всяких прелюдий перейду к рассмотрению структуры.
Любой файл PNG начинается со следующей последовательности байтов (в шестнадцатеричном представлении):
Наиболее важными для нас являются 9 и 10 байты. Давайте рассмотрим их чуть подробнее.
Color type
Используемая цветовая модель. Может принимать следующие значения:
Bits per pixel | ||||||
---|---|---|---|---|---|---|
Color option | Channels | BIts per channel | ||||
1 | 2 | 4 | 8 | 16 | ||
Grayscale | 1 | 1 | 2 | 4 | 8 | 16 |
RGB | 3 | 24 | 48 | |||
Indexed | 1 | 1 | 2 | 4 | 8 | |
Grayscale and alpha | 2 | 16 | 32 | |||
RGBA | 4 | 32 | 64 |
Глубина цвета
То, что я написал выше, очень важно запомнить или записать, так как эта информация обязательно пригодится нам при расшифровке чанка IDAT.
В нашем случае чанк IHDR является следующей строкой (в шестнадцатеричном представлении):
Что же это означает? Color type равняется 2, значит используется цветовая модель RGB, т.е. комбинация красного, зелёного и синего каналов. Глубина цвета равняется 16, а значит каждый из этих каналов закодирован парой байтов. Отсюда также несложно понять, что для кодирования одного пикселя изображения используется 48 бит или 6 байт. Идём дальше.
- Zlib header, содержащий информацию о методе, степени сжатия и некоторую служебную информацию.
- Последовательность блоков со сжатой информацией, следующих друг за другом.
- Последовательность битов, представляющих собой контрольную сумму несжатых данных, сгенерированную при помощи алгоритма Adler-32
Давайте немного подробнее.
Zlib header
Спецификация RFC 1950 говорит нам, что zlib header должен состоять из двух байтов. Первый байт – это Compression Method and Flags (CMF), который в свою очередь содержит:
- Compression Level (FLEVEL) – степень сжатия.
- Preset Dictionary (FDICT) – Если он равен 1, то за байтом FLG должен идти словарь. Если я правильно понял, то это просто последовательность несжатых байтов, которая необходима для кодирования/декодирования содержимого. Словарь полезно использовать при небольших размерах файла.
- Check bits for CMF and FLG (FCHECK) – число, которое дополняет CMF * 256 + FLG до такого значения, чтобы оно было кратно 31.
Схематично Zlib header можно изобразить так:
CMF | FLG | DICT (IF FDICT === 1) | |||
---|---|---|---|---|---|
CINFO | CM | FLEVEL | FDICT | FCHECK | |
0111 | 1000 | 11 | 0 | 11010 |
В нашем случае: 78 DA или в двоичном представлении 0111100011011010:
Нижними подчёркиваниями я обозначил любые значения, ибо они нам в данный момент безразличны.
Как видим, наше значение не попадает в первый промежуток. Идём дальше:
Таблица фиксированных кодов Хаффмана | |||
---|---|---|---|
Коды символов | Длина | Диапазон | |
0-143 | 8 | от 00110000 до 10111111 | |
144-255 | 9 | от 110010000 до 111111111 | |
256-279 | 7 | от 0000000 до 0010111 | |
280-287 | 8 | от 11000000 до 11000111 |
Таблицы длин и обратных смещений
Тут тоже нет ничего сложного. Если код, который мы высчитали по предыдущей таблице попадает в промежуток от 257 до 285 (то есть, 3 и 4 случаи, исключая код 256, 286 и 287), то наше итоговое значение будет высчитываться по длине и обратному смещению. Но что же это? А это и есть результат работы Deflate. Название таблицы говорит само за себя. Когда мы натыкаемся на подобную команду, нужно отступить назад на количество битов, равное обратному смещению и взять количество битов, равное длине.
Таблица длин | ||
---|---|---|
Коды | Доп. биты | Диапазон длин |
257-264 | 0 | 3-10 |
265-268 | 1 | 11-12, 17-18 |
269-272 | 2 | 19-22, 31-34 |
273-276 | 3 | 35-42, 59-66 |
277-280 | 4 | 67-82, 115-130 |
281-284 | 5 | 131-162, 227-257 |
Код 285 обозначает длину 258
Таблица обратных смещений:
Таблица смещений | ||
---|---|---|
Коды | Доп. биты | Диапазон смещений |
0-3 | 0 | 1-4 |
4,5 | 1 | 5-6, 7-8 |
6,7 | 2 | 9-12, 13-16 |
8,9 | 3 | 17-24, 25-32 |
10,11 | 4 | 33-48, 49-64 |
12,13 | 5 | 65-96, 97-128 |
14,15 | 6 | 129-192, 193-256 |
16,17 | 7 | 257-384, 385-512 |
18,19 | 8 | 513-768, 769-1024 |
20,21 | 9 | 1025-1536, 1537-2048 |
22,23 | 10 | 2049-3072, 3073-4096 |
24,25 | 11 | 4097-6144, 6145-8192 |
26,27 | 12 | 8193-12288, 12289-16384 |
28,29 | 13 | 16285-24576, 24577-32768 |
Логика здесь точно такая же. После того, как мы нашли значение длины, считываем еще 5 битов (от младшего к старшему, естественно), переводим в десятичное представление для удобства. Этот код находим в таблице, используем дополнительные биты при необходимости и находим обратное смещение. Поехали дальше, начнём разбирать наш код.
Приступаем
Для удобства переведём всю последовательность в двоичный код (можно написать программу для этого, можно сделать вручную, а можно воспользоваться калькулятором, которых в Интернете полно; лично я предпочитаю первый вариант). Итак, у нас получается код (не забывайте про нули, их здесь может и не быть, но каждый байт обязательно является двоичным восьмибитным числом):
01100010 01011000 10110001 1010100 1000001 10100001 10100000 11100000 11111111 11111111 11111111 11111111 11111111 11111111 1100111 10000000 1010000 10000 | 100001 01000000 00000000 00000000 00000000 11111111 11111111 11011100 01000111 00010000 11001111 1001001 1000101 1001110 1000100Выглядит эффектно, правда? На самом деле, тут всё просто. Начинаем:
2. На этом этапе нужно брать минимум 7 битов, определять префикс. Далее уже переводить в число, либо считать смещения и длины. Что же мы стоим? Вперёд!
Таким же образом проделываем всю остальную работу. Мы помним, что для кодирования 1 пикселя в нашем случае используется 6 байтов. Считаем их всех, а потом ненадолго остановимся:
Символами \(\lceil\) \(\rceil\) обозначают потолок, то есть округление до целого в большую сторону.
Получилось, что R в нашем случае равен 168. Если мы проделаем то же самое с G и B-каналами, то получим, что цвет нашего первого пикселя в RGB-представлении равен (168, 32, 112). Теперь посмотрим на скриншот:
Мы движемся в верном направлении. А это значит, что нет смысла останавливаться! Второй пиксель декодируем так:
Значение попадает в третий промежуток, давайте разбираться. По формуле получим следующий код:
Тем не менее, далее следует блок, который имеет нулевую длину. Честно признаюсь, я не до конца осознал целесообразность его нахождения здесь. Продолжим.
Судя по всему, начало нового блока? Ладно. Сжатия нет, блок последний.
Ну и наконец, в самом конце чанка IDAT следует четырёхбайтовая хеш-сумма несжатых данных, высчитанная по алгоритму Adler-32. В нашем случае, хеш-сумма в двоичном представлении выглядит так:
Adler-32
Последовательность (в десятичном представлении):
0 168 165 32 32 112 112 255 255 255 255 255 255 0 255 255 255 255 255 255 168 165 32 32 112 112Я набросал на PHP простенький код:
$array = explode(' ', $uncompressed);
$start_1 = 1;
$start_2 = 0;
foreach($array as $num)
$temp_1 = $start_1 + trim($num);
$temp_2 = $start_2 + $temp_1;
$start_1 = $temp_1;
$start_2 = $temp_2;
$hash = sprintf("%016b", $temp_2 % 65521) . sprintf("%016b", $temp_1 % 65521);
Переменная $hash после выполнения кода содержит строку:
Найдите 10 отличий. Хеш-суммы совпадают, а поэтому с полной уверенностью заявляю, что мы сделали всё верно! Со следующего байта уже начинается чанк IEND, с IDAT же мы наконец-то закончили.
Стоит признать, что это было довольно просто. В пункте про алгоритм Deflate мы также рассмотрим декодирование строки (да, для примера возьмем обычную строку) с динамическими кодами Хаффмана.
Обязательный чанк, который должен следовать после всех остальных. Символизирует окончание PNG-документа и не содержит какой-либо информации.
Deflate
Алгоритм Хаффмана
Еще один алгоритм сжатия без потерь, заменяющий все символы входящей последовательности на специальные коды таким образом, что самые часто встречающие символы образуют самые короткие коды. Алгоритм пробегается по строке, подсчитывая частоту вхождения каждого символа, потом выстраивается очередь с приоритетом, где самыми первыми встраиваются элементы с наименьшей частотой. После чего выстраивается бинарное дерево, в котором каждый символ является концевой вершиной. В конце концов каждый символ получает свой код исходя из того, насколько далеко он располагается относительно корня. Есть смысл объяснять подробнее?
2. Сортируем его по частоте и создаем очередь. Приоритетом является частота. При этом, элементы с самым низким приоритетом выстраиваются в самом начале:
3. Теперь создаем бинарное дерево. Алгоритм такой: Берем первые два элемента и формируем из них мини-дерево, где листьями будут наши элементы. В таблицу записываем сумму приоритетов, помещая его в нужное место:
Таким же образом делаем со всеми остальными. В итоге получается такое дерево:
Теперь нужно заменить нашу последовательность полученными кодами и выстроить таблицу, чтобы интерпретатор мог без проблем её декодировать. В принципе, это всё.
Ближе к теме
Я уже выше писал, но напомню еще раз. Данные состоят из блоков, следующих друг за другом последовательно. Каждый из них содержит заголовок, состоящий из 3 битов (RFC 1950):
1 бит – BFINAL. Устанавливается в 1, если данный блок последний.
2 бита – BTYPE. Содержит информацию о типе сжатия:
- 00 – без сжатия
- 01 – сжатие с фиксированными кодами Хаффмана
- 10 – сжатие с динамическими кодами Хаффмана
- 11 – зарезервированное значение. Установка его вызовет ошибку.
Для урока я взял простую последовательность, которая не включает в себя длины и смещения. Тем не менее, работать с подобными данными мы уже умеем, так как это было рассмотрено выше, а метод сжатия значения не имеет, ибо всё происходит точно так же. Также уточню, что строка ниже не содержит заголовка zlib и контрольной суммы. Мы рассматриваем только Deflate.
Пример Deflate с динамическими кодами Хаффмана
Предположим, у нас есть следующая последовательность:
05 A0 61 0B 00 00 18 86 2E 68 8F EA D9 3D AE C7 B4 9B 10 E8 FF 40 21 07 14 56 5A DAВ двоичном представлении:
00000101 10100000 01100001 00001011 00000000 00000000 00011000 10000110 00101110 01101000 10001111 11101010 11011001 00111101 10101110 11000111 10110100 10011011 00010000 11101000 11111111 01000000 00100001 00000111 00010100 01010110 01011010 11011010Давайте ненадолго остановимся и рассмотрим всё это. Блок с динамическими кодами состоит из следующих частей (исключая заголовок и описание длин):
Внимательность: в первой части мы получаем лишь длины, которые в последствии пригодятся при декодировании. Мы получаем длины тем же способом, что я описал выше.
2. Далее идёт последовательность из HLIT + 257 команд и значений закодированных символов, которые можно найти, исходя из их позиции в потоке данных (на примере я объясню этот момент). В предыдущем блоке мы получили их длины. Теперь же мы сможем сопоставить распакованное значение и длину кода, которым оно закодировано. Используя приведённый выше алгоритм, зная длины кодов и их значения, мы без проблем найдем сами коды.
3. Последним идёт блок со сжатыми данными. На этом этапе мы уже знаем коды и их значения (или длины и смещения). Поэтому без проблем можем декодировать строку.
Если у вас каша в голове, то не переживайте, сейчас на примере мы всё разберём. Поехали!
Итак, берём HCLEN + 4 (15) трёхбитовых значений:
Отбросим все значение, где длина равна 0 и переведём в десятичную систему:
Теперь восстанавливаем коды длин:
Затем у нас следуют 257 длин и команд. Здесь мы и применяем наши длины символов и команд. Давайте я покажу как найти значение.
Впрочем, интерес не только спортивный - до сих пор PNGRepack относилась к не относящимся к делу фрагментам очень лояльно - записывала их в преобразованный файл, как ни в чем не бывало, хотя от многих из них вполне можно избавиться, а другие, напротив надо нежить, холить и лелеять, и вообще при обработке изображений обращать на них пристальное внимание.
Сегодня про общую структуру и про фрагменты tEXt, zTXt, iTXt, tIME, pHYs и gAMA.
До сих пор не знаю, как слово chunk правильно перевести на русский, пущай будет фрагмент. Каждый фрагмент начинается с 4-х байт длины, затем название из 4-х латинских букв, затем непосредственно данные, а в конце - 4-байтная контрольная сумма CRC. Из того, как построено название, уже можно много почерпнуть.
Если первая буква заглавная, значит фрагмент "критический", т.е считается, что без него изображение не удастся посмотреть ни в каком виде, и если хоть один из критических фрагментов отсутствует, декодер должен грязно выругаться. Критических фрагментов сейчас 4: IHDR (Image Header), PLTE (Palette), IDAT (Image Data) и IEND (Image End). Уже здесь возникает неоднозначность - палитра критически важна только для изображений, у которых ColorType = COLOR_PALETTE, для остальных она допустима (правильный декодер должен попытаться ей воспользоваться, если монитор может отображать одновременно лишь ограниченное число цветов), но как правило отсутствует.
Соответственно, строчная буква означает, что фрагмент "вспомогательный" (Ancillary), т.е что-то похожее отобразится и если их проигнорировать, но зачем-то их все-таки запихнули, наверное, не зря.
Если последняя буква у вспомогательного фрагмента заглавная, значит, что при изменении самого изображения они тоже, возможно, должны определенным образом измениться, поскольку зависят от этого изображения, например, tRNS (transparency) отвечает за "простую прозрачность" (cheap transparency), когда либо один из цветов назначается прозрачным, он-то и записан в tRNS, либо нескольким первым цветам палитры приписываются значения прозрачности. Если какое-то приложение открыло tRNS, ничего не зная о том, что это за зверь, а потом после работы с изображением перемешало палитру, то ничего хорошего не выйдет, и считается лучшим выходом либо вообще выкинуть tRNS после такого издевательства, либо и вовсе не браться за редактирование изображения, если в нем есть такие вспомогательные фрагменты с заглавной последней буквой. Впрочем, это правило мало кто соблюдает, да и расплывчатое оно. Почему gAMA (значение гамма-коррекции) так уж зависит от самой картинки - мне непонятно. Собственно, строчная последняя буква есть только у текстовых фрагментов и у pHYs (pHysical size), хранящего разрешение картинки при сканировании, скажем, 600 dpi.
И наконец, вторая буква названия фрагмента заглавная, если он "публичный", т.е утвержден высочайшим советом и входит в спецификацию, и строчная, если он "частный", таковые тоже существуют, но известно о них немного. Скажем, Adobe Fireworks использовала формат PNG в качестве базового, но добавила туда свои собственные фрагменты mkBS, mkTS, mkBF, prVW и др., чтобы хранить что-то вроде слоёв и векторов, чтобы можно было сохранить проект в PNG, а позже открыть его и продолжить работать как ни в чем ни бывало. О том, как они устроены, Adobe не распространяется. На эти штуки я натыкался, они могут занимать приличные объемы, и если мы не собираемся редактировать эту картинку в Fireworks, их следует удалить.
(что такое FACECAFE - не знаю. Похоже на рекламный слоган: "Хороший mkBT начинается с FACECAFE")
Теперь рассмотрим, наконец, фрагмент каждого типа подробнее.
Текстовые фрагменты (tEXt, zTXt, iTXt)
Произвольный текст, добавленный к изображению. Например, таким образом можно записать EXIF, хотя фотографии обычно все-таки хранятся в JPG. tEXt - самый простой, несжатый текст, каждый фрагмент содержит название (keyword) и непосредственно текст. zTXt (zipped text) - то же самое, только текст сжат тем же самым zlib (оставлена возможность под другие форматы сжатия, но пока доступен только CompressionMethod=0, что означает zlib), а iTXt (international text) имеет кодировку не Latin-1, как два предыдущих, а UTF-8, причем текст может быть сжатым, а может и нет, а кроме Keyword добавляются еще поля Language и Translated keyword.
(Пожалуй, самое нетривиальное, что встретилось мне в текстовых полях)
Для Keyword определены стандартные значения Title, Author, Description, Copyright, Creation Time, Software, Disclaimer, Warning, Source, Comment, но можно использовать и любые другие.
Метка времени tIME
В стандарте сказано, что это должно быть время последнего изменения изображения, оттого последняя буква заглавная (отредактировал изображение - будь добр и время переписать!). Довольно скучная вещь, но может пригодиться.
Физические размеры пикселя (разрешение) pHYs
Совсем короткий фрагмент, но очень важный при обработке сканов - многие программы этого толка отталкиваются не от размера изображения в пикселях, а от физического размера бумаги, чтобы обложка в 600 dpi и последующие страницы в 300 dpi в итоге отображались бы в одном масштабе. Как правило, в pHYs записывается горизонтальное и вертикальное разрешение в пикселях на метр, которые легко пересчитать в пиксели на дюйм.
Возможен вариант, когда единица измерения не указана, тогда узнать реальное разрешение нельзя, только "соотношение сторон пикселя" - такой информацией было бы полезно снабдить кадр анаморфного видео, чтобы можно было корректно отобразить его на экране. Создатели формата рекомендуют проверять, являются ли пиксели "квадратными" у изображения и растягивать его в нужную сторону, если нет, чтобы не получить вытянутые морды. Пока ни одного изображения с "безразмерным" разрешением мне не встретилось.
Снова видим эфемерность регистра последней буквы - тут она строчная, но очевидно же, что при масштабировании изображения нам придется поменять и физическое разрешение. Не удивлюсь, если создатели формата кидали монетку, чтобы определить, где буква будет заглавной, а где строчной.
Гамма-коррекция gAMA
Самый неоднозначный фрагмент. Задумка была хорошая - на разных операционных системах свои соглашения, с какой гаммой показывать изображения, соответственно, (128,128,128) может означать немножко разный цвет, и почти никогда это не будет 50% серый, который ближе к (192,192,192), в общем, хотелось авторам навести некоторый порядок, но почему-то, вместо того, чтобы обозначить, как закодирована яркость в изображении (1 означало бы, что линейно, а число отличное от 1 - наличие степенной зависимости), они постановили, что гамма будет говорить просмотрщику, в какой мере надо еще преобразовать эти пиксели, прежде чем в линейном виде подать на монитор! Это привело к большой путанице, и я не уверен, что хоть где-то её обработка сделана корректно. Вот те немногие гаммы, что я находил в изображениях:
Кажется, вот оно, корректное применение - мы предупреждаем просмотрщик, что вместо стандартной для sRGB гаммы в 2.2 мы задаем 2.4, и просмотрщик теперь чуть-чуть подкорректирует значения и получит качественную картинку. НЕ ТУТ-ТО БЫЛО. Согласно спецификации, в gAMA должно заносится значение, обратное гамме, в данном случае 1/2.4 = 0.42. То, что программы продолжают беспалевно сохранять сюда саму гамму вместо обратной величины, и никто этого не заметил - дурной признак.
Открываем Translhextion, перетягиваем в окно изображение формата png, рассматриваем байты.
PNG(сокращение от Portable Graphic Network) - это растровое изображение, которое имеет следующую структуру:
Первые 8 байт это сигнатура PNG, всегда одна и та же:
89 50 4E 47 0D 0A 1A 0A
Первый байт 89 - идентификация типа файла, для систем, где ожидается, что первые два байта будут идентифицировать тип файла. Первый байт выбирается не как ASCII-символ для того, чтобы файл не был распознан как текст, также это исправляет плохую передачу файла, которая очищает 7-ой бит.
Второй, третий и четвертый 50 4E 47 - PNG в текстовом формате ASCII
Пятый, шестой 0D 0A - конец строки и перевод(DOS, CR+LF)
Седьмой 1A - EOF, End Of Fil, конец файла (DOS)
Восьмой байт 0A - LF, перевод строки(Unix)
После идут несколько обязательных фрагментов(chunks), блоков с данными - IHDR, IDAT, IEND
00 00 00 0D 49 48 44 52
Первые 4 байта фрагмента это размер фрагмента:
00 00 00 0D - размер 13 байт.
После идет тип(имя) фрагмента:
49 48 44 52 - IHDR в ASCII
Далее после имени фрагмента, идет его описание:
IHDR состоит из 13 байт, пример:
00 00 00 80 00 00 00 7F 08 03 00 00 00
Первые 4 байта - 00 00 00 80 это ширина изображения, в десятичной системе 128
Вторые 4 байта - 00 00 00 7F это высота изображения, в десятичной системе 127
Десятый байт - 03 тип цвета, имеет три варианта:
1 - Используется палитра
2 - Цветное изображение, не монохромное
Так как у нас изображение имеет прозрачность, то стоит байт 03
Одиннадцатый байт - 00 метод сжатия, всегда 0
Двенадцатый байт - 00 метод фильтрации, всегда 0
Тринадцатый и последний байт IHDR - 00 переплетение(interlace), 00 - нет переплетения, 01 - переплетение Adam7
Последние 4 байта фрагмента - это контрольная сумма CRC32, рассчитывается, исходя из предыдущих байт фрагмента, байты для расчета, включают имя фрагмента и его данные, но не включают поле длины фрагмента!То есть для расчета берутся байты 49 48 44 52 00 00 00 80 00 00 00 7F 08 03 00 00 00
На данный момент имеем:
89 50 4E 47 0D 0A 1A 0A - сигнатура PNG
00 00 00 0D 49 48 44 52 - Длина и описание блока данных(IHDR)
00 00 00 80 00 00 00 7F 08 03 00 00 00 - Значения полей блока данных IHDR
10 24 3A 35 - Контрольная сумма CRC32 блока данных IHDR
В итоге начало файла выглядит так:
89 50 4E 47 0D 0A 1A 0A 00 00 00 0D 49 48 44 52 00 00 00 80 00 00 00 7F 08 03 00 00 00 10 24 3A 35
IDAT - содержит данные самого изображения
IEND - говорит о конце файла, не имеет полей, значение фиксировано:
Читайте также: