#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <tchar.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <windows.h>
/// ソースコード中のBMPファイルに関するコメントは削除
/// 32ビット形式では、BITMAPFILEHEADER(14Byte)BITMAPINFOHEADER(40Byte)に続いて一画素あたり4バイト
/// (BGRA:リトルエンディアン)で記録されている。 Y軸方向のライン数(bmpIH.biHeight)が、正の数の場合は、
/// 画像の左下を開始点にに、右上までを連続して記録し、(bmpIH.biHeight)が、負の数の場合は、画像の左上
/// 開始点にに、右下までのデータが連続して記録されている。 画像が24ビット形式の場合は、X軸方向の
/// データは4バイト境界になる様に調整用のダミーデータが入っているので注意する事
/// プログラムは、Y軸のマイナス考慮しない。
BITMAPFILEHEADER    bmpFH  ;
BITMAPINFOHEADER    bmpIH  ;
void                *originalBMP ;
int                 *labelData ;
unsigned char       *imageData ;
////////////////////////////////////////////////////////////////////////////////
int     saveDataAsBMP( char *fname, unsigned char *b, BITMAPFILEHEADER *bmpFH, BITMAPINFOHEADER *bmpIH )
{/** 処理結果を 32ビット形式の BMPファイルとして書き出す。 */
FILE *fp ;

    bmpIH->biBitCount    =  32 ;
    bmpIH->biCompression =  BI_RGB ;
    bmpIH->biSizeImage   =  bmpIH->biWidth * abs(bmpIH->biHeight) * 4 ;
    bmpFH->bfOffBits     =  sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER) ;
    bmpFH->bfSize        =  bmpIH->biSizeImage + bmpFH->bfOffBits ;
    
    if( (fp=fopen(fname, "wb")) == NULL ) {
        fprintf( stderr, "File '%s' cannot create.\n", fname );
        return -1;
    } else if ( fwrite( bmpFH, sizeof(BITMAPFILEHEADER), 1, fp ) != 1 ) {
        fclose(fp) ; return -1;
    } else if ( fwrite( bmpIH, sizeof(BITMAPINFOHEADER), 1, fp ) != 1 ) {
        fclose(fp) ; return -1;
    } else if ( fwrite( b, bmpIH->biSizeImage, 1, fp ) != 1 ) {
        fclose(fp) ; return -1;
    }
    fclose(fp) ;
    return 0;
}


void processing( unsigned char *imageData, int *labelData, int label, int lx , int ly )
{/** 指定されたラベル番号とラベルデータを比較してイメージデータに操作を行う。 */
register unsigned char *d = imageData ;
register int   x, y, v=label, *n = (int *)labelData ;

    for( y=0; y<ly; y++ ) { // イメージデータの位置と、ラベルデータの位置は整合している
        for( x=0; x<lx; x++, d+=4, n++ ) { // IMAGE[Y][X] ≡ LABEL[Y][X]
            if( *n == v ) { // 指定番号と一致したら、R,G,B に 255,255,255 をセットする
                *(d+0) = 0xFF ;  // B
                *(d+1) = 0xFF ;  // G
                *(d+2) = 0xFF ;  // R
                *(d+3) = 0x00 ;  // A
            } else if( *n != 0 ) { // 背景以外なら中間色をセットする
*(d+0) = 0x1F ; // B *(d+0) = *(d+0) >> 1 ; 原画の輝度を1/2にする場合
*(d+1) = 0x1F ; // G *(d+1) = *(d+1) >> 1 ; 原画の輝度を1/2にする
場合
*(d+2) = 0x1F ; // R *(d+2) = *(d+2) >> 1 ; 原画の輝度を1/2にする場合
                *(d+3) = 0x00 ;  // A
            } else {
                *(d+0) = 0x00 ;  // B
                *(d+1) = 0x00 ;  // G
                *(d+2) = 0x00 ;  // R
                *(d+3) = 0x00 ;  // A
            }
        }
    }
}


void img2image(unsigned char *img, unsigned char *image, BITMAPINFOHEADER bmpIH)
{/** BMPファイルのデータを BGRA(32bite) 形式の作業エリアにコピーする */
register int  type=bmpIH.biBitCount, lx=bmpIH.biWidth, ly=abs(bmpIH.biHeight) ;
register int  x, y, bytePerLine=((type==32) ? lx*4 : (lx*3+3)&~0x3) ;
register int *si=(int *)img, *di=(int *)image ;
register unsigned char *s=img, *d=image ;

    if( type == 32 ) {

        for( y=0; y<ly; y++ ) {
            for( x=0; x<lx; x++ ) {

*di++ = *si++ ; // 4バイト(BGRA)の一括コピー
            }
// 32ビット環境(sizeof(int)==4)のみで有効。推奨される方法ではない。
        }
    } else if( type == 24 ) {
        for( y=0; y<ly; y++ ) {
            s = img + y * bytePerLine;      // パディングを考慮したアドレス計算
            for( x=0; x<lx; x++ ) {
                *d++ = *s++ ;
                *d++ = *s++ ;
                *d++ = *s++ ;
                *d++ = 0 ;
            }
        }
    }
}


