Как расшифровывается bmp. Что такое расширение файла BMP? Что такое BMP

  • 08.03.2020

Была рассмотрена небольшая программа, перемещающая спрайт по экрану, но, к сожалению, он при этом выглядел не так, как хотелось бы. В этой статье мы попробуем «привести» спрайт в порядок.

Изображение спрайта мы получили из Bmp-файла, из таких же файлов можно брать изображение фона, курсора мыши и элементов интерфейса. Однако на экране мы видим не совсем то, что ожидали: изображение оказалось перевернутым и к тому же с иными, нежели требовалось, цветами. Итак, научимся правильно считывать Bmp-файлы и перевернем картинку «с головы на ноги».

По решению разработчиков формат Bmp-файла не привязан к конкретной аппаратной платформе. Этот файл состоит из четырех частей: заголовка, информационного заголовка, таблицы цветов (палитры) и данных изображения. Если в файле хранится изображение с глубиной цвета 24 бита (16 млн. цветов), то таблица цветов может отсутствовать, однако в нашем, 256-цветном случае она есть. Структура каждой из частей файла, хранящего 256-цветное изображение, дана в , а соответствующие типы записей приведены в .

Заголовок файла начинается с сигнатуры «BM», а затем идет длина файла, выраженная в байтах. Следующие 4 байта зарезервированы для дальнейших расширений формата, а заканчивается этот заголовок смещением от начала файла до записанных в нем данных изображения. При 256 цветах это смещение составляет 1078 - именно столько и пришлось пропустить в нашей прошлой программе, чтобы добраться до данных.

Информационный заголовок начинается с собственной длины (она может изменяться, но для 256-цветного файла составляет 40 байт) и содержит размеры изображения, разрешение, характеристики представления цвета и другие параметры.

Ширина и высота изображения задаются в точках растра и пояснений, пожалуй, не требуют.

Количество плоскостей могло применяться в файлах, имеющих небольшую глубину цвета. При числе цветов 256 и больше оно всегда равно 1, поэтому сейчас это поле уже можно считать устаревшим, но для совместимости оно сохраняется.

Глубина цвета считается важнейшей характеристикой способа представления цвета в файле и измеряется в битах на точку. В данном случае она равна 8.

Компрессия. В Bmp-файлах обычно не используется, но поле в заголовке для нее предусмотрено. Обычно она равна 0, и это означает, что изображение не сжато. В дальнейшем будем использовать только такие файлы.

Размер изображения - количество байт памяти, требующихся для хранения этого изображения, не считая данных палитры.

Горизонтальное и вертикальное разрешения измеряются в точках растра на метр. Они особенно важны для сохранения масштаба отсканированных картинок. Изображения, созданные с помощью графических редакторов, как правило, имеют в этих полях нули.

Число цветов позволяет сократить размер таблицы палитры, если в изображении реально присутствует меньше цветов, чем это допускает выбранная глубина цвета. Однако на практике такие файлы почти не встречаются. Если число цветов принимает значение, максимально допустимое глубиной цвета, например 256 цветов при 8 битах, поле обнуляют.

Число основных цветов - идет с начала палитры, и его желательно выводить без искажений. Данное поле бывает важно тогда, когда максимальное число цветов дисплея было меньше, чем в палитре Bmp-файла. При разработке формата, очевидно, принималось, что наиболее часто встречающиеся цвета будут располагаться в начале таблицы. Сейчас этого требования практически не придерживаются, т. е. цвета не упорядочиваются по частоте, с которой они встречаются в файле. Это очень важно, поскольку палитры двух разных файлов, даже составленных из одних и тех же цветов, содержали бы их (цвета) в разном порядке, что могло существенно осложнить одновременный вывод таких изображений на экран.

За информационным заголовком следует таблица цветов, представляющая собой массив из 256 (по числу цветов) 4-байтовых полей. Каждое поле соответствует своему цвету в палитре, а три байта из четырех - компонентам синей, зеленой и красной составляющих для этого цвета. Последний, самый старший байт каждого поля зарезервирован и равен 0.

После таблицы цветов находятся данные изображения, которое по строкам растра записано снизу вверх, а внутри строки - слева направо. Так как на некоторых платформах невозможно считать единицу данных, которая меньше 4 байт, длина каждой строки выровнена на границу в 4 байта, т. е. при длине строки, некратной четырем, она дополняется нулями. Это обстоятельство обязательно надо учитывать при считывании файла, хотя, возможно, лучше заранее позаботиться, чтобы горизонтальные размеры всех изображений были кратны 4.

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

