天天看點

關于HBITMAP,CBITMAP,BITMAP的轉換以及圖像顯示的一點歸納(2)在VC6中加載圖檔

OK,在上一篇文章中我提到了VC6和VS的差别,在VC6中我們隻能依賴于CBITMAP HBITMAP以及BITMAP之間的轉化關系,而在VS中,我們用CIMAGE類就可以全部搞定,那麼究竟這三個類之間的轉化關系是什麼樣的呢?圖像顯示的時候該怎麼使用呢,别着急,聽我慢慢道來。

一、區分概念:首先:

BITMAP是C++中定義的位圖結構體

HBITMAP是Windows中使用的位圖句柄

CBitmap是MFC封裝的位圖類

HBITMAP是bitmap的指針,

msdn中如是:Handle to a bitmap.typedef HANDLE HBITMAP;

CBitmap是mfc中封裝bitmap的類;

msdn中:Encapsulates(囊括) a Windows graphics device interface (GDI) bitmap and provides member functions to manipulate(操作) the bitmap.

BITMAP是一個結構體,封裝着bitmap的一些資訊。定義了邏輯位圖的高,寬,顔色格式和位值。

MSDN中如是:This structure defines the type, width, height, color format, and bit values of a bitmap.

HBITMAP的全稱是Handle BITMAP,也就是說HBITMAP實際上是一個句柄,如果想要活的位圖的長,寬之類的參量,隻能從BITMAP下手,因為隻有BITMAP中才有這些成員函數。

二:互相轉換(這部分内容是轉載整理的)

1、HBITMAP->CBitmap 

方法一:

HBITMAP hBitmap=(HBITMAP)::LoadImage(NULL, str, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
CBitmap bitmap;
bitmap.Attach(hBitmap);      

方法二:

HBITMAP hBitmap = (HBITMAP)::LoadImage(NULL, str, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);        
CBitmap *bitmap=CBitmap::FromHandle(hBitmap);      

注意:Attach和FromHandle的差別

FromHandle得到的指針是臨時變量,通過Attach連接配接的句柄可以長久保留,但通過FromHandle得到的隻是暫時的,大概隻在一個消息區間内有效,很快便會被删除,是以基本上不能用。我用了FromHandle然後一直出錯!!!

實驗源碼,在(OnPaint函數中添加) 

CString str = _T("E:\\picture\\lena.bmp");
HBITMAP hBitmap=(HBITMAP)::LoadImage(NULL, str, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
CBitmap bitmap;
bitmap.Attach(hBitmap);
CPaintDC dc(this);
CDC MemDC;
MemDC.CreateCompatibleDC(&dc);
MemDC.SelectObject(&bitmap);
CRect rect;
GetClientRect(&rect);
dc.BitBlt(0, 0, rect.Width(), rect.Height(), &MemDC, 0, 0, SRCCOPY);      

注意:CBitmap類中的成員函數:

BOOL LoadBitmap(LPCTSTR lpszRecourceName);   

BOOL LoadBitmap(UINT nIDResource); 

都可以加載位圖,但他們隻能加載工程中的位圖,不能像LoadImage一樣,加載硬碟中的位圖。尤其要注意:

BOOL LoadBitmap(LPCTSTR lpszRecourceName)函數中的lpszRecourceName不能為路徑字元串。它指的是位圖的ID是用字元串表示的。 

比如:我在工程中建立了一個位圖資源IDB_BITMAP1 ,lpszResourceName是指什麼呢,是硬碟上的bitmap1.bmp嗎,如果是,以下代碼為什麼是錯的。

CBitmap   bmp;   
bmp.LoadBitmap("d:\\..\\res\\bitmpa1.bmp");   
CDC   memdc;   
BITMAP   bm;   
bmp.GetBitmap(&bm);   
memdc.CreateCompatibleDC(pDC);   
memdc.SelectObject(&bmp);   
pDC->BitBlt(0,0,bm.bmWidth,bm.bmHeight,&memdc,0,0,SRCCOPY);        

用Notepad打開*.rc檔案,找到類似下面一行:   

IDB_BITMAP   BITMAP   "res\\background.bmp"   

改成:Bitmap1   BITMAP   "res\background.bmp"   

或者,在VC中察看位圖資源的屬性,将其ID欄内改為"Bitmap"(注意,一定要加引号)。

然後調用:bmp.LoadBitmap("Bitmap1"); 保證成功。   

資源可以用一個整數來标示,也可以用一個字元串标示。但無論如何,這些ID都不是指位圖檔案名。 

2、HBITMAP->BITMAP 

CString str = _T("E:\\picture\\lena.bmp");
HBITMAP hBitmap = (HBITMAP)::LoadImage(NULL, str, IMAGE_BITMAP, 0, 0, LR_LOADFROMFILE);
BITMAP bm;
::GetObject(hBitmap, sizeof(bm), &bm);      

3、CBitmap->BITMAP

CBitmap bitmap;
bitmap.LoadBitmapW(IDB_BITMAP1);
BITMAP bm;
bitmap.GetBitmap(&bm);      

4、CBitmap->HBITMAP

方法一:

CBitmap bitmap;        
bitmap.LoadBitmapW(IDB_BITMAP1);
HBITMAP hBitmap = (HBITMAP)bitmap.m_hObject;      

方法二:

CBitmap bitmap;
bitmap.LoadBitmapW(IDB_BITMAP1);
HBITMAP hBitmap = (HBITMAP)bitmap;      

5、BITMAP->HBITMAP

HBITMAP hBitmap;

pbm->GetHBITMAP(NULL, &hBitmap);      

6、BITMAP->CBitmap 

Bitmap* pBitmap = new Bitmap(width,height,PixelFormat24bppRGB);
HBITMAP hBitmap;
BITMAP bm;
pBitmap ->GetHBITMAP(NULL,&hBitmap);
CBitmap* bmp;
bmp.Attach(hBitmap);      

三、CBITMAP能加載的是在資源位圖中的變量,而HBITMAP可以加載路徑中的變量,這是兩者的差別,下面說一下在畫圖的時候的差別:

(1)在VS中加載圖檔用CIMAGE就足夠了,這裡主要實作的功能是将圖檔顯示到picture控件中,如果圖檔小于picture控件,那麼按照原圖顯示,否則的話,等比例縮小(為了保證圖檔不變形)

CString strFilePath;

TCHAR szFilter[]=_T("圖檔(*.bmp)|*.bmp");

CFileDialog fileDlg(TRUE,_T("picture"),NULL,0,szFilter,this);

if(IDOK==fileDlg.DoModal())

{

strFilePath=fileDlg.GetPathName();

strFilePath.Replace(_T("\\"),_T("\\\\"));

    }

int height, width;

    CRect rect;//定義矩形類

    CRect rect1;

    CImage image; //建立圖檔類

//image.Load(imgPath);

//image.Load(_T("D:\\test1.bmp"));  

    image.Load(strFilePath);  

    height = image.GetHeight();

    width = image.GetWidth();

//m_PictureControl.GetClientRect(&rect); //獲得pictrue控件所在的矩形區域

    GetDlgItem(IDC_SHOWPIC_STATIC)->GetClientRect(&rect);  

    CDC *pDc = m_jzmPicture.GetDC();//獲得pictrue控件的Dc

    SetStretchBltMode(pDc->m_hDC,STRETCH_HALFTONE); 

    if(width<=rect.Width() && height<=rect.Width()) //小圖檔,不縮放

    {

     rect1 = CRect(rect.TopLeft(), CSize(width,height));

     image.StretchBlt(pDc->m_hDC,rect1,SRCCOPY); //将圖檔畫到Picture控件表示的矩形區域

    return ;

    }

    else

    {

    float xScale=(float)rect.Width()/(float)width;

    float yScale=(float)rect.Height()/(float)height;

//float ScaleIndex=(xScale>=yScale:xScale,yScale);

   float ScaleIndex=(xScale<=yScale)?xScale:yScale;

    rect1 = CRect(rect.TopLeft(), CSize((int)width*ScaleIndex,(int)height*ScaleIndex));

   image.StretchBlt(pDc->m_hDC,rect1,SRCCOPY); //将圖檔畫到Picture控件表示的矩形區域

    }

   ReleaseDC(pDc);//釋放picture控件的Dc

   return ;

(2)在VC6中加載圖檔

如果整個對話框沒有什麼其他,在onbutton中加入invalidate即可,系統會自動調用onpaint的

那麼,在onpaint中加入如下語句(我的paint_bmp是一個bool變量,是用來判斷是否要在picture控件中畫圖,因為我的對話框中還有别的控件)還要說明的一點,在對話框中加入picture控件時,type不需要改成位圖,直接是邊框就可以啦,這樣你可以限制圖檔的顯示大小,如果是位圖type,是改變不了大小的,是以加載進來的圖檔就那麼大點

CPaintDC dc(this); // device context for painting

if(PAINT_BMP)

{

CPaintDC dc(this);

SendMessage(WM_ICONERASEBKGND,(WPARAM) dc.GetSafeHdc(),0);

int cxIcon=GetSystemMetrics(SM_CXICON);

int cyIcon=GetSystemMetrics(SM_CYICON);

CRect rect;

GetClientRect(&rect);

int x=(rect.Width()-cxIcon+1)/2;

int y=(rect.Height()-cyIcon+1)/2;

dc.DrawIcon(x,y,m_hIcon);

    }

else

{

CDialog::OnPaint();

}

HBITMAP bmpHandle=(HBITMAP)LoadImage(NULL,path_filter,IMAGE_BITMAP,0,0,LR_LOADFROMFILE);

CBitmap bmpPicture;

CDC mdcPicture;

CBitmap *bmpFromHandle=bmpPicture.FromHandle(bmpHandle);

BITMAP bm;

    ::GetObject(bmpHandle,sizeof(bm),&bm);

int height=bm.bmHeight;

int width=bm.bmWidth;

CRect rctPicture,rctPicture1;

m_pic_origin.GetWindowRect(&rctPicture);

    CDC *pDC=m_pic_origin.GetDC();

if(height<=rctPicture.Height()&&width<=rctPicture.Width())

{

mdcPicture.CreateCompatibleDC(&dc);

CBitmap *bmpPrevious=mdcPicture.SelectObject(bmpFromHandle);

ScreenToClient(&rctPicture);

dc.BitBlt(rctPicture.left,rctPicture.top,rctPicture.Width(),rctPicture.Height(),&mdcPicture,0,0,SRCCOPY);

dc.SelectObject(bmpPrevious);

DeleteObject(bmpHandle);

}

else

{

float xscale=(float)rctPicture.Width()/(float)width;

float yscale=(float)rctPicture.Height()/(float)height;

float ScaleIndex=(xscale<=yscale)?xscale:yscale;

mdcPicture.CreateCompatibleDC(&dc);

  CBitmap *bmpPrevious=mdcPicture.SelectObject(bmpFromHandle);

  ScreenToClient(&rctPicture);

  rctPicture1=CRect(rctPicture.TopLeft(),CSize((int)width*ScaleIndex,(int)height*ScaleIndex));

  //dc.BitBlt(rctPicture1.left,rctPicture1.top,rctPicture1.Width(),rctPicture1.Height(),&mdcPicture,0,0,SRCCOPY);

       dc.StretchBlt(rctPicture1.left,rctPicture1.top,rctPicture1.Width(),rctPicture1.Height(),&mdcPicture,0,0,width,height,SRCCOPY);

  dc.SelectObject(bmpPrevious);

  DeleteObject(bmpHandle);

}

另外,還要說明的一點就是BitBlt和StretchBlt,後者是可以調整圖檔大小的随着控件的大小,而前者就是一個點一個點的話,前者隻會将你的圖檔裁剪而不會調整(縮放)