7

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

 2 years ago
source link: https://www.cnblogs.com/dskin/p/14822862.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
C#搞跨平台桌面UI,分别实现Windows,Mac,Linux屏幕截图 - DSkin - 博客园

随笔- 19  文章- 0  评论- 265  阅读-

13万 

搞跨平台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里的 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;
                }
            }
        }
774637-20210528151531159-223645340.png

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;
        }
774637-20210528151445075-625798216.png

上面的就是CPF的内部实现,一般你不需要管这些细节,直接调用CPF的函数就行了。

最终封装到CPF就是一行代码就行

window.Screen.Screenshot();

要实现QQ截图的效果的话,先截图,再搞个全屏的窗体,把截图内容放到里面,再对屏幕截图进行裁剪,绘图处理。

774637-20210528164151094-906757170.png
774637-20210528162212128-197122558.png

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK