C#搞跨平台桌面UI,分别实现Windows,Mac,Linux屏幕截图

Posted DSkin

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C#搞跨平台桌面UI,分别实现Windows,Mac,Linux屏幕截图相关的知识,希望对你有一定的参考价值。

搞跨平台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截图的效果的话,先截图,再搞个全屏的窗体,把截图内容放到里面,再对屏幕截图进行裁剪,绘图处理。

 

 

 

以上是关于C#搞跨平台桌面UI,分别实现Windows,Mac,Linux屏幕截图的主要内容,如果未能解决你的问题,请参考以下文章

C# 搞桌面UI适配国产麒麟Linux+龙芯遇到的一些坑

C# 搞跨平台UI,封装Cef作为Cpf的控件

在 Windows 10 上具有多个虚拟桌面的 C# UI 自动化 API

flutter: SharedPreferences桌面插件

C# 编写 Windows 动态桌面软件实现之桌面交互功能

CMake学习