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