文档章节

Inkpad绘图原理浅析

云贵高原
 云贵高原
发布于 2014/01/06 15:06
字数 1812
阅读 3348
收藏 50
点赞 6
评论 7

Inkpad是一款非常优秀的iPad矢量绘图软件,保管你一看见就忘不了。我的感觉是”一览众山小”、”相见甚晚”,以至于我写的TouchVG就是”小巫见大巫”。必须好好学习这款软件的代码,破解其高性能绘图奥秘。

如果你阅读本文觉得哪里写得不明白,可以提出来交流,如果本文能帮助你一点点就OK了,我也是在学习,本意不是想写漂亮的文章。

一、触摸交互绘图

交互式绘图当然得先看触摸响应机制,先看一张序列图:

触摸绘图响应序列图

WDCanvas是代表绘图画布的主视图,直接响应原始触摸事件(touchesBegan、touchesMoved等),没有使用双击、旋转、长按等很流行的手势识别器,奇怪吧?
我猜想Inkpad不使用手势识别器有两个原因:(1)手势识别器采用延迟识别技术进行手势二义性判断,有几百毫秒的延迟,会影响绘图快速响应的感觉;(2)Inkpad是独立的程序,所有界面都是自己的,不需要与各种带有手势识别器的界面组件共存(例如在滚动视图、电子书页面中绘图)。     


在WDCanvas中通过”[[WDToolManager sharedInstance].activeTool touchesBegan:touches withEvent:event inCanvas:self]”向当前命令传递触摸事件,这当然用的是单实例模式命令模式了。  

 
在WDCanvas的touchesBegan函数中不立即向交互命令WDTool发送触摸开始事件,而是延迟到touchesMoved才去发送。我猜想作者本想区分普通轻击(Tap)和拖动(Pan),但在实现时并不彻底:在下面的touchesEnded函数片段中,没有移动也是要发送touchesBegan的。我认为应该在touchesMoved中检查移动距离来判断是否算是已移动,而不是简单的直接切换到已移动状态。

if (!controlGesture_ && [self canSendTouchToActiveTool]) {
    if (!moved_) {
        [[WDToolManager sharedInstance].activeTool touchesBegan:touches withEvent:event inCanvas:self];
    }
    [[WDToolManager sharedInstance].activeTool touchesEnded:touches withEvent:event inCanvas:self];
}

 

在开始触摸、当前不是钢笔命令(WDPenTool)时,设置WDCanvas的activePath为空(self.drawingController.activePath = nil)。当前路径(activePath)用于记忆钢笔命令的当前图形路径,将activePath定义在WDDrawingController中而不是定义在WDPenTool中,是方便于在多个类中检查使用,否则要到特定的命令类获取数据太麻烦了。

交互命令类的触摸响应函数使用了如下所示的模板方法模式,具体的命令类重写beginWithEvent、moveWithEvent、endWithEvent函数实现具体的绘图逻辑。

- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event inCanvas:(WDCanvas *)canvas
{
    //.....
    WDEvent *genericEvent = [self genericEventForTouch:primaryTouch_ inCanvas:canvas];
    [self beginWithEvent:genericEvent inCanvas:canvas];
    self.previousEvent = genericEvent;
    //......
}
    
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event inCanvas:(WDCanvas *)canvas
{
    //......
    [self moveWithEvent:genericEvent inCanvas:canvas];
    self.previousEvent = genericEvent;
    //......
}
    
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event inCanvas:(WDCanvas *)canvas
{
    [self endWithEvent:genericEvent inCanvas:canvas];
}
    
