//---------------------------------------------------------------------------


#pragma hdrstop

#include "SvImage.h"

//
#include <Dialogs.hpp>
#include <jpeg.hpp>
#include <math.h>
#include <stdio.h>
//
//---------------------------------------------------------------------------

int  g_iDrawLine ;
int  g_iDrawPx   ;


#pragma package(smart_init)
SImage::SImage()
{
    m_pImage  = NULL ;
    m_pImageR = NULL ;
    m_pImageG = NULL ;
    m_pImageB = NULL ;
    m_pImageC = NULL ;

    m_iWidth  = 0  ;
    m_iHeight = 0  ;
    m_iBitCnt = 8  ;


    //LPBITMAPINFO m_lpBitmapInfo = (BITMAPINFO *)GlobalAlloc(GMEM_FIXED, sizeof(BITMAPINFOHEADER) + 256*sizeof(RGBQUAD));
    //LPBITMAPINFO bmpi=(LPBITMAPINFO)new BYTE[sizeof(BITMAPINFO)+256*sizeof(RGBQUAD)];

    //BITMAPINFO OutInfo;
    //ZeroMemory(&OutInfo, sizeof(BITMAPINFO));

    //  BITMAPINFO BitmapInfo;
    //  ZeroMemory(&BitmapInfo,sizeof(BITMAPINFO));

    //bmpi.bmiHeader.biSizeImage=bmpi.bmiHeader.biWidth*bmpi.bmiHeader.biHeight*bmpi.bmiHeader.biBitCount/8;

    //LPBITMAPINFO bmpi=(LPBITMAPINFO)new byte[sizeof(BITMAPINFO)+256*sizeof(RGBQUAD)];


    int iIfByteCnt    = sizeof(BITMAPINFOHEADER) ;
    int iIfByteCntPal = iIfByteCnt + MAX_PIXEL*sizeof(RGBQUAD) ;

    m_pBitInfo   = (BITMAPINFO *) new BYTE[iIfByteCntPal]; ZeroMemory(m_pBitInfo  ,sizeof(iIfByteCntPal));
    m_pBitInfo8  = (BITMAPINFO *) new BYTE[iIfByteCntPal]; ZeroMemory(m_pBitInfo8 ,sizeof(iIfByteCntPal));
    m_pBitInfo24 = (BITMAPINFO *) new BYTE[iIfByteCnt   ]; ZeroMemory(m_pBitInfo24,sizeof(iIfByteCnt   ));

    m_pBitInfo -> bmiHeader.biSize          = 40 ;
    m_pBitInfo -> bmiHeader.biWidth         = 0  ;
    m_pBitInfo -> bmiHeader.biHeight        = 0  ;
    m_pBitInfo -> bmiHeader.biPlanes        = 1  ;
    m_pBitInfo -> bmiHeader.biBitCount      = 8  ;
    m_pBitInfo -> bmiHeader.biCompression   = BI_RGB;
    m_pBitInfo -> bmiHeader.biSizeImage     = 0  ;
    m_pBitInfo -> bmiHeader.biXPelsPerMeter = 0  ;
    m_pBitInfo -> bmiHeader.biYPelsPerMeter = 0  ;
    m_pBitInfo -> bmiHeader.biClrUsed       = 0  ;
    m_pBitInfo -> bmiHeader.biClrImportant  = 0  ;

    //8Bit color pallette
    for (int i=0; i<MAX_PIXEL; i++) {
        m_pBitInfo -> bmiColors[i].rgbRed      = i ;
        m_pBitInfo -> bmiColors[i].rgbGreen    = i ;
        m_pBitInfo -> bmiColors[i].rgbBlue     = i ;
        m_pBitInfo -> bmiColors[i].rgbReserved = i ;
    }

    memcpy(&m_pBitInfo24 -> bmiHeader , m_pBitInfo , iIfByteCnt);
    m_pBitInfo24 -> bmiHeader.biBitCount = 24 ;

    memcpy(&m_pBitInfo8  -> bmiHeader , m_pBitInfo , iIfByteCntPal);

    SetSize(1600,1200,24);
    //SetSize(640,480,8 );
}

SImage::~SImage()
{
    delete [] (BYTE *)m_pBitInfo8  ;
    delete [] (BYTE *)m_pBitInfo24 ;
    delete [] (BYTE *)m_pBitInfo   ;

    if(m_pImage  ){delete [] m_pImage  ;}
    if(m_pImageR ){delete [] m_pImageR ;}
    if(m_pImageG ){delete [] m_pImageG ;}
    if(m_pImageB ){delete [] m_pImageB ;}
    if(m_pImageC ){delete [] m_pImageC ;}
}

void SImage::SetSize(int _iWidth , int _iHeight , int _iBitCnt)
{
    int iPxByteCnt    = _iBitCnt / BYTE_BIT_COUNT ;

    if(m_iWidth != _iWidth || m_iHeight != _iHeight || m_iBitCnt != _iBitCnt) {
        if(m_pImage  ){delete [] m_pImage  ;}
        if(m_pImageR ){delete [] m_pImageR ;}
        if(m_pImageG ){delete [] m_pImageG ;}
        if(m_pImageB ){delete [] m_pImageB ;}
        if(m_pImageC ){delete [] m_pImageC ;}

        m_pImage  = new unsigned char [_iWidth * _iHeight             ] ;
        m_pImageR = new unsigned char [_iWidth * _iHeight             ] ;
        m_pImageG = new unsigned char [_iWidth * _iHeight             ] ;
        m_pImageB = new unsigned char [_iWidth * _iHeight             ] ;
        m_pImageC = new unsigned char [_iWidth * _iHeight * iPxByteCnt] ;
    }

    m_pBitInfo   -> bmiHeader.biWidth     = _iWidth  ;
    m_pBitInfo   -> bmiHeader.biHeight    = _iHeight ;
    m_pBitInfo   -> bmiHeader.biSizeImage = _iWidth * _iHeight ;

    m_pBitInfo8  -> bmiHeader.biWidth     = _iWidth  ;
    m_pBitInfo8  -> bmiHeader.biHeight    = _iHeight ;
    m_pBitInfo8  -> bmiHeader.biSizeImage = _iWidth * _iHeight ;

    m_pBitInfo24 -> bmiHeader.biWidth     = _iWidth  ;
    m_pBitInfo24 -> bmiHeader.biHeight    = _iHeight ;
    m_pBitInfo24 -> bmiHeader.biSizeImage = _iWidth * _iHeight * iPxByteCnt ;

    m_iWidth  = _iWidth  ;
    m_iHeight = _iHeight ;
    m_iBitCnt = _iBitCnt ;
}

bool SImage::LoadBmp (AnsiString _FilePath)
{
    AnsiString filename ;

    if(_FilePath.Pos(".BMP") ||
       _FilePath.Pos(".BMp") ||
       _FilePath.Pos(".BmP") ||
       _FilePath.Pos(".bMP") ||
       _FilePath.Pos(".bMp") ||
       _FilePath.Pos(".bmp") ) filename = _FilePath ;
    else                       filename = _FilePath+".bmp" ;

    FILE * file = fopen(filename.c_str(),"rb") ;

    if(file == NULL) return false ;

    BITMAPFILEHEADER  bfh ;
    BITMAPINFOHEADER  bih ;

//  원래 32bit CPU  시스템에서 메모리할당을 32bit씩 합니다.
//  위와 같은 구조체를 그냥 sizeof하면 16byte가 나옵니다
//  원래 14바이트 인BITMAPFILEHEADER를 sizeof(BITMAPFILEHEADER) 해 보면 16.

    fread(&bfh.bfType      , 2, 1, file);     // 파일의 종류 아스키 ex) BMP=19778   JPG=55551
    fread(&bfh.bfSize      , 4, 1, file);     // 파일의 전체 크기   ex) 787510     4194358
    fread(&bfh.bfReserved1 , 2, 1, file);     // 사용안함 0으로 세팅
    fread(&bfh.bfReserved2 , 2, 1, file);     // 사용안함 0으로 세팅
    fread(&bfh.bfOffBits   , 4, 1, file);     // 비트맵 파일 해더서 부터 실제 데이터까지의 바이트거리. 팔레트 있으면1078 첪으면 54

    int iTemp  = sizeof(BITMAPFILEHEADER) - 2; //빌더 버그 32비트 cpu라 4바이트씩 할당하여 원래 14바이트 크기인데 16으로 나옴.
                                      
    fread(&bih.biSize          , 4, 1, file); //DWORD  biSize;           40     BITMAPINFOHEADER 구조체의 크기 최소 40이상.
    fread(&bih.biWidth         , 4, 1, file); //LONG   biWidth;          1024   이미지 넓이
    fread(&bih.biHeight        , 4, 1, file); //LONG   biHeight;         768    + 일 경우 아래에서 위로 증가, -일 경우 위에서 아래로 증가.. biWidth는 상관없음 왼쪽에서 오른쪽으로 증가.
    fread(&bih.biPlanes        , 2, 1, file); //WORD   biPlanes;         1      컬러 Plane의 수.(???)
    fread(&bih.biBitCount      , 2, 1, file); //WORD   biBitCount        8      몇비트 이미지인지(1,4,8,16,24).  (단색,16색,256색,24비트)
    fread(&bih.biCompression   , 4, 1, file); //DWORD  biCompression;    0      BI_RGB(압축되지 않은 비트맵), BI_RLE8이면 8비트 압축, BI_RLE4이면 4비트 압축
    fread(&bih.biSizeImage     , 4, 1, file); //DWORD  biSizeImage;      786432 이미지 크기를 바이트 단위로 나타내며, BI_RGB 일 경우 0
    fread(&bih.biXPelsPerMeter , 4, 1, file); //LONG   biXPelsPerMeter;  1      수평해상도.
    fread(&bih.biYPelsPerMeter , 4, 1, file); //LONG   biYPelsPerMeter;  1      수직해상도.
    fread(&bih.biClrUsed       , 4, 1, file); //DWORD  biClrUsed;        0      비트맵에 사용된 색상수를 의미하며, 이값에 따라 RGBQUAD의 배열을 메모리 할당하여서 읽어오시면 됩니다. 이 값이 0이면 모든 색깔을 다 사용한다는 의미입니다.
    fread(&bih.biClrImportant  , 4, 1, file); //DWORD  biClrImportant;   0      비트맵을 출력하는데 필수적인 색상수를 나타내며, 이 값이 0이면 모든 색상을 다 사용한다는 의미입니다.

    int iTemp1 = sizeof(BITMAPINFOHEADER) ;

    if(bih.biBitCount != 8 && bih.biBitCount != 24 ){ShowMessage("8 , 24 비트이미지가 아닙니다."); return false ;} //8비트 혹은 24비트 아니면 로딩 안됨.


    SetSize(bih.biWidth , bih.biHeight , bih.biBitCount); //m_pBitmapInfo이거 건드리기 전에 이미지 버퍼 싸이즈 맞게 바꿈.



    //비트맵 인포 해더 할당.
    if(bih.biBitCount == 8)memcpy(&m_pBitInfo8  ->bmiHeader , &bih , sizeof(BITMAPINFOHEADER)); //m_pBitInfo8     -> bmiHeader = bih ;
    else                   memcpy(&m_pBitInfo24 ->bmiHeader , &bih , sizeof(BITMAPINFOHEADER)); //m_pBitInfo24    -> bmiHeader = bih ;

    const int iMaxPx = pow(2,bih.biBitCount) ;  //한픽셀이 표현 할수 있는 색수 ^


    //팔레트 리딩.
    if(bih.biBitCount == 8) { // 1,4,8에만 팔라트 존재.
        RGBQUAD Temp ;
        for (int i = 0 ; i < iMaxPx ; i++){

            fread(&Temp , 1, 1, file);
            fread(&Temp , 1, 1, file);
            fread(&Temp , 1, 1, file);
            fread(&Temp , 1, 1, file);

            //비트맵파일에 있는 팔레트 안쓰고 무조건 흑백 출력.
            //fread(&m_pBitInfo8 ->bmiColors[i].rgbBlue     , 1, 1, file);
            //fread(&m_pBitInfo8 ->bmiColors[i].rgbGreen    , 1, 1, file);
            //fread(&m_pBitInfo8 ->bmiColors[i].rgbRed      , 1, 1, file);
            //fread(&m_pBitInfo8 ->bmiColors[i].rgbReserved , 1, 1, file);
        }
    }
    //else {
    //    m_pBitInfo8 ->bmiHeader.biBitCount  = 8                          ; //WORD   biBitCount        8
    //    m_pBitInfo8 ->bmiHeader.biSizeImage = bih.biWidth * bih.biHeight ; //DWORD  biSizeImage;      786432      bfSize - bih.biSizeImage = bfh.bfOffBits(1078)
    //}

    int iTemp2   = bih.biBitCount <= 8 ? sizeof(RGBQUAD) * iMaxPx : 0 ;
    int iTemp012 = iTemp + iTemp1 + iTemp2 ;


//이미지 데이터 추출.

    RGBQUAD   rgb4 ;
    RGBTRIPLE rgb3 ;
    int a = sizeof(RGBTRIPLE);
    int iPxByte = bih.biBitCount / BYTE_BIT_COUNT ;

    int r,g,b ;
    int iTemp3 = bih.biWidth * bih.biHeight * bih.biBitCount/8;//* pow(2 , bih.biBitCount) ;
    int iTemp4 = iTemp + iTemp1 + iTemp2 + iTemp3 ;
    int iTemp5 = bfh.bfSize - iTemp4 ;

    if (bih.biHeight < 0) {
        if ( bih.biBitCount == 8 ) {
            for(int y=m_iHeight-1;y>=0;y--) {
                fread(m_pImage + y * m_iWidth , bih.biWidth, 1, file);
            }
        }
        else if(bih.biBitCount == 24) {
            for(int y=m_iHeight-1;y>=0;y--) {
                fread(m_pImageC + y * m_iWidth * iPxByte, bih.biWidth * iPxByte , 1, file);
                for(int x = 0 ; x < m_iWidth; x++ ){
                    memcpy(&rgb3 , m_pImageC + (y * m_iWidth + x) * iPxByte , sizeof(RGBTRIPLE) );
                    int bbb = (rgb3.rgbtRed + rgb3.rgbtGreen + rgb3.rgbtBlue )/3 ;
                    *(m_pImage  + y * m_iWidth + x) = (rgb3.rgbtRed + rgb3.rgbtGreen + rgb3.rgbtBlue )/3 ;  //(rgb.Red * 0.222 + rgb.Green * 0.707 + rgb.Blue * 0.071)/3 ;
                    *(m_pImageR + y * m_iWidth + x) =  rgb3.rgbtRed   ;
                    *(m_pImageG + y * m_iWidth + x) =  rgb3.rgbtGreen ;
                    *(m_pImageB + y * m_iWidth + x) =  rgb3.rgbtBlue  ;
                }
            }
        }
    }
    else {
        if ( bih.biBitCount == 8 ) {
            BYTE * pLine = new BYTE [bih.biWidth] ;
            for(int y=0;y<m_iHeight;y++) {
                fread(pLine , bih.biWidth , 1 , file);
                memcpy(m_pImage + y * m_iWidth , pLine , bih.biWidth);
                //fread(m_pImage + y * m_iWidth , bih.biWidth, 1, file);
            }
            delete [] pLine ;
        }
        else if(bih.biBitCount == 24) {
            for(int y=0;y<m_iHeight;y++) {
                fread(m_pImageC + y * m_iWidth * iPxByte, bih.biWidth * iPxByte , 1, file);
                for(int x = 0 ; x < m_iWidth; x++ ){
                    memcpy(&rgb3 , m_pImageC + (y * m_iWidth + x) * iPxByte , sizeof(RGBTRIPLE) );
                    *(m_pImage  + y * m_iWidth + x) = (rgb3.rgbtRed + rgb3.rgbtGreen + rgb3.rgbtBlue )/3 ;  //(rgb.Red * 0.222 + rgb.Green * 0.707 + rgb.Blue * 0.071)/3 ;
                    *(m_pImageR + y * m_iWidth + x) =  rgb3.rgbtRed   ;
                    *(m_pImageG + y * m_iWidth + x) =  rgb3.rgbtGreen ;
                    *(m_pImageB + y * m_iWidth + x) =  rgb3.rgbtBlue  ;
                }
            }
        }
    }

  	int ret = fclose(file);

    return true ;

}

bool SImage::SaveBmp (AnsiString _FilePath)
{
    AnsiString filename ;

    if(_FilePath.Pos(".BMP") ||
       _FilePath.Pos(".BMp") ||
       _FilePath.Pos(".BmP") ||
       _FilePath.Pos(".bMP") ||
       _FilePath.Pos(".bMp") ||
       _FilePath.Pos(".bmp") ) filename = _FilePath ;
    else                       filename = _FilePath+".bmp" ;

    if(!m_pImage) return false ;

    FILE * file = fopen(filename.c_str(),"wb") ;
    if(file == NULL) {ShowMessage("이미지를 저장 할 수 없습니다."); return false ;}

    BITMAPFILEHEADER  bfh ;
    BITMAPINFOHEADER  bih ;

    if(m_iBitCnt < 24) bih = m_pBitInfo8     -> bmiHeader ;
    else               bih = m_pBitInfo24    -> bmiHeader ;

    bih.biHeight =  m_iHeight ;
    bih.biWidth  =  m_iWidth  ;

    const int iMaxPx = pow(2,bih.biBitCount) ;  //한픽셀이 표현 할수 있는 색수 ^
    const int iSizeOfBfh  = 14 ; //sizeof(BITMAPFILEHEADER) ; 빌더버그 있어서 14인데 16으로 나옴.
    const int iSizeOfPlt  = m_iBitCnt < 24 ? sizeof(RGBQUAD) * iMaxPx : 0 ;
    //biSizeImage = ((((bih.biWidth * m_iBitCnt) + 31) & ~31) >> 3) * bih.biHeight 귀찮아서 4바이트 단위 고려안함.
    const int iSizeOfImg  = bih.biWidth * bih.biHeight * m_iBitCnt ;
    const int iOftToImg   = iSizeOfBfh + sizeof(BITMAPINFOHEADER) + iSizeOfPlt ;
    const int iSizeOfFile = iSizeOfBfh + sizeof(BITMAPINFOHEADER) + iSizeOfImg + iSizeOfPlt ;

    bfh.bfType          = 0x4d42;        // BM => Bitmap file
    bfh.bfSize          = iSizeOfFile ;  // FileSize.
    bfh.bfReserved1     = 0;             // not use
    bfh.bfReserved2     = 0;             // not use
    bfh.bfOffBits       = iOftToImg;     // 비트맵 파일 해더서 부터 실제 데이터까지의 바이트거리. 팔레트 있으면1078 첪으면 54

    bih.biSizeImage     = iSizeOfImg;
    bih.biHeight        = abs(bih.biHeight);

    fwrite(&bfh.bfType      , 2, 1, file);     // 19778
    fwrite(&bfh.bfSize      , 4, 1, file);     // 787510
    fwrite(&bfh.bfReserved1 , 2, 1, file);     // 0
    fwrite(&bfh.bfReserved2 , 2, 1, file);     // 0
    fwrite(&bfh.bfOffBits   , 4, 1, file);     // 1078

    fwrite(&bih.biSize          , 4, 1, file); //DWORD  biSize;           40
    fwrite(&bih.biWidth         , 4, 1, file); //LONG   biWidth;          1024
    fwrite(&bih.biHeight        , 4, 1, file); //LONG   biHeight;         768
    fwrite(&bih.biPlanes        , 2, 1, file); //WORD   biPlanes;         1
    fwrite(&bih.biBitCount      , 2, 1, file); //WORD   biBitCount        8
    fwrite(&bih.biCompression   , 4, 1, file); //DWORD  biCompression;    0
    fwrite(&bih.biSizeImage     , 4, 1, file); //DWORD  biSizeImage;      786432
    fwrite(&bih.biXPelsPerMeter , 4, 1, file); //LONG   biXPelsPerMeter;  1
    fwrite(&bih.biYPelsPerMeter , 4, 1, file); //LONG   biYPelsPerMeter;  1
    fwrite(&bih.biClrUsed       , 4, 1, file); //DWORD  biClrUsed;        0
    fwrite(&bih.biClrImportant  , 4, 1, file); //DWORD  biClrImportant;   0

    if(m_iBitCnt < 24){
        BYTE a ;
        for (int i = 0 ; i < MAX_PIXEL ; i++) {
            a = m_pBitInfo8    ->bmiColors[i].rgbBlue     ;
            a = m_pBitInfo8    ->bmiColors[i].rgbGreen    ;
            a = m_pBitInfo8    ->bmiColors[i].rgbRed      ;
            a = m_pBitInfo8    ->bmiColors[i].rgbReserved ;
            fwrite(&m_pBitInfo8    ->bmiColors[i].rgbBlue     , 1, 1, file);
            fwrite(&m_pBitInfo8    ->bmiColors[i].rgbGreen    , 1, 1, file);
            fwrite(&m_pBitInfo8    ->bmiColors[i].rgbRed      , 1, 1, file);
            fwrite(&m_pBitInfo8    ->bmiColors[i].rgbReserved , 1, 1, file);
        }
        for(int y=0;y<m_iHeight;y++) {
            fwrite(m_pImage + m_iWidth*y*m_iBitCnt/BYTE_BIT_COUNT , m_iWidth*m_iBitCnt/BYTE_BIT_COUNT , 1, file);
        }
    }

    else {
        for(int y=0;y<m_iHeight;y++) {
            fwrite(m_pImageC + m_iWidth*y*m_iBitCnt/BYTE_BIT_COUNT , m_iWidth*m_iBitCnt/BYTE_BIT_COUNT , 1, file);
        }
    }



    fclose(file);

    return true ;

}

