zoukankan      html  css  js  c++  java
  • 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截图的效果的话,先截图,再搞个全屏的窗体,把截图内容放到里面,再对屏幕截图进行裁剪,绘图处理。

    签名:<-CPF C# 跨平台桌面UI框架,支持Windows,Mac,Linux,包括XP,国产麒麟Linux等等->
  • 相关阅读:
    一些基础的前端技术面试问题
    HTTP与HTTPS区别
    XML与JSON
    js的内置对象
    二分法查找
    img下面的留白解决
    大家好
    HEOI2020游记
    CSP-S2019游记
    省选模拟八十六 题解
  • 原文地址:https://www.cnblogs.com/dskin/p/14822862.html
Copyright © 2011-2022 走看看