Модуль для чтения 256-цветных Bmp-файлов имеет всего две процедуры. Как видно из листинга, в процедуру чтения файла ReadBMP необходимо передать размеры изображения. Это удобно, если картинку нужно считывать не полностью. Когда заранее известны размеры, это не вызывает проблем, однако было бы хорошо, если бы с помощью нашего модуля можно было читать любые изображения, в том числе и такие, размер которых заранее неизвестен. Для этого предусмотрена процедура ReadBMPheader, считывающая только заголовок файла. Вызвав ее, можно проверить, записано ли изображение в выбранном 256-цветном формате, узнать его размеры и только потом выделять для него память и помещать в отведенный буфер.

Теперь подключим к нашей программе новый модуль. Для этого пропишем его имя в директиве uses, а также предусмотрим массив для хранения данных о палитре, который может быть описан так:

P: arrayof byte;

Процедура CreateSprite, вызывающая операцию чтения файла из нового модуля, упростилась (см. ).

Структура Bmp-файла

Имя Длина Смещение Описание
Заголовок файла (BitMapFileHeader)
Type 2 0 Сигнатура "BM"
Size 4 2 Размер файла
Reserved 1 2 6 Зарезервировано
Reserved 2 2 8 Зарезервировано
OffsetBits 4 10 Смещение изображения от начала файла
Информационный заголовок (BitMapInfoHeader)
Size 4 14 Длина заголовка
Width 4 18 Ширина изображения, точки
Height 4 22 Высота изображения, точки
Planes 2 26 Число плоскостей
BitCount 2 28 Глубина цвета, бит на точку
Compression 4 30 Тип компрессии (0 - несжатое изображение)
SizeImage 4 34 Размер изображения, байт
XpelsPerMeter 4 38 Горизонтальное разрешение, точки на метр
YpelsPerMeter 4 42 Вертикальное разрешение, точки на метр
ColorsUsed 4 46 Число используемых цветов (0 - максимально возможное для данной глубины цвета)
ColorsImportant 4 50 Число основных цветов
Таблица цветов (палитра) (ColorTable)
ColorTable 1024 54 256 элементов по 4 байта
Данные изображения (BitMap Array)
Image Size 1078 Изображение, записанное по строкам слева направо и снизу вверх

Листинг 1

unit bmpread; {процедуры для работы с Bmp} interface type artype = arrayof byte; arptr = ^artype; bmFileHeader = record {заголовок файла} Typf: word; {сигнатура } Size: longint; {длина файла в байтах} Res1: word; {зарезервировано} Res2: word; {зарезервировано} OfBm: longint; {смещение изображения в байтах (1078)} end; bmInfoHeader = record {информационный заголовок} Size: longint; {длина заголовка в байтах (40)} Widt: longint; {ширина изображения (в точках)} Heig: longint; {высота изображения (в точках)} Plan: word; {число плоскостей (1)} BitC: word; {глубина цвета (бит на точку) (8)} Comp: longint; {тип компрессии (0 - нет)} SizI: longint; {размер изображения в байтах} XppM: longint; {горизонтальное разрешение} {(точек на метр - обычно 0)} YppM: longint; {вертикальное разрешение} {(точек на метр - обычно 0)} NCoL: longint; {число цветов} {(если максимально допустимое - 0)} NCoI: longint; {число основных цветов} end; bmHeader = record {полный заголовок файла} f: bmFileHeader; {заголовок файла} i: bmInfoHeader; {информационный заголовок} p: arrayof byte; {таблица палитры} end; bmhptr = ^bmHeader; {чтение изображения из Bmp-файла} procedure ReadBMP(image:arptr; {массив с изображением} xim,yim:word; {размеры} pal:arptr; {палитра} filename:string); {имя файла} {чтение заголовка Bmp-файла} procedure ReadBMPheader(header:bmhptr;filename:string); implementation {$R-} {чтение изображения из Bmp-файла} procedure ReadBMP(image:arptr; xim,yim:word; pal:arptr; filename:string); var h: bmHeader; i: integer; bmpfile: file; s: longint; begin assign(bmpfile,filename); reset(bmpfile,1); blockread(bmpfile,h,sizeof(h)); {чтение заголовка} for i:= 0 to yim-1 do begin {построчное чтение} blockread(bmpfile,image^[(yim-i-1)*xim],xim); if (xim mod 4) <> 0 then blockread(bmpfile,s,4 - (xim mod 4)); end; close(bmpfile); for i ^= 0 to 255 do begin {преобразование палитры} pal^ := h.p shr 2; {синий} pal^ := h.p shr 2; {зеленый} pal^ := h.p shr 2; {красный} end; end; {чтение заголовка Bmp-файла} procedure ReadBMPheader(header:bmhptr;filename:string); var bmpfile:file; begin assign(bmpfile,filename); reset(bmpfile,1); blockread(bmpfile,header^,sizeof(header^)); close(bmpfile); end; end.

Листинг 2

{ спрайта} procedure CreateSprite(s:string; x,y,dx,dy:integer); var f: file; {файл с изображением спрайта} begin getmem(Sprt.Img,sizeof(SpriteArrayType)); {выделяем память для спрайта} getmem(Sprt.Back,sizeof(SpriteArrayType)); {выделяем память для буфера} Readbmp(@(Sprt.Img^),Xsize,Ysize,@p,s); Sprt.x:= x; Sprt.y:= y; {задаем начальные значения} Sprt.dx:= dx; {координат и приращений} Sprt.dy:= dy; end;

Формат файла bmp относиться к растровым изображением и пользуется немалой популярностью. Его хорошо «понимает» любая операционная система виндовс.

Открытие файла с расширением bmp на компьютере, должно происходить автоматически – достаточно кликнуть по нему дважды левой кнопкой мыши.

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

Для этого нажмите на ваш файл bmp правой мышью, подведите курсор к строке «открыть с помощью» и выберите любую программу (если таковы установлены) по своему усмотрению, например паинт.

Какой программой открыть расширение bmp

Самый простой способ открыть формат bmp – это программа фотоальбом виндовс. Скачивать ее не нужно – идет вместе с операционкой, то есть должна быть у всех.

Вторая программа «паинт». Скачивать также не нужно – встроена в виндовс по умолчанию, кроме того, ею можно расширение bmp не только редактировать, но и после открытия сохранить в другом формате, например jpg – для просмотра в телефоне.

Третья программа «PhotoScape». Ее придется скачать. Она бесплатна, на русском и при ее помощи кроме просмотра можно картинки bmp обрабатывать.

Четвертое приложение Paint.NET. Она также бесплатная имеет русский интерфейс, удобная и очень проста в использовании, с множеством инструментов для корректировки и редактирования изображений и фото – это как бы заменитель стандартного «Paint»

Пятая программа «GIMP». Ее приравнивают к фотошопу. Это бесплатный графический редактор для профессионального использования, обладающий всеми необходимыми функциями и отличающийся простотой.

Предоставленные выше программы, открывающие файлы в этом формате – далеко не все. Их десятки, но этих обычному пользователю компьютера вполне достаточно. Успехов.

Эта статья про то, как выглядит графический формат bmp. Хоть это и один из простых форматов, но из-за того, что существует много вариаций этого формата, то не все моменты очевидны. Итак, хватит лить воду, начнем.

Структуры формата

Формат bmp (от слов BitMaP - битовая карта, или, говоря по-русски, битовый массив) представляет из себя несжатое (в основном) изображение, которое довольно легко читается и выводится в ОС Windows, в которой есть специальные функции API, которые в этом помогают.

Для начала приведем графическое представление данных в bmp (картинка взята из MSDN).

В начале стоит заголовок файла (BITMAPFILEHEADER). Он описан следующим образом:

bfType определяет тип файла. Здесь он должен быть BM. Если Вы откроете любой файл BMP в текстовом (а лучше в 16-ричном редакторе), то увидите, что первые два символа - это BM (от слова BitMap, как вы уже, наверное, догадались).
bfSize - это размер самого файла в байтах. Строго говоря вы должны его высчитывать (что рекомендуется), но я ставил размер файла неправильно (правда, не нарочно:)) и никаких проблем не было (ACDSee читало без проблем, моя программа работала), но я вам не рекомендую писать его заведомо неправильно, вдруг появится добросовестная программа, которая сверит этот размер с настоящим и решит, что это не bmp, а что-нибудь другое. В идеале все программы для того, чтобы убедиться, что перед ними действительно bmp, а не подделка, должны, во-первых, проверить, что bfType содержит "BM" (без кавычек), а, во-вторых, что bfSize равен размеру файла.
bfReserved1 и bfReserved2 зарезервированы и должны быть нулями.
bfOffBits . Это один из самых важных полей в этой структуре. Он показывает, где начинается сам битовый массив относительно начала файла (или, как написано в MSDN, "от начала структуры BITMAPFILEHEADER"), который и описывает картинку. То есть, чтобы гарантированно попадать на начало массива вы должны писать:

typedef struct tagBITMAPINFOHEADER
{
DWORD biSize;
LONG biWidth;
LONG biHeight;
WORD biPlanes;
WORD biBitCount;
DWORD biCompression;
DWORD biSizeImage;
LONG biXPelsPerMeter;
LONG biYPelsPerMeter;
DWORD biClrUsed;
DWORD biClrImportant;
} BITMAPINFOHEADER, * PBITMAPINFOHEADER;

