Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > C/C++: Системное программирование и WinAPI > Сохрание BMP файла


Автор: Евгений 19.5.2007, 19:50
Всем доброго дня!

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

За ранее благодарю и низко кланяюсь!

Автор: jonie 19.5.2007, 22:04
Цитата

 подскажите как сохранить изображение в BMP файл из имеющегося битмапа!
?!  "толи лыжи не едут...".....

Автор: ZC1989 19.5.2007, 22:24
Евгений, а разве fwrite уже не работает?

Автор: Евгений 19.5.2007, 23:32
Я имел ввиду как записать эти структуры 

BITMAPFILEHEADER bmfh;
BITMAPINFOHEADER bmih;
RGBQUAD aColors[];
BYTE aBitmapBits[];

в файл и как их заполнить?

Автор: Alexeis 19.5.2007, 23:38
Цитата(Евгений @  19.5.2007,  19:50 Найти цитируемый пост)
Уважемые программисты, подскажите как сохранить изображение в BMP файл из имеющегося битмапа!

  Битмап это тот который в памяти, т.е. DDB? Который создается функциями CreateBitmap, CreateCompatibleBitmap и т.д. ? Или тот который DIB секция? У которого разрешен доступ к битовой карте?

Автор: Евгений 19.5.2007, 23:43
Верно, который создается CreateBitmap!

Добавлено через 8 минут и 21 секунду
Я понял что структура файла BMP состоит из следущих структур:
BITMAPFILEHEADER bmfh;
BITMAPINFOHEADER bmih;
RGBQUAD aColors[];
BYTE aBitmapBits[];
так перед записью в файл их необходимо какимто образом заполнить, каждой переменной своё! Так вот подскажите как это сделать!

Автор: Alexeis 19.5.2007, 23:52
Цитата(Евгений @  19.5.2007,  23:43 Найти цитируемый пост)
Верно, который создается CreateBitmap!

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

Добавлено через 2 минуты и 59 секунд
О значении каждого из полей и о том что туда писать можно подробно прочитать тут. 
http://forum.vingrad.ru/index.php?show_type=forum&showtopic=94227&hl=

Автор: Евгений 19.5.2007, 23:56
Вот я попробовал сделать как понял из прочитанного из книги:
Код

GetObject(hBitm,sizeof(BITMAP),(LPVOID) &Bitmap);
    BITMAPFILEHEADER bitfileh;
    memset(&bitfileh,0,sizeof(BITMAPFILEHEADER));
    bitfileh.bfType            =0;
    bitfileh.bfSize            =Bitmap.bmWidthBytes * Bitmap.bmHeight;
    bitfileh.bfReserved1    =0;
    bitfileh.bfReserved2    =0;
    bitfileh.bfOffBits =sizeof(BITMAPFILEHEADER)+(sizeof(BITMAPINFOHEADER)*24)+sizeof(RGBQUAD);

    BITMAPINFOHEADER bitinfoh;
    memset(&bitinfoh,0,sizeof(BITMAPINFOHEADER));
    bitinfoh.biSize            =sizeof(BITMAPINFOHEADER);
    bitinfoh.biWidth        =Bitmap.bmWidthBytes;
    bitinfoh.biPlanes        =1;
    bitinfoh.biBitCount        =24;

    RGBQUAD hColor[24];// = GetDIBColorTable(hdc,

    long Size = Bitmap.bmWidthBytes * Bitmap.bmHeight;
    BYTE *mBits = new BYTE[Size];
    GetBitmapBits(hBitm, Size, mBits);

    FILE *File;
    File = fopen(szFile,"w");
    fwrite(&bitfileh,sizeof(bitfileh),1,File);
    fwrite(&bitinfoh,sizeof(bitinfoh),1,File);
    fwrite(&hColor,sizeof(hColor),1,File);
    fwrite(&mBits,sizeof(mBits),1,File);
    fclose(File);



Исправте пожалуйста если чтото напутал!

Автор: Alexeis 20.5.2007, 00:07
Цитата

 bfSize - это размер самого файла в байтах.


