๋ฐ˜์‘ํ˜•

์ด์ œ ๊ทธ๋ฆผํŒ ํ”„๋กœ์ ํŠธ์˜ ๋งˆ์ง€๋ง‰ ํฌ์ŠคํŒ…์ž…๋‹ˆ๋‹ค. ์ด๋ฒˆ ํฌ์ŠคํŒ…์—์„œ๋Š” MFC์—์„œ์˜ ๋™์ž‘์„ ์„ค๋ช…ํ•ฉ๋‹ˆ๋‹ค. ๋ณธ๋ฌธ์˜ ํ•˜๋‹จ์—๋Š” ์ƒ˜ํ”Œ ์ฝ”๋“œ๊ฐ€ ์ฒจ๋ถ€๋˜์–ด ์žˆ์œผ๋‹ˆ ์ฐธ๊ณ ํ•ด์ฃผ์„ธ์š”.

[MFC] ๊ทธ๋ฆผํŒ(mspaint) ๋งŒ๋“ค๊ธฐ ํ”„๋กœ์ ํŠธ #1

[MFC] ๊ทธ๋ฆผํŒ(mspaint) ๋งŒ๋“ค๊ธฐ ํ”„๋กœ์ ํŠธ #2

[MFC] ๊ทธ๋ฆผํŒ(mspaint) ๋งŒ๋“ค๊ธฐ ํ”„๋กœ์ ํŠธ #3

์บ”๋ฒ„์Šค(Canvas) ์ดˆ๊ธฐํ™”

์บ”๋ฒ„์Šค๋Š” ํ˜„์žฌ ๊ทธ๋ฆฌ๊ณ  ์žˆ๋Š” ๊ฒƒ(m_canvasDuringDraw)๊ณผ, ๋งˆ์ง€๋ง‰์œผ๋กœ ๊ทธ๋ฆฐ ๊ฒƒ์„ ๋ฐฑ์—…ํ•˜๋Š” ๊ฒƒ(m_canvasAfterDrawing) ๋‘ ๊ฐœ๊ฐ€ ์žˆ๋‹ค๊ณ  ์„ค๋ช…ํ–ˆ์Šต๋‹ˆ๋‹ค. initializeCanvas() ํ•จ์ˆ˜๋Š” ์บ”๋ฒ„์Šค๋ฅผ Dialog ํฌ๊ธฐ๋งŒํผ ์ƒ์„ฑํ•˜๊ณ  ํ•˜์–—๊ฒŒ ์ฑ„์šฐ๋Š” ์ž‘์—…์„ ์ˆ˜ํ–‰ํ•ฉ๋‹ˆ๋‹ค. ํ•จ์ˆ˜ ์ธ์ž๋กœ Width, Height๋ฅผ ๋„˜๊ฒจ ์ž์œ ๋กœ์šด ํฌ๊ธฐ๋กœ ์ƒ์„ฑํ•˜๊ฒŒ ํ•˜๋Š” ๊ฒƒ๋„ ์ข‹์€ ๋ฐฉ๋ฒ•์ž…๋‹ˆ๋‹ค.

//ExamPaintDlg.h
std::shared_ptr<Gdiplus::Bitmap> m_canvasAfterDrawing;
std::shared_ptr<Gdiplus::Bitmap> m_canvasDuringDraw;

//ExamPaintDlg.cpp
void CExamPaintDlg::initializeCanvas()
{
	CRect rect;
	this->GetClientRect(&rect);

	m_canvasAfterDrawing = std::make_shared<Bitmap>(rect.Width(), rect.Height());
	m_canvasDuringDraw = std::make_shared<Bitmap>(rect.Width(), rect.Height());

	Gdiplus::Graphics graphicsOfcanvasAfterDrawing(m_canvasAfterDrawing.get());
	Gdiplus::Graphics graphicsOfcanvasDuringDraw(m_canvasAfterDrawing.get());

	SolidBrush whiteBrush(Color(255, 255, 255, 255));
	graphicsOfcanvasAfterDrawing.FillRectangle(&whiteBrush, 0, 0, rect.Width(), rect.Height());
	graphicsOfcanvasDuringDraw.FillRectangle(&whiteBrush, 0, 0, rect.Width(), rect.Height());
}

์ด ํ•จ์ˆ˜๋Š” ํ”„๋กœ๊ทธ๋žจ ์ตœ์ดˆ ์‹คํ–‰ ์‹œ ํ˜ธ์ถœ๋˜๋Š” OnInitDialog()์™€ New ๋ฉ”๋‰ด๋ฅผ ํด๋ฆญํ–ˆ์„ ๋•Œ ํ˜ธ์ถœ๋˜๋Š” OnFileNew()์—์„œ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค.