biSize - это размер самой структуры. Ее нужно инициализировать следующим образом: bih.biSize = sizeof (BITMAPINFOHEADER);
Снова здесь и дальше будем считать, что bih объявлена следующим образом: BITMAPINFOHEADER bih;
biWidth и biHeight задают соответственно ширину и высоту картинки в пикселях.
biPlanes задает количество плоскостей. Пока оно всегда устанавливается в 1.
biBitCount - Количество бит на один пиксель. Подробнее про это поговорим ниже.
biCompression обозначает тип сжатия. Не удивляйтесь и не пугайтесь, что в bmp и вдруг сжатие. Я лично не видел не одной сжатой bmp (но я не говорю, что таких не существует). Если сжатия нет, то этот флаг надо устанавливать в BI_RGB. В этой статье мы говорим про несжатый формат, поэтому другие флаги я даже не буду перечислять. Похоже, что эта же структура используется и в файлах JPEG и PNG, потому что, начиная с Windows 98 тут появились варианты BI_JPEG, которая показывает, что эта картинка - JPEG и BI_PNG, что это PNG (про формат Jpeg я ничего не знаю, я только сделал эти выводы исходя из того, что написано в MSDN).
biSizeImage обозначает размер картинки в байтах. Если изображение несжато (то есть предыдущее поле установлено в BI_RGB), то здесь должен быть записан ноль. biXPelsPerMeter и biYPelsPerMeter обозначают соответственно горизонтальное и вертикальное разрешение (в пикселях на метр) конечного устройства, на которое будет выводиться битовый массив (растр). Приложение может использовать это значение для того, чтобы выбирать из группы ресурсов наиболее подходящий битовый массив для нужного устройства. Дело в том, что формат bmp - это по сути аппаратно-независимый растр, то есть когда внешний вид того, что получается не зависит от того, на что этот растр проецируется (если можно так выразится). Например, картинка будет выглядеть одинаково вне зависимости от того, рисуется она на экране монитора или печатается на принтере. Но вот разрешение у устройств разное, и именно для того, чтобы выбрать наиболее подходящую картинку из имеющихся и используют эти параметры.
biClrUsed определяет количество используемых цветов из таблицы. Если это значение равно нулю, то в растре используется максимально возможное количество цветов, которые разрешены значением biBitCount. Это актуально только для сжатых картинок. Если biClrUsed не нуль и biBitCount меньше 16, то biClrUsed определяет текущее число цветов графического движка или доступного драйвера устройства. Если biBitCount больше или равно 16, то biClrUsed определяет размер таблицы цветов, используемой для оптимизации текущей системной палитры.
biClrImportant - это количество важных цветов. Определяет число цветов, которые необходимы для того, чтобы изобразить рисунок. Если это значение равно 0 (как это обычно и бывает), то все цвета считаются важными.

Виды формата BMP

Все разновидности формата bmp условно можно разделить на два типа: палитровые и беспалитровые. То есть используется в данном с формате палитра или нет. Заметьте, что палитра может быть даже в беспалитровых форматах, только там она не используется. В беспалитровых bmp цвет высчитывается прямо из тех битов, которые идут в файле, начиная с некоторого места. А в палитровых каждый байт описывает один или несколько пикселей, причем значения байта (или битов) - это индекс цвета в палитре. Для начала приведу таблицу, которая сравнивает возможные варианты. Вид картинки (палитровая или беспалитровая) зависит от того, сколько бит отдается на один пиксель, то есть от значения biBitCount структуры BITMAPINFOHEADER.

