刚刚做完的一个屏幕截图程序,分享一下

周银辉

好长时间没有更新博客了啊,把绝大多数时间花在了那个开源的WPFToolkit 和 《深入理解操作系统》上, 然后见缝插针地做了这个截图程序。

你可以在这里下载DEMO程序试试效果(我感觉还行~)

拖拽的效果和Windows7自带的snipping tool 差不多,拖拽区域之外是半透明遮罩,拖拽区域之内被镂空的,但其拖拽完成后并不立即截图,你可拖拽手柄来重新调节截图区域,然后双击截图区域,完成截图

1, 选用C++,WinForm还是WPF来完成该程序

选用C++来做的话,我们可以很方便地用bitblt函数来进行屏幕图像的拷贝,似乎大多数截图程序都是这么干的

选用WinForm的话,可以采用Graphics对象的CopyFromScreen函数来屏幕图像的拷贝,这也很方便,不过其相对于WPF更大的好处在于,GDI+是实时绘图的,在你绘制上图中的那个蓝色框时不会有明显的滞后感,特别是对应高分辨率多显示器这样的环境下,WPF的OnRender函数的滞后感是很严重的

选用WPF嘛,从程序本身看,没什么好处,并且WPF貌似没有截图的API吧~~ 况且还有上面所说的滞后感呢。

但我还是选择了WPF,原因是,需要和其他WPF应用集成,我希望是清一色的WPF。洁癖??不是啦,主要的原因还是想偷懒,因为以前做过一个ImageEditor控件,其中的拖拽控件(就是上图的蓝色框)是可以重用的,至于刚才所说的弊端嘛,有办法可以绕过去。

2, 基本思路

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

3, 如何截取屏幕图像

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;

}

4,如何绘制选择框

所谓选择框,就是用户拖拽鼠标时显示的那个框选线框,专业一点的术语叫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了

5, 如何实现遮罩和镂空效果

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

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

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

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

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

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

        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;
        }

7, 其他的

省着点用CompositionTarget.Rendering事件,也就是说,让其回调函数的效率尽量的高,因为其会被不间断地频繁调用。

刚刚做完的一个屏幕截图程序,分享一下相关推荐

  1. 借助libbpf/libxdp使用AF_XDP,我们都需要做什么——以一个简单程序为例

    前言 AF_XDP是一种Linux提供的针对高性能数据包处理进行优化的地址族协议,为了进一步的理解和熟悉,我们以一个返回IPv4 ICMP Ping报文的程序为例,看看借助libbpf/libxdp使 ...

  2. 实现一个小程序分享图 wxml2canvas

    我们经常会遇上动态生成海报的需求,而在小程序中,生成图片非Canvas莫属.但是在实际工作当中,为了追求效率,我们会不可避免地去使用一些JS插件,而 wxml-to-canvas 就是一款官方推荐且非 ...

  3. 如果做不到一个好程序员,建议别满嘴跑火车

    之前公司有个同事,满嘴跑火车,说他朋友多么牛逼,什么月薪多少K,什么技术最牛逼,之类的.每次听他说话我都只能微微一笑以作回应.后来项目上线后出了点问题,让他排查,结果他一点头绪都没.遇到报错,他就一顿 ...

  4. 自已做的第一个autoconf程序(不断完善中)

    2019独角兽企业重金招聘Python工程师标准>>> 1.先写个简单的main函数,并按如下目录结构存放: timesync-- |--conf |--include |--lib ...

  5. c语言程序执行完main函数后,一个c程序在执行main函数之前和main之后都做了那些事情啊该如何解决...

    当前位置:我的异常网» C语言 » 一个c程序在执行main函数之前和main之后都做了那些 一个c程序在执行main函数之前和main之后都做了那些事情啊该如何解决 www.myexceptions ...

  6. 艾永亮:产品做完后,第一步就是马上推广吗?

    前几天,我在外面偶遇了一位许久未见的好友. 难得偶遇,我们便一起到一家咖啡厅坐下聊聊天,互道衷肠. 不聊不知道,这一聊,倒让我发现一些问题. 这些问题,不仅仅正在他身上发生,很多企业家也同样面临着. ...

  7. 一个屌丝程序猿的人生(五十一)

    林萧躺在床上,回想起当初和安玲聪的种种往事,不由得有些感慨.此时所发生的事,和当初是何其相似,唯一不同的是,当初安玲聪是自己眼睁睁看着奔向对方怀抱的,而欧阳晓,则是此刻已经躺在了对方的怀里. 那一晚, ...

  8. 抖音小程序实践四:实现小程序分享

    有时候我们要把一个小程序分享给别人,去看套餐.买东西之类的,是一个很常见的功能,但是在接入抖音小程序的时候,初始化右上角三个点并没有分享的入口,那看来不是要申请,就是有别的开发的口子了.下面我们一起了 ...

  9. 上月和本月对比叫什么_线雕前后对比,线雕做完后要注意什么事项即将上线

    线雕前后对比,线雕做完后要注意什么事项即将上线,线雕前后对比还是比较明显的,因为线雕是利用可吸收的蛋白线,通过耳前或者额头置入到面部皮下的相应层次,蛋白线上有相应的倒刺,利用蛋白倒刺向后.向上提拉相应 ...

最新文章

  1. [转] javaweb学习-jstl-c:forEach中 varStatus的属性简介
  2. 大型计算机主机通常采用,全国计算机一级选择题真题集
  3. jira图片_JIRA使用不求人-从菜鸟到高手
  4. C语言程序程序设计—数组
  5. 2个字段并在一次插入一个字段里面_elasticsearch外用与内观(二)-当插入文档时,elasticsearch都在做什么...
  6. hash ruby 定义 嵌套_【译】rails的嵌套属性(Nested Attributes)使用
  7. C语言程序设计谭浩强第五版复习梳理3
  8. 毕设-基于SpringBoot宠物医院管理系统
  9. 第九届蓝桥杯,赛后感!!含泪写完。
  10. Postgresql动态执行EXECUTING语法解析过程
  11. 蓝牙锁定计算机,win7怎么设置蓝牙加密狗|win7蓝牙加密狗的使用方法
  12. 离散型Hopfield神经网络(DHNN)
  13. rasp java tomcat_Java安全之JNI绕过RASP
  14. MOS管当开关控制时,一般用PMOS做上管NMOS做下管的原因
  15. MATLAB 数据分析方法(第2版)2.3 数据变换
  16. widerperson数据集转voc
  17. 几种常见波形的傅里叶级数展开式
  18. nacos server 安装报错 macOS 10.12.6
  19. jzoj3823 遇见 [高斯消元解异或方程组]
  20. java gif 帧_修复Java中动画gif的帧频

热门文章

  1. OAuth2.0_授权服务配置_Spring Security OAuth2.0认证授权---springcloud工作笔记140
  2. java图像处理002---JAVA图片压缩_图片缩放_图片按照比例缩放_图片指定长宽缩放_Java使用google开源工具Thumbnailator实现图片压缩
  3. EJB3.0异常总结--- Need to specify class name in environment or system property,
  4. c语言中static使用方法
  5. Linux查看版本信息及CPU内核、型号等
  6. sed命令 linux下的超强查找和替换命令
  7. ospf配置方法及相关问题
  8. linux进程通信system v,【linux高级程序设计】(第十一章)System V进程间通信 4
  9. python 仿真模拟_Python SimPy 仿真系列 (1)
  10. 量子计算机迷宫,工程杰作诞生可编程的光学量子计算机