孫鑫: 第十一講 圖形保存和繪畫

 

1.創建4個菜單,爲其添加消息響應,用成員變量保存繪畫類型。添加LButtonDown和Up消息。
2.當窗口重繪時,如果想再顯示原先畫的數據,則需要保存數據。爲此創建一個新類來記錄繪畫類型和兩個點。
class CGraph 
{
public:
CPoint m_ptOrigin;//起點
CPoint m_ptEnd;//終點
UINT m_nDrawType;//繪畫類型
CGraph();
CGraph(UINT m_nDrawType,CPoint m_ptOrigin,CPoint m_ptEnd);//此爲構造函數。
virtual ~CGraph();

};
   然後在void CGraphicView::OnLButtonUp(UINT nFlags, CPoint point)中加入如下代碼
//CGraph graph(m_nDrawType,m_ptOrigin,point);//不能用局部變量
//m_ptrArray.Add(&graph);//加入這種指針數組中
//加入到指針數組中
在GraphicView.h中有如下代碼
CPtrArray m_ptrArray;
   在OnDraw中重畫時調出數據
for(int i=0;i<m_ptrArray.GetSize();i++)


3.在CView::OnPaint()調用了OnDraw(),但在void CGraphicView::OnPaint()中MFC的Wizard沒有調用OnDraw(),要注意這個區別。如果你此時想調用,必須手動添加代碼。 OnDraw(&dc);


4.讓窗口具有滾動條的功能。
   第1.將CGraphicView的頭文件中的CView全部替換成CSrollView
   第2.添加如下的代碼
void CGraphicView::OnInitialUpdate()
{
CScrollView::OnInitialUpdate();

// TOD Add your specialized code here and/or call the base class
SetScrollSizes(MM_TEXT,CSize(800,600));//設置映射模式,設定窗口大小。OK!
}
5.座標系的轉換,此處不再詳細介紹,需要時請查閱相關資料。
6.解決重繪時線跑到上面的問題。爲什麼會錯位?因爲邏輯座標和設備座標沒有對應起來。
解決方法:
  在OnLButtonDown畫完圖後,保存之前。調用

7.另外兩種方法來保存數據。
  一種是用CMetaFileDC
  另一種是利用兼容DC,重繪時利用 pDC->BitBlt(0,0,rect.Width(),rect.Height(),&m_dcCompatible,0,0,SRCCOPY);
將兼容DC的圖拷貝到屏幕DC上去。
此處不再詳細介紹這兩種方法,因爲介紹多了容易搞暈。呵呵

 

 

如何讓CDC上輸出的文字、圖形具有保持功能,集合類CPtrArray的使用,CPaintDC與CClientDC的區別與應用,OnPaint與OnDraw在CView中的關係及實現內幕,滾動窗口的實現,座標空間,映射方式,設備座標與邏輯座標的轉換。元文件設備描述表的使用,如何利用兼容DC實現圖形的保存和再現。

 

#先實現一下上節課的繪圖功能

#保存所繪製的圖像以及圖像的重繪00:09

3個要素 繪製的類型,起點,終點

保存這3個要素就可以在OnDraw中對其進行重繪

×新建一個類來保存CGraph

class CGraph 

{

public:

    CPoint m_ptOrigin; //起點

    CPoint m_ptEnd; //終點

    UINT m_nDrawType; //類型

    CGraph();

    CGraph(UINT m_nDrawType,CPoint m_ptOrigin,CPoint m_ptEnd);

    virtual ~CGraph();

};

×動態存儲這些對象

用MFC的集合類CPtrArray,支持void指針數組。CPtrArray與CObArray相似

MSDN:

The CPtrArray class supports arrays of void pointers.

The member functions of CPtrArray are similar to the member functions of class CObArray. Because of this similarity, you can use the CObArray reference documentation for member function specifics. Wherever you see a CObject pointer as a function parameter or return value, substitute a pointer to void.

CObject* CObArray::GetAt( int <nIndex> ) const;

for example, translates to

void* CPtrArray::GetAt( int <nIndex> ) const;

 

#增加Add,獲取元素GetAt,獲取元素數目GetSize

#代碼:

//void CGraphicView::OnLButtonUp(UINT nFlags, CPoint point)

//使用new在堆中分配空間,在棧中分配的話CGraph的對象析構以後內存被回收

       CGraph *pGraph=new CGraph(m_nDrawType,m_ptOrigin,point);

       m_ptrArray.Add(pGraph); //CPtrArray m_ptrArray;

 

//void CGraphicView::OnDraw(CDC* pDC)

       CBrush *pBrush=CBrush::FromHandle((HBRUSH)GetStockObject(NULL_BRUSH));

       pDC->SelectObject(pBrush);

 

       for(int i=0;i<m_ptrArray.GetSize();i++)

       {

             //void* to CGraph* 類型轉換

              switch(((CGraph*)m_ptrArray.GetAt(i))->m_nDrawType)

              {

              case 1:

                     pDC->SetPixel(((CGraph*)m_ptrArray.GetAt(i))->m_ptEnd,RGB(0,0,0));

                     break;

              case 2:

                     pDC->MoveTo(((CGraph*)m_ptrArray.GetAt(i))->m_ptOrigin);

                     pDC->LineTo(((CGraph*)m_ptrArray.GetAt(i))->m_ptEnd);

                     break;

              case 3:

                     pDC->Rectangle(CRect(((CGraph*)m_ptrArray.GetAt(i))->m_ptOrigin,

                            ((CGraph*)m_ptrArray.GetAt(i))->m_ptEnd));

                     break;

              case 4:

                     pDC->Ellipse(CRect(((CGraph*)m_ptrArray.GetAt(i))->m_ptOrigin,

                            ((CGraph*)m_ptrArray.GetAt(i))->m_ptEnd));

                     break;

              }

       }

 

#OnDraw函數00:32

OnDraw是個虛函數。

Vc安裝目錄vc98-》MFC-》SRC-》viewcore.cpp

void CView::OnPaint() //可以在此設置斷點看是否能夠進入這裏

