1. 程式人生 > >利用相容DC和相容點陣圖實現圖形重繪

利用相容DC和相容點陣圖實現圖形重繪

我們在開發GDI程式時,會實現簡單的畫圖功能,當我們放大或縮小視窗時,我們所繪製的圖形元素就消失了。因為視窗在變化過程中發生了重繪,導致原先的圖案訊息,這裡將解決這個問題。我們利用相容DC完成圖形重新繪製,從而保持圖形不消失;

這裡將要繪製矩形,橢圓形,直線這三個圖形為例;完成一個圖形繪製需要三個關鍵資訊:起點、終點、繪畫型別,這裡用CGraph這個類進行描述,Graph.h是類宣告資訊,Graph.cpp是實現部分,具體程式碼如下:

//Graph.h繪圖三要素資訊
class CGraph
{
public:
    enum
    {
      EN_RECT = 0,
      EN_ELLIPSE,
      EN_LINE,
    };
    CGraph(CPoint ptBegin, CPoint ptEnd, int
DrawType); CGraph(void); ~CGraph(void); public: void SetDrawType(int nType); void SetBeginPoint(CPoint ptBegin); void SetEndPont(CPoint ptEnd); CPoint GetBeginPoint(); CPoint GetEndPont(); int GetDrawType(); private: CPoint m_ptBegin; CPoint m_ptEnd; int m_DrawType; };
//Graph.cpp
#include "StdAfx.h"
#include "Graph.h"

CGraph::CGraph(void)
{
    m_DrawType = 0;
}
CGraph::CGraph(CPoint ptBegin, CPoint ptEnd, int DrawType)
{
    this->m_ptBegin = ptBegin;
    this->m_ptEnd = ptEnd;
    this->m_DrawType = DrawType;
}

CGraph::~CGraph(void)
{
}

void CGraph::SetDrawType(int
nType) { m_DrawType = nType; } void CGraph::SetBeginPoint(CPoint ptBegin) { m_ptBegin = ptBegin; } void CGraph::SetEndPont(CPoint ptEnd) { m_ptEnd = ptEnd; } CPoint CGraph::GetBeginPoint() { return m_ptBegin; } CPoint CGraph::GetEndPont() { return m_ptEnd; } int CGraph::GetDrawType() { return m_DrawType; }

主要原理:我們建立一個相容DC,並在相容DC上進行畫圖操作,然後在OnDraw函式中將相容DC儲存的圖形複製到目的視窗中。

1) 建立一個Graphic的單文件工程
2)在選單欄中增加“繪圖”子選單,併為其增加三個選單項,其ID分別是IDM_RECT、IDM_ELLIPSE、IDM_LINE;
3)在CGraphicView類中新增這三個選單項增加事件響應函式;
4) 在CGraphicView類中增加LBUTTONDOWN 、LBUTTONUP訊息響應函式;
5)為CGraphicView類新增CGraph類成員函式m_GrahpInfo、CDC成員函式m_dcCompatible;
CGraphicView類中的主要程式碼實現如下:

// CGraphicView 繪製

/****************************************************************
*函式名稱: 
*功    能:圖形相容DC的畫圖顯示到螢幕
*作    者:Jin
*日    期:2017年2月12日
****************************************************************/
void CGraphicView::OnDraw(CDC* pDC)
{
    CGraphicDoc* pDoc = GetDocument();
    ASSERT_VALID(pDoc);
    if (!pDoc)
        return;

    // TODO: 在此處為本機資料新增繪製程式碼
    CRect rect;
    GetClientRect(&rect);
    //將相容DC的顯示錶面拷貝到目的DC,即顯示到螢幕
    pDC->BitBlt(0,0,rect.Width(),rect.Height(),&m_dcCompatible,0,0,SRCCOPY);
}

// CGraphicView 訊息處理程式

/****************************************************************
*函式名稱: 設定畫矩形型別
*功    能:
*作    者:Jin
*日    期:2017年2月12日
****************************************************************/
void CGraphicView::OnRect()
{
    // TODO: 在此新增命令處理程式程式碼
    m_GrahpInfo.SetDrawType(CGraph::EN_RECT);
}

/****************************************************************
*函式名稱:設定橢圓形型別
*功    能:
*作    者:Jin
*日    期:2017年2月12日
****************************************************************/
void CGraphicView::OnEllipse()
{
    // TODO: 在此新增命令處理程式程式碼
    m_GrahpInfo.SetDrawType(CGraph::EN_ELLIPSE);
}