int main(int argc, char *argv[])
{
FILE          *fp ;
struct stat   fileInfo1, fileInfo2 ;
unsigned char *img ;
char          *oname = "processed.bmp" ;
int           labelNumber ;

    if( argc != 4 ) { // 引数の評価とデータの読み込み
        fprintf( stderr, "Usage: %s bmp_file label_file object_No\n", argv[0] ) ;
        exit(1) ;
    } else labelNumber = atoi(argv[3]) ;     // object_No
    
    if ((fp=fopen(argv[1],"rb")) == NULL ) { // load bmp_file  BMP ファイルの読み込み
        fprintf( stderr, "File '%s' cannot open.\n", argv[1] ); exit(1) ;
    } else if ( fstat(fileno(fp), &fileInfo1) != 0 ) {
        fprintf( stderr, "File '%s' fstat error.\n", argv[1] ); exit(1) ;
    } else if( (originalBMP=(void *)malloc(fileInfo1.st_size) ) == NULL ) {// 動的メモリーの確保
        fprintf( stderr, "Couldn't allocate memory.(%dbyte)\n", (int)fileInfo1.st_size ) ; exit(1) ;
    } else if ( fread(originalBMP, fileInfo1.st_size, 1, fp) != 1 ) {
        fprintf( stderr, "File '%s' read error.\n", argv[1] ); exit(1) ;
    } else fclose(fp) ;

    if ((fp=fopen(argv[2],"rb")) == NULL ) { // load label_file ラベルファイルの読み込み
        fprintf( stderr, "File '%s' cannot open.\n", argv[2] ); exit(1) ;
    } else if ( fstat(fileno(fp), &fileInfo2) != 0 ) {
        fprintf( stderr, "File '%s' fstat error.\n", argv[2] ); exit(1) ;
    } else if( (labelData=(int *)malloc(fileInfo2.st_size) ) == NULL ) {// 動的メモリーの確保
        fprintf( stderr, "Couldn't allocate memory.(%dbyte)\n", (int)fileInfo2.st_size ) ; exit(1) ;
    } else if ( fread(labelData, fileInfo2.st_size, 1, fp) != 1 ) {
        fprintf( stderr, "File '%s' read error.\n", argv[2] ); exit(1) ;
    } else fclose(fp) ;

    bmpFH = *(BITMAPFILEHEADER *)(originalBMP) ;  // BMPファイルの構造を確認
    bmpIH = *(BITMAPINFOHEADER *)((unsigned char *)originalBMP + sizeof(BITMAPFILEHEADER)) ;
    if( bmpFH.bfType != 0x4D42 || bmpFH.bfReserved1 != 0 || bmpFH.bfReserved2 != 0  ) {
        fprintf( stderr, "Bad BMP file format.\n" );
        free(originalBMP); free(labelData); exit(1) ;
// 動的メモリーの開放(free)も exit() に全て任せる方法もある
    } else if( bmpIH.biBitCount != 24 && bmpIH.biBitCount != 32 ) {
        fprintf( stderr, "BITMAPINFOHEADER->biBitCount must be 24|32.\n" ) ;
        free(originalBMP); free(labelData); exit(1) ;
// 動的メモリーの開放(free)も exit() に全て任せる方法もある
    } else img = ((unsigned char *)originalBMP + bmpFH.bfOffBits) ;

    // 32ビット形式の処理用バッファーを確保 (BGRAタイプのデーター形式)
    if( bmpIH.biWidth*abs(bmpIH.biHeight)*4 != fileInfo2.st_size ) {
        fprintf( stderr, "The size of a label data file is different from the BMP file.\n" ) ;
        free(originalBMP); free(labelData); exit(1) ;
    } else if( (imageData=(unsigned char *)malloc(bmpIH.biWidth*abs(bmpIH.biHeight)*4)) == NULL ) {// 動的メモリーの確保
        fprintf( stderr, "Couldn't allocate memory.(%dbyte)\n", (int)(bmpIH.biWidth*abs(bmpIH.biHeight)*4) ) ;
        free(originalBMP); free(labelData); exit(1) ;
    } else img2image(imgimageDatabmpIH) ;
// 32ビット形式の処理用バッファーに画像データをコピー

    // BMPデータと、ラベルデータと、特定の対象物を示すラベル番号 を使った処理の実行
    processing( imageDatalabelDatalabelNumberbmpIH.biWidth , abs(bmpIH.biHeight) ) ;

    // 処理結果をBMPファイルとして書き出す。
    saveDataAsBMP( oname, imageData, &bmpFH, &bmpIH ) ;

    free(imageData) ;   // 動的に確保したメモリーの解放
    free(labelData) ;   // 動的に確保したメモリーの解放
    free(originalBMP) ; // 動的に確保したメモリーの解放
    return 0 ;
}