biBitCount Палитровый или беспалитровый формат Максимально возможное количество цветов Примечания 1 Палитровый 2 Двуцветная, заметьте, не обязательно черно-белая, палитровая картинка. Если бит растра (что это такое чуть ниже) сброшен (равен 0), то это значит, что на этом месте должен быть первый цвет из палитры, а если установлен (равен 1), то второй. 4 Палитровый 16 Каждый байт описывает 2 пикселя. Вот пример из MSDN .Если первый байт в картинке 0x1F, то он соответствует двум пикселям, цвет первого - второй цвет из палитры (потому что отсчет идет от нуля), а второй пиксель - 16-й цвет палитры. 8 Палитровый 256 Один из самых распространенных вариантов. Но в то же время и самых простых. Палитра занимает один килобайт (но на это лучше не рассчитывать). Один байт - это один цвет. Причем его значение - это номер цвета в палитре. 16 Беспалитровый 2^16 или 2^15 Это самый запутанный вариант. Начнем с того, что он беспалитровый, то есть каждые два байта (одно слово WORD) в растре однозначно определяют один пиксель. Но вот что получается: битов-то 16, а компонентов цветов - 3 (Красный, Зеленый, Синий). А 16 никак на 3 делиться не хочет. Поэтому здесь есть два варианта. Первый - использовать не 16, а 15 битов, тогда на каждую компоненту цвета выходит по 5 бит. Таким образом мы можем использовать максимум 2^15 = 32768 цветов и получается тройка R-G-B = 5-5-5. Но тогда за зря теряется целый бит из 16. Но так уж случилось, что наши глаза среди всех цветов лучше воспринимают зеленый цвет, поэтому и решили этот один бит отдавать на зеленую компоненту, то есть тогда получается тройка R-G-B = 5-6-5, и теперь мы может использовать 2^16 = 65536 цветов. Но что самое неприятное, что используют оба варианта. В MSDN предлагают для того, чтобы различать сколько же цветов используется, заполнять этим значением поле biClrUsed из структуры BITMAPINFOHEADER. Чтобы выделить каждую компоненту надо использовать следующие маски. Для формата 5-5-5: 0x001F для синей компоненты, 0x03E0 для зеленой и 0x7C00 для красной. Для формата 5-6-5: 0x001F - синяя, 0x07E0 - зеленая и 0xF800 красная компоненты соответственно. 24 Беспалитровый 2^24 А это самый простой формат. Здесь 3 байта определяют 3 компоненты цвета. То есть по компоненте на байт. Просто читаем по структуре RGBTRIPLE и используем его поля rgbtBlue, rgbtGreen, rgbtRed. Они идут именно в таком порядке. 32 Беспалитровый 2^32 Здесь 4 байта определяют 3 компоненты. Но, правда, один байт не используется. Его можно отдать, например, для альфа-канала (прозрачности). Читать растр в данном случае удобно структурами RGBQUAD, которая описана так:

Хранение данных в формате bmp