BOOL CExamPaintDlg::OnInitDialog()
{
	CDialogEx::OnInitDialog();

	// ...

	initializeCanvas();

	return TRUE;  // ํฌ์ปค์Šค๋ฅผ ์ปจํŠธ๋กค์— ์„ค์ •ํ•˜์ง€ ์•Š์œผ๋ฉด TRUE๋ฅผ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.
}

void CExamPaintDlg::OnFileNew()
{
	initializeCanvas();

	Invalidate(FALSE);
}

๋งˆ์šฐ์Šค ์ด๋ฒคํŠธ

์‚ฌ์‹ค ๋งˆ์šฐ์Šค ์ด๋ฒคํŠธ ๋ถ€๋ถ„์ด ์ œ์ผ ๊นŒ๋‹ค๋กญ์Šต๋‹ˆ๋‹ค. ๋งˆ์šฐ์Šค ์ด๋ฒคํŠธ๋Š” ์™ผ์ชฝ ๋ฒ„ํŠผ์ด ๋‚ด๋ ค๊ฐ”์„ ๋•Œ(LButtonDown), ์™ผ์ชฝ ๋ฒ„ํŠผ์ด ์˜ฌ๋ผ์™”์„ ๋•Œ(LButtonUp) ๊ทธ๋ฆฌ๊ณ  ๋งˆ์šฐ์Šค๊ฐ€ ์›€์ง์ผ ๋•Œ(OnMouseMove)๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. ์ฐธ๊ณ ๋กœ ๋„ํ˜•์€ ์‹œ์ž‘ ์ ๊ณผ ๋ ์  ๋‘ ๊ฐœ๋งŒ ์žˆ์œผ๋ฉด ๊ทธ๋ฆด ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๋งˆ์šฐ์Šค ์ด๋ฒคํŠธ ์„ค๋ช…

์™ผ์ชฝ ๋ฒ„ํŠผ์ด ๋ˆŒ๋ ธ์„ ๋•Œ(LButtonDown)

๋ฒ„ํŠผ์ด ๋ˆŒ๋ฆฌ๋ฉด ์ขŒํ‘œ(Point)๋ฅผ ๋ณด๊ด€ํ•˜๊ณ , ๋‚˜์ค‘์— OnMouseMove()์—์„œ ํ˜„์žฌ ์ปค์„œ์˜ ์ด๋™ ๋Ÿ‰(Offset)์„ ๊ณ„์‚ฐํ•  ๋•Œ ์‚ฌ์šฉ๋ฉ๋‹ˆ๋‹ค.

๋งˆ์šฐ์Šค ์ขŒํ‘œ ๊ณ„์‚ฐ

void CExamPaintDlg::OnLButtonDown(UINT nFlags, CPoint point)
{
	if (m_figure == nullptr) return;
	
	m_pointOfLeftDown = point;
	
	m_isLButtonUp = false;
	
    //๋„ํ˜•์„ ํด๋ฆญํ•ด์„œ Moving ์ค‘์ด๋ผ๋ฉด ๋„˜์–ด๊ฐ‘๋‹ˆ๋‹ค.
	if (m_isInBound == true)
	{
		CDialogEx::OnLButtonDown(nFlags, point);
		return;
	}
	
    //๋„ํ˜•์„ ๊ทธ๋ฆฌ๊ธฐ ์œ„ํ•œ ๋‘ ์ ์„ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.
	m_figure->SetPoint(0, point.x, point.y);
	m_figure->SetPoint(1, point.x, point.y);
	
	m_isAddNewObject = true;

	//๋งˆ์ง€๋ง‰์— ๊ทธ๋ ค์ง„ Canvas๋ฅผ ๋ฐฑ์—…ํ•˜๋Š” ๋ถ€๋ถ„.
	Gdiplus::Graphics memG(m_canvasAfterDrawing.get());
	memG.DrawImage(m_canvasDuringDraw.get(), 0, 0, m_canvasDuringDraw->GetWidth(), m_canvasDuringDraw->GetHeight());

	Invalidate(FALSE);

	CDialogEx::OnLButtonDown(nFlags, point);
}

์™ผ์ชฝ ๋ฒ„ํŠผ์ด ์˜ฌ๋ผ์™”์„ ๋•Œ(LButtonUp)

CropBox๋Š” ์กฐ๊ธˆ ๊นŒ๋‹ค๋กญ์Šต๋‹ˆ๋‹ค. ์›๋ณธ ์ด๋ฏธ์ง€์—์„œ ์ž˜๋ผ๋‚ด๋Š” ์˜์—ญ์„ ์„ค์ •ํ•ด์•ผ ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ํ˜„์žฌ ์œ„์น˜ ๊ธฐ์ค€์œผ๋กœ Crop ์˜์—ญ์„ ์„ค์ •ํ•˜๋Š” ํ•จ์ˆ˜๋ฅผ ํ˜ธ์ถœํ•ฉ๋‹ˆ๋‹ค.