int SImage::ShowImg(HDC _hHdc , int _x, int _y, int _iWidth, int _iHeight)
{
    int iRet  ;
    int iRet2 = 0 ;

    if(!m_pImage) {iRet = -1 ; return iRet ; }
    //SetDIBitsToDevice(handle of device context                           ,
    //                  x-coordinate of upper-left corner of dest. rect.   ,
    //                  y-coordinate of upper-left corner of dest. rect.   ,
    //                  source rectangle width                             ,
    //                  source rectangle height                            ,
    //                  x-coordinate of lower-left corner of source rect.  ,
    //                  y-coordinate of lower-left corner of source rect.  ,
    //                  first scan line in array                           ,
    //                  number of scan lines                               ,
    //                  address of array with DIB bits                     ,
    //                  address of structure with bitmap info.             ,
    //                  RGB or palette indices                             );
    BITMAPINFO    * DpInfo ;

    int iIfByteCnt    = sizeof(BITMAPINFOHEADER) ;
    int iIfByteCntPal = iIfByteCnt + MAX_PIXEL*sizeof(RGBQUAD) ;


    SetStretchBltMode(_hHdc, COLORONCOLOR);
    //SetStretchBltMode(_hHdc, HALFTONE);
    //SetStretchBltMode(_hHdc, BLACKONWHITE);
    //SetStretchBltMode(_hHdc, STRETCH_DELETESCANS);

    if(m_iWidth == _iWidth && m_iHeight == _iHeight){
        if(m_iBitCnt == 8)iRet = SetDIBitsToDevice(_hHdc , _x , _y , _iWidth , _iHeight , 0 , 0 , 0        , m_iHeight , m_pImage  , m_pBitInfo8   , DIB_RGB_COLORS) ;
        else              iRet = SetDIBitsToDevice(_hHdc , _x , _y , _iWidth , _iHeight , 0 , 0 , 0        , m_iHeight , m_pImageC , m_pBitInfo24 , DIB_RGB_COLORS) ;
    }

    else{
        if(m_iBitCnt == 8)iRet = StretchDIBits    (_hHdc, _x , _y , _iWidth , _iHeight , 0 , 0 , m_iWidth , m_iHeight , m_pImage  , m_pBitInfo8  , DIB_RGB_COLORS , SRCCOPY) ;
        else              iRet = StretchDIBits    (_hHdc, _x , _y , _iWidth , _iHeight , 0 , 0 , m_iWidth , m_iHeight , m_pImageC , m_pBitInfo24 , DIB_RGB_COLORS , SRCCOPY) ;
    }


    //이미지 버퍼 있나 테스트용.
    for(int y = 0 ; y < m_iHeight ; y++) {
        for(int x = 0 ; x < m_iWidth ; x++) {
            if(m_iBitCnt == 24) {
                for(int z = 0 ; z < m_iBitCnt/BYTE_BIT_COUNT ; z++) {
                    iRet2 +=(int) (*(m_pImageC + y*m_iWidth* m_iBitCnt/BYTE_BIT_COUNT + z)) ;
                }
            }
            else {
                iRet2 += (int)(*(m_pImage + (y*m_iWidth + x))) ;
            }
        }
    }

    g_iDrawPx   = iRet2 ;
    g_iDrawLine = iRet  ;

    return iRet ;
}