{

       // standard paint routine

       CPaintDC dc(this);

       OnPrepareDC(&dc);

在view類中重寫OnPaint消息,則系統會調用自定義的OnPaint函數。

void CGraphicView::OnPaint()

{

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

       // TODO: Add your message handler code here

       OnPrepareDC(&dc);

       OnDraw(&dc);

       // Do not call CScrollView::OnPaint() for painting messages

}

 

 

#讓窗口具有滾動的能力00:44

#在頭文件和源文件中手動把view的基類改爲CScrollView

#對CScrollView的尺寸進行設置

SetScrollSizes

//什麼時候調用呢?窗口創建之後調用

//使用OnInitialUpdate(該函數是當第一個視圖和文檔關聯之後被調用)(虛函數)

//而且它在第一次調用OnDraw的調用之前

void CGraphicView::OnInitialUpdate()

{

       CScrollView::OnInitialUpdate();

       // TODO: Add your specialized code here and/or call the base class

       SetScrollSizes(MM_TEXT,CSize(800,600));

}

 

//?當移動到窗口下端劃線,窗口重繪時所畫的線條位置發生了改變

//座標點並沒有發生改變

//但我們作圖的時候使用的是邏輯座標,windows需要將邏輯座標改變爲設備座標來輸出圖形

//而OnPrepareDC(&dc)用來調整顯示上下文的屬性

//可能就是它改變了上下文屬性

//在MFC中查找OnPrepareDC函數(viewscrl.cpp)(先搜索CSrollView)

其中

       switch (m_nMapMode)

       {

       case MM_SCALETOFIT:

              pDC->SetMapMode(MM_ANISOTROPIC);

              pDC->SetWindowExt(m_totalLog); // window is in logical coordinates

              pDC->SetViewportExt(m_totalDev);

              if (m_totalDev.cx == 0 || m_totalDev.cy == 0)

                     TRACE0("Warning: CScrollView scaled to nothing.\n");

              break;

       default://設置了MM_TEXT就到這裏

              ASSERT(m_nMapMode > 0);

              pDC->SetMapMode(m_nMapMode);

              break;

       }

       CPoint ptVpOrg(0, 0);       // assume no shift for printing

       if (!pDC->IsPrinting())//是否在打印中?不在打印中則執行

       {

              ASSERT(pDC->GetWindowOrg() == CPoint(0,0));

 

              // by default shift viewport origin in negative direction of scroll

              ptVpOrg = -GetDeviceScrollPosition();//返回值有符號

 

              if (m_bCenter)

              {

                     CRect rect;

                     GetClientRect(&rect);

 

                     // if client area is larger than total device size,

                     // override scroll positions to place origin such that

                     // output is centered in the window

                     if (m_totalDev.cx < rect.Width())

                            ptVpOrg.x = (rect.Width() - m_totalDev.cx) / 2;

                     if (m_totalDev.cy < rect.Height())

                            ptVpOrg.y = (rect.Height() - m_totalDev.cy) / 2;

              }

       }

       pDC->SetViewportOrg(ptVpOrg);//設置視口的原點

 

#視口和窗口原點的改變01:25?????????????????????

CDC中提供了兩個成員函數函數SetViewportOrg和SetWindowOrg,用來改變視口和窗口的原點。(獲取則爲Get……) 

1#CMetaFileDC m_dcMetaFile;

2#m_dcMetaFile.Create();//參數爲NULL則一個內存源文件被創建

3#把繪圖時的dc都換成m_dcMetaFile

4#ONDRAW中使用close來返回一個源文件的句柄

 HMETAFILE hmetaFile;

 hmetaFile=m_dcMetaFile.Close();

 pDC->PlayMetaFile(hmetaFile);//播放

//再次創建一個源文件

 m_dcMetaFile.Create();

//在源文件dc中播放先前的源文件,從而保存了上次的操作

 m_dcMetaFile.PlayMetaFile(hmetaFile);

//刪除源文件資源

 DeleteMetaFile(hmetaFile);

 

#把源文件保存到文件中

添加菜單上打開和保存的命令響應

×CopyMetaFile

void CGraphicView::OnFileSave()

{

 // TODO: Add your command handler code here

 HMETAFILE hmetaFile;

 hmetaFile=m_dcMetaFile.Close();

 CopyMetaFile(hmetaFile,"meta.wmf");//擴展名一定的嗎?

 m_dcMetaFile.Create();

 DeleteMetaFile(hmetaFile);

}

#打開文件

GetMetaFile××××××××××

//GetEnhMetaFile

void CGraphicView::OnFileOpen()

{

 // TODO: Add your command handler code here

 HMETAFILE hmetaFile;

 hmetaFile=GetMetaFile("meta.wmf");

 m_dcMetaFile.PlayMetaFile(hmetaFile);

 DeleteMetaFile(hmetaFile);

 Invalidate();

}

 

(2)使用兼容DC

#CDC m_dcCompatible;

#先判斷兼容dc是否已經創建

 if(!m_dcCompatible.m_hDC)

 {

         m_dcCompatible.CreateCompatibleDC(&dc);

         CRect rect;

         GetClientRect(&rect); //客戶區的大小

         CBitmap bitmap;

        //通過指定的寬和高來創建一個兼容位圖

         bitmap.CreateCompatibleBitmap(&dc,rect.Width(),rect.Height());

//如果要在兼容dc上繪圖必須要選入一幅位圖,導入的也行

         m_dcCompatible.SelectObject(&bitmap);

//m_dcCompatible.BitBlt(0,0,rect.Width(),rect.Height(),&dc,0,0,SRCCOPY);CreateCompatibleBitmap返回的位圖對象只包含相應設備描述表中的位圖的位圖信息頭,不包含顏色表和象素數據塊。因此,選入該位圖對象的設備描述表不能像選入普通位圖對象的設備描述表一樣應用,必須在SelectObject函數之後,調用BitBlt將原始設備描述表的顏色表及象素數據塊拷貝到兼容設備描述表。

         m_dcCompatible.SelectObject(pBrush);

 }

同樣把dc換成m_dcCompatible,由於是內存中dc,同樣操作時是看不見的

OnDraw中貼圖:

 CRect rect;

 GetClientRect(&rect);

 pDC->BitBlt(0,0,rect.Width(),rect.Height(),&m_dcCompatible,0,0,SRCCOPY);

 
發佈了27 篇原創文章 · 獲贊 5 · 訪問量 9萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章