//--------------------------------------------------------------------------------------
// ■ビットマップI/Oクラス ToDIB(xDib.h) 
//
//                                          「Programming Library」
//                                           http://homepage1.nifty.com/MADIA/ 
//                                           papy@hamal.freemail.ne.jp
//                                           Copyright(C)2001/12/8 Takeshi Okamoto(PAPY)
//--------------------------------------------------------------------------------------

#include <Windows.h>

//
// 主な機能
//   ・ビットマップをロード/セーブ (RLE圧縮されたビットマップも読込み可能)
//   ・ビットマップを1/4/8/16/24/32bitに減色・増色
//   ・クリップボードへ転送/取りこみ
//  

/*-----サンプル-----*/

/*
 //ビットマップを読込み24bitに変換して保存するサンプル

	#include <Windows.h>
	#include "Dib.h"

	void main(void)
	{
	 ToDIB hoge;

	 //ビットマップの読込み
	 hoge.LoadFromFile("c:\\雲.bmp");

	 //ビットマップを24bitに変換
	 hoge.SetBitsPixel(24);

	 //ビットマップの保存
	 hoge.SaveToFile("c:\\花ちゃん.bmp");
	}
*/


/*-----ビットマップの読込み-----*/

//
// BOOL LoadFromFile(char *FileName)
//
// 概要    ディスク上のファイルからビットマップを読込む
// 引数    FileName (開くファイル名) 
// 戻り値  成功 TRUE  失敗 FALSE
//

//
// BOOL LoadFromClipboard()
//
// 概要    クリップボードからビットマップを読み込む
// 戻り値  成功 TRUE  失敗 FALSE
//

//
// BOOL LoadFromResourceName(HINSTANCE hinst,char *ResName)
//
// 概要    リソースからリソース文字列を使用してビットマップを読み込む
// 引数    hinst   (リソースが存在するインスタンスハンドル)
//         ResName (リソース文字列)
// 戻り値  成功 TRUE  失敗 FALSE
//

//
// BOOL LoadFromResourceID(HINSTANCE hinst,int ResID)
//
// 概要    リソースからリソースIDを使用してビットマップを読み込む
// 引数    hinst (リソースが存在するインスタンスハンドル)
//         ResID (リソースID)
// 戻り値  成功 TRUE  失敗 FALSE
// 

/*-----ビットマップの書込み-----*/

//
// BOOL SaveToFile(const char *FileName)
//
// 概要    ビットマップをディスク上に保存する
// 引数    FileName(保存先ファイル名)
// 戻り値  成功 TRUE  失敗 FALSE
//

//
// void SaveToClipboard()
//
// 概要    ビットマップをクリップボードに転送する
// 戻り値  成功 TRUE  失敗 FALSE
//


/*-----ビットマップの属性-----*/


//
// int GetWidth()
//
// 概要    ビットマップの横幅を取得する
// 戻り値  成功 ビットマップの横幅  失敗 0
//

//
// int GetHeight()
//
// 概要    ビットマップの縦幅を取得する
// 戻り値  成功 ビットマップの縦幅  失敗 0
//

//
// BOOL SetSize(int Width=0,int Height=0)
//
// 概要    ビットマップのサイズを変更する
// 引数    Width  横幅
//         Height 縦幅
// 戻り値  成功 TRUE  失敗 FALSE
//

//
// int GetBitsPixel()
//
// 概要    ビットマップの色数を取得する
// 戻り値  成功 (1bpp=1,4bpp=4,8bpp=8,16bpp=16,24bpp=24,32bpp=32)  失敗 0
//

//
// BOOL SetBitsPixel(int BPP)
//
// 概要    ビットマップの色数を変更する
// 引数    BPP  色数を指定する(1=1bpp,4=4bpp,8=8bpp,16=16bpp,24=24bpp,32=32bpp)
// 戻り値  成功 TRUE  失敗 FALSE
//　

//
// int GetBitsDataSize() 
//
// 概要    ビットマップのビットデータサイズを取得する
// 戻り値  成功 ビットデータサイズ 失敗 0
//
// 


/*-----ビットマップハンドルの操作-----*/


//
// void FreeImage()
//
// 概要    ToDIBオブジェクトで保持しているビットマップを破棄する(このメソッドを使用しなくても消滅子で自動破棄します)
// 