/****************************************************************
*函式名稱:設定畫直線型別
*功    能:
*作    者:Jin
*日    期:2017年2月12日
****************************************************************/
void CGraphicView::OnLine()
{
    // TODO: 在此新增命令處理程式程式碼
    m_GrahpInfo.SetDrawType(CGraph::EN_LINE);
}


/****************************************************************
*函式名稱:設定畫圖起點
*功    能:
*作    者:Jin
*日    期:2017年2月12日
****************************************************************/
void CGraphicView::OnLButtonDown(UINT nFlags, CPoint point)
{
    // TODO: 在此新增訊息處理程式程式碼和/或呼叫預設值
    m_GrahpInfo.SetBeginPoint(point);

    CView::OnLButtonDown(nFlags, point);
}


/****************************************************************
*函式名稱: 主要完成相容DC圖形繪製
*功    能:
*作    者:Jin
*日    期:2017年2月12日
****************************************************************/
void CGraphicView::OnLButtonUp(UINT nFlags, CPoint point)
{
    // TODO: 在此新增訊息處理程式程式碼和/或呼叫預設值
    m_GrahpInfo.SetEndPont(point); 

    //開始繪圖操作流程
    CClientDC dc(this);
    CBrush* pBrush = CBrush::FromHandle((HBRUSH)GetStockObject(NULL_BRUSH));

    if (!m_dcCompatible.m_hDC)
    {
        //1)建立相容DC且僅建立一個
        m_dcCompatible.CreateCompatibleDC(&dc);
        CRect DrawRangle;
        GetClientRect(&DrawRangle);

        //2)建立相容點陣圖
        CBitmap bitmap;
        bitmap.CreateCompatibleBitmap(&dc,DrawRangle.Width(), DrawRangle.Height());

        //3)選人點陣圖,來確定顯示錶面大小,從而可以在相容DC上畫圖
        m_dcCompatible.SelectObject(&bitmap);

        //4)將原始DC中的顏色表和畫素資料塊複製到相容DC;
        m_dcCompatible.BitBlt(0,0,DrawRangle.Width(),DrawRangle.Height(),&dc,0,0,SRCCOPY);

        m_dcCompatible.SelectObject(pBrush);
    }

    //畫圖選擇
    switch (m_GrahpInfo.GetDrawType())
    {
    case CGraph::EN_RECT :
        {
            CRect rect(m_GrahpInfo.GetBeginPoint(), m_GrahpInfo.GetEndPont());
            m_dcCompatible.Rectangle(&rect);
            break;
        }

    case CGraph::EN_LINE :
        {
            m_dcCompatible.MoveTo(m_GrahpInfo.GetBeginPoint());
            m_dcCompatible.LineTo(m_GrahpInfo.GetEndPont());
            break;
        }

    case CGraph::EN_ELLIPSE :
        {
            CRect Ellipse(m_GrahpInfo.GetBeginPoint(), m_GrahpInfo.GetEndPont());
            m_dcCompatible.Ellipse(&Ellipse);
            break;
        }

    default:
        break;
    }
    //視窗無效,引發重繪操作
    Invalidate(TRUE);
    //立即顯示繪畫內容
    UpdateWindow();

    CView::OnLButtonUp(nFlags, point);
}

為了能夠螢幕整個區域都能繪製圖形,我們將應用程式初始啟動時以最大化形式;我們將Graphic.cpp中的ShowWindow引數由SW_SHOW改為SW_MAXIMIZE,如下所示:

// 唯一的一個視窗已初始化,因此顯示它並對其進行更新
m_pMainWnd->ShowWindow(SW_MAXIMIZE/*SW_SHOW*/);

執行效果:
這裡寫圖片描述

我們變化視窗時,先前所繪製的圖形不會訊息;

總結
本文主要利用相容DC和相容點陣圖的方式實現圖形的儲存,當視窗發生重繪時會呼叫OnDraw函式,我們在該函式中將相容DC上的內容複製到螢幕上,從而儲存視窗上的內容不消失;

視窗中顯示BMP圖片這篇博文中也提到了相容DC;所不同的是現在載入的不是真正的點陣圖,而是相容點陣圖,和相容DC是一樣的道理,都是一個記憶體;

需要特別說明的是,CreateCompatibleBitmap函式返回的點陣圖物件只包含相應裝置描述表中的點陣圖資訊頭,不包含該點陣圖的顏色表和畫素塊;因此,我們在呼叫SelectObj操作將相容點陣圖選人到相容DC之後,還需要呼叫BitBlt函式將原始裝置描述表的顏色表和畫素資料塊複製到相容裝置描述表中;