- (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event inCanvas:(WDCanvas *)canvas
{
    [self touchesEnded:touches withEvent:event inCanvas:canvas];
}

下面就要说说Inkpad的一大亮点:在动态拖曳绘图中使用OpenGL ES高速绘图了!

在绘图命令(例如可绘制多种图形的WDShapeTool)中,设置WDCanvas的shapeUnderConstruction属性(待构建的图形,WDPath对象),将当前的临时图形(每次都构造一个WDPath对象)传递给WDCanvas。WDCanvas的setShapeUnderConstruction函数将调用WDSelectionView成员的立即绘制函数,WDSelectionView使用OpenGL ES 1.1绘制各种动态图形。用了OpenGL ES,拖动上千图形也回显毫无延迟感觉,这是Inkpad的亮点。

(我觉得光把这段OpenGL ES渲染矢量图形、文字的类提取出来都足够实现一个很炫的动画显示软件了吧)

Inkpad在使用OpenGL ES 1.1绘制动态图形时,采用的优化显示方法有:(1)单点线宽,(2)忽略虚线样式,(3)不填充,(4)缓存图形轮廓路径。Inkpad对显示性能是下了狠工的。

 

触摸操作完成后就要向文档提交静态图形了:(1)绘图命令在endWithEvent中向当前层(WDLayer)提交新的图形对象([canvas.drawing addObject:path]);(2)WDDrawingController 触发WDCanvas视图的重绘消息;(3)在WDCanvas的drawRect:函数中,遍历各个图形,调用各个对象的renderInContext函数显示所有图形,其实质是将图形的path显示到当前CGContext上(看似普通)。

与动态图形的绘制相比,提交静态图形并没有采用OpenGL ES,而是使用最简单的CGContext显示方式,而且是重画全部图形,没有使用CALayer等高级渲染方式。这就产生一个疑问,大量图形会不会太慢?我还没去做性能分析实验,初步估计Inkpad采用的显示优化方法是:(1)缓存CGPath轮廓路径和填充路径等对象;(2)避免几何计算和对象重构。

Inkpad已经这么好了,就需要重新评估TouchVG最近做的一些显示优化实验,看看这些计算是否有必要:(1)在GCD中异步绘制,使用前台图形列表和后台图形列表避免多线程冲突;(2)在CALayer上提前绘制,在主视图中只贴图(使用renderInContext,利用GPU贴图);(3)每一层图形一个CALayer,避免重绘所有图形;(4)是否有必要使用OpenGL ES 2.0绘制静态图形?

 

二、相关核心类

Inkpad绘图核心类

1、WDCanvas:绘图主视图,包含标尺视图、选择集渲染视图、删除提示线视图,包含交互命令类要用的临时图形对象。

2、WDCanvasController:绘图界面操作类,负责多种UI控件的管理和操作分发。

3、WDDrawingController:负责各种图形的增删改查逻辑。WDCanvasController是外壳功能,WDDrawingController是内核功能。

4、WDDrawing:图形文档类,容纳所有绘图内容。

5、WDLayer:一个层,包含多个图形元素WDElement。

6、WDStylable:可描边、填充的图形的基类。

7、WDAbstractPath:具有矢量路径的图形的基类。

8、WDPath:可指定线端箭头形状的矢量路径的图形类。

9、WDCompoundPath:复合路径的图形类。

 

10、WDTool:命令基类。

11、WDShapeTool:添加几何形状的命令,可绘制矩形、椭圆、星形、多边形、直线段、螺旋线。这些不同图形的绘图工具是在WDToolManager中构造的。

12、WDPenTool:贝塞尔曲线绘图工具。

13、WDFreehandTool:自由画光滑曲线工具。

 

对于蓝色部分的Core.Model类,主要基于矢量路径设计了几何图形模型类,好像不包含图形的特征数据(即后续编辑保持形状特征)吧。

我觉得我们可以基于Inkpad对图形种类和交互命令工具进行扩充,做点行业相关的软件来,如果你感兴趣就加入讨论吧。

这两个UML图是用EA画的,InkpadUml.xmi可以导入到其他UML建模工具。

 

写了半天有点累了,先写到这吧。期望目标是把Inkpad吃透,结合TouchVG和TouchVGAnimation创造出一个新框架或软件:)

© 著作权归作者所有

共有 人打赏支持
云贵高原

云贵高原

粉丝 82
博文 36
码字总数 21429
作品 12
海淀
技术主管
加载中

评论(7)

云贵高原
云贵高原

引用来自“TianPad”的评论

