bmp圖片轉換成16進位制資料
最近在開發中要在aboot中顯示一張圖片。但是發現aboot中顯示圖片不是直接拿圖片檔案來顯示的,而是把一個16進位制的資料序列依次往螢幕上搬運,就可以了。
那問題是,怎麼把一張圖片轉換成16進位制的資料序列?
在網上也找了一些資料,也諮詢了一些同事,最後終於搞定,下面把相關的做法寫下來備忘。
我們的aboot中顯示方式只支援24bit bmp圖片,所以要先看一下拿到的圖片是不是這種格式。不是的話,就先用小畫家或更高階的工具轉成這種格式。然後,到ubuntu下執行命令:
這個命令會把圖片轉換成16進位制數字,寫進一個頭檔案logo.h,這個標頭檔案中會有一個數組,和一個表示陣列長度(也就是bmp檔案長度)的變數。xxd -i logo.bmp logo.h
logo.h:
好了,已經拿到16進位制的資料序列了,這就是我們要用的資料序列嗎?不!還有好幾步要做: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;
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圖片的資料。