搞跨平台IM,截图功能少不了。
Windows
创建GDI的兼容位图,把桌面的图像通过BitBlt拷贝到兼容位图里,通过兼容位图的数据指针创建Bitmap对象,由于兼容位图的内存是非托管的,Bitmap无法释放该内存,拷贝一下,把兼容位图的释放掉,新的Bitmap的内存就可以由新Bitmap来自己托管释放。
public override Bitmap Screenshot() { var srcDC = GetDC(IntPtr.Zero); var bounds = Bounds; IntPtr memDc = CreateCompatibleDC(srcDC); BITMAPINFOHEADER info = new BITMAPINFOHEADER(); info.biSize = (uint)Marshal.SizeOf(typeof(BITMAPINFOHEADER)); info.biBitCount = 32; info.biHeight = -(int)bounds.Height; info.biWidth = (int)bounds.Width; info.biPlanes = 1; var hBitmap = CreateDIBSection(memDc, ref info, 0, out var ppvBits, IntPtr.Zero, 0); var oldBits = SelectObject(memDc, hBitmap); BitBlt(memDc, 0, 0, (int)bounds.Width, (int)bounds.Height, srcDC, (int)bounds.X, (int)bounds.Y, TernaryRasterOperations.SRCCOPY); Bitmap temp = new Bitmap((int)bounds.Width, (int)bounds.Height, (int)bounds.Width * 4, PixelFormat.Bgra, ppvBits); Bitmap bitmap = (Bitmap)temp.Clone(); temp.Dispose(); SelectObject(memDc, oldBits); DeleteObject(hBitmap); DeleteDC(memDc); ReleaseDC(IntPtr.Zero, srcDC); return bitmap; }
Mac
直接使用Mac里的 CGWindowListCreateImage 来截图,由于数据格式不同,需要读取像素,一个个设置给Bitmap
Mac里在不同权限选项下截取的图片内容不同的,没有录制权限的情况下只能截取当前程序的界面和空的桌面。
public unsafe override Bitmap Screenshot() { using (var img = new CGImage(CGImage.CGWindowListCreateImage(screen.Frame, CGWindowListOption.All, 0, CGWindowImageOption.Default), owns: true)) { //CGImage imageRef = CGImage.CGWindowListCreateImage(mainRect, kCGWindowListOptionOnScreenOnly, kCGNullWindowID, kCGWindowImageNominalResolution | kCGWindowImageShouldBeOpaque); var height = img.Height; var width = img.Width; var bpr = img.BytesPerRow; var bpp = img.BitsPerPixel; var bpc = img.BitsPerComponent; var bytes_per_pixel = bpp / bpc; using (var data = img.DataProvider.CopyData()) { var bitmap = new Bitmap(img.Width, img.Height); var bytes = (byte*)data.Bytes; using (var b = bitmap.Lock()) { for (var row = 0; row < height; row++) { for (var col = 0; col < width; col++) { var pixel = &bytes[row * bpr + col * bytes_per_pixel]; b.SetPixel(col, row, pixel[3], pixel[2], pixel[1], pixel[0]); } } } return bitmap; } } }
Linux
使用XGetImage来截图,同样需要转换一下位图格式
public override Bitmap Screenshot() { var bounds = Bounds; var image = XGetImage(LinuxPlatform.Platform.Display, LinuxPlatform.Platform.Info.RootWindow, (int)bounds.X, (int)bounds.Y, (int)bounds.Width, (int)bounds.Height, ~0, 2 /* ZPixmap*/); if (image == IntPtr.Zero) { string s = String.Format("XGetImage returned NULL when asked to for a {0}x{1} region block", bounds.Width, bounds.Height); throw new InvalidOperationException(s); } Bitmap bmp = new Bitmap((int)bounds.Width, (int)bounds.Height); var visual = LinuxPlatform.Platform.Info.TransparentVisualInfo; int red, blue, green; int red_mask = (int)visual.red_mask; int blue_mask = (int)visual.blue_mask; int green_mask = (int)visual.green_mask; using (var b = bmp.Lock()) { for (int y = 0; y < bounds.Height; y++) { for (int x = 0; x < bounds.Width; x++) { var pixel = XGetPixel(image, x, y); switch (visual.depth) { case 16: /* 16bbp pixel transformation */ red = (int)((pixel & red_mask) >> 8) & 0xff; green = (int)(((pixel & green_mask) >> 3)) & 0xff; blue = (int)((pixel & blue_mask) << 3) & 0xff; break; case 24: case 32: red = (int)((pixel & red_mask) >> 16) & 0xff; green = (int)(((pixel & green_mask) >> 8)) & 0xff; blue = (int)((pixel & blue_mask)) & 0xff; break; default: string text = string.Format("{0}bbp depth not supported.", visual.depth); throw new NotImplementedException(text); } b.SetPixel(x, y, 255, (byte)red, (byte)green, (byte)blue); } } } XDestroyImage(image); return bmp; }
上面的就是CPF的内部实现,一般你不需要管这些细节,直接调用CPF的函数就行了。
最终封装到CPF就是一行代码就行
window.Screen.Screenshot();
要实现QQ截图的效果的话,先截图,再搞个全屏的窗体,把截图内容放到里面,再对屏幕截图进行裁剪,绘图处理。
签名:<-CPF C# 跨平台桌面UI框架,支持Windows,Mac,Linux,包括XP,国产麒麟Linux等等->
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库