C++使用GDI+进行简单绘图与擦除

C++使用GDI+进行简单绘图与擦除

绘图主要是捕捉鼠标下落与升起事件,文中应用的是Duilib库进行界面编程的,对应的也就是WM_LBUTTONDOWN和WM_LBUTTONUP消息事件,画图的主要实现是开启一个线程,一直循环去捕捉用户的WM_LBUTTONDOWN和WM_LBUTTONUP事件,遇到WM_LBUTTONDOWN事件时,运用GetMousePos()函数或GetCursorPos()函数去获取用户点击的点,获取两个点之后就能利用DrawCurve()函数使用Pen类new一个有色值的笔出来进行曲线绘制,橡皮擦的实现使用FillRectangle()函数使用SolidBrush类new一个背景色值的画刷出来进行区域擦除。遇到WM_LBUTTONUP事件时,跳出循环进行递归下次循环。

绘图之前需要先初始化GDI+

// 初始化GDI+.
GdiplusStartupInput gdiplusStartupInput;
ULONG_PTR m_gdiplusToken;
GdiplusStartup((ULONG_PTR)&m_gdiplusToken, &gdiplusStartupInput, NULL);

//绘制自定义界面
.......

 Graphics* graphics = new Graphics(m_pHwnd);//根据自定义界面的窗口句柄在窗口初始化函数里new一个graphics
 
 delete graphics;//在窗口销毁函数里记得delete,不然会造成内存泄漏

绘图代码实现如下:

unsigned __stdcall OnPaint(LPVOID lpParam)
{
	while (!m_isPainting)
	{ 
		Sleep(10);
	}
	
	Pen pen(Color(255, 123, 456, 789));
	SolidBrush *Brush = new SolidBrush(Color(255, 255, 255, 255));

	while (m_isPainting)
	{
		GetCursorPos(&p2);
		//POINT p2 = m_pm.GetMousePos();
		if (rect.PtInRect(p2))
		{
			if (p1.x == 0 && p1.y == 0)  //第一个点不足以绘制曲线,需要等待捕捉倒第二个点再进行曲线绘制
			{
				p1 = p2;
				continue;
			}
			
			if (m_isDrawing)  //绘图
			{
				Point curPoints[2] = { { p1.x, p1.y }, { p2.x, p2.y } };
				graphics->DrawCurve(&pen, curPoints, 2);
			}
			else  //橡皮擦,背景色为白色,橡皮擦大小是点周围的10px
			{
				int x = (p1.x + p2.x) / 2 - 5;
				int y = (p1.y + p2.y) / 2 -5;
				int width = 10;
				int height = 10;
				graphics->FillRectangle(Brush, x, y, width, height);
			}
			p1 = p2;
		}
	}
	delete Brush;
	
	if (isRunning)  //程序还在运行时进行递归操作
	{
		OnPaint(NULL);
	}
}

将绘制的图案保存为图片的代码如下(采取屏幕截图方式,图片保存为 png格式)

BOOL MakePNG(HDC hDC, CRect rect, CString strFilePath)
{
	BITMAP bmp;
	PBITMAPINFO pbmi;
	PBITMAPINFOHEADER pbih;     // bitmap info-header 
	BITMAPFILEHEADER  hdr;      // bitmap file-header
	WORD    cClrBits;
	LPBYTE lpBits;              // memory pointer
	DWORD dwTmp;
	DWORD cb;                   // incremental count of bytes 
	BYTE *hp;                   // byte pointer 
	HANDLE hfile;               // file handle 
	CString szBMPFilename = strFilePath.Left(strFilePath.GetLength() - 3) + _T("bmp");//先保存成位图
	HDC hdcCompatible = CreateCompatibleDC(hDC);
	HBITMAP hbmScreen = CreateCompatibleBitmap(hDC, rect.Width(), rect.Height());

	if (hbmScreen == NULL)
	{
		return FALSE;
	}

	// Select the bitmaps into the compatible DC. 
	if (!SelectObject(hdcCompatible, hbmScreen))
	{
		return FALSE;
	}

	//Copy color data for the entire display into a 
	//bitmap that is selected into a compatible DC. 
	if (!BitBlt(hdcCompatible,
		0, 0,
		rect.Width(), rect.Height(),
		hDC,
		rect.left, rect.top,
		SRCCOPY))
	{
		return FALSE;
	}
	
	// Retrieve the bitmap's color format, width, and height. 
	if (!GetObject(hbmScreen, sizeof(BITMAP), (LPSTR)&bmp))
	{
		return FALSE;
	}
	
	// Convert the color format to a count of bits. 
	cClrBits = (WORD)(bmp.bmPlanes * bmp.bmBitsPixel);
	if (cClrBits == 1)
		cClrBits = 1;
	else if (cClrBits <= 4)
		cClrBits = 4;
	else if (cClrBits <= 8)
		cClrBits = 8;
	else if (cClrBits <= 16)
		cClrBits = 16;
	else if (cClrBits <= 24)
		cClrBits = 24;
	else cClrBits = 32;

	// Allocate memory for the BITMAPINFO structure. (This structure 
	// contains a BITMAPINFOHEADER structure and an array of RGBQUAD 
	// data structures.) 
	if (cClrBits != 24)
	{
		pbmi = (PBITMAPINFO)LocalAlloc(LPTR,
		sizeof(BITMAPINFOHEADER) +
		sizeof(RGBQUAD) * (1 << cClrBits));
	}
	else    // There is no RGBQUAD array for the 24-bit-per-pixel format. 
	{
		pbmi = (PBITMAPINFO)LocalAlloc(LPTR,
		sizeof(BITMAPINFOHEADER));
	}
	
	// Initialize the fields in the BITMAPINFO structure. 
	pbmi->bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
	pbmi->bmiHeader.biWidth = bmp.bmWidth;
	pbmi->bmiHeader.biHeight = bmp.bmHeight;
	pbmi->bmiHeader.biPlanes = bmp.bmPlanes;
	pbmi->bmiHeader.biBitCount = bmp.bmBitsPixel;
	
	if (cClrBits < 24)
		pbmi->bmiHeader.biClrUsed = (1 << cClrBits);

	// If the bitmap is not compressed, set the BI_RGB flag. 
	pbmi->bmiHeader.biCompression = BI_RGB;

	// Compute the number of bytes in the array of color 
	// indices and store the result in biSizeImage. 
	pbmi->bmiHeader.biSizeImage = ((pbmi->bmiHeader.biWidth * cClrBits + 31) & ~31) / 8
		* pbmi->bmiHeader.biHeight;
	
	// Set biClrImportant to 0, indicating that all of the device colors are important. 
	pbmi->bmiHeader.biClrImportant = 0;
	pbih = (PBITMAPINFOHEADER)pbmi;
	lpBits = (LPBYTE)GlobalAlloc(GMEM_FIXED, pbih->biSizeImage);

	if (!lpBits) 
	{
		return FALSE;
	}
	
	// Retrieve the color table (RGBQUAD array) and the bits 
	// (array of palette indices) from the DIB. 
	if (!GetDIBits(hDC, hbmScreen, 0, (WORD)pbih->biHeight, lpBits, pbmi,
		DIB_RGB_COLORS))
	{
		return FALSE;
	}

	// Create the .BMP file. 
	hfile = CreateFile(szBMPFilename,
		GENERIC_READ | GENERIC_WRITE,
		(DWORD)0,
		NULL,
		CREATE_ALWAYS,
		FILE_ATTRIBUTE_NORMAL,
		(HANDLE)NULL);
	if (hfile == INVALID_HANDLE_VALUE)
	{
		return false;
	}
	hdr.bfType = 0x4d42;        // 0x42 = "B" 0x4d = "M" 
	// Compute the size of the entire file. 
	hdr.bfSize = (DWORD)(sizeof(BITMAPFILEHEADER) +
		pbih->biSize + pbih->biClrUsed
		* sizeof(RGBQUAD) + pbih->biSizeImage);
	hdr.bfReserved1 = 0;
	hdr.bfReserved2 = 0;

	// Compute the offset to the array of color indices. 
	hdr.bfOffBits = (DWORD) sizeof(BITMAPFILEHEADER) +
		pbih->biSize + pbih->biClrUsed
		* sizeof(RGBQUAD);

	// Copy the BITMAPFILEHEADER into the .BMP file. 
	if (!WriteFile(hfile, (LPVOID)&hdr, sizeof(BITMAPFILEHEADER),
		(LPDWORD)&dwTmp, NULL))
	{
		return FALSE;
	}

	// Copy the BITMAPINFOHEADER and RGBQUAD array into the file. 
	if (!WriteFile(hfile, (LPVOID)pbih, sizeof(BITMAPINFOHEADER)
		+ pbih->biClrUsed * sizeof(RGBQUAD),
		(LPDWORD)&dwTmp, (NULL)))
	{
		return FALSE;
	}

	// Copy the array of color indices into the .BMP file. 
	cb = pbih->biSizeImage;
	hp = lpBits;
	if (!WriteFile(hfile, (LPSTR)hp, (int)cb, (LPDWORD)&dwTmp, NULL))
	{
		return FALSE;
	}

	// Close the .BMP file. 
	CloseHandle(hfile);
	
	// Free memory. 
	GlobalFree((HGLOBAL)lpBits);

	//转换成PNG
	USES_CONVERSION;
	LPWSTR pwBMPFilename = new wchar_t[szBMPFilename.GetLength() + 1];
	wcscpy(pwBMPFilename, T2W((LPCTSTR)szBMPFilename));
	LPWSTR pwFilePath = new wchar_t[strFilePath.GetLength() + 1];
	wcscpy(pwFilePath, T2W((LPCTSTR)strFilePath));

	if (!BMptoPNG(pwBMPFilename, pwFilePath))
	{
		DeleteFile(szBMPFilename);
		return FALSE;
	}
	DeleteObject(hbmScreen);
	DeleteFile(szBMPFilename);
	return TRUE;
}
// //转换BMP文件为PNG文件            
BOOL BMptoPNG(LPCWSTR StrBMp, LPCWSTR StrPNG)
{
	CLSID encoderClsid;
	Status stat;
	Image* image = NULL;
	image = Bitmap::FromFile(StrBMp, TRUE);
	if (!GetEncoderClsid(L"image/png", &encoderClsid))
	{
		return FALSE;
	}
	stat = image->Save(StrPNG, &encoderClsid, NULL);
	if (stat != Ok)
	{
		return FALSE;
	}
	delete image;
	return TRUE;
}

