///////////////////////////////////////////////////////
//
// 代码简介:
// 在原作者实现的桌面截图函数的基础上
// 修改而成的多扩展屏的桌面截图
//
// 修改为了屏幕轮廓抓取程序,附带有简单的鼠标远程操作相应
// 修改为在不同电脑上运行的程序后,可以丰富鼠标操作内容
// 支持多屏幕
//
//
// 编译环境:Visual Studio 2022 | EasyX_20220116
//
// 原作者:yangw80 <yw80@qq.com>
// 修改者:huidong <mailhuid@163.com>
// 最后修改:2022.06.03
//
#include <Windows.h>
#include <WinUser.h>
#include <EasyX.h>
// 存储整个屏幕的大小信息(多显示器)
struct ScreenSize
{
int left; // 多显示器的左上角 x 坐标
int top; // 多显示器的左上角 y 坐标
int w; // 多显示器的总和宽度
int h; // 多显示器的总和高度
};
ScreenSize s; // 屏幕信息
const float ratio = 0.5; // 画面缩放比例
// 获取多显示器大小信息
ScreenSize GetScreenSize()
{
int left = GetSystemMetrics(SM_XVIRTUALSCREEN);
int top = GetSystemMetrics(SM_YVIRTUALSCREEN);
int w = GetSystemMetrics(SM_CXVIRTUALSCREEN);
int h = GetSystemMetrics(SM_CYVIRTUALSCREEN);
return { left,top,w,h };
}
// 抓取桌面图像到 *pimg 对象中
void CaptureDesktop(IMAGE* pimg)
{
Resize(pimg, s.w, s.h);
HDC srcDC = ::GetDC(NULL);
HDC dstDC = GetImageHDC(pimg);
BitBlt(dstDC, 0, 0, s.w, s.h, srcDC, s.left, s.top, SRCCOPY);
}
// 图片拉伸
// width, height 拉伸后的图片大小
// img 原图像
void ImageToSize(int width, int height, IMAGE* img)
{
IMAGE* pOldImage = GetWorkingImage();
SetWorkingImage(img);
IMAGE temp_image(width, height);
StretchBlt(
GetImageHDC(&temp_image), 0, 0, width, height,
GetImageHDC(img), 0, 0,
getwidth(), getheight(),
SRCCOPY
);
Resize(img, width, height);
putimage(0, 0, &temp_image);
SetWorkingImage(pOldImage);
}
// 是否为边缘点
// b 灰度差阈值
bool isEdgePoint(DWORD* pBuf, int w, int h, int x, int y, int b)
{
POINT t[4] = { {0,1}, {0,-1}, {1,0}, {-1,0} };
int r = GetRValue(pBuf[y * w + x]);
for (int i = 0; i < 4; i++)
{
int x2 = x + t[i].x, y2 = y + t[i].y;
if (x2 >= 0 && x2 < w && y2 >= 0 && y2 < h && GetRValue(pBuf[y2 * w + x2]) - r > b)
{
return true;
}
}
return false;
}
// 彩色图像转换为灰度图像
void ColorToGray(IMAGE* pimg)
{
DWORD* p = GetImageBuffer(pimg); // 获取显示缓冲区指针
COLORREF c;
// 逐个像素点读取计算
for (int i = pimg->getwidth() * pimg->getheight() - 1; i >= 0; i--)
{
c = BGR(p[i]);
c = (GetRValue(c) * 299 + GetGValue(c) * 587 + GetBValue(c) * 114 + 500) / 1000;
p[i] = RGB(c, c, c); // 由于是灰度值,无需再执行 BGR 转换
}
}
// 将图像处理为轮廓图像并绘制
// img 原图像
// b 轮廓扫描阈值
void ImageProcess(IMAGE* img, int b = 30)
{
IMAGE imgGray = *img;
int w = img->getwidth();
int h = img->getheight();
POINT* pEdge = new POINT[w * h];
int sum = 0;
ColorToGray(&imgGray);
DWORD* pBuf = GetImageBuffer(&imgGray);
for (int i = 0; i < h; i++)
{
for (int j = 0; j < w; j++)
{
if (isEdgePoint(pBuf, w, h, j, i, b))
{
pEdge[sum] = { j, i };
sum++;
}
}
}
//putimage(0, 0, img);
cleardevice();
for (int i = 0; i < sum; i++)
{
putpixel((int)(pEdge[i].x * ratio), (int)(pEdge[i].y * ratio), GREEN);
}
FlushBatchDraw();
if (pEdge && sum)
{
delete pEdge;
}
}
// 处理鼠标消息
// 简单模拟鼠标操作
void MouseMsgProcess()
{
ExMessage msg;
while (peekmessage(&msg, EM_MOUSE))
{
if (msg.message == WM_LBUTTONUP)
{
// 坐标映射到系统,兼容多屏情况
SetCursorPos((int)(msg.x / ratio) + s.left, (int)(msg.y / ratio) + s.top);
mouse_event(MOUSEEVENTF_LEFTDOWN, 0, 0, 0, 0);
mouse_event(MOUSEEVENTF_LEFTUP, 0, 0, 0, 0);
}
}
}
// 主函数
int main()
{
s = GetScreenSize();
// 创建绘图窗口
initgraph((int)(s.w * ratio), (int)(s.h * ratio));
BeginBatchDraw();
// 定义 IMAGE 对象
IMAGE img;
while (true)
{
// 调用抓取桌面图像的函数
CaptureDesktop(&img);
// 输出图像
ImageProcess(&img);
// 响应鼠标消息
MouseMsgProcess();
Sleep (10);
}
// 按任意键退出
getmessage(EM_KEY);
EndBatchDraw();
closegraph();
return 0;
}