1. 程式人生 > >MFC圖片上繪圖,左鍵繪圖,右鍵擦除

MFC圖片上繪圖,左鍵繪圖,右鍵擦除

效果圖:

需求:視窗上顯示圖片,並可以在圖片上畫線,還可以擦除,擦除不影響圖片顯示,只擦除橡皮擦經過的部分

設計:

1.視窗顯示圖片

2.畫圖

3.顯示繪圖,並且不消失

4.擦除自己作的圖

 

程式碼:

標頭檔案

    //用來顯示圖片
	CImage ppt;

	//原始相容DC,用來擦除時將原圖部分覆蓋到作圖部分
	CDC *pOrignMyDC;

	//顯示DC,使用者繪圖onPaint
	CDC *pShowUpMyDC;

    //兩個bitmap用來設定上面兩個dc的點陣圖
	CBitmap bmp;
	CBitmap bmp2;

1.顯示圖片

//縮放顯示圖片
VOID videoDialog::showPPT()
{
	CWnd *pWnd = CWnd::FromHandle(m_hWnd);

	if (NULL == pWnd)
	{
		return;
	}

	CDC* pDC = pWnd->GetDC();
	HDC hDC = pDC->m_hDC;

	CRect rect_frame;
	CImage image;
	pWnd->GetClientRect(&rect_frame);
	HRESULT ret = image.Load(_T("D:\\222huojian.png"));

	if (0 != ret)
	{
		return;
	}

    //圖片顯示在左下角,可以單獨定義控制元件用來顯示圖片
	rect_frame.left += 200;
	rect_frame.top += 100;

	image.Draw(hDC, rect_frame);
	ReleaseDC(pDC);
}

2.畫圖

LRESULT videoDialog::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)
{
	static POINT pt_Begin = { 0 }, pt_End = { 0 };
	static int mouseState = 0;

	if (message <= WM_USER)
	{

		switch (message)
		{
			case WM_PAINT:
			{
				//如果在視窗上畫圖,直接在onPait中畫圖即可,
				//如果在控制元件上畫圖,onPait中畫圖還不夠
				break;
			}
			case WM_LBUTTONDOWN:
			{
				int xPos = GET_X_LPARAM(lParam);
				int yPos = GET_Y_LPARAM(lParam);

				CRect rect;
				this->GetClientRect(&rect);


				if (xPos > 200 && yPos < 100)
				{
					//沒有標題欄,制定一個區域,用來滑鼠拖動視窗
					PostMessage(WM_NCLBUTTONDOWN, HTCAPTION, lParam);
				}
				else
				{
					mouseState = 1;

					pt_Begin = pt_End = { xPos, yPos };
					CClientDC dc(this);

					if (NULL == pOrignMyDC)
					{
						pOrignMyDC = new CDC();
						pShowUpMyDC = new CDC();

						pOrignMyDC->CreateCompatibleDC(&dc);
						pShowUpMyDC->CreateCompatibleDC(&dc);

						bmp.CreateCompatibleBitmap(&dc, rect.right, rect.bottom);
						bmp2.CreateCompatibleBitmap(&dc, rect.right, rect.bottom);


						//CreateCompatibleDC之後是1*1畫素的區域,重新選擇一個位圖設定自己的區域,
						//CreateCompatibleBitmap可以建立一個跟dc相容的點陣圖,,用來是新舊dc大小匹配
						pOrignMyDC->SelectObject(&bmp);
						pShowUpMyDC->SelectObject(&bmp2);

						pOrignMyDC->BitBlt(rect.left, rect.top, rect.right, rect.bottom,
							&dc, 0, 0, SRCCOPY);
					}

				}

				break;
			}
			case WM_RBUTTONDOWN:
			{
				int xPos = GET_X_LPARAM(lParam);
				int yPos = GET_Y_LPARAM(lParam);
				mouseState = 2;

				CRect rect;
				this->GetClientRect(&rect);
				CClientDC dc(this);

				pt_Begin = pt_End = { xPos, yPos };

				if (NULL == pOrignMyDC)
				{
					pOrignMyDC = new CDC();
					pShowUpMyDC = new CDC();

					pOrignMyDC->CreateCompatibleDC(&dc);
					pShowUpMyDC->CreateCompatibleDC(&dc);

					bmp.CreateCompatibleBitmap(&dc, rect.right, rect.bottom);
					bmp2.CreateCompatibleBitmap(&dc, rect.right, rect.bottom);


					//CreateCompatibleDC之後是1*1畫素的區域,重新選擇一個位圖設定自己的區域,
					//CreateCompatibleBitmap可以建立一個跟dc相容的點陣圖,,用來是新舊dc大小匹配
					pOrignMyDC->SelectObject(&bmp);
					pShowUpMyDC->SelectObject(&bmp2);

					pOrignMyDC->BitBlt(rect.left, rect.top, rect.right, rect.bottom,
						&dc, 0, 0, SRCCOPY);
					
				}

				break;
			}
			case WM_RBUTTONUP:
			case WM_LBUTTONUP:
			{
				mouseState = 0;

				CRect rect;
				this->GetClientRect(&rect);
				CClientDC dc(this);

				pShowUpMyDC->BitBlt(rect.left, rect.top, rect.right, rect.bottom, &dc, 0, 0, SRCCOPY);

				break;
			}
			case WM_MOUSEMOVE:
			{
				int xPos = GET_X_LPARAM(lParam);
				int yPos = GET_Y_LPARAM(lParam);


				CRect rect;//, rect2;

				//GetClientRect left和top永遠是0
				this->GetClientRect(&rect);

				////GetWindowRect 相對螢幕座標
				//this->GetWindowRect(&rect2);
				////ScreenToClient 轉為視窗客戶區座標
				//ScreenToClient(&rect2);

				pt_End = { xPos, yPos };

				CClientDC dc(this);

				if (0 == mouseState)
				{
					break;
				}
				else if(1 == mouseState)
				{
					CPen pen(PS_SOLID, 5, RGB(255, 255, 0));//建立畫筆物件
					CPen* pOldPen = dc.SelectObject(&pen);
				
					dc.MoveTo(pt_Begin);
					dc.LineTo(pt_End);

					dc.SelectObject(pOldPen);

				}
				else if (2 == mouseState)
				{
					CRect rt;

					rt.left = pt_Begin.x > 10 ? (pt_Begin.x - 10) : 0;
					rt.top = pt_Begin.y > 10 ? (pt_Begin.y - 10) : 0;
					rt.right = pt_End.x + 10;
					rt.bottom = pt_End.y + 10;

					this->GetDC()->BitBlt(rt.left, rt.top, rt.right - rt.left, rt.bottom - rt.top,
						pOrignMyDC, rt.left, rt.top, SRCCOPY);
					
				}

				pt_Begin = pt_End;

				break;
			}
		}
		return CDialogEx::WindowProc(message, wParam, lParam);
	}
}