//	转换PNG文件为BMP文件      
BOOL PNGtoBMp(LPCWSTR StrPNG, LPCWSTR StrBMp)
{
	CLSID encoderClsid;
	Status stat;
	Image* pImage;
	pImage = Bitmap::FromFile(StrPNG, TRUE);
	if (!GetEncoderClsid(L"image/bmp", &encoderClsid))
	{
		return FALSE;
	}
	stat = pImage->Save(StrBMp, &encoderClsid, NULL);
	if (stat != Ok)
	{
		return FALSE;
	}
	delete pImage;
	return TRUE;
}

BOOL GetEncoderClsid(WCHAR* pFormat, CLSID* pClsid)
{
	UINT num = 0, size = 0;
	ImageCodecInfo* pImageCodecInfo = NULL;
	GetImageEncodersSize(&num, &size);
	if (size == 0)
	{
		return FALSE;
	}
	pImageCodecInfo = (ImageCodecInfo*)(malloc(size));
	if (pImageCodecInfo == NULL)
	{
		return FALSE;
	}
	GetImageEncoders(num, size, pImageCodecInfo);
	BOOL bfound = FALSE;
	for (UINT i = 0; !bfound && i < num; i++)
	{
		if (_wcsicmp(pImageCodecInfo[i].MimeType, pFormat) == 0)
		{
			*pClsid = pImageCodecInfo[i].Clsid;
			bfound = TRUE;
		}
	}
	free(pImageCodecInfo);
	return bfound;
}

wstring GetAppPathW()
{
	wchar_t szExePath[MAX_PATH] = {0};
	GetModuleFileNameW(NULL, szExePath, MAX_PATH);
	wchar_t *pstr = wcsrchr(szExePath, '\\');
	memset(pstr + 1, 0, 2);
	wstring strAppPath(szExePath);
	return strAppPath;
}

CString ScreenShot(void)
{
	RECT rect;
	HWND hwnd = FindWindow(TEXT("画图"), NULL); //注意窗口不能最小化 
	if (hwnd == NULL)
	{
		return 0;
	}
	GetClientRect(hwnd, &rect);
	
	CDC *pDC;//屏幕DC
	HDC dc = GetDC(m_pHwnd);
	pDC = CDC::FromHandle(dc);

	rect.top = (rect.bottom - 600) / 2 + 60 + 7;
	rect.bottom = rect.top + 465;
	rect.left = (rect.right - 800) / 2 + 7;
	rect.right = rect.left + 800 - 14;

	//保存到的文件名
	CString strFileName(GetAppPathW().c_str());
	strFileName += _T("Image\\");
	CreateDirectory((LPCTSTR)strFileName, NULL);
	CTime t = CTime::GetCurrentTime();
	CString tt = t.Format("%Y%m%d_%H%M%S");
	strFileName += tt;
	strFileName += _T(".PNG");
	//保存为PNG
	CMakePNG MakePNG;
	MakePNG.MakePNG(pDC->m_hDC, rect, strFileName);
	ReleaseDC(m_pHwnd,*pDC);
	return strFileName;
}

注释
[1]: https://blog.csdn.net/sunflover454/article/details/49533651

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值