学习了,看来那段“OpenGL ES绘制矢量图形”是核心。想想也是,动态绘制时主要是线,等线段绘制完成后,再慢慢异步填充。期待你的新框架。
目前正在翻译解析,见 https://github/rhcad/Inkpad 的demos分支。近期要写一篇Inkpad设计结构和定制开发文章,敬请关注。
TianPad
TianPad
学习了,看来那段“OpenGL ES绘制矢量图形”是核心。想想也是,动态绘制时主要是线,等线段绘制完成后,再慢慢异步填充。期待你的新框架。
mzq63
mzq63
看了两天 脑袋乱哄哄的
云贵高原
云贵高原
Inkpad无阻塞绘图的秘诀:不使用GCD或线程,所有交互和绘制都在主线程,动态拖曳显示使用OpenGL ES 1.1立即绘制,所有要绘制的图形和上下文属性都先算好并缓存到图形对象中。
云贵高原
云贵高原
Inkpad动态绘图的图形存于WDPath的displayNodes属性中(不在命令工具中),是对其nodes属性克隆并加入新BezierNodes而成的。
BugScanner
BugScanner
楼主UML画的很给力
oldfeel
oldfeel
支持支持,虽然我只做android.不过看你的TouchVG很NB的样子...
Inkpad中文翻译已合并到官方项目