//
// HBITMAP ReleaseHandle()
//
// 概要    ビットマップのハンドルを返しToDIBオブジェクトはそのハンドルを認識しなくなります。
// 戻り値  ビットマップのハンドル
//

//
// HBITMAP CopyBitmap()
//
// 概要    新しいビットマップを作成して作成したビットマップに現在保持しているビットマップの属性をコピーします。
// 戻り値  ビットマップのハンドル
//

//
// BOOL Empty()
//
// 概要    ToDIBがビットマップを保持しているか判別します。
// 戻り値  NULLの場合 TRUE NULLでない場合 FALSE
//
 

/*-----オペレーター-----*/

//
// operator HBITMAP()
//  ToDIBをHBITMAPに自動変換
//

//
// ToDIB& operator=(ToDIB& Value )	
//  ToDIBにToDIBを代入
//
//    ※既にハンドルを保持してる場合はハンドルを破棄してから代入する。
//    　代入元のハンドルは破棄しないので注意が必要。
//

//
// ToDIB& operator=(HBITMAP Value )
//  ToDIBにHBITMAPを代入
//
//    ※既にハンドルを保持してる場合はハンドルを破棄してから代入する。
//    　代入元のハンドルは破棄しないので注意が必要。
//



//BITMAPINFOHEADER改
typedef struct  
{
 BITMAPINFOHEADER bmiHeader;      //通常のBITMAPINFOHEADER
 RGBQUAD          bmiColors[256]; //カラーパレット領域を確保
}BITMAPINFO_8BIT;
 
/*
 ToDIB ビットマップI/Oクラス
*/
class ToDIB
{
 private:

   HBITMAP hBMP; 

 protected:
	    
   //ハンドル削除
   void DeleteHandle()  
   { 
	   if (! Empty()) 
	   {
		   DeleteObject(hBMP); 
		   hBMP=NULL;
	   }
   }

   //ビットマップハンドルをコピー
   // 既にハンドルを保持してる場合は破棄してからコピーする。
   // ※コピー元のハンドルは破棄しないので注意
   void HandleCopy(HBITMAP Src) 
   {
	  if (hBMP!=Src)
	  {
	     DeleteHandle();
	     hBMP=Src;
	  }
   }

   //ビットマップ情報を返す 
   int GetBitmapInfo(BYTE Value)
   {
	  BITMAP   BMPINFO; 
		  
	  if (Empty())
	     return(0);
      else
	  {
		GetObject(hBMP, sizeof(BITMAP), &BMPINFO);
		//Width
	    if (Value==1)
		  return(BMPINFO.bmWidth);
        //Height
		else if (Value==2)
		  return(BMPINFO.bmHeight);
        //色数
		else if (Value==3)
		  return(BMPINFO.bmBitsPixel);
		else
          return(0); 
	  }
   }

