Linuxでビットマップを読み込む際の注意点


Linuxでビットマップを読み取る問題は、Windowsオペレーティングシステムとの違いをよく示しています.ビットマップのフォーマットはオペレーティングシステムに関係なく、読み取りも関係ないはずですが、実際にはビットマップがメモリに読み込まれたときとは異なります.以下では、Linuxでビットマップを操作する際に発生した問題について説明します.
(一)、ビットマップ構造
ビットマップは最初は2つの構造体であり、ビットマップの詳細を含め、後のデータを読み取る鍵である.したがって、ビットマップを読み取るには、まず、BITMAPFILEHEADERとBITMAPINFOHEADERの2つの構造体を正しく読み取る必要があります.具体的には、次のように定義されます.
typedef struct tagBITMAPFILEHEADER
{ // bmfh
    WORD    bfType;
    DWORD   bfSize;
    WORD    bfReserved1;
    WORD    bfReserved2;
    DWORD   bfOffBits;
}__attribute__ ((packed))BITMAPFILEHEADER;
typedef struct tagBITMAPINFOHEADER
{ // bmih
    DWORD  biSize;
    LONG   biWidth;
    LONG   biHeight;
    WORD   biPlanes;
    WORD   biBitCount;
    DWORD  biCompression;
    DWORD  biSizeImage;
    LONG   biXPelsPerMeter;
    LONG   biYPelsPerMeter;
    DWORD  biClrUsed;
    DWORD  biClrImportant;
}__attribute__ ((packed))BITMAPINFOHEADER;

上の2つの構造はWindowsで正常に使用できます.しかし、LinuxにはWORD、DWORDのような変数タイプはありません.そのため、Linuxの一般的な変数タイプにマッピングする必要があります.
typedef unsigned short WORD;
typedef unsigned int DWORD;
typedef int LONG;//use int not long here!!!
typedef unsigned char BYTE;

上記のマッピングでは、各タイプのバイト数に特に注意してください.オペレーティングシステムの変数の長さによって異なり、定義時にまずsizeofで本マシンの変数タイプ長を取得し、ビットマップの各属性長に基づいて適切な変数タイプを選択する必要があります.ここで3番目の変数LONGはwindowsでは4バイトですが、Linuxでは8バイトなので、LONGの代わりにintを使う必要があります.
(二)、位置合わせ
ビットマップ構造の定義では、構造体名の前に文__を追加しました.attribute__ ((packed)).__attribute__ ((packed))の役割は,コンパイラに構造のコンパイル中の最適化された位置合わせをキャンセルし,実際の占有バイト数で位置合わせすることを教えることであり,GCC特有の構文である.Windowsでは読み取り操作は最適化されず、構造体の実際のサイズで読み取りますが、Linuxではアクセス速度を速めるためにアクセスの整列操作が有効になります.このとき、メモリに読み込まれた構造体のサイズが最初の定義よりも大きくなると、ビットマップ属性に前のサイズでアクセスすると、誤った数値が読み出されます.アクセスを容易にするためには、整列最適化を禁止する必要があります.
(三)、ビットマップデータ
24ビットの真彩色ビットマップの場合、ビットマップにはパレットは含まれず、ビットマップデータはRGB色の値である.だから多くの人はデータの大きさが3*height*widthだと思って、データを読み取る時直接この大きさを利用して、しかしこれは間違いです.24ビットの真彩色ビットマップの各行は、データ長が4で除去され、そうでなければ0で4で除去される条件を満たす必要があります.したがって、読み取りのプロセスは1行1行で完了する必要があります.また、各行の末尾では、0の数をスキップする必要があります.この計算式は次のようになります.
                                           skip=(4-(3*width)%4)%4;
C言語では、次のように読み出されます.
    for(int i=0;i<height;i++)
    {
        fread(p,sizeof(unsigned char)*width*3,1,fp);
        p+=sizeof(unsigned char)*width)*3;
        fseek(fp,skip*sizeof(unsigned char),SEEK_CUR);

    }

(四)、RGB順
前述したように、24ビットの真彩色ビットマップはパレットを含まず、ビットマップデータはRGB色の値であり、各色が1バイトを占める.この場合,色の順序はR,G,Bと考える人が多いが,これも誤りであり,実際の順序はB,G,Rであるべきである.この点も特に注意が必要です.