1. 程式人生 > >bmp圖片轉換成16進位制資料

bmp圖片轉換成16進位制資料

最近在開發中要在aboot中顯示一張圖片。但是發現aboot中顯示圖片不是直接拿圖片檔案來顯示的,而是把一個16進位制的資料序列依次往螢幕上搬運,就可以了。

那問題是,怎麼把一張圖片轉換成16進位制的資料序列?

在網上也找了一些資料,也諮詢了一些同事,最後終於搞定,下面把相關的做法寫下來備忘。

我們的aboot中顯示方式只支援24bit bmp圖片,所以要先看一下拿到的圖片是不是這種格式。不是的話,就先用小畫家或更高階的工具轉成這種格式。然後,到ubuntu下執行命令:

 xxd -i logo.bmp logo.h
這個命令會把圖片轉換成16進位制數字,寫進一個頭檔案logo.h,這個標頭檔案中會有一個數組,和一個表示陣列長度(也就是bmp檔案長度)的變數。

logo.h:

unsigned char logo_bmp[] = {
  0x42, 0x4d, 0xde, 0xc9, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0x00,
  0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x86, 0x00, 0x00, 0x00, 0x22, 0x01,
  0x00, 0x00, 0x01, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0xc4, 0x0e, 0x00, 0x00, 0xc4, 0x0e, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
...
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
unsigned int logo_bmp_len = 117214;
好了,已經拿到16進位制的資料序列了,這就是我們要用的資料序列嗎?不!還有好幾步要做:

1. 瞭解bmp圖片格式的知道,這個陣列的前面一段是檔案頭資訊,但是aboot中不需要這一段,所以要把它刪除。

2. 另外,bmp圖片如果每一行的位元組數不是4的倍數,就會在每一行的最後新增若干位元組空資料以對齊。這個空資料也要刪除。

以 24bit bmp: 134X290 為例:

圖片每一行134個畫素,每個畫素3個位元組,所以每一行是134*3 == 402個位元組,bmp會在每一行的最後新增兩個位元組來對齊。我們就要把這兩個位元組刪除。

到這裡可以了嗎?還是沒有,如果你試一下,就會發現,顯示出來的圖片是頭向下倒過來的!因為bmp的資料就是從下往上存的。知道這一點,你可以一開始就在windows上把圖片先做一個180度旋轉,否則,就還要做下面兩步處理:

3.反轉整張圖,即把陣列的陣列頭尾互換一下。

4.做完步驟3,你會發現圖片的顏色不對了!怎麼回事? 是啊,R,B的位置也換了,R、G、B各佔一個位元組,所以還要每三個位元組,就要把R和B的位置換一下。

好了,大功告成!

不過只是對左右對稱的圖片是可以的,如果左右不對稱,則要先把圖片轉個180度,然後,只做上面的前兩步,就可以了。

下面看一下具體處理圖片的程式碼:

main.c:

#include <stdio.h>

//TO_DO ++++++++

#include "logo.h"

//bmp width, height
int bytes_per_bpp = 3;
int bmp_width = 134;
int bmp_height = 290;
unsigned int file_len = 117214;
unsigned char *bmp_file_data = logo_bmp;
#define REVERT_BMP  1   //revert the bmp: 1, don't revert: 0
//TO_DO  --------


/*bmp檔案的前部是檔案頭資訊,aboot中畫圖不需要這些
 * 1 刪除檔案頭資訊,
 * 
 * 以 24bit bmp: 134X290 為例
 * 圖片每一行134個畫素,每個畫素3個位元組,所以每一行是134*3 == 402個位元組,
* bmp會在每一行的最後新增兩個位元組對齊。
* aboot中要用這個圖片的話,
* 2.要把每行最後的兩個對齊位元組刪除
* 
* 每行最後的兩個位元組刪除以後
* 因為bmp本身的圖片是倒的,
* 3.反轉整張圖
* 
* 反轉整張圖以後,R和B也互換了,導致顏色不對
* R、G、B各佔一個位元組,
* 4.這裡是使其中的R 和 B互換。
* 
* 所以,如果把拿到手的圖片先在windows上做一下180度的旋轉,
* 應該可以省略步驟3和4,把上面的 REVERT_BMP 設定為0 即可。
*/
int main()
{
	unsigned int i = 0;
	unsigned int j = 0;
	unsigned int bmp_info_len = 0;
	unsigned int temp = 0;
	unsigned int actual_data_len = 0;
	unsigned char result_arry[file_len];
	unsigned char result_arry2[file_len];
	unsigned int data_bytes_per_row = (bmp_width*bytes_per_bpp);
	unsigned int total_bytes_per_row = 0;
	unsigned int null_bytes_per_row = 0;
	unsigned int array_len = 0; 
	if(data_bytes_per_row%4 != 0)
	{
		null_bytes_per_row = 4-data_bytes_per_row%4;
	}
	
	total_bytes_per_row = data_bytes_per_row + null_bytes_per_row;//total bytes per row
	bmp_info_len = file_len - total_bytes_per_row*bmp_height;//bmp file info len,or len to delete
	printf("file_len:---------------> %d \n",file_len);
	printf("bmp_info_len:-----------> %d \nnull_bytes_per_row:-----> %d\n",bmp_info_len,null_bytes_per_row);
	
	
	//1 刪除檔案頭資訊,
	for(i=bmp_info_len,j=0;i<file_len;i++,j++)
	{
		result_arry2[j] = bmp_file_data[i];
	}
	array_len = j;
	printf("step 1 over.\n");
	printf("data total_bytes:-------> %d \n",file_len-bmp_info_len);
	printf("total_bytes_per_row:----> %d \n",total_bytes_per_row);
	
	//in data <-- bmp_file_data[..]
	//2.刪除每行最後的若干個位元組,存於result_arry2
	for(i=1,j=0;i<=array_len;i++)
	{
		//delete null data at the end of row 
		result_arry[j] = result_arry2[i-1];
		j++;
		if(i%total_bytes_per_row==0)
		{
			j -= null_bytes_per_row;
		}

	}// out data to -->result_arry[..]
	printf("step 2 over.\n");
	
	array_len = j;
	
	#if REVERT_BMP
	//3.這裡是反轉整張圖
	for(i=0,j=0;i<array_len;i++,j++)
	{
		result_arry2[j] = result_arry[array_len-i-1];
	}
	printf("step 3 over.\n");
	//in data <-- result_arry[..]
	//4. R、G、B各佔一個位元組,這裡是使其中的R 和 B互換。
	for(i=0,j=0;i<array_len;)
	{
		result_arry[j] = result_arry2[i+2];
		result_arry[j+1] = result_arry2[i+1];
		result_arry[j+2] = result_arry2[i];
		i += 3;
		j += 3;
	}
	printf("step 4 over.\n");
	#endif
	printf("actual data array_len:---> %d \n",array_len);
	
    FILE* fout = fopen("resutfile.h","w+");
    if (fout == NULL) {
        printf("Failed to open\n");
        return;
    }
   for (i=0;i<array_len;)
    {
		fprintf(fout,"0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x, \n",
		result_arry[i],result_arry[i+1],result_arry[i+2],result_arry[i+3],
		               result_arry[i+4],result_arry[i+5],result_arry[i+6],result_arry[i+7],
		              result_arry[i+8],result_arry[i+9],result_arry[i+10],result_arry[i+11]);
		              i +=12;
	}

  if (fclose(fout) != 0) {
        printf("Failed to close \n");
        return;
    }

	
}

上面已經說過處理的步驟,這裡再說一下上面程式碼的用法:


0.在ubuntu下執行命令:

xxd -i logo.bmp logo.h

看main.c中的 TO_DO 段,填寫相應的資訊:
1.在main.c中引用上面生成的標頭檔案
2.把bmp檔案的資訊:寬,高,在main.c填寫好。
3.把這個標頭檔案logo.h中的陣列長度賦給變數file_len.
4.把這個標頭檔案logo.h中的陣列地址賦給(unsigned char *bmp_file_data).
5.定義 REVERT_BMP 的值,如果在這裡要圖片反轉一下,就設1, 如果已經是反轉的圖片,就設0.

6.把logo.h放在main程式所在的路徑
7.編譯:
gcc -o main main.c

8.執行:
./main
就會生成一個resultfile.h,這個檔案的內容就是最後aboot中要用的bmp圖片的資料。