bitinfoh.biBitCount        =24; 
  это беспалитровый формат так что 

bitfileh.bfOffBits =sizeof(BITMAPFILEHEADER)+(sizeof(BITMAPINFOHEADER)*24)+sizeof(RGBQUAD);
Абсолютно неверно.

Верно будет
bitfileh.bfOffBits =sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER)

дальше 
fwrite(&mBits,sizeof(mBits),1,File); mBits - это указатель, его размер всегда 4 байта, а нужна длинна массива в байтах

   fwrite(&hColor,sizeof(hColor),1,File); - это тоже не нужно.

Добавлено через 2 минуты и 27 секунд
кстати, к тому же fwrite(&mBits,sizeof(mBits),1,File); &mBits - это же указатель на указатель smile

Автор: 586 20.5.2007, 00:18
строка 17: 24-битный битмап не имеет палитры, т.е. массив RGBQUAD писать не нужно
строка 28: fwrite(&mBits,Size,1,File);

Автор: Евгений 20.5.2007, 00:20
Что то не пойму, так что надо записывать в файл, только это
fwrite(mBits,Size,1,File);
или BITMAPFILEHEADER и BITMAPINFOHEADER структуры тоже надо?

Цитата

bfSize - это размер самого файла в байтах.

А как узнать размер файла?

Автор: Евгений 20.5.2007, 00:50
Вроде что то в файл уже записывается но изображение не открывается, посмотрите ещё раз код что тут непраильно, у меня предположение что структуры BITMAPFILEHEADER и BITMAPINFOHEADER заполнены неверно!
Код

GetObject(hBitm,sizeof(BITMAP),(LPVOID) &Bitmap);
    long Size = Bitmap.bmWidthBytes * Bitmap.bmHeight;

    BITMAPFILEHEADER bitfileh;
    memset(&bitfileh,0,sizeof(BITMAPFILEHEADER));
    bitfileh.bfType            =Bitmap.bmType;
    bitfileh.bfSize            =sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER)+Size;
    bitfileh.bfReserved1    =0;
    bitfileh.bfReserved2    =0;
    bitfileh.bfOffBits        =sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER);

    BITMAPINFOHEADER bitinfoh;
    memset(&bitinfoh,0,sizeof(BITMAPINFOHEADER));
    bitinfoh.biSize            =sizeof(BITMAPINFOHEADER);
    bitinfoh.biWidth        =Bitmap.bmWidthBytes;
    bitinfoh.biHeight        =Bitmap.bmHeight;
    bitinfoh.biPlanes        =1;
    bitinfoh.biBitCount        =24;

    RGBQUAD hColor[24];

    BYTE *mBits = new BYTE[Size];
    GetBitmapBits(hBitm, Size, mBits);

    FILE *File;
    File = fopen(szFile,"w");
    fwrite(&bitfileh,sizeof(bitfileh),1,File);
    fwrite(&bitinfoh,sizeof(bitinfoh),1,File);
    
    fwrite(mBits,Size,1,File);

Автор: Alexeis 20.5.2007, 00:58
Вроде уже грубых ошибок не видно, кроме отсутствия fclose(File);

Автор: zkv 20.5.2007, 01:38
тут опечатка?
Цитата(Евгений @  20.5.2007,  00:50 Найти цитируемый пост)
    bitinfoh.biWidth        =Bitmap.bmWidthBytes;

Автор: 586 20.5.2007, 01:41
строка 22: замени BYTE на RGBTRIPLE, или long Size = Bitmap.bmWidthBytes * Bitmap.bmHeight * sizeof(RGBTRIPLE)

Автор: Alexeis 20.5.2007, 11:12
Цитата(zkv @  20.5.2007,  01:38 Найти цитируемый пост)
тут опечатка?

Точно! Выше по коду 
Цитата

  long Size = Bitmap.bmWidthBytes * Bitmap.bmHeight;

  Значит Bitmap.bmWidthBytes - это число байт в строке а не ее длина.

Цитата(586 @  20.5.2007,  01:41 Найти цитируемый пост)
строка 22: замени BYTE на RGBTRIPLE, или long Size = Bitmap.bmWidthBytes * Bitmap.bmHeight * sizeof(RGBTRIPLE) 