Ну вот и подошли к самому интересному. После структур BITMAPFILEHEADER и BITMAPINFOHEADER идет палитра. Причем, если формат беспалитровый, то ее может и не быть, однако, на это рассчитывать не надо. Дело в том, что, когда я только начинал разбираться с форматом bmp, в одной книжке я вычитал, что, якобы, если формат беспалитровый, то у нее вообще нет палитры. Там даже были две картинки - схемы формата: одна с палитрой, другая без. А я в это время писал программу, которая усердно оперирует с bmp-шками. И мне надо было преобразовывать входящие картинки из 256 цветов в 24-битные (если таковые имелись) во временные файлы. И я в 24-битных палитру просто не создавал (bfOffBits из структуры BITMAPFILEHEADER у меня был равен сумме sizeof(BITMAPFILEHEADER) + sizeof (BITMAPINFOHEADER), а входящие 24-разрядные оставлял без изменений. С 256-цветными растрами все работало как надо, пока мне не попалась 24-разрядная картинка, у которой внизу вместо нужной части отображался мусор. Я не сразу понял в чем дело. Пока не сравнил размер исходного файла с теоретическим, который должен был быть, не будь палитры. Разница оказалась ровно 1 Kб (ровно 1024 байта). Там была палитра. Поэтому никогда не рассчитывайте на то, есть ли палитра и не надейтесь на ее размер (хотя все картинки, которые мне попадались имели размер палитры 256 цветов, или 1Кб), всегда перемещайтесь по файлу на начало растра, используя bfOffBits. Палитра представляет из себя массив структур RGBQUAD идущих друг за другом. Даже если в палитре используются не все цвета (а только, например, 16), то часто все равно под палитру отводят 256 полей. А 256 * 4 = 1024, где 4 - размер структуры RGBQUAD, то есть и получается тот самый один килобайт.

Сразу за палитрой идет сам растр. Тут уже более запутано. Во-первых, пиксели тут описываются так, как написано в таблице выше в зависимости от формата. И могут сами содержать значение компонентов цвета (для беспалитровых), а могут быть индексами массива-палитры. Сама картинка записывается построчно. Во-вторых, картинка идет как бы перевернутая вверх ногами. То есть сначала записана нижняя строка, потом предпоследняя и так далее до самого верха. И, в-третьих, как написано в , если размер строки растра не кратен 4, то она дополняется от 1 до 3 пустыми (нулевыми) байтами, чтобы длина строки оказалась кратна параграфу. Вот это и есть самое неприятное. Дело в том, что для каждого формата приходится подстраивать это число пустых байтов (правда, я люблю туда записывать часть палитры, просто мне не хочется заводить лишние "нулевые" переменные, если все-равно эти байты пропускают и никому они не нужны). Я привожу таблицу с формулами, которые показывают для какого формата сколько байт надо дописывать в конец строки. Там под переменной Width, как можно догадаться, подразумевается ширина картинки. Все эти формулы были установлены экспериментально. Я приведу пример только для наиболее используемых форматов. Для остальных вы можете написать сами.

Примеры программ

Все исходники вы можете скачать .Я особо не буду тут много писать. Просто приведу функции с комментариями.

Привет 1. Создание картинки в формате bmp.
Здесь создается однотонная картинка. В примерах таких функций три: создание bmp 8, 16 и 24 бит. Я приведу только для 16-битных.

// Создадим картинку в формате bmp 16 бит типа 5-5-5, которая будет просто однотонной
void CreateBmp555 (char * fname, WORD color)
{
HANDLE hFile;
DWORD RW;
int i, j;

// Объявим нужные структуры
BITMAPFILEHEADER bfh;
BITMAPINFOHEADER bih;
BYTE Palette [ 1024 ] ; // Палитра

// Пусть у нас будет картинка размером 35 x 50 пикселей
int Width = 35 ;
int Height = 50 ;

memset (Palette, 0 , 1024 ) ; // В палитре у нас нули заполним их
memset (& bfh, 0 , sizeof (bfh) ) ;

Bfh.bfType = 0x4D42 ; // Обозначим, что это bmp "BM"
bfh.bfOffBits = sizeof (bfh) + sizeof (bih) + 1024 ; // Палитра занимает 1Kb, но мы его использовать не будем
bfh.bfSize = bfh.bfOffBits +
sizeof (color) * Width * Height +
Height * ((sizeof (color) * Width) % 4 ) ; // Посчитаем размер конечного файла
memset (& bih, 0 , sizeof (bih) ) ;
bih.biSize = sizeof (bih) ; // Так положено
bih.biBitCount = 16 ; // 16 бит на пиксель
bih.biClrUsed = 32768 ; // Мы используем 5-5-5
bih.biCompression = BI_RGB; // Без сжатия
bih.biHeight = Height;
bih.biWidth = Width;
bih.biPlanes = 1 ; // Должно быть 1
// А остальные поля остаются 0

HFile = CreateFile (fname, GENERIC_WRITE, 0 , NULL , CREATE_ALWAYS, 0 , NULL ) ;
if (hFile == INVALID_HANDLE_VALUE)
return ;

// Запишем заголовки
WriteFile (hFile, & bfh, sizeof (bfh) , & RW, NULL ) ;
WriteFile (hFile, & bih, sizeof (bih) , & RW, NULL ) ;

// Запишем палитру
WriteFile (hFile, Palette, 1024 , & RW, NULL ) ;
for (i = 0 ; i < Height; i++ )
{
for (j = 0 ; j < Width; j++ )
{
WriteFile (hFile, & color, sizeof (color) , & RW, NULL ) ;
}

// Выровняем по границе
WriteFile (hFile, Palette, (sizeof (color) * Width) % 4 , & RW, NULL ) ;
}
CloseHandle(hFile) ;
}

color - цвет картинки. Значение этой переменной должно быть заполнено в соответствии с первой таблицей. Получившуюся картинку вы можете посмотреть в ACDSee, например. Просто я пробовал ее открыть в Photoshop"е, оказалось, что в этом формате он их читать не умеет. А вы можете:).

Пример 2. Преобразование картинки из формата 8 бит (256 цветов) в 24 бит.

BOOL Convert256To24 (char * fin, char * fout)
{
BITMAPFILEHEADER bfh;
BITMAPINFOHEADER bih;
int Width, Height;
RGBQUAD Palette[ 256 ] ;
BYTE * inBuf;
RGBTRIPLE * outBuf;
HANDLE hIn, hOut;
DWORD RW;
DWORD OffBits;
int i, j;

HIn = CreateFile (fin, GENERIC_READ, FILE_SHARE_READ, NULL , OPEN_EXISTING, 0 , NULL ) ;
if (hIn == INVALID_HANDLE_VALUE)
return FALSE;

HOut = CreateFile (fout, GENERIC_WRITE, 0 , NULL , CREATE_ALWAYS, 0 , NULL ) ;
if (hOut == INVALID_HANDLE_VALUE)
{
CloseHandle (hIn) ;
return FALSE;
}

// Прочтем данные
ReadFile (hIn, & bfh, sizeof (bfh) , & RW, NULL ) ;
ReadFile (hIn, & bih, sizeof (bih) , & RW, NULL ) ;
ReadFile (hIn, Palette, 256 * sizeof (RGBQUAD) , & RW, NULL ) ;

// Установим указатель на начало растра
SetFilePointer (hIn, bfh.bfOffBits , NULL , FILE_BEGIN) ;
Width = bih.biWidth ;
Height = bih.biHeight ;
OffBits = bfh.bfOffBits ;

// Выделим память
inBuf = new BYTE [ Width] ;
outBuf = new RGBTRIPLE [ Width] ;

// Заполним заголовки
bfh.bfOffBits = sizeof (bfh) + sizeof (bih) ; // Не будем писать палитру
bih.biBitCount = 24 ;
bfh.bfSize = bfh.bfOffBits + 4 * Width * Height + Height * (Width % 4 ) ; // Размер файла

// А остальное не меняется
// Запишем заголовки
WriteFile (hOut, & bfh, sizeof (bfh) , & RW, NULL ) ;
WriteFile (hOut, & bih, sizeof (bih) , & RW, NULL ) ;

// Начнем преобразовывать
for (i = 0 ; i < Height; i++ )
{
ReadFile (hIn, inBuf, Width, & RW, NULL ) ;
for (j = 0 ; j < Width; j++ )
{
outBuf[ j] .rgbtRed = Palette[ inBuf[ j] ] .rgbRed ;
outBuf[ j] .rgbtGreen = Palette[ inBuf[ j] ] .rgbGreen ;
outBuf[ j] .rgbtBlue = Palette[ inBuf[ j] ] .rgbBlue ;
}
WriteFile (hOut, outBuf, sizeof (RGBTRIPLE) * Width, & RW, NULL ) ;

// Пишем мусор для выравнивания
WriteFile (hOut, Palette, Width % 4 , & RW, NULL ) ;
SetFilePointer (hIn, (3 * Width) % 4 , NULL , FILE_CURRENT) ;
}

delete inBuf;
delete outBuf;
CloseHandle (hIn) ;
CloseHandle (hOut) ;
return TRUE;
}

В функцию надо передавать имена исходного и конечного файла соответственно.

Формат файла BMP (сокращенно от BitMaP) - это "родной" формат растровой графики для Windows, поскольку он наиболее близко соответствует внутреннему формату Windows, в котором эта система хранит свои растровые массивы. Для имени файла, представленного в BMP-формате, чаще всего используется расширение BMP, хотя некоторые файлы имеют расширение RLE, означающее run length encoding (кодирование длины серий). Расширение RLE имени файла обычно указывает на то, что произведено сжатие растровой информации файла одним из двух способов сжатия RLE, которые допустимы для файлов BMP-формата.

В файлах BMP информация о цвете каждого пикселя кодируется 1, 4, 8, 16 или 24 бит (бит/пиксель). Числом бит/пиксель, называемым также глубиной представления цвета, определяется максимальное число цветов в изображении. Изображение при глубине 1 бит/пиксель может иметь всего два цвета, а при глубине 24 бит/пиксель - более 16 млн. различных цветов.

На приведенной схеме показана структура типичного BMP-файла, содержащего 256-цветное изображение (с глубиной 8 бит/пиксель). Файл разбит на четыре основные раздела: заголовок файла растровой графики, информационный заголовок растрового массива, таблица цветов и собственно данные растрового массива. Заголовок файла растровой графики содержит информацию о файле, в том числе адрес, с которого начинается область данных растрового массива. В информационном заголовке растрового массива содержатся сведения об изображении, хранящемся в файле, например, его высоте и ширине в пикселях. В таблице цветов представлены значения основных цветов RGB (красный, зеленый, синий) для используемых в изображении цветов. Программы, считывающие и отображающие BMP-файлы, в случае использования видеоадаптеров, которые не позволяют отображать более 256 цветов, для точной цветопередачи могут программно устанавливать такие значения RGB в цветовых палитрах адаптеров.

Формат собственно данных растрового массива в файле BMP зависит от числа бит, используемых для кодирования данных о цвете каждого пикселя. При 256-цветном изображении каждый пиксель в той части файла, где содержатся собственно данные растрового массива, описывается одним байтом (8 бит). Это описание пикселя не представляет значений цветов RGB, а служит указателем для входа в таблицу цветов файла. Таким образом, если в качестве первого значения цвета RGB в таблице цветов файла BMP хранится R/G/B=255/0/0, то значению пикселя 0 в растровом массиве будет поставлен в соответствие ярко-красный цвет. Значения пикселей хранятся в порядке их расположения слева направо, начиная (как правило) с нижней строки изображения. Таким образом, в 256-цветном BMP-файле первый байт данных растрового массива представляет собой индекс для цвета пикселя, находящегося в нижнем левом углу изображения; второй байт представляет индекс для цвета соседнего справа пиксела и т. д. Если число байт в каждой строке нечетно, то к каждой строке добавляется дополнительный байт, чтобы выровнять данные растрового массива по 16-бит границам.


Не все файлы BMP имеют структуру, подобную показанной на схеме. Например, файлы BMP с глубиной 16 и 24 бит/пиксель не имеют таблиц цветов; в этих файлах значения пикселей растрового массива непосредственно характеризуют значения цветов RGB. Также могут различаться внутренние форматы хранения отдельных разделов файла. Например, информация растрового массива в некоторых 16 и 256-цветных BMP-файлах может сжиматься посредством алгоритма RLE, который заменяет последовательности идентичных пикселей изображения на лексемы, определяющие число пикселей в последовательности и их цвет. В Windows допускается работа с BMP-файлами стиля OS/2, в которых используются различные форматы информационного заголовка растрового массива и таблицы цветов.

О том, как спрятать текст в bitmap-изображении. К сожалению, топиков на эту тему я не нашел и решил восполнить данный пробел. Под катом Вы найдете способ сокрытия текста в bitmap"е, а также реализацию на C#.

Постановка задачи

Спрятать произвольный текст в кодировке windows-1251 в 24-разрядный bitmap рисунок и излвечь его обратно без искажений.

Структура bmp-файла

Для начала напомню, что из себя представляет bitmap-файл. Я уверен, что Вы все это прекрасно знаете, просто будет нагляднее описывать алгоритм сокрытия текста на базе изложенного материала. И так. Любой bmp-файла состоит из четырех частей:
  1. Заголовок файла
  2. Заголовок изображения (может отсутствовать)
  3. Палитра (может отсутствовать)
  4. Само изображение
Заголовок файла содержит служебную информацию, в том числе разрядность рисунка. Кстати, для 24-разрядного рисунка палитра не используется. По скольку мы в задании четко обозначили, что будем работать только с 24-разрядными изображениями, то в идеале можно проверить входное изображение на соответствие требованиям.
Теперь перейдем, непосредственно, к самому изображению. Как Вам известно, формат bmp по умолчанию не предусматривает сжатие (хотя есть поддержка сжатия по алгоритму RLE). Таким образом каждый пиксел в нашем случае кодируется 24 битами, по байту на каждую компоненту цвета. Следовательно, мы можем закодировать ни больше ни меньше, а ровно 16777216 цветов. Для наглядности приведу рисунок:

Идея алгоритма сокрытия текста

Наверное, Вы уже догадались в чем идея. Дело все в том, что глаз среднестатистического человека (не профессионального художника или фотографа) различает намного меньше цветов, чем было указано выше. Ни в одной книге нет четкого ответа на вопрос, сколько все же цветов различает глаз, но самая большая цифра, которую я встретил - 10 млн. Отсюда следует, что несколько младших битов из восьми, отводимых на каждую компоненту цвета, можно позаимствовать для наших корыстных целей.
Немного цифр: для примера возьмем и нагло отнимем у RGB компонент по два младщих бита. То есть из 24 бит у нас останется 18, которыми можно закодировать ровно 262144 цветов. Теперь возьмем текст в кодировке windows-1251, в которой каждый символ представляется 8ю битами. Путем несложных математический вычислений получаем, что 3 символа можно сохранить в 4 пикселах. Таким образом, в картинке 1024x768, где 786432 пикселов можно сохранить 589824 символа. Неплохо, да? Для наглядности приведу две картинки. На первой исходное изображение, а на второй изображение, у которого младшие два бита каждой компоненты цвета заполнены текстом. Сразу оговорюсь, что изображения сконвертированы в png, чтобы пожалеть траффик.

Исходное изображение:


Изображение, содержащее текст


Если приглядется, то на втором изображении цвета кажутся тусклее. Да, это так. Но мы то с Вами знаем, что в представленном изображении что-то не так, а если бы не знали, то и не догадались бы, что в нем спрятан какой-то текст. Кстати, в изображении спрятана фраза «Hello World!!! =)» размноженная 100 раз.
Вот и все. Как Вы видите, идея предельно проста. Кстати говоря, представленный метод называется LSB (спасибо frol за подсказку). Напоследок, можете посмотреть реализацию на языке C#.

Реализация на C#

Представленная реализация не претендует на награду в стиле «Совершенный код», она лишь демонстрирует описанный алгоритм на практике. В данном случае я гнался не за красотой кода, а за наглядностью.