   //ビットマップの色数を変更
   // 再読込みしているので若干遅いです(^^;
   BOOL ChangePixel(int BPP)
   {	  
	  int        Dummy;
      HDC        hMem,hDC;
      BYTE       *buffer=NULL;
	  char       *TempName;
	  HANDLE     hFile=NULL;
      DWORD      _Size,work;
      BITMAP     BMP;
      BITMAPINFO_8BIT   BI;
      BITMAPFILEHEADER  BF;
	  HBITMAP    hTembmp ;

	  //テンポラリ用ビットマップの作成
	  hTembmp=CopyBitmap();
	  if (hTembmp==NULL) return(FALSE);

      ZeroMemory(&BI,sizeof(BITMAPINFO));
      ZeroMemory(&BF,sizeof(BITMAPFILEHEADER));
      GetObject(hTembmp, sizeof(BITMAP), &BMP);

      //BITMAPINFOの準備
      BI.bmiHeader.biSize       = 40;
      BI.bmiHeader.biWidth      = BMP.bmWidth;
      BI.bmiHeader.biHeight     = BMP.bmHeight;
      BI.bmiHeader.biPlanes     = 1;
      BI.bmiHeader.biBitCount   = BPP;
      BI.bmiHeader.biCompression= BI_RGB;

      //ビットマップデータサイズを算出
      _Size=((BPP * BI.bmiHeader.biWidth + 31) / 32) * 4 * abs(BI.bmiHeader.biHeight);
      
      //BITMAPFILEHEADERの準備
      BF.bfType     =0x4D42;

	  switch (BPP)
	  {
	    case 1:  BF.bfOffBits  =40+14+(4*2);  break;
	    case 4:  BF.bfOffBits  =40+14+(4*16); break;
		case 8:  BF.bfOffBits  =40+14+(4*256);break;
		default: BF.bfOffBits  =40+14;
	  }

      BF.bfSize     =_Size+BF.bfOffBits;

      buffer=(BYTE *)malloc(_Size);

	  //メモリ不足
	  if (buffer==NULL)
	  {
    	 DeleteObject(hTembmp);
	     return(FALSE);
	  }

      //ここで色数を変更
      hDC=GetDC(0);
      hMem = CreateCompatibleDC(hDC);
      SelectObject(hMem,hTembmp);
      Dummy=GetDIBits(hMem, hTembmp, 0,BMP.bmHeight,buffer,(LPBITMAPINFO)&BI, DIB_RGB_COLORS);
      DeleteDC(hMem) ; 
	  ReleaseDC(0,hDC);    
	  
	  if (Dummy==0)
	  {
		 //色数変更失敗
    	 DeleteObject(hTembmp);
     	 free(buffer);
	     return(FALSE);
	  }
	  else
	  {
          //テンポラリファイル名の取得
		  TempName=(char * )malloc(MAX_PATH);
		  GetTempPath(MAX_PATH,TempName);
		  wsprintf(TempName,"%s~ToDIB.tmp",TempName);

          //ファイル作成
          hFile = CreateFile(TempName, GENERIC_WRITE, 0,NULL, CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL | FILE_FLAG_WRITE_THROUGH, NULL);
          if (hFile>0)
		  {
		        //各ヘッダーの書きこみ
                WriteFile(hFile,&BF,14,&work,NULL);
                
			    switch (BPP)
				{
	              case 1:  WriteFile(hFile,&BI,40+(4*2),&work,NULL);  break;
	              case 4:  WriteFile(hFile,&BI,40+(4*16),&work,NULL); break;
		          case 8:  WriteFile(hFile,&BI,40+(4*256),&work,NULL);break;
	              default: WriteFile(hFile,&BI,40,&work,NULL);
				} 
                WriteFile(hFile,buffer,_Size,&work,NULL);
                CloseHandle(hFile);
	       }
     	        free(buffer);
      
				//リロード
            	DeleteObject(hTembmp);
				hTembmp=(HBITMAP)LoadImage(0,TempName,IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE | LR_CREATEDIBSECTION); 
                DeleteFile(TempName); free(TempName);

		        if (hTembmp==NULL)
			      return(FALSE);
		        else
				{
	              DeleteHandle();
	       	      hBMP=hTembmp;
                  return(TRUE);
				}
	  }
   }

 public:

    //構築子/消滅子

	    ToDIB(){hBMP=NULL;}
	   ~ToDIB(){DeleteHandle();}

    //オペレーター

		//　自動変換
	    operator HBITMAP() {return hBMP; }
	    //　代入
	    ToDIB& operator=(ToDIB& Value )	    { HandleCopy(Value.hBMP);return (*this); }
        ToDIB& operator=(HBITMAP Value )	{ HandleCopy(Value);     return (*this); }

	//プロパティ/メソッド

		//ディスク上のファイルから読込み
        BOOL LoadFromFile(const char *FileName)
		{
          HBITMAP hoge;

		  hoge =(HBITMAP)LoadImage(0,FileName,IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE | LR_CREATEDIBSECTION); 
          if (hoge==NULL)
			 return(FALSE);
		  else
		  {            
			 DeleteHandle();
			 hBMP=hoge;
		     return(TRUE);
		  }
		}

		//ビットマップリソースを読み込む(文字列)
		BOOL LoadFromResourceName(HINSTANCE hinst,const char *ResName)
		{
		  HBITMAP hoge;

		  hoge =(HBITMAP)LoadImage(hinst,ResName,IMAGE_BITMAP, 0, 0, LR_CREATEDIBSECTION); 
          if (hoge==NULL)
			 return(FALSE);
		  else
		  {            
			 DeleteHandle();
			 hBMP=hoge;
		     return(TRUE);
		  }		
		}