А вот это уже не верно. Bitmap.bmWidthBytes * Bitmap.bmHeight * sizeof(RGBTRIPLE) будет в 3 раза больше нужного размера. Да и какой смысл использовать RGBTRIPLE, обработки данных на этом этапе нет, только запись в файл, байты как раз точно отражают размер блока данных.

Автор: zkv 20.5.2007, 13:54
Цитата(586 @  20.5.2007,  01:41 Найти цитируемый пост)
ong Size = Bitmap.bmWidthBytes * Bitmap.bmHeight * sizeof(RGBTRIPLE) 

 smile 
Size правильно определен:
Цитата(Евгений @  20.5.2007,  00:50 Найти цитируемый пост)
long Size = Bitmap.bmWidthBytes * Bitmap.bmHeight;

так как Bitmap.bmWidthBytes - это длина строки в байтах (уже выровненная)

сорри, не заметил, что еще страница есть.

Автор: Евгений 20.5.2007, 20:55
Вот немного переделал, теперь хоть размер сохраняемого файла сходится с исходным. Посмотрите как я передел, но всёравно изображение не открывается.

Код

            GetObject(hBitm,sizeof(BITMAP),(LPVOID) &Bitmap);
    long Size = Bitmap.bmWidth * Bitmap.bmHeight * sizeof(RGBTRIPLE);

    BITMAPFILEHEADER bitfileh;
    memset(&bitfileh,0,sizeof(BITMAPFILEHEADER));
    bitfileh.bfType            =Bitmap.bmType;
    bitfileh.bfSize            =sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER)+Size;
    bitfileh.bfReserved1    =0;
    bitfileh.bfReserved2    =0;
    bitfileh.bfOffBits        =sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER);

    BITMAPINFOHEADER bitinfoh;
    memset(&bitinfoh,0,sizeof(BITMAPINFOHEADER));
    bitinfoh.biSize            =sizeof(BITMAPINFOHEADER);
    bitinfoh.biWidth        =Bitmap.bmWidth;
    bitinfoh.biHeight        =Bitmap.bmHeight;
    bitinfoh.biPlanes        =1;
    bitinfoh.biBitCount        =24;

    BYTE *mBits = new BYTE[Size];
    GetBitmapBits(hBitm, Size, mBits);

    FILE *File;
    File = fopen(szFile,"w");
    fwrite(&bitfileh,sizeof(bitfileh),1,File);
    fwrite(&bitinfoh,sizeof(bitinfoh),1,File);
    
    fwrite(mBits,Size,1,File);
    fclose(File);


Автор: Alexeis 20.5.2007, 21:30
Была еще одна маленькая ошибочка 
 bitfileh.bfType    =Bitmap.bmType;

Bitmap.bmType всегда равна нулю а не сигнатуре битмапа

Правильно будет
  bitfileh.bfType         = 0x4D42;

Вот как только изменил сразу все заработало
Код

void __fastcall TForm1::FormCreate(TObject *Sender)
{
    HDC DC = GetDC(0);
    HBITMAP hBitm = CreateCompatibleBitmap(DC, 512, 512);

    BITMAP Bitmap;
    GetObject(hBitm, sizeof(BITMAP),(LPVOID) &Bitmap);
    long Size = Bitmap.bmWidth * Bitmap.bmHeight * sizeof(RGBTRIPLE);
    BITMAPFILEHEADER bitfileh;
    memset(&bitfileh,0,sizeof(BITMAPFILEHEADER));
    bitfileh.bfType         = 0x4D42;
    bitfileh.bfSize         = sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER)+Size;
    bitfileh.bfReserved1    = 0;
    bitfileh.bfReserved2    = 0;
    bitfileh.bfOffBits      = sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER);
    BITMAPINFOHEADER bitinfoh;
    memset(&bitinfoh,0,sizeof(BITMAPINFOHEADER));
    bitinfoh.biSize         = sizeof(BITMAPINFOHEADER);
    bitinfoh.biWidth        = Bitmap.bmWidth;
    bitinfoh.biHeight       = Bitmap.bmHeight;
    bitinfoh.biPlanes       = 1;
    bitinfoh.biBitCount     = 24;
    BYTE *mBits = new BYTE[Size];
    GetBitmapBits(hBitm, Size, mBits);
    FILE *File;
    char szFile[] = "C:\\1.bmp";
    File = fopen(szFile, "w");
    fwrite(&bitfileh,sizeof(bitfileh),1,File);
    fwrite(&bitinfoh,sizeof(bitinfoh),1,File);

    fwrite(mBits,Size,1,File);
    fclose(File);

    DeleteDC(DC);
}

