title author date CreateTime categories
WPF 高性能笔
lindexi
2019-1-28 14:21:5 +0800
2018-2-13 17:23:3 +0800
笔迹 WPF

本文告诉大家WPF的INK的实现,和如何做一个高性能的笔。

高性能的笔迹在 WPF 包含两个部分,一个是就是输入,第二个就是渲染。

如果需要经过路由事件才收到输入,如果有人在路由事件做了很多需要很长事件的代码,那么等待用户的路由事件就会使用很长的时间。

如果需要等待主界面的布局也就是如果主线程卡住了,就需要等待主线程才可以渲染。

所以按照原来的元素的输入渲染是无法做到高性能的,那么 WPF 的笔迹是如何做到很快?这里需要用到两个科技,一个就是输入使用 StylusPlugin 一个就是使用另一个 UI 线程解决渲染的速度。

这里说的另一个 UI 线程解决渲染速度而不是使用另一个渲染线程是因为在 WPF 是分开主线程渲染线程,具体请看 WPF 渲染原理。

为什么 Stylusplugin 可以做到高性能?

这个需要从触摸开始讲。在我的另一篇博客有告诉大家从触摸到事件,在 WPF 是通过触摸线程拿到触摸信息。

在触摸线程获取触摸消息的时候,会根据收到的触摸消息转发不同的方法。

在转发的过程,在 WPF 会通过 StylusPlugins 里静态字典,存放用户设置的类。在触摸线程会通过判断触摸点时候在命中对应的元素矩形区判断当前时候命中到这个元素。这里判断命中测试和 WPF 说的命中测试使用的不是同相同的方法,这里只是简单获取每个界面元素的矩形,然后用触摸的点坐标判断是否在这个矩形内,也就是不判断元素是否被其他的元素挡住。所以这个判断方法不需要遍历视觉树,性能相对很高。

这是就为什么使用 StylusPlugin 的获取输入性能比较快。因为这个过程是从触摸线程拿到的,而且触摸线程在执行 StylusPlugin 后才执行到路由事件的代码,使用 StylusPlugin 的速度会比路由事件快很多,加上路由事件需要做命中测试,可能用户会在路由事件做很多事件。而 Stylusplugin 只是从触摸线程拿到,完全不需要等用户在路由事件代码。

下面就是在触摸线程调用 Stylusplugin 的代码

在使用渲染这里用另一个线程做 UI 线程,在 WPF 不是只有主线程可以做 UI 线程,这里的 UI 线程和渲染线程是不相同,因为渲染线程是收集 UI 线程发过来的数据然后才进行渲染。

这里通过 VisualHost 的方法创建一个 UI 线程,在这个线程计算笔迹,然后添加到这个线程的元素,通过这个方式可以在主线程做其他代码的时候还可以快速在用户触摸的时候告诉渲染线程。

在 WPF 的 笔迹是没有额外创建一个线程作为另一个 UI 线程,而是直接将触摸收集线程作为另一个 UI 线程。当然这个方法如果没用好可能就会在用户多个手指书写时无法做到足够高的速度。

如果要做高性能的笔必须要了解 WPF 的触摸和渲染原理,具体请看WPF 渲染原理WPF 触摸到事件

于是下面告诉大家如何做出一个高性能的笔。

本文主要告诉大家如何继承 StylusPlugIn 来做高性能的笔。需要知道 StylusPlugIn 提供了底层的触摸事件,这个事件从 Wisp 进程获得数据然后直接给框架,然后给 UIElement 所以继承StylusPlugIn可以拿到比路由事件更快。

为什么说 StylusPlugIn 拿到比 路由事件更快,这需要了解一下 lnk 的底层。

如果直接从 StylusDown 事件拿到,那么这个事件是经过 WispLogic 和 StylusLogic 处理之后的值才会传给 Stylus.StylusDownEvent ,然后使用路由事件的方式,先经过隧道然后冒泡才到 UIElement ,如果有人在到 UIElement 之前写了代码,或者主线程做了其他不清真的(while xx)那么用户触摸到 UIElement 收到消息就过去很久。