		//ビットマップリソースを読み込む(ID)
		BOOL LoadFromResourceID(HINSTANCE hinst,int ResID)
		{
		  HBITMAP hoge;

		  hoge =(HBITMAP)LoadImage(hinst,MAKEINTRESOURCE(ResID),IMAGE_BITMAP, 0, 0, LR_CREATEDIBSECTION); 
          if (hoge==NULL)
			 return(FALSE);
		  else
		  {            
			 DeleteHandle();
			 hBMP=hoge;
		     return(TRUE);
		  }		
		}

		//ビットマップハンドルを保持しているか判別
		BOOL Empty()
		{
		 if (hBMP==NULL)  return(TRUE);
         else    		  return(FALSE);	  		
		}

		//ビットマップのハンドルを返しToDIBオブジェクトはそのハンドルを認識しなくなります。
		HBITMAP ReleaseHandle()
		{
         HBITMAP hoge;
         
		 hoge=hBMP;
		 hBMP=NULL;
		 return(hoge);		
		}

		//ビットマップハンドルを破棄する
		void FreeImage(){DeleteHandle(); }

		//新しいビットマップを作成し、
		//作成したビットマップに既存のビットマップの属性をコピーします。
		HBITMAP CopyBitmap(){return ((HBITMAP)CopyImage(hBMP,IMAGE_BITMAP,0,0,LR_CREATEDIBSECTION));}

		//クリップボードからビットマップハンドルを取得
		BOOL LoadFromClipboard()
		{
          HBITMAP hoge=NULL;

          OpenClipboard(NULL);
 	      hoge=(HBITMAP)GetClipboardData(CF_BITMAP);
		  CloseClipboard();	
		  if (hoge!=NULL) 
		  {
		    //DIBSECTIONを作成してハンドルを渡す
		    HandleCopy((HBITMAP)CopyImage(hoge,IMAGE_BITMAP,0,0,LR_CREATEDIBSECTION|LR_COPYDELETEORG));
		    return(TRUE);
          }
		  else 
			return(FALSE);   
		}

		//保持しているビットマップをクリップボードにコピー
		void SaveToClipboard()
		{
	      OpenClipboard(NULL);
		  HANDLE handle = SetClipboardData(CF_BITMAP,(HBITMAP)CopyImage(hBMP,IMAGE_BITMAP,0,0,LR_CREATEDIBSECTION));
			if ( handle == NULL )
			{
				LPVOID lpMsgBuf;

				FormatMessage( 
					FORMAT_MESSAGE_ALLOCATE_BUFFER | 
					FORMAT_MESSAGE_FROM_SYSTEM | 
					FORMAT_MESSAGE_IGNORE_INSERTS,
					NULL,
					GetLastError(),
					MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // デフォルト言語
					(LPTSTR) &lpMsgBuf,
					0,
					NULL 
				);

				::MessageBox( NULL, (char*)lpMsgBuf, "Error", MB_OK | MB_ICONINFORMATION );
				LocalFree( lpMsgBuf );
			}
		  CloseClipboard();	
		}

		//ビットマップの横幅を取得
		int GetWidth()	    { return (GetBitmapInfo(1));}

		//ビットマップの縦幅を取得
		int GetHeight()    { return (GetBitmapInfo(2));}

		//ビットマップの色数を取得
		int GetBitsPixel() { return (GetBitmapInfo(3));}
        
		//ビットマップのビットデータサイズを取得
		int GetBitsDataSize() 
		{
            return ((GetBitsPixel() * GetWidth()+ 31) / 32) * 4 * abs(GetHeight());
		}

		//ビットマップのサイズを変更する
		BOOL SetSize(int Width=0,int Height=0)
		{
		  if (hBMP==NULL) return(FALSE);
		  HBITMAP   hBitmap=(HBITMAP)CopyImage(hBMP,IMAGE_BITMAP,Width,Height,LR_CREATEDIBSECTION);
          
		  if (hBitmap!=NULL)
		  {
		     DeleteHandle();
			 hBMP=hBitmap;
			 return (TRUE);
		  }
		  else
		     return (FALSE);
		}


