一、准备
1、屏幕截图
使用BitBlt来进行截图
2、强制刷新
截图的时候,有可能由于界面没有及时刷新,导致截到的内容,这时就需要强制立刻刷新重绘
cpp
InvalidateRect(m_hWnd, NULL, TRUE);
UpdateWindow(m_hWnd);
二、 截长图
1、截全屏【不考虑表头、顶部、底部等因素】
需求:界面只有一个List控件m_pList,放着数据m_vecData,需要将整个List的所有内容都截图拼成一个长图。先说简单粗暴的,只是截图,然后拼接。(不考虑拼接的长图表头只截一次的问题)
思路:
- 先计算出长图的总高度,然后创建该高度的兼容位图并写入内存DC
- 拷贝屏幕内容到设备上下文(内存DC),翻页,继续拷贝当前页码的屏幕内容到设备上下文(挨着上次内存DC拷贝后位置的底部放),直到翻页到最后。
- 保存为.png或者.bmp格式
cpp
#include<windows.h>
#include <gdiplus.h>
using namespace Gdiplus;
#pragma comment(lib, "gdiplus.lib")
#define LIST_HEADER_HEIGHT 36 //表头高度
#define LIST_ITEM_ROW_HEIGHT 35 //列表每行高度
void ScreenShotLongImage()
{
//列表
CListUI* m_pList = nullptr;
//列表内容
std::vector<Data> m_vecData;
...
//一页列表高度
int nListHeight = m_pList->GetHeight();
//列表内容总高度
int nContentTotalHeight = m_vecData.size() * LIST_ITEM_ROW_HEIGHT;
//总页数
int nTotalPage = nContentTotalHeight / (nListHeight - LIST_HEADER_HEIGHT);
//最后一页剩余高度
int nLeftHeight = nContentTotalHeight % (nListHeight - LIST_HEADER_HEIGHT);
if (nLeftHeight != 0) nTotalPage++;
//计算长图总高度
int nDstTotalHeight = 0;
for (int nPage = 0; nPage < nTotalPage; nPage++)
{
RECT rcSrcClient;
GetClientRect(m_hWnd, &rcSrcClient);
int nHeight = rcSrcClient.bottom - rcSrcClient.top;
nDstTotalHeight += nHeight;
}
//截长图
//1、确定长图区域
RECT rcDstClient;
GetClientRect(m_hWnd, &rcDstClient);
rcDstClient.bottom = nDstTotalHeight;
int nDstWidth = rcDstClient.right - rcDstClient.left;
//2、获得指定窗口的dc(源dc)
//注意GetWindowDC会把窗口的标题栏也同时截图,如果不需要窗口的标题就使用GetDC(hwnd)
HDC sourceDC = GetDC(m_hWnd);
//3、根据源dc创建兼容内存DC
HDC memDC = CreateCompatibleDC(sourceDC);
//3、根据源dc创建兼容位图
HBITMAP memBitmap = CreateCompatibleBitmap(sourceDC, rcDstClient.right - rcDstClient.left, rcDstClient.bottom - rcDstClient.top);
//4、将兼容位图写入内存dc
SelectObject(memDC, memBitmap);
//5、翻页截图拼接成长图
int nDstTop = 0;
for (int nPage = 0; nPage < nTotalPage; nPage++)
{
//强制重绘
UpdateWindow(m_hWnd);
// 拷贝屏幕内容到设备上下文
RECT rcSrcClient;
GetClientRect(m_hWnd, &rcSrcClient);
if (nPage == nTotalPage)
{
//最后一页
int nTopPadding = LIST_HEADER_HEIGHT + (nListHeight - LIST_HEADER_HEIGHT - nLeftHeight);
rcSrcClient.top += nTopPadding;
}
int nDstHeight = rcSrcClient.bottom - rcSrcClient.top;
BitBlt(memDC, 0, nDstTop, nDstWidth, nDstHeight, GetDC(m_hWnd), rcSrcClient.left, rcSrcClient.top, SRCCOPY);
nDstTop += rcSrcClient.bottom - rcSrcClient.top;
//翻页
m_pList->PageDown();
}
//6、保存为.png格式
GdiplusStartupInput gdiplusStartupInput;
ULONG_PTR gdiplusToken;
GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
Gdiplus::Bitmap bmp(memBitmap, NULL);
CLSID pngClsid;
int i = GetEncoderClsid(L"image/png", &pngClsid);
bmp.Save(L"test.png", &pngClsid, NULL);
GdiplusShutdown(gdiplusToken);
//保存位.bmp格式
{
//6、以下代码保存截图信息到文件中保存为bmp格式
//6.1获得位图信息
BITMAP bmp;
GetObject(memBitmap, sizeof(BITMAP), &bmp);
//6.2图片保存路径和方式
FILE* fp;
fopen_s(&fp, L"test.bmp", "w+b");
//6.3创建位图文件头
//位图文件头设置默认值为0
BITMAPFILEHEADER bfh = { 0 };
//到位图数据的偏移量(此步骤固定,位图编译量即为位图文件头 + 位图信息头 + 调色板的大小,调色板设置为0)
bfh.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);
//文件总的大小
bfh.bfSize = bfh.bfOffBits + bmp.bmWidthBytes * bmp.bmHeight;
//bfType固定为BM,(WORD)0x4d42表示为BM
bfh.bfType = (WORD)0x4d42;
//6.4创建文件信息头
//位图信息头,默认设置为0
BITMAPINFOHEADER bih = { 0 };
//每个像素字节大小
bih.biBitCount = bmp.bmBitsPixel;
bih.biCompression = BI_RGB;
//高度
bih.biHeight = bmp.bmHeight;
bih.biPlanes = 1;
bih.biSize = sizeof(BITMAPINFOHEADER);
//图像数据大小
bih.biSizeImage = bmp.bmWidthBytes * bmp.bmHeight;
//宽度
bih.biWidth = bmp.bmWidth;
//6.5写入位图文件头
fwrite(&bfh, 1, sizeof(BITMAPFILEHEADER), fp);
//6.6写入位图信息头
fwrite(&bih, 1, sizeof(BITMAPINFOHEADER), fp);
//6.7申请内存保存位图数据
byte* p = new byte[bmp.bmWidthBytes * bmp.bmHeight];
//6.8获取位图数据
GetDIBits(memDC, (HBITMAP)memBitmap, 0, rcDstClient.bottom - rcDstClient.top, p,
(LPBITMAPINFO)&bih, DIB_RGB_COLORS);
//6.9写入位图数据
fwrite(p, 1, bmp.bmWidthBytes * bmp.bmHeight, fp);
//6.10先删除开辟的内存,然后关闭文件
delete[] p;
fclose(fp);
}
// 释放资源
DeleteObject(memBitmap);
DeleteDC(memDC);
ReleaseDC(m_hWnd, sourceDC);
}
int GetEncoderClsid(const WCHAR *format, CLSID *pClsid)
{
UINT num = 0; // number of image encoders
UINT size = 0; // size of the image encoder array in bytes
ImageCodecInfo* pImageCodecInfo = NULL;
GetImageEncodersSize(&num, &size);
if (size == 0)
return -1; // Failure
pImageCodecInfo = (ImageCodecInfo*)(malloc(size));
if (pImageCodecInfo == NULL)
return -1; // Failure
GetImageEncoders(num, size, pImageCodecInfo);
for (UINT j = 0; j < num; ++j)
{
if (wcscmp(pImageCodecInfo[j].MimeType, format) == 0)
{
*pClsid = pImageCodecInfo[j].Clsid;
free(pImageCodecInfo);
return j; // Success
}
}
free(pImageCodecInfo);
return -1; // Failure
}
2、截长图【考虑表头、头部、底部内容等】
cpp
#define LIST_HEADER_HEIGHT 36 //表头高度
#define LIST_TOP_HEIGHT 42 //列表顶部高度
#define LIST_BOTTOM_HEIGHT 105 //列表顶部高度
#define LIST_ITEM_ROW_HEIGHT 35//列表每行高度
#define LIST_YEAR_Q_WITH 705 //全端年榜列表宽度
#define LIST_YEAR_D_WITH 615 //点播年榜列表宽度
#define FRAME_MAX_WITH 1920//最大化宽度
void CMainFrame::ScreenShotLongImage(int &nSplitLineSize, bool bQ)
{
int nRightPadding = bQ ? FRAME_MAX_WITH - LIST_YEAR_Q_WITH : FRAME_MAX_WITH - LIST_YEAR_D_WITH;
int nListHeight = bQ ? m_pKyTVPlayYearQList->GetHeight(): m_pKyTVPlayYearDList->GetHeight();
int nTotalHeight = m_vecKyTVPlayYear.size() * LIST_ITEM_ROW_HEIGHT + nSplitLineSize;
int nTotalPage = nTotalHeight / (nListHeight - LIST_HEADER_HEIGHT);
int nLeftHeight = nTotalHeight % (nListHeight - LIST_HEADER_HEIGHT);
if (nLeftHeight != 0) nTotalPage++;
//计算长图总高度
int nDstTotalHeight = 0;
for (int nPage = 0; nPage < nTotalPage; nPage++)
{
RECT rcSrcClient;
int nHeight = GetSrcRect(rcSrcClient, nRightPadding, nListHeight, nLeftHeight, nPage, nTotalPage);
nDstTotalHeight += nHeight;
}
//截长图
//1、确定长图区域
RECT rcDstClient;
GetClientRect(m_hWnd, &rcDstClient);
rcDstClient.bottom = nDstTotalHeight;
rcDstClient.right -= nRightPadding;
int nDstWidth = rcDstClient.right - rcDstClient.left;
//2、获得指定窗口的dc(源dc)
//注意GetWindowDC会把窗口的标题栏也同时截图,如果不需要窗口的标题就使用GetDC(hwnd)
HDC sourceDC = GetDC(m_hWnd);
//3、根据源dc创建兼容内存DC
HDC memDC = CreateCompatibleDC(sourceDC);
//3、根据源dc创建兼容位图
HBITMAP memBitmap = CreateCompatibleBitmap(sourceDC, rcDstClient.right - rcDstClient.left, rcDstClient.bottom - rcDstClient.top);
//4、将兼容位图写入内存dc
SelectObject(memDC, memBitmap);
//5、翻页截图拼接成长图
int nDstTop = 0;
for (int nPage = 0; nPage < nTotalPage; nPage++)
{
//强制重绘
UpdateWindow(m_hWnd);
// 拷贝屏幕内容到设备上下文
RECT rcSrcClient;
int nDstHeight = GetSrcRect(rcSrcClient, nRightPadding, nListHeight, nLeftHeight, nPage, nTotalPage);
BitBlt(memDC, 0, nDstTop, nDstWidth, nDstHeight, GetDC(m_hWnd), rcSrcClient.left, rcSrcClient.top, SRCCOPY);
nDstTop += rcSrcClient.bottom - rcSrcClient.top;
//翻页
if (bQ)
{
m_pKyTVPlayYearQList->PageDown();
}
else
{
m_pKyTVPlayYearDList->PageDown();
}
}
//6、保存为.png格式
GdiplusStartupInput gdiplusStartupInput;
ULONG_PTR gdiplusToken;
GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL);
Gdiplus::Bitmap bmp(memBitmap, NULL);
CLSID pngClsid;
int i = miscutil::system::GetEncoderClsid(L"image/png", &pngClsid);
string strImageName = "q.png";
bmp.Save(miscutil::convert::ANSI2Unicode(strImageName.c_str()).c_str(), &pngClsid, NULL);
GdiplusShutdown(gdiplusToken);
//保存位.bmp格式
{
//6、以下代码保存截图信息到文件中保存为bmp格式
//6.1获得位图信息
BITMAP bmp;
GetObject(memBitmap, sizeof(BITMAP), &bmp);
//6.2图片保存路径和方式
strImageName.append(".bmp");
FILE* fp;
fopen_s(&fp, strImageName.c_str(), "w+b");
//6.3创建位图文件头
//位图文件头设置默认值为0
BITMAPFILEHEADER bfh = { 0 };
//到位图数据的偏移量(此步骤固定,位图编译量即为位图文件头 + 位图信息头 + 调色板的大小,调色板设置为0)
bfh.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER);
//文件总的大小
bfh.bfSize = bfh.bfOffBits + bmp.bmWidthBytes * bmp.bmHeight;
//bfType固定为BM,(WORD)0x4d42表示为BM
bfh.bfType = (WORD)0x4d42;
//6.4创建文件信息头
//位图信息头,默认设置为0
BITMAPINFOHEADER bih = { 0 };
//每个像素字节大小
bih.biBitCount = bmp.bmBitsPixel;
bih.biCompression = BI_RGB;
//高度
bih.biHeight = bmp.bmHeight;
bih.biPlanes = 1;
bih.biSize = sizeof(BITMAPINFOHEADER);
//图像数据大小
bih.biSizeImage = bmp.bmWidthBytes * bmp.bmHeight;
//宽度
bih.biWidth = bmp.bmWidth;
//6.5写入位图文件头
fwrite(&bfh, 1, sizeof(BITMAPFILEHEADER), fp);
//6.6写入位图信息头
fwrite(&bih, 1, sizeof(BITMAPINFOHEADER), fp);
//6.7申请内存保存位图数据
byte* p = new byte[bmp.bmWidthBytes * bmp.bmHeight];
//6.8获取位图数据
GetDIBits(memDC, (HBITMAP)memBitmap, 0, rcDstClient.bottom - rcDstClient.top, p,
(LPBITMAPINFO)&bih, DIB_RGB_COLORS);
//6.9写入位图数据
fwrite(p, 1, bmp.bmWidthBytes * bmp.bmHeight, fp);
//6.10先删除开辟的内存,然后关闭文件
delete[] p;
fclose(fp);
}
// 释放资源
DeleteObject(memBitmap);
DeleteDC(memDC);
ReleaseDC(m_hWnd, sourceDC);
}
int CMainFrame::GetSrcRect(RECT &rcClient, int nRightPadding, int nListHeight, int nLeftHeight, int nPageNO, int nTotalPage)
{
GetClientRect(m_hWnd, &rcClient);
int nTopPadding = 0, nBottomPadding = 0, nLeftPadding = 0;
if (nPageNO == 0)
{
//第一页
nBottomPadding = LIST_BOTTOM_HEIGHT;
}
else if (nPageNO == (nTotalPage - 1))
{
//最后一页
nTopPadding = LIST_TOP_HEIGHT + LIST_HEADER_HEIGHT + (nListHeight - LIST_HEADER_HEIGHT - nLeftHeight);
}
else
{
nTopPadding = LIST_TOP_HEIGHT + LIST_HEADER_HEIGHT;
nBottomPadding = LIST_BOTTOM_HEIGHT;
}
rcClient.top += nTopPadding;
rcClient.bottom -= nBottomPadding;
rcClient.left += nLeftPadding;
rcClient.right -= nRightPadding;
return (rcClient.bottom - rcClient.top);
}