那么StylusPlugIn为什么会比较快,原因是 StylusPlugIn 没有经过那么多处理,也没有经过隧道,而且他可能还不在主线程,不管主线程被写了多少代码,他这个线程都不会被影响。调用的线程级别是输入,除非主线程真的占用整个CPU,不然主线程的代码对这个线程影响很小。

因为 StylusPlugIn 是从 StylusInput 修改来的,所有的 UIElement 都有 StylusPlugIns 属性,但是这个属性是只有继承 UIElement 的类才可以拿到。从 StylusPlugIn 拿到的数据就是系统拿到的 xy 点和触摸压力,还有触摸宽度。但是这里的宽度是需要反射才可以拿到,不是所有的触摸屏都可以报告触摸宽度。

如果需要加入到 StylusPlugIn 首先需要继承 StylusPlugIn

先创建一个类 TtkSwvlypxm 继承 StylusPlugIn ,那么可以通过重写获得

  • OnAdded 被添加时

  • OnRemoved

  • OnStylusEnter 触摸时

  • OnStylusLeave

  • OnStylusDown

  • OnStylusMove

  • OnStylusUp

  • OnStylusDownProcessed 可以判断是否失焦

  • OnStylusUpProcessed

那么在这里类,几乎可以不写代码就获得触摸事件,从这里获得触摸事件比路由会快,因为这里是 rawStylusInput ,没有处理的事件,可以获得触摸宽度和触摸的元素。

那么如何加入这个类?

使用 InkPresenter 创建一个类,这个类用来显示笔迹,之后需要在添加 InkPresenter 的类 上添加事件

例如 SlwqntthSpeswbrj 添加了 InkPresenter ,那么需要使用下面的代码

            var dynamicRenderer = new TtkSwvlypxm();

            dynamicRenderer.Enabled = true;

            SlwqntthSpeswbrj.StylusPlugIns.Add(dynamicRenderer);

这样尝试在触摸时就可以获得触摸事件,因为获得事件比较快,所以性能比较高。

其他的代码因为在公司使用,所以我就不写下来

只要获得了触摸事件,要画出来是很简单。

如果支持多指,其实只需要多创建 TtkSwvlypxm 就可以支持多指

可能存在的问题,刚才有附加的代码 StylusPlugIns.Add ,实际上 StylusPlugIns 是 UIElement 的保护,所以需要写一个函数把这个属性给外面。

如果需要移除,那么请设置dynamicRenderer.Enabled = false; 直接移除会出现直接退出

那么使用 StylusPlugIn 的作用除了做高性能的笔之外还有什么作用?实际上可以看到这个方法可以用来过滤输入,因为他在路由事件之前,而且可以修改点,所以用它来修改过滤。

自己定义的 StylusPlugIn 实际上作为笔迹还是存在很多坑,所以一般都是继承 DynamicRenderer ,这个类对输入做了很多处理,当然也存在一些坑。

参见:Intercepting Input from the Stylus

WPF 渲染原理

WPF 触摸到事件

其他自己写的笔迹算法