		//ビットマップをディスク上に保存する
		BOOL SaveToFile(const char *FileName)
		{
		  BITMAPFILEHEADER BF;
          DIBSECTION       DIB;
          HANDLE           hFile=NULL;
          DWORD            Work;
          int              Colors=0; 
          RGBQUAD          *RGBQuad;
          HDC              hMem,hDC;
		  HBITMAP          hBitmap=NULL;

		  if (hBMP==NULL) return(FALSE);
         
		  //DIBSECTIONを作成する(DDBの可能性があるので必ずDIBに変換)
		  hBitmap=(HBITMAP)CopyImage(hBMP,IMAGE_BITMAP,0,0,LR_CREATEDIBSECTION);
          
		  if (hBitmap==NULL) return(FALSE);
          
		  //ファイル作成
          hFile = CreateFile(FileName, GENERIC_WRITE, 0,NULL, CREATE_ALWAYS,FILE_ATTRIBUTE_NORMAL | FILE_FLAG_WRITE_THROUGH, NULL);
          if (hFile!=NULL)
		  {
             ZeroMemory(&BF,sizeof(BITMAPFILEHEADER));
             GetObject(hBitmap, sizeof(DIBSECTION), &DIB);
    
             BF.bfType=0x4D42;
             
			 //カラーパレットのサイズを計算
             if (DIB.dsBmih.biClrUsed==0)
			 {
                  switch (DIB.dsBmih.biBitCount)
				  {
                     case 1:  Colors=2;   break;
                     case 4:  Colors=16;  break;
                     case 8:  Colors=256; break;
				  }
			 }
             else
			 {
                  switch (DIB.dsBmih.biBitCount)
				  {
                     case 1:  Colors=DIB.dsBmih.biClrUsed;  break;
                     case 4:  Colors=DIB.dsBmih.biClrUsed;  break;
                     case 8:  Colors=DIB.dsBmih.biClrUsed;  break;
				  }         
			 }
  
			 //サイズ計算
		     BF.bfOffBits=sizeof(BITMAPFILEHEADER)+sizeof(BITMAPINFOHEADER)+Colors*sizeof(RGBQUAD);
             // BF.bfSize=BF.bfOffBits+DIB.dsBmih.biSizeImage;
             BF.bfSize=BF.bfOffBits+((GetBitsPixel() * GetWidth() + 31) / 32) * 4 * abs(GetHeight());
             
			 //ファイルに書き込み

			    //ヘッダの書きこみ
                WriteFile(hFile,&BF,sizeof(BITMAPFILEHEADER),&Work,NULL);
                WriteFile(hFile,&(DIB.dsBmih),sizeof(BITMAPINFOHEADER),&Work,NULL);
              
			    //カラーパレットの書き込み
                if (Colors!=0)
				{
                  hDC=GetDC(0);
                  hMem = CreateCompatibleDC(hDC);
                  SelectObject(hMem, hBitmap);
                  RGBQuad=(RGBQUAD *)malloc(Colors*sizeof(RGBQUAD));
                  GetDIBColorTable(hMem,0,Colors,RGBQuad);
                  WriteFile(hFile,RGBQuad,Colors*sizeof(RGBQUAD),&Work,NULL);
                  free(RGBQuad);
                  DeleteDC(hMem);    
                  ReleaseDC(0,hDC);    
				}
   
			    //イメージデータの書き込み
                WriteFile(hFile,DIB.dsBm.bmBits,DIB.dsBmih.biSizeImage,&Work,NULL);

			    DeleteObject(hBitmap);
                CloseHandle(hFile);
			    return(TRUE);
		  }
		  else
		  {
			  DeleteObject(hBitmap);
			  return(FALSE);
		  }
		}

		//ビットマップの色数を変更(1=1bpp,4=4bpp,8=8bpp,16=16bpp,24=24bpp,=32=32bpp)
		BOOL SetBitsPixel(int BPP)
		{ 
          if (Empty()) return(FALSE);
          if (GetBitsPixel()==BPP) return(FALSE);
          if  (!(GetBitsPixel()==1  || GetBitsPixel()==4  || GetBitsPixel()==8 ||
			    GetBitsPixel()==16  || GetBitsPixel()==24 || GetBitsPixel()==32   )) return(FALSE);
          //色数を変更
		  return (ChangePixel(BPP));	
		}
      
};