void CExamPaintDlg::OnLButtonUp(UINT nFlags, CPoint point)
{
	if (m_figure == nullptr) return;

	//CropBox๊ฐ€ ์„ ํƒ๋˜์—ˆ๊ณ  ๋„ํ˜•์„ ์ถ”๊ฐ€ํ•˜๋Š” ์ค‘์ด๋ผ๋ฉด,
	//SetCropPoints๋ฅผ ํ˜ธ์ถœํ•˜์—ฌ Crop ์˜์—ญ์„ ์ง€์ •ํ•ฉ๋‹ˆ๋‹ค.
	if (m_isCropping == true && m_isAddNewObject == true)
	{
		std::shared_ptr<Figure::CropRectangle> crop = std::dynamic_pointer_cast<Figure::CropRectangle>(m_figure);
		crop->SetCropPoints();
	}

	m_isAddNewObject = false;
	m_isLButtonUp = true;
	
	Invalidate(FALSE);

	CDialogEx::OnLButtonUp(nFlags, point);
}

๋งˆ์šฐ์Šค ์ปค์„œ๊ฐ€ ์›€์ง์ผ ๋•Œ(OnMouseMove)

๋งˆ์šฐ์Šค ์ปค์„œ์— ๋”ฐ๋ฅธ ๋™์ž‘์˜ ์˜๋ฏธ๋Š” ์•„๋ž˜ ์ฝ”๋“œ์— ์ฃผ์„์œผ๋กœ ์„ค๋ช…ํ•ฉ๋‹ˆ๋‹ค.

void CExamPaintDlg::OnMouseMove(UINT nFlags, CPoint point)
{
	if (m_figure == nullptr)
		return;

	if (point.x < 0 || point.x > m_canvasDuringDraw->GetWidth() || point.y < 0 || point.y > m_canvasDuringDraw->GetHeight())
		return;

	if (nFlags == MK_LBUTTON && m_isAddNewObject == true)
	{
		//์™ผ์ชฝ ๋ฒ„ํŠผ์ด ๋ˆŒ๋ ธ๊ณ  ๋งˆ์šฐ์Šค ์ปค์„œ๊ฐ€ ์›€์ง์—ฌ ๋„ํ˜•์„ ๊ทธ๋ฆฌ๋Š” ์ค‘์„ ์˜๋ฏธ.
        
		//๋„ํ˜•์„ ๊ทธ๋ฆฌ๊ธฐ ์œ„ํ•œ ๋‘ ๋ฒˆ์งธ ํฌ์ธํŠธ๋ฅผ ์ง€์ •.
		m_figure->SetPoint(1, point.x, point.y);

		Invalidate(FALSE);
	}
	else if (nFlags == MK_LBUTTON && m_isAddNewObject == false && m_isInBound == true)
	{
		//์™ผ์ชฝ ๋ฒ„ํŠผ์ด ๋ˆŒ๋ ธ๊ณ , ๋ฒ„ํŠผ์ด ํ•œ๋ฒˆ ์˜ฌ๋ผ์™”์—ˆ๊ณ , ์ปค์„œ๊ฐ€ ๋„ํ˜• ๋‚ด์— ์žˆ์œผ๋ฏ€๋กœ
		//๋„ํ˜•์„ ํด๋ฆญํ•ด์„œ Dragging ์ค‘์ด๋ผ๋Š” ์˜๋ฏธ.
        
		//์‹œ์ž‘ ์ ๊ณผ ํ˜„์žฌ ์ ๊ณผ์˜ ์ด๋™ ๋Ÿ‰์„ ๊ณ„์‚ฐ.
		Point offset;
		offset.X = point.x - m_pointOfLeftDown.x;
		offset.Y = point.y - m_pointOfLeftDown.y;

		m_figure->Drag(offset.X, offset.Y);
			
		Invalidate(FALSE);

		m_pointOfLeftDown.x = point.x;
		m_pointOfLeftDown.y = point.y;
	}
	else
	{
		//๋„ํ˜•์„ ๊ทธ๋ฆฌ๊ฑฐ๋‚˜ ์›€์ง์ด๋Š” ์ค‘์ด ์•„๋‹ˆ๋ฏ€๋กœ
		//ํ˜„์žฌ ๋งˆ์šฐ์Šค ์ปค์„œ๊ฐ€ ๋„ํ˜• ์œ„์— ์žˆ๋Š”์ง€ ํ™•์ธํ•˜๊ณ ,
		//์œ„์น˜์— ๋”ฐ๋ผ ์ปค์„œ๋ฅผ ์ง€์ •.
		m_isInBound = m_figure->IsInBound(point.x, point.y);
		if (m_isInBound == true)
			SetCursor(AfxGetApp()->LoadStandardCursor(IDC_SIZEALL));
		else
			SetCursor(AfxGetApp()->LoadStandardCursor(IDC_ARROW));
	}

	CDialogEx::OnMouseMove(nFlags, point);
}