2019-1-28-WPF-高性能笔的更多相关文章

  1. WPF 高性能笔

    原文:WPF 高性能笔 本文告诉大家WPF的INK的实现,和如何做一个高性能的笔. 高性能的笔迹在 WPF 包含两个部分,一个是就是输入,第二个就是渲染. 如果需要经过路由事件才收到输入,如果有人在路 ...

  2. 「FFT」题单(upd 2019.4.28)

    持续更新(last upd 2019.4.28) ZJOI2014 力 [题目链接] 解法 对原式进行转换,然后卷积FFT套上去求解就可以了. 推导过程简洁版: \[F_i=\sum_{j<i} ...

  3. Alpha冲刺(5/10)——2019.4.28

    所属课程 软件工程1916|W(福州大学) 作业要求 Alpha冲刺(5/10)--2019.4.28 团队名称 待就业六人组 1.团队信息 团队名称:待就业六人组 团队描述:同舟共济扬帆起,乘风破浪 ...

  4. 2019.3.28&2019.3.30考试

    2019.3.28 : 肥肠爆芡,因为这场考试的题太屑了,所以我咕咕了 Upd on 2019.3.30 压进来一篇(因为都没啥意义) 2019.3.30 : 全机房读错题+没有大样例=T2全体爆炸 ...

  5. 2019.2.28&2019.3.1 考试

    因为没A/改几道题,就一起写了 题目在LOJ上都能找到 2019.2.28 100+20+12 前两个小时一直在睡觉+想题也没思路,我太菜了 T1 洗衣服 分开处理出洗衣服和烘干的时间,然后一边正着排 ...

  6. 梦想MxWeb3D协同设计平台 2019.02.28更新

    梦想MxWeb3D协同设计平台 2019.02.28更新 SDK开发包下载地址: http://www.mxdraw.com/ndetail_10130.html 在线演示网址: http://www ...

  7. Beta冲刺(7/7)——2019.5.28

    所属课程 软件工程1916|W(福州大学) 作业要求 Beta冲刺(7/7)--2019.5.28 团队名称 待就业六人组 1.团队信息 团队名称:待就业六人组 团队描述:同舟共济扬帆起,乘风破浪万里 ...

  8. AI2(App Inventor 2)离线版服务器(2019.04.28更新)

    我们的目标:搭建一个本地多用户的App Inventor 2 服务器   演示: http://ai2.fsyz.net  [旧 win]     http://ai2n.fsyz.net [新 Ce ...

  9. Cheatsheet: 2018 11.01 ~ 2019 02.28

    Golang FromXToGo micro - A microservice toolkit Other Easy parsing of Excel spreadsheet format with ...

  10. 项目Beta冲刺(6/7)(追光的人)(2019.5.28)

    所属课程 软件工程1916 作业要求 Beta冲刺博客汇总 团队名称 追光的人 作业目标 描述Beta冲刺每日的scrum和PM报告两部分 队员学号 队员博客 221600219 小墨 https:/ ...

随机推荐

  1. Envoy的线程模型[翻译]

    Envoy threading Model 关于envoy 代码的底层文档相当稀少.为了解决这个问题我计划编写一系列文档来描述各个子系统的工作.由于是第一篇, 请让我知道你希望其他主题覆盖哪些内容. ...

  2. Nginx配置参数详解参考示例

    user nobody; worker_processes 2; events{ worker_connections 1024; } http{ #设置默认类型为二进制流 default_type ...

  3. spring data mongodb CURD

    一.添加 Spring  Data  MongoDB 的MongoTemplate提供了两种存储文档方式,分别是save和insert方法,这两种的区别: (1)save :我们在新增文档时,如果有一 ...

  4. BZOJ3207 花神的嘲讽计划I

    Time Limit: 10 Sec Memory Limit: 128 MB Summary 给你一个模式串P,q个询问,对每个询问回答从Pl到Pr是否存在与给定串相同的子串,同时有所有的给定串长度 ...

  5. shell编程基础知识3

    1.Linux下scp的用法 scp就是secure copy,一个在linux下用来进行远程拷贝文件的命令.有时我们需要获得远程服务器上的某个文件,该服务器既没有配置ftp服务器,也没有做共享,无法 ...

  6. 2.Javascript 函数(主要)

    定义函数 在JavaScript中,定义函数的方式如下: function abs(x) { if (x >= 0) { return x; } else { return -x; } } 上述 ...

  7. c++使用boost库遍历文件夹

    1.只在当前目录下遍历 #include <boost/filesystem.hpp> string targetPath="/home/test/target"; b ...

  8. Python基础教程(021)--Pycharm简介

    前言 学习Pycharm开发工具 内容 项目:就是一个功能复杂的软件 目标 必须掌握的工具

  9. paper 166:梯度下降法及其Python实现

    参考来源:https://blog.csdn.net/yhao2014/article/details/51554910 梯度下降法(gradient descent),又名最速下降法(steepes ...

  10. BUUCTF | [CISCN2019 华北赛区 Day2 Web1]Hack World

    id=0 id=1 id=2 id=3 发现结果不一样,尝试 : ">4","=4","<4" : 在自己的环境下验证一下: 爆 ...