Автор: zkv 20.5.2007, 21:32
Цитата(Евгений @  20.5.2007,  20:55 Найти цитируемый пост)
    long Size = Bitmap.bmWidth * Bitmap.bmHeight * sizeof(RGBTRIPLE);

не учитываешь выравнивание строк, может быть ошибка из за этого вылазит тут:
 
Цитата(Евгений @  20.5.2007,  20:55 Найти цитируемый пост)
   BYTE *mBits = new BYTE[Size];
    GetBitmapBits(hBitm, Size, mBits);

когда не хватает места под растр

раньше у тебя правильно было:
Код

long Size = Bitmap.bmWidthBytes * Bitmap.bmHeight;


А вообще смотри какие значения возвращают функции + GetLastError() юзай

Автор: Alexeis 20.5.2007, 21:32
Евгений, WinHex лучший друг программиста smile . Когда не ясно почему файл кривой, всегда можно открыть в нем файл и просмотреть содержимое.

Автор: Евгений 20.5.2007, 21:46
Alexeis сделал по твоему коду вроде всё ок, только вот при открытии изображения вне моей программы он пишет  "Ошибка при построении изображения". Открыл Paint там кракозябли но изображение! Незнаешь в чем дело?

Автор: Alexeis 20.5.2007, 22:00
Цитата(Евгений @  20.5.2007,  21:46 Найти цитируемый пост)
Открыл Paint там кракозябли но изображение! Незнаешь в чем дело?

  Если чесно, то функцией GetBitmapBits я ни разу не пользовался и подозреваю, что она работает как-то по другому. Я всегда использовал GetDibBits. Она точно возвращает растр. Если изображение кривое значит не получил битовую карту.

Автор: Alexeis 20.5.2007, 22:43
Таки я был прав эта функция работает для совместимости и битмапы больше 64кб видимо ей не по зубам, кроме того я точно вычислил размер буфера данных исходя из документации а не брал его из структуры (там он дан для цвета 32 бита на пиксел)
Вот в итоге получилось так
Код

void __fastcall TForm1::FormPaint(TObject *Sender)
{
    HDC DC = GetDC(0);
    HBITMAP hBitm = CreateCompatibleBitmap(DC, 512, 512);
    HDC BMP_DC = CreateCompatibleDC(DC);
    SelectObject(BMP_DC, hBitm);
    HPEN pen = CreatePen(PS_SOLID, 1, RGB(255, 0, 0));
    SelectObject(BMP_DC, pen);
    LineTo(BMP_DC, 512, 512);

    BITMAP Bitmap;
    GetObject(hBitm, sizeof(BITMAP),(LPVOID) &Bitmap);

    long Size = (Bitmap.bmWidth * ((Bitmap.bmHeight * 3 + 3) / 4 * 4) + 3) / 4 * 4;

    BITMAPFILEHEADER bitfileh;
    memset(&bitfileh,0,sizeof(BITMAPFILEHEADER));
    bitfileh.bfType         = 0x4D42;
    bitfileh.bfSize         = sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER)+Size;
    bitfileh.bfReserved1    = 0;
    bitfileh.bfReserved2    = 0;
    bitfileh.bfOffBits      = sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER);

    BITMAPINFOHEADER bitinfoh;
    memset(&bitinfoh, 0, sizeof(BITMAPINFOHEADER));
    bitinfoh.biSize         = sizeof(BITMAPINFOHEADER);
    bitinfoh.biWidth        = Bitmap.bmWidth;
    bitinfoh.biHeight       = Bitmap.bmHeight;
    bitinfoh.biPlanes       = 1;
    bitinfoh.biBitCount     = 24;
    BYTE *mBits = new BYTE[Size];

    GetDIBits(BMP_DC, hBitm, 0, Bitmap.bmHeight, mBits, (BITMAPINFO*)&bitinfoh, DIB_RGB_COLORS);

    FILE *File;
    char szFile[] = "C:\\1.bmp";
    File = fopen(szFile, "w");
    fwrite(&bitfileh,sizeof(bitfileh),1,File);
    fwrite(&bitinfoh,sizeof(bitinfoh),1,File);
    fwrite(mBits,Size,1,File);
    fclose(File);
    DeleteDC(DC);


    delete[] mBits;
    BitBlt(Canvas->Handle, 0, 0, 512, 512, BMP_DC, 0, 0, SRCCOPY);
    DeleteObject(pen);
    DeleteObject(hBitm);
    DeleteDC(BMP_DC);
}