GDI+ ์ด๋ฏธ์ง€ ๋ถˆ๋Ÿฌ์˜ค๊ธฐ/์ €์žฅํ•˜๊ธฐ

์ด๋ฏธ์ง€๋ฅผ ํŒŒ์ผ๋กœ๋ถ€ํ„ฐ ๋ถˆ๋Ÿฌ์˜ค๊ณ , ๋ถˆ๋Ÿฌ์˜จ ์ด๋ฏธ์ง€๋ฅผ m_canvasAfterDrawing์— ๊ทธ๋ ค์ค๋‹ˆ๋‹ค.

void CExamPaintDlg::OnFileOpen()
{
	CString filter = _T("Bitmap(*.BMP)|*.BMP|JPEG(*.JPG)|*.JPG|All Files(*.*)|*.*||");
	CFileDialog dlg(TRUE, _T(""), _T(""), OFN_HIDEREADONLY, filter);
	if (dlg.DoModal() == IDOK)
	{
		Image* img = Image::FromFile(dlg.GetPathName());
		
		m_canvasAfterDrawing = std::make_shared<Bitmap>(img->GetWidth(), img->GetHeight());
		m_canvasDuringDraw = std::make_shared<Bitmap>(img->GetWidth(), img->GetHeight());

		Graphics g(m_canvasAfterDrawing.get());
		g.DrawImage(img, 0, 0, img->GetWidth(), img->GetHeight());
		
		Invalidate(FALSE);
	}
}

GDI+๋กœ ์ด๋ฏธ์ง€๋ฅผ ์ €์žฅํ•˜๋Š” ๋ถ€๋ถ„์ด ์กฐ๊ธˆ ๊นŒ๋‹ค๋กญ์Šต๋‹ˆ๋‹ค. ์ €์žฅํ•˜๋Š” ์ด๋ฏธ์ง€ ํฌ๋งท์— ๋”ฐ๋ผ CLSID ์ •๋ณด๋ฅผ ๊ฐ€์ ธ์™€์•ผ ํ•ฉ๋‹ˆ๋‹ค.

void CExamPaintDlg::OnFileSave()
{
	CString filter = _T("Bitmap(*.BMP)|*.BMP|JPEG(*.JPG)|*.JPG|PNG Files(*.png)|*.png||");
	CFileDialog dlg(FALSE, _T(""), _T(""), OFN_HIDEREADONLY, filter);
	if (dlg.DoModal() == IDOK)
	{
		CLSID clsid;
		CString extension = dlg.GetFileExt();
		if (extension == _T("BMP"))
			CLSIDFromString(_T("{557cf400-1a04-11d3-9a73-0000f81ef32e}"), &clsid);
		else if (extension == _T("JPG"))
			CLSIDFromString(_T("{557cf401-1a04-11d3-9a73-0000f81ef32e}"), &clsid);
		else if (extension == _T("PNG"))
			CLSIDFromString(_T("{557cf406-1a04-11d3-9a73-0000f81ef32e}"), &clsid);

		m_canvasAfterDrawing.get()->Save(dlg.GetPathName(), &clsid, NULL);
	}
}

์ •๋ฆฌ

์ด๋ฒˆ ๊ทธ๋ฆผํŒ ํ”„๋กœ์ ํŠธ ์ž˜ ๋”ฐ๋ผ์˜ค์…จ๋‚˜์š”? ์ œ๊ฐ€ ๋ด๋„ ์„ค๋ช…์ด๋‚˜ ์ฝ”๋“œ์— ๋ถ€์กฑํ•œ ์ ์ด ๋งŽ์•„ ๋ณด์ž…๋‹ˆ๋‹ค. ๊ทธ๋ž˜๋„ ์ฒจ๋ถ€๋œ ์ƒ˜ํ”Œ ์ฝ”๋“œ๋ฅผ ๋ณด๋ฉด ๋Œ€์ถฉ ๊ฐ์€ ์žกํžˆ๋ฆฌ๋ผ ์ƒ๊ฐ๋ฉ๋‹ˆ๋‹ค. ๊ทธ๋ฆฌ๊ณ  ์›๋ฆฌ๋งŒ ์ดํ•ดํ•˜๋ฉด MFC๋‚˜ C#, ๋‹ค๋ฅธ UI ํ”Œ๋žซํผ์—์„œ๋„ ๋™์ผํ•˜๊ฒŒ ๋งŒ๋“ค ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

ExamMspaint.zip
0.14MB

 

๋ฐ˜์‘ํ˜•