今天 Steve Sprang 已合并了[#100][1]提交请求,Inkpad即将在AppStore上发布简体中文版了! 20天前因一个偶然原因启动翻译的: 当晚(周六)我想对iPad上的矢量绘图软件进行交互界面功能分析...

云贵高原 ⋅ 2014/04/04 ⋅ 2

Inkpad文档生成工具--InkpadDoxygen

为矢量绘图软件Inkpad自动生成漂亮清晰的UML图、文件依赖关系图。 目前是在Mac OS X下使用Doxygen自动生成文档,通过脚本优化文件关系。Windows下设置Doxygen和GraphViz的安装位置后也可生成...

云贵高原 ⋅ 2014/04/20 ⋅ 0

少有人知的 GitHub 使用技巧

早上读了“少有人知的 GitHub 使用技巧”一文,看看自己对国外项目提交过多少次代码: SwiftGraphics 39次 Inkpad 5次 MonkSVG 6次 MonkVG 10次 SVGKit 5次 rapidjson 1次 你也来试试吧?...

云贵高原 ⋅ 2014/04/22 ⋅ 0

iPad 矢量插图应用--Inkpad

Inkpad 是一个矢量插图应用,重新为 iPad 而设计。支持路径、复合路径、文本、图像、组、面具、渐变填充,和无限数量的层。 Inkpad 的性能是其设计过程中重点考虑的问题,可轻松处理成全上万的...

红薯 ⋅ 2013/11/09 ⋅ 4

用Doxygen优化Inkpad的模块关系

为了得到Inkpad的模块关系图和设计结构,创建了一个开源项目InkpadDoxygen,使用Doxygen从代码生成目录包含三个图。 得到的模块关系图比较混乱,见 问题108 中的图。 下面就由易到难分成几个...

云贵高原 ⋅ 2014/04/18 ⋅ 0

浅析ElasticSearch原理

浅析ElasticSearch原理 运维派2018-01-051 阅读 ElasticSearch 女主宣言 最近女主在项目中使用到ElasticSearch来做索引。但是对ElasticSearch的一些原理 […] 点赞 ElasticSearch 作者:运维...

运维派 ⋅ 01/05 ⋅ 0

Inkpad —— iPad 上开源的矢量插图应用

Inkpad 是一个矢量插图应用,重新为 iPad 而设计。支持路径、复合路径、文本、图像、组、面具、渐变填充,和无限数量的层。

oschina ⋅ 2013/11/09 ⋅ 0

Spring Boot 2.0 自动配置原理浅析

Spring Boot 2.0 自动配置原理浅析 泥瓦匠BYSocket2017-12-221 阅读 SpringJava技术 本章内容 外化配置和自动配置 自动配置原理浅析 Starter 组件浅析 小结 阅读时间:6 分钟 摘录:至简,记...

泥瓦匠BYSocket ⋅ 2017/12/22 ⋅ 0

Java系列文章(全)

JVM JVM系列:类装载器的体系结构 JVM系列:Class文件检验器 JVM系列:安全管理器 JVM系列:策略文件 Java垃圾回收机制 深入剖析Classloader(一)--类的主动使用与被动使用 深入剖析Classloader(二...

www19 ⋅ 2017/07/04 ⋅ 0

博客导航——一站式搜索(所有博客的汇总帖)

博客导航——一站式搜索 以后博客肯定会越来越多的,所以这做一个整理,方便各位朋友能快速的锁定自己想要的资源 课程 巧用第三方快速开发Android App 热门第三方SDK及框架 Android Studio G...

qq_26787115 ⋅ 2016/01/08 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

zblog2.3版本的asp系统是否可以超越卢松松博客的流量[图]

最近访问zblog官网,发现zlbog-asp2.3版本已经进入测试阶段了,虽然正式版还没有发布,想必也不久了。那么作为aps纵横江湖十多年的今天,blog2.2版本应该已经成熟了,为什么还要发布这个2.3...

原创小博客 ⋅ 29分钟前 ⋅ 0

聊聊spring cloud的HystrixCircuitBreakerConfiguration

序 本文主要研究一下spring cloud的HystrixCircuitBreakerConfiguration HystrixCircuitBreakerConfiguration spring-cloud-netflix-core-2.0.0.RELEASE-sources.jar!/org/springframework/......

go4it ⋅ 53分钟前 ⋅ 0

二分查找

二分查找,也称折半查找、二分搜索,是一种在有序数组中查找某一特定元素的搜索算法。搜素过程从数组的中间元素开始,如果中间元素正好是要查找的元素,则搜素过程结束;如果某一特定元素大于...

人觉非常君 ⋅ 今天 ⋅ 0

VS中使用X64汇编

需要注意的是,在X86项目中,可以使用__asm{}来嵌入汇编代码,但是在X64项目中,再也不能使用__asm{}来编写嵌入式汇编程序了,必须使用专门的.asm汇编文件来编写相应的汇编代码,然后在其它地...

simpower ⋅ 今天 ⋅ 0

ThreadPoolExecutor

ThreadPoolExecutor public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, ......

4rnold ⋅ 昨天 ⋅ 0

Java正无穷大、负无穷大以及NaN

问题来源:用Java代码写了一个计算公式,包含除法和对数和取反,在页面上出现了-infinity,不知道这是什么问题,网上找答案才明白意思是负的无穷大。 思考:为什么会出现这种情况呢?这是哪里...

young_chen ⋅ 昨天 ⋅ 0

前台对中文编码,后台解码

前台:encodeURI(sbzt) 后台:String param = URLDecoder.decode(sbzt,"UTF-8");

west_coast ⋅ 昨天 ⋅ 0

实验楼—MySQL基础课程-挑战3实验报告

按照文档要求创建数据库 sudo sercice mysql startwget http://labfile.oss.aliyuncs.com/courses/9/createdb2.sqlvim /home/shiyanlou/createdb2.sql#查看下数据库代码 代码创建了grade......

zhangjin7 ⋅ 昨天 ⋅ 0

一起读书《深入浅出nodejs》-node模块机制

node 模块机制 前言 说到node,就不免得提到JavaScript。JavaScript自诞生以来,经历了工具类库、组件库、前端框架、前端应用的变迁。通过无数开发人员的努力,JavaScript不断被类聚和抽象,...

小草先森 ⋅ 昨天 ⋅ 0

Java桌球小游戏

其实算不上一个游戏,就是两张图片,不停的重画,改变ball图片的位置。一个左右直线碰撞的,一个有角度碰撞的。 左右直线碰撞 package com.bjsxt.test;import javax.swing.*;import j...

森林之下 ⋅ 昨天 ⋅ 0

没有更多内容

加载失败,请刷新页面

加载更多

下一页

返回顶部
顶部