Автор: Евгений 20.5.2007, 23:39
Огромное тебе спасибо, Alexeis! Все получилось! Благодаря таким людям и набираешься опыта, разрешишь по всем вопросам обращаться к тебе? 

Автор: Alexeis 21.5.2007, 00:11
Цитата(Евгений @  20.5.2007,  23:39 Найти цитируемый пост)
Благодаря таким людям и набираешься опыта, разрешишь по всем вопросам обращаться к тебе?  

  Неа, мне работать тоже нужно, ну хоть иногда  smile . Так что на форум. Когда есть время я все равно тут smile .

Автор: Евгений 22.5.2007, 20:42
Всё равно спасибо! Ещё вопросик, не подскажите что собой представляет mBits, это набор цветов или чего? Про хочу попробовать написать программу самму простую шифрования изображений. Это вообще возможно? Если кто-нибудь знает какой-нибудь простой способ шифрования изображений поделитесь!

Автор: Alexeis 22.5.2007, 21:20
Цитата(Евгений @  22.5.2007,  20:42 Найти цитируемый пост)
что собой представляет mBits, это набор цветов или чего? 

  Матрица пикселей, т.е. набор строк, в которых данные о цвете пикселов расположены слева направо, снизу вверх. Это значит что массив начнется не с первой строки а с последней (правда бывают исключения). Цвет это либо самого его значение (верно для форматов 16, 24, 32 бита на пиксел) либо индекс цвета в палитре (верно для форматов 1, 4, 8 бит на пиксел).

Цитата(Евгений @  22.5.2007,  20:42 Найти цитируемый пост)
Про хочу попробовать написать программу самму простую шифрования изображений.


А какая разница что шифровать? Файлы все одинаковые, что звук что графика что текст, просто массив байтов.

Цитата(Евгений @  22.5.2007,  20:42 Найти цитируемый пост)
Если кто-нибудь знает какой-нибудь простой способ шифрования изображений поделитесь! 

  В Windows есть CryptoApi.
Первая ссылка в гугле http://www.cryptopro.ru/cryptopro/documentation/capi.htm

Автор: Евгений 22.5.2007, 22:19
Я имею ввиду чтобы изображение зашифровалось потом его открыл, а там место нормальной картинки всякая чушь.

Автор: Alexeis 23.5.2007, 09:38
Евгений, будет не так просто. Размер шифровки может оказаться больше размера исходных данных, потому вероятно прийдеться перед этим графические данные упаковать, например при помощи библиотеки zLib (идет с виндой), а затем уже зашифровать. Если картинка была со смыслом, а не просто рэндом, то она сожмется неплохо.  После шифровки размер блока растра должен получиться меньше исходного, потому для записи понадобиться дописать падинг (т.е. баласт).

  В MSDN е есть пример использования CryptoApi. Эти методы шифрования проверены и надежны. Т.е. устойчивы при том что алгоритм шифровки/расшифровки известен. http://msdn2.microsoft.com/en-us/library/aa382052.aspx

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)