畫圖原理:

1.滑鼠左鍵單擊時,記下座標,起始點和終止點都初始化為此點,設定左鍵按下狀態(mouseState = 1)

2.滑鼠移動時,如果有左鍵按下的狀態(1 == mouseState),就執行畫線,moveto lineto

3.滑鼠左鍵鬆開時,結束左鍵按下狀態(mouseState = 0),並把當前視窗的繪圖資訊記錄到pShowUpMyDC中

長期顯示,不因onPaint而消失

1.初始化兩個DC,pOrignMyDC,pShowUpMyDC。(程式碼中初始化了兩次,其實沒必要,也懶得封裝),其中pOrignMyDC設定為視窗初始狀態,(根據視窗dc設定)。pShowUpDC設定為視窗DC畫完之後存放的dc,因為onpait之後視窗dc中的繪圖全部消失,只有記憶體DC中的繪圖依然保留,所以要在onPait中重新繪製DC

void videoDialog::OnPaint()
{
	showPPT();

	if (NULL != pShowUpMyDC)
	{
		CRect rect;
		this->GetClientRect(&rect);
		CClientDC dc(this);
		dc.BitBlt(0, 0, rect.right, rect.bottom, pShowUpMyDC, 0, 0, SRCCOPY);
	}

	CDialog::OnPaint();
}

擦除原理

1.滑鼠右鍵按下時,記錄右鍵按下狀態(mouseState = 2)

2.滑鼠移動時,如果有右鍵按下的狀態(2 == mouseState),就執行bitblt,將pOrignMyDC中的點陣圖資訊的指定區域,移動到當前視窗的繪圖資訊中,即用最開始的影象資訊,覆蓋繪圖之後的影象資訊,達到擦除的效果

3.滑鼠右鍵鬆開時,結束滑鼠右鍵按下狀態(mouseState = 0),並把當前視窗的繪圖資訊記錄到pShowUpMyDC中

 

//記錄//

//onPaint時視窗的繪圖訊息,不是控制元件的,

//控制元件獲取自己在視窗中的座標,還要轉換,麻煩,

//new CDC()初始化只有一個畫素,所以需要設定bitmap,以具備畫圖的空間

//cpen就是畫筆,用來畫線

//cbrush就是畫刷,用來畫區域