zoukankan      html  css  js  c++  java
  • wpf截屏源码

    截图前,先拷贝整个屏幕图像到一个Image中,我们称之为ScreenSnapshot, 然后用户通过鼠标操作,确定一个矩形区域Rect,将Rect传递给函数BitmapSource Clip(Rect r) , Clip函数在ScreenSnapshot上截取Rect对于的那一部分图像,并返回。

    如何截取屏幕图像

    WPF没有内置的函数,但可以借用WinForm的Graphics来完成,其将图像截取并保存在一个System.Drawing.Bitmap上,然后我们使用一个辅助函数将System.Drawing.Bitmap转化为WPF版本的System.Media.Imaging.BitmapSource对象就可以了

    public static Bitmap GetScreenSnapshot()
            {
                Rectangle rc = SystemInformation.VirtualScreen;
                var bitmap = new Bitmap(rc.Width, rc.Height, PixelFormat.Format32bppArgb);
    
                using (Graphics g = Graphics.FromImage(bitmap))
                {
                    g.CopyFromScreen(rc.X, rc.Y, 0, 0, rc.Size, CopyPixelOperation.SourceCopy);
                }
    
                return bitmap;
            }
    
            public static BitmapSource ToBitmapSource(this Bitmap bmp)
            {
                BitmapSource returnSource;
    
                try
                {
                    returnSource = Imaging.CreateBitmapSourceFromHBitmap(bmp.GetHbitmap(),IntPtr.Zero, Int32Rect.Empty, BitmapSizeOptions.FromEmptyOptions());
                }
                catch
                {
                    returnSource = null;
                }
    
                return returnSource;
    
            }

            如何绘制选择框

    所谓选择框,就是用户拖拽鼠标时显示的那个框选线框,专业一点的术语叫rubber band

    我最开始的做法是在OnRender函数中drawingContext.DrawingRectangle(....) ,对于单屏显示器而言,效果还不错,但在双屏(1920x1200x2)上效果很差,明显的滞后感,另外一位帅哥在“OnRender is not as cheap as OnPaint”中也说到了该问题,总结了下面一段话,让人看了很伤心:

    It is ok to use OnRender for things that won’t change, but what you render changes on MouseOver or SizeChanged, you’ll may be causing a layout perf problem for your app.
    The way to avoid the OnRender tax is to predefine your UI in a template, then for the bits that change, use triggers within the template to change it.

    不过,这个问题可以轻松绕过去:不就想画一个框吗?给你一个框(System.Windows.Shapes.Rectangle)便是。也就是这个框不是靠我们先前的DrawingContext绘制出来的,而是作为一个子控件添加进去的

    至于何时去刷新选择框的大小和位置嘛,你可以再用户鼠标拖拽时立即更新,也可以定时更新,我选择的后者,当然,不是自己写定时器,而是才用了CompositionTarget.Rendering事件, 这是一个帧回调,这里的帧就是FPS(frame per second)中的F,所以,这个刷得到多快,就取决于你计算机的FPS了

    如何实现遮罩和镂空效果

    遮罩很容易实现:在你要遮盖的东西上放置一个半透明控件(比如canvas)就可以了,不过,不要使用让被遮盖物体半透明的方式来实现,比如窗口半透明了,窗口上的子控件也会半透明显示,这不是我们所需要的,我们那个选择框就不是半透的。

    镂空嘛,以前会有很学院派的想法,然需要镂空的区域使用OpacityMask或者Xor画刷刷一下不就OK了么?No,太学院派了,费力不讨好。OpacityMask效率很低啦。

    实际情况是这样的,如下图, 用四个面板(图中的绿色,橙色,淡蓝,紫色)排列在一起,中间留个洞就可以了,那四个面板共同构成我们的半透明遮罩。

    当用户拖拽鼠标时,我们重新排列那四个遮罩面板来改变中间镂空区域的大小和位置,用户就会感觉真的像是在屏幕上画了洞。

    如何根据用户拖拽区域截图

           

    internal BitmapSource CopyFromScreenSnapshot(Rect region)
            {
                var sourceRect = region.ToRectangle();
                var destRect = new Rectangle(0, 0, sourceRect.Width, sourceRect.Height);
    
                if (screenSnapshot != null)
                {
                    var bitmap = new Bitmap(sourceRect.Width, sourceRect.Height, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
                    using (Graphics g = Graphics.FromImage(bitmap))
                    {
                        g.DrawImage(screenSnapshot, destRect, sourceRect, GraphicsUnit.Pixel);
                    }
    
                    return bitmap.ToBitmapSource();
                }
    
                return null;
            }

    这就非常简单了,同样利用Graphics对象在先前的ScreenSnapshot上截取一部分就可以了

    转载:http://hi.baidu.com/%BF%B5%BC%D2%D7%AF%D4%B0/blog/item/694ebc3278fa48f61a4cff99.html

  • 相关阅读:
    基于Python的人脸动漫转换
    let 与 var的区别
    【LeetCode】汇总
    【HDU】4632 Palindrome subsequence(回文子串的个数)
    【算法】均匀的生成圆内的随机点
    【LeetCode】725. Split Linked List in Parts
    【LeetCode】445. Add Two Numbers II
    【LeetCode】437. Path Sum III
    【LeetCode】222. Count Complete Tree Nodes
    【LeetCode】124. Binary Tree Maximum Path Sum
  • 原文地址:https://www.cnblogs.com/sunhappy0318/p/2526630.html
Copyright © 2011-2022 走看看