本发明和下面的一起待批准的每个都在2002年6月27日提交的美国专利申请:序列号No.10/184,795,标题为“多层次图形处理系统和方法”;序列号No.10/184,796,标题为“场景图形的通用参数化”;序列号No.10/185,775,标题为“直接模式图形的智能缓存数据结构”;及与此同时提交的标题为“可视及场景图形接口”(律师代理流水号No.3470)的美国专利申请相关。每个相关的申请都是指派给本专利申请的代理人的并因此包含在这里全体引用。
具体实施方式
示范性的操作环境
图1展示了一个适合的计算系统环境100的例子,本发明可以实现在其中。计算系统环境100仅为适合的计算环境的一个例子并不意味着对本发明的使用范围或功能的任何限制。也不应将计算环境100解释为与在示范性操作环境100中展示的组件的任何一个或组合有任何的依赖性或为必要条件。
本发明可以工作在大量其他的通用或专用计算系统环境或配置中。可以适合与使用本发明的众所周知的计算系统、环境和/或配置的例子包括,但不仅限于,个人计算机、服务器计算机、手持设备或膝上设备、平板设备,多处理器系统、基于微处理器的系统、机顶盒、可编程消费者电子设备、网络PC、小型机、大型机、包括任意上述系统或设备的分布式计算机环境,及类似设备。
本发明可以在计算机可执行指令的通用的语境中说明,如由一个计算机执行的程序模块。通常,程序模块包括子程序、程序、对象、组件、数据结构等等,执行特定的任务或实现特定的抽象数据类型。本发明也可以应用在任务由通过一个通讯网络连接的远程处理设备执行的的分布式计算环境中。在一个分布式计算环境中,程序模块可以位于本地和远程计算机存储媒体,包括存储器存储设备。
参考图1,一个实现本发明的示范性的系统包括形式为一个计算机110的一个通用计算设备。计算机110的组件可以包括,但不仅限于,一个处理单元120、一个系统存储器130,及连接不同的系统组件,包括系统存储器到处理单元120的一个系统总线121。系统总线121可以为几种类型的总线结构中的任何一种,包括一个存储器总线或存储器控制器、一个外围设备总线,及使用多种总线结构中的任何一种的一个本地总线。作为例子,而非限制,这样的结构包括工业标准结构(ISA)总线、微通道结构(MCA)总线、增强的ISA(EISA)总线、视频电子标准协会(VESA)本地总线、加速图形接口(AGP)总线,及也称为中层楼总线的周边元件扩展接口(PCI)总线。
计算机110通常包括各种计算机可读媒体。这样的媒体可以为能由计算机110访问的任何可用的媒体,并包括易失的和非易失的媒体、可移动的和不可移动的媒体。作为例子,而非限制,计算机可读媒体可以包括计算机存储媒体和通讯媒体。计算机存储媒体包括以任何存储信息的方法或技术,如计算机可读指令、数据结构、程序模块或其他数据,实现的易失的和非易失的媒体、可移动的和不可移动的媒体。计算机存储媒体包括,但不仅限于,RAM、ROM、EEPROM、闪存或其他存储器技术、CD-ROM、数字多功能盘(DVD)或其他光盘存储、盒式磁带、磁带、磁盘存储或其他磁存储设备,或任何其他可以用来存储所需的信息并且能够由计算机110访问的媒体。通讯媒体通常包括计算机可读指令、数据结构、程序模块或其他以调制过的数据信号表达的数据,如载波或其他传输机制,也包括其他任何信息传输媒体。术语“调制过的数据信号”是一种信号,它的一个或多个特性被设置或改变用以在信号中对信息进行编码。作为例子,而非限制,通讯媒体包括以声音、RF、红外线或其他载体实现的有线或无线的技术。上述任何媒体的组合应包括在计算机可读媒体的范围内。
系统内存130包括形式为可移动和/或不可移动、易失的和/或不易失的存储器的计算机存储媒体如只读存储器(ROM)131和随机存取存储器(RAM)132。包含基本的子程序来帮助在计算机110的元件之间传输信息,如在启动过程中的基本输入/输出系统133(BIOS)一般存储在ROM 131中。RAM 132通常包含需要能够马上访问到和/或正在由处理单元120处理的数据和/或程序模块。作为例子,而非限制,图1用图示说明操作系统134、应用程序135、其他程序模块136和程序数据137。
计算机110可以包含其他的可移动的/不可移动的、易失的/不易失的计算机存储媒体。仅作为例子,图1展示了一个读写不可移动的、不易失的磁媒体的硬盘驱动器141,一个读写可移动的、不易失的磁盘152的磁盘驱动器151,一个读写可移动的、不易失的光盘156的光盘驱动器155如CD-ROM或其他光学媒体。其他可以用在示范性的操作环境里的可移动的/不可移动的、易失的/不易失的存储媒体包括,但不仅限于,磁带和盒式磁带、闪存卡、数字式多用途盘、数字视频磁带、固态的RAM、固态的ROM等等。硬盘驱动器141通常与系统总线121通过不可移动的存储器界面相连,如界面140,以及磁盘驱动器151和光盘驱动器165通常与系统总线121通过可移动的存储器界面相连,如界面150。
上面在图1中展示的驱动器以及它们相关的计算机存储媒体为计算机110存储计算机可读指令、数据结构、程序模块和其他数据。在图1中,例如,硬盘驱动器141用来存储操作系统144、应用程序145、其他程序模块146以及程序数据147。注意这些组件既可以与操作系统134、应用程序135、其他程序模块136以及程序数据137一样也可以与它们不同。操作系统144、应用程序145、其他程序模块146以及程序数据147用不同的数字标出用来展示,至少它们是不同的拷贝。一个用户可以通过输入设备,如一个平板(电子数字转换器)164、一个麦克风163、一个键盘162和定点设备161,通常称为鼠标、轨迹球或触摸垫,提供命令和信息到计算机110中。其他输入设备(未标出)可以包括一个操纵杆、游戏垫、圆盘式卫星电视天线、扫描仪或类似设备。这些和其他输入设备通过一个连接到总线的用户输入接口160连接到处理单元120,但也可以通过其他接口和总线结构,如并行口、游戏口,或一个通用串行总线(USB)来连接。一个显示器191或其他显示设备也通过一个接口,如视频适配器190与系统总线121连接。显示器191也可以集成和一个触摸屏面板193或类似的可以通过一个接口,如一个触摸屏接口192,输入数字化的输入,如手写输入,到计算机系统110中的设备。注意显示器和/或触摸屏面板可以物理上连接到一个包含计算设备110的外壳,如在一个平板类型的个人计算机中,其中触摸屏面板192本质上作为平板164。另外,计算机如计算设备110还可以包括通过输出周边接口194或类似的接口连接的其他周边输出设备(未标出),如音箱195和打印机196。
计算机110可以使用到一个或多个远程计算机,如远程计算机180的逻辑连接在一个网络环境中运行。远程计算机180可以是个人计算机、服务器、路由器、网络PC、对等设备或其他通用网络节点,并通常包括多个或所有上述相对于计算机110的组件,虽然只有一个存储器存储设备181在图1中展示。图1中所示逻辑连接是一个局域网(LAN)171和一个广域网(WAN)173,但也可以包括其他网络。这样的网络环境在办公室范围或企业范围的计算机网络、企业内部互联网和因特网中是很常见的。
当用在一个局域网联网环境中,计算机110通过一个网络接口或适配器170与局域网171相连。当用在一个广域网联网环境中,计算机110通常包括一个调制解调器172或其他在广域网173上建立通讯的方法。调制解调器172,可以为内置的或外置的,通过用户输入接口160或其他适合的机制连接到系统总线121。在一个网络环境中,所述相对于计算机110的程序模块,或其部分,可以存储在远程存储器存储设备中。作为例子,而非限制,如图1所示,远程应用程序185可以驻留在远程计算机181的一个存储器设备中。应理解所示网络连接仅为示范性的,也可以使用其他在计算机之间建立通讯连接的方法。
图形体系结构
本发明的一个发明总的来说集中于允许程序代码,如一个应用或操作系统组件,传输绘画指令和其他信息(例如,图像位图)到图形组件从而在系统显示器上呈现图形输出。这里,本发明提供一种标记语言和一组形状元素和其他元素,一个分组和合成系统,及与在一个对象模型中的一个通用属性系统集成,从而使程序能够用数据结构、绘画图元(命令),及其他图形相关的数据组装一个场景图形。当被处理时,场景图形生成被显示在屏幕上的图形。
图2表示一个通用的分层体系结构200,本发明可以在其中实现。如图2所示,程序代码202(例如,一个应用程序或操作系统组件或类似组件)可以被开发以一种或多种方法,包括通过成像204,通过矢量图形元素206,和/或通过直接对一个可视应用程序编程接口(API)层212的函数/方法调用输出图形数据。和API层的直接交互在上述一起待批准的标题为“可视及场景图形接口”的专利申请中进一步说明。
总的来说,成像204对程序代码202提供一种机制来加载、编辑和保存图像,例如,位图。这些图像可以由系统的其他部分使用,并也有一种方法来使用图元绘画码来直接画出一个图像。
根据本发明的一个方面,矢量图形元素206提供另一种方法来画出图形,与对象模型的其他部分一致(如下所述)。矢量图形元素206可以通过一种标记语言创建,一个元素/属性系统208和呈现器系统210处理它来对可视API层212进行适合的调用。如下所述,引用附图26,总的来说矢量图形元素206被解析为通过它画出一个场景图形的对象模型的对象,可以按如下所述通过一个元素层次,通过元素/属性系统208和呈现器系统210,或在资源层次以一种更有效的方式提供给场景图形。
在一个实现中,图形层体系结构200包括一个高层次合成和动画引擎214,它包括或关联于一个缓存数据结构216。如下所述,缓存数据结构216包含一个包括根据一个已定义的对象模型管理的分层排列的对象的场景图形。总的来说,可视API层212向程序代码202(及呈现器系统210)提供对缓存数据结构216的一个接口,包括创建对象、打开和关闭对象以提供数据给它们等等的能力。换句话说,高层次合成及动画引擎214暴露出一个统一的媒体API层212,通过它开发者可以表示和图形及媒体有关的意图来显示图形信息,并向一个底层的平台提供足够的信息,使得该平台可以为该程序代码优化硬件的使用。例如,底层平台将负责缓存、资源协商和媒体集成。
在一个实现中,高层次合成和动画引擎214传送一个指令流及可能的其他数据(例如,指向位图的指针)到一个快速的、低层次的合成和动画引擎218。如这里所用,术语“高层次”和“低层次”类似于那些用在其他计算场景种的属于,其中总的来说,一个软件组件相对于一个高层组件越低,该组件越靠近硬件。因此,例如,从高层次合成和动画引擎214发送出的图形信息可以在低层次合成和动画引擎214中接收,其中信息被用来发送图形数据到包括硬件222的图形子系统。
高层次合成和动画引擎214和程序代码202一起建立一个场景图形来表示一个由程序代码202提供的图形场景。例如,每一个将被画出的项可以用能被系统缓存在场景图形数据结构216中的绘画指令来加载。如下所述,有多种方法来确定这个数据结构216,及画出什么。进一步来说,高层次合成和动画引擎214与定时和动画系统220集成来提供声明性(或其他)的动画控制(例如,动画间隔)及定时控制。注意动画系统允许动画值本质上能在系统的任何部分传送,包括,例如,在元素属性层次208,在可视API层222内部,及在任何其他资源中。定时系统暴露在元素及可视层次。
低层次合成和动画引擎218管理合成、动画及随后被提供给图形子系统222的场景的呈现。低层次引擎218合成多个应用的场景的呈现,并通过呈现组件,实现图形实际上到屏幕的呈现。然而,注意有时一些呈现发生在较高的层次是必须和/或有利的。例如,当较低的层服务从多个应用请求时,较高的层对每个应用进行实例化,通过成像机制204在较高的层次执行耗时的或特定于应用的呈现,并传送一个位图的引用到较低的层次,这是可能的。
场景图形对象模型
如下所述,呈现模型由较高层次、基于控制的矢量图形元素206,及由在场景图形数据结构216中使用的可视API层212创建的较低层次对象共享。这在本发明的较高层次元素,及较低层次对象之间提供了显著的相关性。下面说明场景图形对象模型的一个实现。
图3和4各自展示了例子场景图形300和400,包括一个称为可视组件的基对象。总的来说,一个可视组件包括表示对用户的一个虚拟表面、并在显示器上有一个可视的表示的一个对象。如图5所示,一个基类可视组件为其他可视组件类型提供基本的功能,即,可视组件类500是一个衍生出可视组件类型(例如,501-506)的抽象基类。
如图3所示,一个顶层次(或根)可视组件302连接到一个可视组件管理器对象304,可视组件管理器对象也和一个窗口(HWnd)306或类似的在其中为程序代码输出图形数据的单元相关(例如,通过一个句柄)。VisualManager304管理画出顶层次可视组件(及该可视组件的任何子组件)到窗口306上。图6展示VisualManager作为在这里说明的图形系统的对象模型中的一组其他对象620中的一个。
为了绘画,可视组件管理器304按照一个调度程序308的安排处理(例如,遍历或传输)场景图形,并为它的对应的窗口306提供图形指令和其他数据到低层次组件218(图2),例如在美国专利申请序列号No.10/184,795,10/184,796及10/185,755中一般性地说明的那样。场景图形处理将一般由调度程序308以一个相对低于较低层次的组件218和/或图形子系统222的刷新速率的速率来安排。图3展示了一些在顶层次(根)可视组件302之下按层次安排的子可视组件310-315,其中的一些通过绘画上下文316、317(以虚线框表示来说明它们是临时的)表示为已经用各自相关的指令列表318和319被组装,例如,包含绘画图元和其他可视组件。可视组件也可以包含其他属性信息,如在下面的例子可视组件类中所示:
public abstract class Visual:Visual Component{public Transform Transform {get;set;}public float Opacity{get;set;}public BlendMode BlendMode{get;set;}public Geometry Clip {get;set;}public bool Show{get;set;}public HitTestResult HitTest(Point point);public bool IsDescendant(Visual visual);public static Point TransformToDescendant(Visual reference,Visual descendant,Point point);public static Point TransformFromDescendant(Visual reference,Visual descendant,Point point);public Rect CalculateBounds ();//Loose boundspublic Rect CalculateTightBounds();//public bool HitTestable {get;set;}public bool HitTestIgnoreChildren{get;set;}public bool HitTestFinal{get;set;}} |
一个转换,通过转换属性设置,定义了一个可视组件的子图形的坐标系统。在转换之前的坐标系统被称为转换前坐标系统,在转换之后的坐标系统被称为转换后坐标系统,即,一个做了转换的可视组件等价于一个以转换节点作为父节点的可视组件。图7总的来说提供了转换的一个例子,标识相对于一个可视组件的转换前和转换后坐标系统。为了得到或设置一个可视组件的转换,Transform属性会被用到。
注意坐标转换可以通过一种统一的方法应用到所有组件,好像在一个位图中一样。注意这并不意味着转换始终应用于位图,而是所呈现的都被转换等价地影响。作为例子,如果用户用一个1英寸宽的圆头钢笔画一个圆,然后对那个圆应用一个缩放在X方向增加2,钢笔将在左边和右边为2英寸宽而在顶部和底部为1英寸宽。这有时称为一个合成或位图转换(与仅影响几何形状的框架或几何图形缩放相反)。图8A展示缩放转换,一个未转换的图像800出现在左边,用一个非统一的尺度转换后的图像802出现在右边。图8B展示缩放转换,未转换的图像800出现在左边,用几何图形缩放转换后的图像804出现在右边。
根据一个可视组件的坐标转换,TransformToDescendant从参照可视组件转换一个点到一个子代可视组件。点被从参照可视组件的转换后坐标空间转换到子代可视组件的转换后坐标空间。TransformFromDescendant从子代可视组件沿父链向上转换一个点到参照可视组件。点被从子代可视组件的转换后坐标空间转换到参照可视组件的转换后坐标空间。CalculateBounds方法返回Visual的内容在转换后坐标空间中的边界框。注意可以有作为其他选择的API版本,其中允许更加具体的说明以使对一个可视组件如何做转换在一个坐标转换过程中被解释。例如,可以考虑也可以不考虑对参照和子代可视组件的转换。在这中选择中,因而有四中选项,例如,可以从转换前到转换前空间、转换前到转换后空间、转换后到转换前空间,及转换后到转换后空间转换坐标。通用的概念也适用于点击测试,例如,点击测试可以开始于转换前或转换后转换坐标空间,同时点击测试结果可以在转换前或转换后坐标空间。
剪切属性设置(及取得)一个可视组件的剪切区域。任何Geometry(几何图形类在下面引用图12说明)可以被用作一个剪切区域,同时剪切区域被应用在转换后坐标空间。在一个实现中,对剪切区域的缺省设置为null,即不剪切,这可以被认为是一个从(-∞,-∞)到(+∞,+∞)的无限大的剪切矩形。
Opacity属性取得/设置可视组件的不透明度值,使得可视组件的内容基于不透明度值及所选的混合模式混合在绘画表面上。BlendMode属性可以用来设置(或取得)使用的混合模式。例如,一个不透明度(alpha)值可以设置在0.0到1.0之间,设置为模式线性alpha混合,例如,颜色=alpha*前景色+(1.0-alpha)*背景色。其他服务,如特效属性,可以包括在一个可视组件中,例如,模糊、单色等等。
不同的服务(包括转换、不透明度、剪切)可以在一个绘画上下文中进行压入或弹出,同时压入/弹出操作可以嵌套,只要一个弹出调用与一个压入调用匹配。例如,PushTransform(...);PushOpacity(...);PopTransform(...);是非法的,因为在PopTransform调用之前,应调用PopOpacity。
PushTransform方法对一个转换压入。随后的绘画操作根据压入的转换来执行。PopTransform弹出匹配的PushTransform调用压入的转换:
void PushTransform(Transform transform);
void PushTransform(Matrix matrix);
void PopTransform();。
类似地,PushOpacity方法压入一个不透明度值。随后的绘画操作用指定的不透明度值在一个临时表面上呈现,然后组合到场景中。PopOpacity弹出由匹配的PushOpacity调用压入的不透明度值:
void PushOpacity(float opacity);
void PushOpacity(NumberAnimationBase opacity);
void PopOpacity();。
PushClip方法压入一个剪切几何图形。随后的绘画操作被剪切到该几何图形中。剪切应用在转换后空间中。PopClip弹出由匹配的PushClip调用压入的剪切区域:
void PushClip(Geometry clip);
void PopClip();。
注意压入操作可以任意嵌套,只要弹出操作和一个压入操作匹配。例如,下面的调用是合法的:
PushTransform(...);DrawLine(...);PushClip(...);DrawLine(...);PopClip();PushTransform(...);DrawRect(...);PopTransform();PopTransform(); |
点击测试在转换后坐标空间中执行,并返回每个被点击的可点击测试的可视组件的一个标识,例如,当一个钢笔或鼠标单击被检测到时。一个接口的可选版本可以允许点击测试开始在一个相对于点击测试开始于其中的可视组件的转换前坐标空间中。被点击的可视组件以从右到左,深度优先的顺序返回。点击测试可以用不同的标志来控制,包括HitTestable,它确定该可视组件是否为可点击测试的(缺省为真),及HitTestFinal,它确定当可视组件被点击时点击测试是否终止,即,如果一个Visual被点击同时该可视组件的HitTestFinal属性为真,点击测试终止并返回到这一点上收集到的结果(缺省为假)。另一个标志是HitTestIgnoreChildren,它确定当在一个可视组件上执行点击测试时,是否考虑一个可视组件的子组件(缺省为假)。
一个ProxyVisual是一个可以不止一次被加入到场景图形中的可视组件。因为任何由一个ProxyVisual引用的可视组件可以从根通过多条路径达到,读取服务(TransformToDescendent,TransformFromDescendent和HitTest)并不通过一个ProxyVisual来工作。本质上,从任何可视组件到可视组件树的根都有一条规范的路径同时那条路径不包含任何ProxyVisual。
如图5所示,不同类型的可视组件都在对象模型中定义,包括ContainerVisual 501,DrawingVisual 502,ValidationVisual 503,SurfaceVisual 504和HwndVisual 505。下面的表列出一个DrawingVisual的例子方法:
public class DrawingVisual:Visual{public DrawingVisual();public IDrawingContext Open();public IDrawingContext Append();} |
一个DrawingVisual是图形内容(例如,线、文本、图像等等)的一个容器。注意可能加入一个Visual到一个DrawingVisual中,但在一些实现中这是不允许的。DrawingVisual 502包括一个Open方法,它返回一个如下所述,可以用来组装该DrawingVisual,例如,用其他的可视组件和绘画图元的IDrawingContext。在一种实现中,由于下述的不同的原因,一个DrawingVisual只可以被打开一次来组装它的绘画上下文;换句话说,这样的一个DrawingVisual是不可变的。在DrawingVisual被组装后,使用一个Close方法来关闭DrawingVisual,例如,在绘画上下文中。注意一个Open调用可以清除一个可视组件的任何内容(子组件),然而在一个作为其他选择的实现中,提供了一个Append方法,用来以附加到该可视组件的方式打开一个当前的可视组件。换句话说,一个OpenForAppend调用象Open一样工作,除了DrawingVisual的当前内容在打开的时候不会被清空。
下面是如何用一个绘画上下文来组装一个可视组件的一个例子:
ContainerVisual cv1=new ContainerVisual();DrawingVisual dv1=new DrawingVisual(); |
//Open a drawing context.The context//will automatically be closed when//exiting the using block.This will also//replace any contents that might already//be in dv1.using(IDrawingContext dc=dv1.Open()){dc.DrawLine(new Pen(Brushes.Blue),new Point(...),new Point(...));}//Add dv1 to the child collection of cv1cv1.Children.Add(dv1);//Add another arbitrary visual to cv1cv1.Children.Add(someOtherVisual);//Create another DrawingVisualDrawingVisual dv2=new DrawingVisual();using(IDrawingContext dc=dv2.Open()){//This sets up a new coordinate system//where everything is twice as bigdv.PushTransform(new Scale(2.0,2.0));//This line is drawn in the new scaled//coordinate system.dc.DrawLine(new Pen(Brushes.Red),new Point(...),new Point(...));//This reverts to the original coordinate system.dv.PopTransform();dc.DrawLine(new Pen(Brushes.Green),new Point(...),new Point(...));}//Add dv2 to the child collection of cv1;cv1.Children.Add(dv2); |
总的来说,一个ValidationVisual 503概念上类似于一个DrawingVisual,除了一个ValiationVisual当系统请求它被填充,而非当程序代码需要组装它时才被组装。例如,如美国序列号No.10/185,775中的说明,高层次的合成和动画引擎214(图2)在需要资源时可以使场景图形数据无效,如当一个场景图形的部分不可见时。例如,如果部分被滚动出显示屏,被剪切,等等。如果在后来需要这些无效的场景图形数据,被调用的程序代码202将被回调来重画(使有效)场景的无效的部分。到这里,一种典型的使用场合是用一个程序代码建ValidationVisual的子类并覆盖OnValidate方法。当系统调用OnValidate方法时,一个绘画上下文被传入,同时程序使用这个绘画上下文来组装ValidationVisual。
下面的例子展示一种实现一个简单的ValidationVisual的方法,例如,画出一根有特定颜色的直线。线的颜色可以通过调用SetColor来改变。为了强制ValidationVisual更新,SetColor调用Invalidate来强制图形子系统来重新使ValidationVisual有效:
public class MyValidationVisual:ValidationVisual{public override void OnValidate(IDrawingContext dc){dc.DrawLine(m_color,...);}public void SetColor(Color newColor){m_color=color;Invalidate();//Force a redraw of the ValidationVisual to//reflect the color change.}private Color m_color} |
这个例子展示如何使用ValidationVisual:
MyValidationVisual myVV=new MyValidationVisual();container.Children.Add(myVV);myVV.SetColor(new Color(...)); |
图4展示了一个例子场景图形400在其中ContainerVisual和DrawingVisual在一个场景图形中关联,并有相关的形式为绘画图元的数据,(例如,对应于绘画上下文)。ContainerVisual是Visual的一个容器,同时ContainerVisual可以互相嵌套。一个ContainerVisual的子组件可以用从VisualContainer的一个Children属性返回的一个VisualCollection来处理。Visual在VisualCollection里的顺序以呈现Visual的顺序来确定,即,从最小的下标到最大的下标从后往前(绘画顺序)来呈现Visual。例如,用合适的参数用三个绘画可视组件在一个可视组件容器下分层地表示红、绿、蓝矩形,下面的代码将使三个矩形被画出(转换到右和下方),一个红色的矩形在后面,一个绿色的矩形在中间及一个蓝色的矩形在前面:
VisualCollection vc=m_cv.Children;vc.Add(new DrawingVisual());vc.Add(new DrawingVisual());vc.Add(new DrawingVisual());for(int i=0;i<vc.Count;i++) |
{DrawingVisual v=(DrawingVisual)(vc[i]);if(v!=null){v.Transform=Transform.CreateTranslation(i*20.0f,i*20f);IDrawingContext dc=v.Open();dc.DrawRectangle(new Brush(colors[i]),null,new Point2D(0,0),new Point2D(100.0f,100.0f));v.Close(dc);}} |
如图5所示,另一种类型的可视组件对象是SurfaceVisual 504。总的来说,如图3所示,SurfaceVisual对象315引用程序代码202(图2)可以访问的一个内存中的表面(位图)322。客户程序代码202可以提供它自己的表面内存,或者它可以请求由表面对象分配内存。
程序代码202可以选择打开一个SurfaceVisual并得到一个绘画上下文323,程序代码202可以写入象素数据324或类似数据到其中并直接把这些象素放到表面上。这在图3中由表面对象322、绘画上下文323(用虚线框表示它是临时的)和象素数据324之间的虚线表示。
程序代码202还可以选择创建一个表面可视组件管理器330并关联一个可视组件子图形332和SurfaceVisual 315。这种选项在图3中由表面对象322和表面可视组件管理器330之间的虚线表示。注意,也如图3所示,可视组件子图形332也可以嵌套其他的表面可视组件。表面可视组件管理器330(在图6中的集合620中表示为其他对象的一种)遍历可视组件子图形332来更新SurfaceVisual位图322。进一步,注意这个遍历由调度程序308来调度,并为了效率可以被调节以控制这个位图322多长时间更新一次。表面可视组件管理器330并不必须每次都遍历可视组件子图形322并/或以顶层次可视组件管理器302遍历场景图形余下部分的同样的速率。
对表面来说,如引用图9A-9C的进一步说明,总的来说,本图形模型因而允许合成一组可视组件到一个表面,以直接模式呈现矢量和位图图元到一个表面中,合成一个表面到桌面上或到其他表面上,并控制在一个表面列表中的哪个表面被用来合成进去或画进去。一个表面列表定义为用来存储可视组件或图形绘画的合成,或两者的物理内存(系统的或视频的)的一个或多个表面(即,帧/缓冲区)的一个集合。表面列表中的一个表面可以被设置为当前后端缓冲区,在其中进行绘画和/或合成,同时表面列表中的一个表面被设置为一个当前主缓冲区或前端缓冲区,它被用来合成到另外的呈现目标。
表面可以通过很多方法使用。作为例子,图9A展示合成到一个表面。在图9A中,一个表面可视组件管理器对象900连接一个表面列表902作为可视组件树904的一个呈现目标。在每个合成循环中,可视组件被合成到表面列表中当前作为表面列表的活动后端缓冲区的表面上。被合成的表面可以包括一个由客户/高层次合成引擎214(图2)所有的表面用于进程内合成情景,一个由低层次合成引擎218所有的表面用于在其中客户不需要这些比特,但低层次合成引擎218需要它们来合成该表面到另外的呈现目标上的情景,或一个跨进程表面,用于在其中客户需要访问表面比特,但低层次合成引擎218也需要该表面用于其他合成工作的情景。
合成由一个连接到可视组件管理器的定时服务控制。定时服务的一个例子,是一个可以如下面的例子中那样使用的手动模式:
//create a manual timing service and attach a visual managerTimingService timingService=new ManualTimingService(visualManager);//composite the visual tree to the current back buffer of the surfacevisualManager.Render();foreach(Tick tick in timingService) |
{ //advance the back buffer to the next frame of the surfacesurfaceList.NextFrame();//advance the time of the visual treetimingService.Tick(tick);//composite the visual tree to the current back buffer of surfacevisualManager.Render();} |
另一种使用表面的方法是通过一个上下文对一个表面使用直接模式呈现。连接一个表面列表到一个可视组件(一个表面可视组件)使得能够对当前作为表面列表的活动后端缓冲的表面列表中的表面使用直接模式呈现。如上所述,这种呈现是通过从表面可视组件获得一个绘画上下文并在该上下文中执行绘画命令来进行的。注意获得一个绘画上下文对该表面加锁以使得不能对它进行其他合成操作。每个绘画命令都是立即执行的,并且向量和其他表面可以被画出(混合)到该表面上。然而,其他可视组件不可以被画到该表面上,但可以通过关联它和一个可视组件管理器被合成到该表面中,如上所述(例如,在图9A中)。
//attach a surface list to a visualSurfaceVisual surfaceVisual=new SurfaceVisual(surfaceList);//enable immediate-mode rendering to(and lock)back buffersurfaceBaseDrawingContext dc=surfaceVisual.Open();//draw a line(immediately)to the current back buffer of thesurfacedc.DrawLine(pen,startPoint,endPoint); |
//unlcck the surface-we′re done with immediate-mode renderingsurfaceVisual.Close(dc); |
表面的另一个用途是合成一个表面到另一个呈现目标。到这一端,一旦一个表面列表被连接到一个表面可视组件,该表面可以作为一个可视组件树中的一个节点被连接,并且该表面列表中的当前作为主要的或前端缓冲的表面可以被合成到另一个表面或到桌面。这在图9B及下面的例子中展示:
//attach a surface list to a visualSurfaceVisual surfaceVisual=new SurfaceVisual(surfaceList);//Add the surfaceVisual to a visual tree for compositing onto another//render targetrootVisual.Add(surfaceVisual); |
//attach a surface list to a visualSurfaceVisual surfaceVisual=new SurfaceVisual(surfaceList);//Add the surface Visual to a visual tree for compositing onto another//render targetrootVisual.Add(surfaceVisual); |
现场合成到/来自一个表面在图9C中展示,其中上述能力被合并使得合成到一个表面列表的后端缓冲表面及从一个表面列表的前端缓冲表面(例如,到桌面)合成是同时发生的。注意为了消除不期望的称为撕裂的视频效果,该表面列表至少应有两个表面,一个前端缓冲表面和一个后端缓冲表面。一个在图9C中使用的表面可能由低层次引擎218拥有,或为一个使在低层次引擎218中的合成更好地进行的跨进程表面。
表面作为独立的对象构建,如在下面构造函数的例子中所示:
public class Surface{//create and allocate a blank surface without initial datapublic Surface(int width,int height,int dpi,PixelFormat pixelFormat,Surface Flags flags)//create a surface using the supplied memorypublic Surface(int width,int height,int dpi,PixelFormat pixelFormat,IntPtr pixels,//managed memory for the surfaceInt stride)//create from a source(i.e.Clone)public Surface(Surface sourceSurface,SurfaceFlags flags)//Create from File or URLpublic Surface(String filename,SurfaceFlags flags)//Create from Streampublic Surface(System.IO.Stream stream,SurfaceFlags flags)//Create from HBITMAP(which can′t be selected into an HDC)public Surface(HBITMAP hbitmap,HPALETTE hPalette)//Create from HICONpublic Surface(HICON hicon)//read-only propertiespublic Int Width {get;}public Int Height{get;}public Int Dpi{get;}public PixelFormat Format {get;}public int Stride{get;}public IntPtr Buffer{get;}} |
public class SurfaceList{//Create a list of blank surfaces(without initial data).public SurfaceList(int width,int height,int dpi,PixelFormat pixelFormat,int numSurfaces,SurfaceFlags flags)//Create a SurfaceList that uses the specified surfaces//All the surfaces must have identical properties(w,h,//dpi,etc).public SurfaceList(Surface[]surfaces)//change the front buffer to the first-in-line back bufferpublic Flip()//advance the back buffer to the next surfacepublic Next()public int FrontBufferIndex{get;set;}public int BackBufferIndex{get;set;}public Surface Get FrontBuffer()public Surface Get BackBuffer()public Surface Get Surface(int surfaceIndex)} |
一旦被构建出,一个表面和/或一个表面列表可以连接到一个表面可视组件对象或到一个可视组件管理器对象。
//Create a surface visualpublic SurfaceDrawingVisual(Surface surface)public SurfaceDrawingVisual(SurfaceList surfaceList)//Create a visual manager with a surface render targetpublic VisualManager(Surface surface)public VisualManager(SurfaceList surfaceList) |
进一步来说,一个表面可以从一个解码器得到数据,并/或发送它的数据到一个编码器来写到一个特定的文件格式。表面也可以接收/发送数据从/到效果接口。一个表面可以被构建为所支持的表面格式类型的整个集合的任何象素格式。然而,可以对特定的象素格式做调整,例如,如果特定的象素格式少于32比特每象素,那么该格式可以升级到32比特每象素。无论何时从初始格式的一个表面请求比特,该表面将被复制到一个使用一个格式转换过滤器、为所请求的象素格式的缓冲。
回到图5,另一个可视组件为一个HwndVisual 505,在场景图形中作为一个Win32子HWnd。更特别地,遗留系统将仍然基于以前的图形技术通过WM_PAINT方法(或类似的)工作画出到一个子HWnd(或类似的)。为了在新的图形处理模型中支持这样的程序,,HwndVisual允许Hwnd被包含在一个场景图形中并且它在父可视组件被重新定位时也被移动,如图10A所示。然而,作为现有的Hwnd的限制的一个结果,当被呈现时,一个子Hwnd仅可以在其他窗口的上面,并且不能象其他上述的可视组件那样旋转或缩放。某些剪切是可以的,如图10B所示,其中虚线表明该HWnd的显示矩形在和它的父可视组件的相对移动过程中被剪切。
其他类型的可视组件506也是可行的,并且本对象模型是可扩充的以允许其他的组件被开发。例如,如图11所示,一个分层的可视组件1100使得一个应用开发者能够通过多个数据流单独地控制一个可视组件中的信息,相对于只有一个数据流的可视组件提供了更细粒度的控制。注意类似的控制粒度可以通过在单个父可视组件下有(例如,三个)独立的子可视组件来实现,然而这需要程序代码处理多个可视组件,这比处理一个有到多层的索引的单层的可视组件更加复杂。
作为例子,在图11中,背景数据,内容数据及边界数据都包含在一个单层可视组件中,但用一个层值索引,例如,各自为0、1或2,来互相分离。层可以被插入,包括钉到任一端上,并/或被删除,有定义一个显示的Z-顺序的分层顺序(例如,如所示,从左到右)。注意为了安全,在一个分层的可视组件中的子内容和其他数据是不可列举的。
其他类型的可视组件包括容器可视组件,及重定向子HWnd可视组件,在其中内容被画出到一个位图,并包含到一个表面可视组件中。三维可视组件使得能够连接两维和三维世界,例如,通过一个有到一个三维世界中的视图的两维可视组件,一个类似相机的视图是可能的。
很多资源对象一旦创建出后是不变的,即,一旦它们被创建出,它们不能被改变,这有各种原因,包括简化线程事务、防止由其他对象造成的损坏,并简化和元素及API的交互。注意这通常简化了系统。然而应注意,有一个其中这样的对象是不可变的系统是可行的,但例如,会要求管理一个相关性图。例如,虽然可以有一个其中这样的对象是不可变的系统,如果程序代码改变了在一个Visual上的剪切集合,该可视组件将需要被重新呈现,因此需要一个通知/注册机制,例如,如果一个新剪切被赋给一个可视组件,该可视组件为了通知向剪切注册它自身(例如,一个剪切改变通知)。因此,在一个实现中,为了简单起见,资源对象是不可变的。
这些资源对象可以用一个构造函数来定义,这个构造函数是来创建一个对象的一个简单的、通用的方法,或使用一个伙伴构建器对象,如下所述。例如,为了创建一个SolidColorBrush,(画笔对象在下面说明),可以使用一个构造函数:
Brush MyBrush=new SolidColorBrush(Colors.Red);
用户也可以使用Brush类的静态成员来得到一组预定义的颜色。
由于不可变对象不能被改变,为了有效地改变一个对象,用户需要创建一个新对象并且用它来代替原来的对象。到这一端,在系统中的很多资源对象可以利用构建器模式,在该模式中不可变对象通过一个作为可变的伙伴类的构建器类来创建。用户创建一个不可变对象来镜像构建器上的参数集合,为该对象创建一个新的构建器,并通过不可变对象初始化它。然后用户根据需要改变该构建器。一旦完成了,用户可以构建一个新的对象,通过改变该构建器并重用它来创建另一个不可变对象。注意有设置属性的不可变对象是所期望的,并且不可变对象不能被改变,但只能通过触发一个属性改变事件来替换。
因此,除了如上所述使用一个构造函数来创建一个SolidColorBrush,可以使用一个SolidColorBrushBuilder:
SolidColorBrushBuilder MyBuilder=new
SolidColorBrushBuilder();
MyBuilder.Color=Colors.Red;
Brush MyBrush=MyBuilder.ToBrush();
很多取静态值的对象也可以取动画对象。例如,在DrawingContext上有一个对DrawCircle的覆盖取一个PointAnimationBase为圆的中心。通过这种方法,用户可以在图元层次指定丰富的动画消息。对资源对象,除了基值之外有一个动画集合。这些被合成,借此如果用户需要使上面的例子活动起来,用户可以在画笔被构建之前指定下面的例子行:
MyBuilder.ColorAninations.Add(new ColorAnimation(...));
注意一个有动画参数的对象仍然使不可变的,因为它的动画参数是静态的。然而,当场景图形被处理时(例如,遍历),动画参数的意义随着事件改变,给出了动画的表现,而非静态的,数据。
如上所述,可视组件可以通过用不同的绘画图元,包括Geometry、ImageData和VideoData组装它们的绘画上下文画出。进一步来说,有一组由整个栈共享的资源和类。这包括Pen、Brush、Geometry、Transform和Effect。IDrawingContext暴露了一组可以被用来组装一个DrawingVisual、ValidationVisual的绘画操作。ISurfaceDrawingContext,IDrawing上下文的一个基接口,可以被用来组装一个SurfaceVisual。换句话说,绘画上下文暴露了一组绘画操作;对每个绘画操作,有两个方法,一个取常数为参数、一个取动画为参数。
DrawLine方法从开始点到结束点用指定的笔画出一条直线。
public void DrawLine(Pen pen,Point start,Point end);public void DrawLine(Pen pen,PointAnimationBase start,PointAnimationBase end); |
DrawRoundedRectangle方法用指定的画笔和笔画出一个圆角矩形;画笔和笔可以为null。
public void DrawRoundedRectangle(Brush brush,Pen pen,Point topLeft,Size size,float radius);public void DrawRoundedRectangle(Brush brush,Pen pen,PointAnimationBase topLeft,SizeAnimationBase size,NumberAnimationBase radius);public void DrawRoundedRectangle(Brush brush,Pen pen,Point topLeft,Point bottomRight,float rx,float ry);public void DrawRoundedRectangle(Brush brush,Pen pen,PointAnimationBase topLeft,PointAnimationBase bottomRight,NumberAnimationBase radiusX,NumberAnimationBase radiusY); |
DrawGeometry方法用指定的画笔和笔画出一个路径;画笔和笔可以为null。
public void DrawGeometry(Brush brush,Pen pen,Geometry geometry); |
DrawRectangle方法用指定的画笔和笔画出一个矩形;画笔和笔可以为null。
public void DrawRectangle(Brush brush,Pen pen,Point topLeft,Size size);public void DrawRectangle(Brush brush,Pen pen,PointAnimationBase topLeft,SizeAnimationBase size); |
DrawSurface方法画出一个表面。
public void DrawSurface(Surface surface,Point topLeft,Size size,float opacity);public void DrawSurface(Surface image,PointAnimationBase topLeft,SizeAnimationBase size,NumberAnimationBase opacity); |
Geometry是一种定义一个向量图形框架,而没有笔触或填充的类(图12)。每个几何图形对象都是一个简单的形状(LineGeometry,EllipseGeometry,RectangleGeometry),一个复杂的单个形状(PathGeometry)或这样的形状的一个有一个指定的合并操作(例如,并集,交集,等等)的列表GeometryList。如图12中所示,这些对象构成一个类层次结构。
如图13所示,PathGeometry是Figure对象的一个集合。按顺序,每个Figure对象由一个或多个实际上定义该图形的形状的Segment对象组成。一个Figure是定义一个片段集合的Geometry的一个子部分。这个片段集合是两维Segment对象的一个单个连接的序列。Figure可以为有一个定义的区域的一个封闭的形状,或只是定义一个曲线而非封闭区域的Segmen连接序列。
PathGeometry的填充区域通过取所包含的其Filled属性设为true的Figure来定义,并且应用一个FillMode来确定该封闭区域。注意FillMode列举确定包含在一个Geometry中的Figure对象的交叉区域如何合并来构成该Geometry的结果区域。一个“交替”规则通过概念性地画出在任意方向上从那一点到无限远的光线,然后检查穿过该光线的该形状的一个片段所在的地方确定一个点是否在画布中。通过开始于为0的一个计数并每次当一个Segment从左到右穿过该光线时加1并每次当一个路径片段从右到左穿过该光线时减1,在计数交叉点后,如果结果为0,那么该点是在路径之外的。否则,该点在路径之内。一个“缠绕”规则确定一个在画布上的点是否在内部,并通过概念性地画出在任意方向上从那一点到无限远的光线并计数该光线穿过的给定的形状中的路径Segment的数量来工作。如果这个数字为奇数,该点在内部;如果为偶数,该点在外部。
如图14所示,当画出几何图形(例如,一个矩形)时,可以指定一个画笔或笔,如下所述。进一步来说,笔对象也可以有一个画笔对象。一个画笔对象定义如何图形化地填充一个平面,并且有画笔对象的一个类层次结构。这在图14中用当可视组件包括该矩形且画笔指令和参数被处理时得到的被填充的矩形1402表示。
如下所述,一些类型的Brush(如梯度和9格)确定自己的尺寸。在使用时,通过边界框获得这些画笔的尺寸,例如,当该Brush的GradientUnit/DestinationUnit被设为ObjectBoundingBox时,使用正在被画出的图元的边界框。如果这些属性被设为UserSpaceOnUse,那么使用坐标空间。
一个Pen对象包括一个Brush和Width、LineJoin、LineCap、MiterLimit、DashArray和DashOffset这些属性,如下面的例子所示:
public enum System.Windows.Media.PenLineCap{Butt,Round,Square}public enum System.Windows.Media.PenLineJoin{Miter,Round,Bevel}public class System.Windows.Media.Pen{//Constructorspublic Pen(Color color,float width);public Pen(Brush brush,float width);//Propertiespublic float[]DashArray{get;}public float DashOffset{get;}public FloatAnimationCollection DashOffsetAnimations{get;}public PenLineCap LineCap{get;}public PenLineJoin LineJoin{get;}public float MiterLimit{get;}public FloatAnimationCollection MiterLimitAnimations{get;}public float Opacity{get;}public FloatAnimationCollection OpacityAnimations{get;}public Brush Brush{get;}public float Width{get;}public FloatAnimationCollection WidthAnimations{get;}}public sealed class System.Windows.Media.PenBuilder:Builder{//Fields//Constructorspublic PenBuilder();public PenBuilder(Color color);public PenBuilder(Brush brush); |
public PenBuilder(Pen pen);//Propertiespublic float[]DashArray{get;set;}public float DashOffset{get;set;}public FloatAnimationCollectionBuilder DashOffsetAnimations{get;}public PenLineCap LineCap{get;set;}public PenLineJoin LineJoin{get;set;}public float MiterLimit{get;set;}public FloatAnimationCollectionBuilder MiterLimitAnimations{get;}public float Opacity{get;set;}public FloatAnimationCollectionBuilder OpacityAnimations{get;}public Brush Brush{get;set;}public float Width{get;set;}public FloatAnimationCollectionBuilder WidthAnimations{get;}//Methodspublic Pen ToPen();} |
如上所述,本发明的图形对象模型包括一个Brush对象模型,它总的来说集中于用象素覆盖一个平面的概念。各种类型的画笔的例子在图15的层次结构中展示,并且,在一个Brush基类下,包括SolidColorBrush、GradientBrush、ImageBrush、VisualBrush(可以作为一个Visual引用)及NineGridBrush。GradientBrush包括LinearGradientBrush和RadialGradient对象。如上所述,Brush对象是不可变的。
public abstract class System.Windows.Media.Brush{float Opacity{get;}FloatAnimationCollection OpacityAnimations{get;} } |
下面是一个BrushBuilder类的例子:
public abstract class System.Windows.Media.BrushBuilder:Builder{public virtual Brush ToBrush();public override sealed object CreateInstance();{return ToBrush();}float Opacity{get;set;}FloatAnimationCollectionBuilder OpacityAnimations{get;}} |
注意Brush对象可以认出当它们被用到的时候它们与坐标系统有何关系,并/或它们和在其上用到它们的形状的边界框有何关系。总的来说,可以通过在其上画笔被画出的对象推断出如尺寸这样的信息。更特别地,很多画笔类型使用一个坐标系统来确定它们的部分参数。这个坐标系统可以定义为相对于简单的对其应用该画笔的形状的边界框,或可以相对于在该画笔被用到时活跃的坐标空间。这些各自被称为ObjectBoundingMox模式和UserSpaceOnUse模式。
public enum System.Window s.Media.BrushMappingMode{ObjectBoundingBox,UserSpaceOnUse,} |
一个SolidColorBrush对象用实心的颜色填充标识出的平面。如果有该颜色的一个alpha成分,它以一种乘法的方式与对应的在Brush基类中的不透明度属性合并。下面是SolidColorBrush对象的一个例子:
public sealed class System.Windows.Media.SolidColorBrush:Brush{//Constructorspublic SolidColorBrush();//initialize to blackpublic SolidColorBrush(Color color);public SolidColorBrush(System.Windows.Media.Animation.ColorComposercolorComposer);//Propertiespublic Color Color{get;}public IEnumerator ColorAnimations{get;}}public class System.Windows.Media.SolidColorBrushBuilder:BrushBuilder{//Constructorspublic SolidColorBrushBuilder();public SolidColorBrushBuilder(Color color);public SolidColorBrushBuilder(SolidColorBrush scp);//Propertiespublic Color Color{get;set;}public AnimationList ColorAnimations{get;}//Methodspublic virtual Brush ToBrush();} |
GradientBrush对象,或简单地梯度,提供了一种梯度填充,并且是通过指定一组指定颜色按某种方式渐变的梯度站画出的。梯度是通过在一个gamma2.2RGB颜色空间中的在梯度站之间进行线性插值画出的;通过其他gamma或其他颜色空间(HSB、CMYK等等)插值,也是一种可行的其他选择。两者类型的梯度对象包括线性和放射梯度。
总的来说,梯度由一组梯度站构成。每个这样的梯度站包括一种颜色(包括alpha值)和一个偏移量。如果没有指定梯度站,画笔作为一个实心的透明黑色画出,就像没有画笔被指定一样。如果只指定了一个梯度站,画笔作为一个实心的颜色用指定的颜色画出。象其他资源类一样,梯度站类(例子在下面的表中)是不可变的。
public class System.Windows.Media.GradientStop{public GradientStop(Color color,float offset);public Color Color{get;}public AnimationEnumerator ColorAnimations{get;}public float Offset{get;}public AnimationEnumerator OffsetAnimations{get;}}public class System.Windows.Media.GradientStopBuilder:Builder{public GradientStopBuilder();public GradientStopBuilder(Color color,float offset);public Color Color{get;set;}public AnimationList ColorAnimations{get;}public float Offset{get;set;}public AnimationList OffsetAnimations{get;}public GradientStop ToGradientStop();} |
也有一个集合类,如下面例子所示:
public class System.Windows.Media.GradientStopCollection:ICollection{public GradientStopCollection();//empty listpublic GradientStopCollection(GradientStop[]GradientStops);public GradientStopCollection(ICollection c);//IEnumerablepublic IEnumerator GetEnumerator();//ICollectionpublicvoid CopyTo(Array array,int index);public bool ICollection.IsSynchronized{get{return false;}}public int Count{get;}public object ICollection.SyncRoot{get;}//Extra functionspublic GradientStop this[int index]{get;}public bool Contains(GradientStop value);public int IndexOf(GradientStop value);//returns first onepublic int IndexOf(GradientStop value,int startIndex);public int IndexOf(GradientStop value,int startIndex,int count);public int LastIndexOf(GradientStop value);public int LastIndexOf(GradientStop value,int startIndex);public int LastIndexOf(GradientStop value,int startIndex,int count);public GradientStopCollection GetRange(int index,int count);}public class System.Windows.Media.GradientStopCollectionBuilder:Builder,IList{public GradientStopCollectionBuilder();public GradientStopCollectionBuilder(GradientStop[]GradientStops);public GradientStopCollectionBuilder(ICollection c);public GradientStopCollectionBuilder(GradientStopCollectionGradientStops);//IEnumerablepublic IEnumerator GetEnumerator();//ICollectionpublic void CopyTo(Array array,intindex);public bool ICollection.IsSynchronized{get{return false;}}public int Count{get;}public object ICollection.SyncRoot{get;}//IListpublic bool IsFixedSize{get{return false;}}public bool IsReadOnly{get{return false;}}public object IList.this[int index]{get;set;}public int IList.Add(object value);public void Clear();public bool IList.Contains(object value);public int IList.IndexOf(object value);//returns first one |
public void IList.Insert(int index,object value);public void IList.Remove(object value);//removes first onepublic void RemoveAt(int index);//Extra functionspublic GradientStop this[int index]{get;set;}public int Add(GradientStop value);public bool Contains(GradientStop value);public int IndexOf(GradientStop value);//returns first onepublic intIndexOf(GradientStop value,int startIndex);public intIndexOf(GradientStop value,int startIndex,int count);public int LastIndexOf(GradientStop value);public int LastIndexOf(GradientStop value,int startIndex);public int LastIndexOf(GradientStop value,int startIndex,int count);public void Insert(int index,GradientStop value);public void Remove(GradientStop value);//removes first onepublic void AddRange(ICollection c);public void InsertRange(int index,ICollection c);public void RemoveRange(int index,int count);public void SetRange(int index,ICollection c);public GradientStopCollectionBuilder GetRange(int index,int count);//Capacity is a hint.It will throw an exception if it is set lessthan Count.public int Capacity{get;set;}//Builder overloadspublic override object Build();public override void ResetBuilder();public override void SetBuilder(Object example);public GradientStopCollection ToGradientStopCollection();} |
如下表所示,GradientSpreadMethod指定梯度在指定的向量或空间之外应如何被画出。有三个值,包括pad,在其中边缘颜色(第一个和最后一个)被用来填充余下的空间,reflect,在其中站被以相反的顺序重复地重放来填充该空间,及repeat,在其中站被按顺序重复直到该空间被填满:
图16展示了GradientSpreadMethod的例子。每个形状有一个从白到灰的线性梯度。该实线表示梯度向量。
LinearGradient沿着一个向量指定一个线性梯度画笔。单个的站指定沿着那个向量的颜色站。一个例子在下表中展示:
public class System.Windows.Media.LinearGradient:GradientBrush{//Sets up a gradient with two colors and a gradient vector//specified to fill the object the gradient is applied to.//This implies ObjectBoundingBox for the GradientUnits//propertypublic LinearGradient(Color color1,Color color2,floatangle);public BrushMappingMode GradientUnits{get;}public Transform GradientTransform{get;}public GradientSpreadMethod SpreadMethod{get;}//Gradient Vectorpublic Point VectorStart{get;}public PointAnimationCollection VectorStartAnimations{get;}public Point VectorEnd{get;}public PointAnimationCollection VectorEndAnimations{get;}//Gradient Stopspublic GradientStopCollection GradientStops{get;}}public class System.Window.Media.LinearGradientBuilder:GradientBrushBuilder{public LinearGradientBuilder();public LinearGradientBuilder(Color color1,Color color2,floatangle);public LinearGradientBuilder(LinearGradient lg);//GradientUnits:Default is ObjectBoundingBoxpublic BrushMappingMode GradientUnits{get;set;}//GradientTransform:Default is identitypublic Transform GradientTransform{get;set;}//SpreadMethod:Default is Padpublic GradientSpreadMethod SpreadMethod{get;set;}//Gradient Vector//Default vector is(0,0)-(1,0)public Point VectorStart{get;set;}public PointAnimationCollectionBuilder VectorStartAnimations{get;set;}public Point VectorEnd{get;set;}public PointAnimationCollectionBuilder VectorEndAnimations{get;set;} |
RadialGradient在编程模型中和线性梯度是类似的。然而,鉴于线性梯度有一个开始和结束点来定义梯度向量,放射梯度有一个圆和一个焦点来定义梯度行为。圆定义了梯度的结束点,即,一个在1.0的梯度站定义了圆上的颜色。焦点定义了梯度的中心。一个在0.0的梯度站定义了在焦点上的颜色。
图17展示了一个从白到灰的放射梯度。文本的圆代表梯度圆而那个点代表焦点。这个例子梯度的SpreadMethod被设为Pad:
public class System.Windows.Media.RadialGradient:GradientBrush{//Sets up a gradient with two colors.//This implies ObjectBoundingBox for the GradientUnits//property along with a center at(0.5,0.5)//a radius of 0.5 and a focal point at(0.5,0.5)public RadialGradient(Color color1,Color color2);public BrushMappingMode GradientUnits{get;}public Transform GradientTransform{get;}public GradientSpreadMethod SpreadMethod{get;}//Gradient definitionpublic Point CircleCenter{get;}public PointAnimationCollection CircleCenterAnimations{get;}public float CircleRadius{get;}public FloatAnimationCollection CircleRadiusAnimations{get;}public Point Focus{get;}public PointAnimationCollection FocusAnimations{get;}//Gradient Stopspublic GradientStopCollection GradientStops{get;}}public class System.Windows.Media.RadialGradientBuilder:GradientBrushBuilder{public RadialGradientBuilder();public RadialGradient(Color color1,Color color2);public RadialGradientBuilder(RadialGradient rg);//GradientUnits:Default is ObjectBoundingBoxpublic BrushMappingMode GradientUnits{get;set;}//GradientTransform:Default is identitypublic Transform GradientTransform{get;set;}//SpreadMethod:Default is Padpublic GradientSpreadMethod SpreadMethod{get;set;}//Gradient definitionpublic Point CircleCenter{get;set;}//Default:(0.5,0.5)public PointAnimationCollectionBuilder CircleCenterAnimations{get;set;}public float CircleRadius{get;set;}//Default:0.5public FloatAnimationCollectionBuilder CircleRadiusAnimations{get;set;}public Point Focus{get;set;}//Default:(0.5,0.5)public PointAnimationCollectionBuilder FocusAnimations{get;set;}//Gradient Stopspublic void AddStop(Color color,float offset);public GradientStopCollectionBuilder GradientStops{get;set;}} |
另一种在图15中展示的画笔对象是一个VisualBrush对象。概念上来说,VisualBrush提供了一种方法以一种重复的、平铺的方式作为一个填充来画出一个可视组件。可视组件绘画对象也为标记语言提供一种机制来直接在资源层次使用API层,如下所述。这样的一个填充的另一个例子在图14中通过引用指定单个圆形形状1420的一个可视组件(及任何子可视组件),用该圆形形状填充一个矩形1442的可视组件画笔展示。因此,VisualBrush对象可以引用一个可视对象来定义该画笔将如何被画出,这引入了一种类型的可视组件的多种用途。通过这种方式,一个程序可以使用一个任意图形“元文件”通过一个画笔或笔来填充一个区域。因为这是一种存储和使用任意图形的压缩的形式,它用作一个图形资源。下面给出一个VisualBrush对象的例子。
public class System.Windows.Media.VisualBrush:Brush{public VisualBrush(Visual v);public BrushMappingMode DestinationUnits{get;}public BrushMappingMode ContentUnits{get;}public Transform Transform{get;}public Rect ViewBox{get;}public Stretch Stretch{get;}public HorizontalAlign HorizontalAlign{get;}public VerticalAlign VerticalAlign{get;}public Point Origin{get;}public PointAnimationCollection OriginAnimations{get;}public Size Size{get;}public SizeAnimationCollection SizeAnimations{get;}//Visualpublic Visual Visual{get;}}public class System.Windows.Media.VisualBrushBuilder:BrushBuilder{public VisualBrushBuilder();public VisualBrushBuilder(Visual v); |
public VisualBrushBuilder(VisualBrush vb);//DestinationUnits:Default is ObjectBoundingBoxpublic BrushMappingMode DestinationUnits{get;set;}//ContentUnits:Default is ObjectBoundingBoxpublic BrushMappingMode ContentUnits{get;set;}//Transform:Default is Identitypublic Transform Transform{get;set;}//ViewBox:Default is(0,0,0,0)--unset and ignoredpublic Rect ViewBox{get;set;}//Stretch:Default is None--and ignored//because the ViewBox is not setpublic Stretch Stretch{get;set;}//HorizontalAlign:Default is Center and ignoredpublic HorizontalAlign HorizontalAlign{get;set;}//VerticalAlign:Default is Center and ignoredpublic VerticalAlign VerticalAlign{get;set;}//Origin:Default is(0,0)public Point Origin{get;set;}public PointAnimationCollectionBuilder OriginAnimations{get;set;}//Size:Default is(1,1)public Size Size{get;set;}public SizeAnimationCollectionBuilder SizeAnimations{get;set;}//Visual:Default is null--nothing drawnpublic Visual Visual{get;set;}} |
一个VisualBrush的内容没有内在的边界,并且有效地描述了一个无限的平面。这些内容存在于它们自己的坐标空间中,并且通过VisualBrush填充的空间在应用的时候是本地的坐标空间。内容空间基于ViewBox、ViewPort、Alignment和Stretch属性被映射到本地空间。ViewBox在内容空间中指定,并且这个矩形被映射到ViewPort(如通过Origin和Size属性指定)矩形。
ViewPort定义内容最终将被画出的位置,为这个Brush创建基平铺。如果DestinationUnits的值是UserSpaceOnUse,Origin和Size属性在应用的时候被当作在本地空间中。如果相反DestinationUnits的值是ObjectBoundingBox的话,那么一个Origin和Size被当作在该坐标空间中,其中0,0为被画出的对象的边界框的左上角,及1,1为同一个框的右下角。例如,考虑一个从100,100到200,200画出,正被填充的RectangleGeometry。在这样的一个例子中,如果DestinationUnits为UserSpaceOnUse,一个为100,100的原点及一个为100,100的Size将描述整个内容区域。如果DestinationUnits为ObjectBoundingBox,一个为0,0原点及一个为1,1的Size将描述整个内容区域。如果Size为空,这个Brush将不呈现任何内容。
ViewBox是在内容空间中指定的。这个矩形由Alignment属性和Stretch属性确定被转换以适应到ViewPort中。如果Stretch为空,那么不对内容做任何缩放。如果Stretch为Fill那么ViewBox将在X和Y上单独地缩放到和ViewPort同样的大小。如果Stretch为Uniform或UniformToFill,逻辑是类似的但是X和Y维度被统一地缩放,保持内容的高宽比。如果Stretch为Uniform,ViewBox被缩放到等于ViewPort尺寸的有较多限制的维度。如果Stretch为UniformToFill,ViewBox被缩放到等于ViewPort尺寸的有较少限制的维度。换句话说,Uniform和UniformToFill都保持长宽比,但Uniform确保整个ViewBox在ViewPort中(可能留下ViewPort的部分未被ViewBox覆盖),同时UniformToFill确保整个ViewPort被ViewBox填满(可能使得ViewBox的部分落在ViewPort之外)。如果ViewBox为空,那么不应用Stretch。注意,对齐仍然将发生,并且将放置该“点”ViewBox。
图18展示了一个用不同的伸展设置呈现的单个图形的平铺1800,包括当伸展被设为“空”时的一个平铺800。平铺1802展示了当伸展被设为“Uniform”时的显示,平铺1804展示了当伸展被设为“UniformToFill”时的显示,及平铺1802展示了当伸展被设为“Fill”时的显示。
一旦确定了ViewPort(基于DestinationUnits)和ViewBox的尺寸(基于Stretch),ViewBox需要被放置在ViewPort中。如果ViewBox和ViewPort的尺寸相同(如果Stretch为Fill,或在其他三个Stretch值时正好这样,那么ViewBox被放置在Origin以使得和ViewPort相同。否则,HorizontalAlignment和VerticalAlignment将被考虑。基于这些属性,ViewBox在维度X和Y上对齐。如果HorizontalAlignment为Left,那么ViewBox的左边将被放置在ViewPort的左边。如果它为Center,那么ViewBox的中心将被放置在ViewPort的中心,及如果为Right,那么右边将符合这样的设置。这个过程对Y维度重复。
如果ViewBox为(0,0,0,0),它被认为是未设置过的,同时考虑ContentUnits。如果ContentUnits为UserSpaceOnUse,不发生伸缩或偏移,并且该内容不经转换被画到ViewPort中。如果ContentUnits为ObjectBoundingBox,那么内容原点与ViewPort原点对齐,并且通过该对象的边界框的宽度和高度缩放该内容。
当用一个VisualBrush填充一个空间时,内容如上所述被映射到ViewPort中,并剪切到ViewPort中。这为填充构成了基平铺,并基于该Brush的TileMode填充余下的空间。最后,如果设置过,该Brush的转换将被应用-发生在所有其他的映射、缩放、偏移等等之后。
TileMode列举被用来描述一个空间是否并且如何被它的Brush填充。一个可以被平铺的Brush定义了一个平铺矩形,并且这个平铺在被填充的空间中有一个基位置。余下的空间基于TileMode值被填充。图19展示了用不同TileMode设置的例子图形,包括“空”1900,“Tile”1902,“FlipX”1904,“FlipY”1906和“FlipXY”1908。最左上方的平铺在不同的例子图形中包括基平铺。
图20表示为这个画笔产生象素的过程。注意在图20中描述的逻辑仅为实现该逻辑的一种可能的方法,并且应理解其他方法,包括更有效的方法,也是可行的。例如,可能有更有效的方法来处理数据,例如,用画出并缓存的平铺使得内容无需对每次重复画出。然而,图20提供了一种简单的说明。
总的来说,每次图案的内容被画出时,都创建一个新的坐标系统。每次重复的原点和偏移量由Origin和Size属性指定,如通过DestinationUnits和Transform属性过滤。
基于DestinationUnits属性设置一个坐标框架。到这一端,如果在步骤2000,DestinationUnits属性为UserSpaceOnUse,在画笔被使用的时候的当前的坐标框架为起始坐标框架,通过步骤2002。如果相反在步骤2004该属性为ObjectBoundingBox,则使用这个画笔应用到的几何图形的边界框,如步骤2004表示的那样,设置一个新的坐标框架使得该边界框的左上角映射到(0,0)同时该边界框的左下角映射到(1,1)。在任何一种情况下,在步骤2006,Transform属性都应用到这个本质上定义了一个网格的坐标框架。
图21表示一个定义用来在一个VisualBrush中平铺的VisualBrush网格。第一个圆时一个简单网格,并且第二个圆有一个在x方向上为47的倾斜的Transform。
在步骤2008,该可视组件被画出到该网格的每个格子,如图22所示,其中该可视组件画出适合的数据。如果在步骤2010指定了一个ViewBox,该可视组件将根据ViewBox、Stretch、HorizontalAlign和VerticalAlign属性的指定,通过步骤2012,纳入该网格的格子。DestinationUnits和Transform属性被用来应用正确的转换使得该可视组件排列在网格框中。
如果没有指定ViewBox,那么在步骤2014建立一个新的坐标系统来画出该内容。
设置该坐标框架使得它的原点位于正被画出的特定的网格格子的原点。
在步骤2018基于Size属性应用一个剪切使得不在该格子的边界之外画出这个平铺。根据DestinationUnits属性适当地修改Origin和Size。
然后基于SourceUnits属性修改该坐标系统。到这一端,在步骤2020如果SourceUnits属性为ObjectBoundingBox,在步骤2026应用合适的缩放转换,否则它为UserSpaceOnUse,并且不应用新的转换。在步骤2024应用Transform属性,并且在步骤2026画出内容。
注意如果尺寸的任意部分为0,那么不画出任何东西,并且如果Stretch为“空”,那么设置视图框的转换使得新坐标框架中的一个单元等于原来的坐标框架中的一个单元。该转换本质上成为基于对齐属性和ViewBox尺寸的一个偏移。如上所述,在步骤2010和2012,Stretch和对齐属性仅当指定了一个ViewBox时应用。ViewBox为内容指定一个新的坐标系统,并且Strech帮助指定这些内容如何映射到ViewBox中。对象选项对齐该ViewBox,而非内容。因此,例如,如果视图框被设为“0 0 10 10”并且一些东西被画出在-10,-10,并对齐到左上角,那个东西将被剪切出去。
回到图15,图像画笔可以被当作一种特殊的VisualBrush。虽然一个程序可以创建一个可视组件,放一个图像到其中并连接它到VisualBrush,这样做的API会很笨重。因为不需要内容坐标框架,不再应用ViewBox和ContentUnits属性成员。
public class System.Windows.Media.ImageBrush:Brush{public ImageBrush{ImageData image};public BrushMappingMode DestinationUnits{get;}public Trans form Transform{get;}public Stretch Stretch{get;}public HorizontalAlign HorizontalAlign{get;}public VerticalAlign VerticalAlign{get;}public Point Origin{get;}public PointAnimationCollection OriginAnimations{get;}public Size Size{get;}public SizeAnimationCollection SizeAnimations{get;}public ImageData ImageData{get;}}public class System.Windows.Media.ImageBrushBuilder:BrushBuilder{public ImageBrushBuilder();public ImageBrushBuilder(ImageData image);public ImageBrushBuilder(ImageBrush ib);//DestinationUnits:Default is ObjectBoundingBoxpublic BrushMappingMode DestinationUnits{get;set;}//Transform:Default is identitypublic Transform Transform{get;set;}//Stretch:Default is Nonepublic Stretch Stretch{get;set;}//HorizontalAlign:Default is Centerpublic HorizontalAlign HorizontalAlign{get;set;}//VerticalAlign:Default is Centerpublic VerticalAlign VerticalAlign{get;set;}//Origin:Default is(0,0)public Point Origin{get;set;}public PointAnimationCollectionBuilder OriginAnimations{get;set;}//Size:Default is(1,1)public Size Size{get;set;}public SizeAnimationCollectionBuilder SizeAnimations{get;set;}//ImageData:Default is null--nothing drawnpublic ImageData ImageData{get;set;}} |
NineGridBrush和ImageBrush很相似,处理该图象基于尺寸被扭曲。本质上,NineGridBrush可以被看作是一种自定义类型的Stretch,在其中伸展图像的特定部分,而其他部分(如边界)不伸展。因此,虽然ImageBrush中的图像的尺寸将导致一种简单的缩放,NineGridBrush将产生一种非统一的缩放到期望的尺寸。当应用该画笔时,非缩放的区域的单位是用户单位,这意味着ContentUnits(如果对NineGridBrush存在的话)会被设为UserUnitsOnUse。画笔的Transform属性可以被有效地使用。注意边界成员从图像的边缘开始算入其中。
作为例子,图23表示一个九网格图像用四种类型的区域从第一个实例2302被放大到第二个实例2304。如图23所示,为了保持边界相同,标记为“a”的区域水平地扩展,标记为“b”的区域垂直地扩展,标记为“c”的区域水平地和垂直地扩展,并且标记为“d”的区域在尺寸上不改变。
public class System.Windows.Media.NineGridBrush:Brush{public NineGridBrush(ImageData image,int LeftBorder,int RightBorder,int TopBorder,int BottomBorder);public BrushMappingMode DestinationUnits{get;}public Trans form Transform{get;}public Point Origin{get;}public PointAnimationCollection OriginAnimations{get;}public Size Size{get;}public SizeAnimationCollection SizeAnimations{get;}public int LeftBorder{get;}public int RightBorder{get;}public int TopBorder{get;}public int BottomBorder{get;}public ImageData ImageData{get;}} |
总的来说,如上所述,本发明的图形对象模型包括一个Transform对象模型,它包括层次结构图24所示在一个Transform基类下的各种类型的转换。这些构成一个转换的不同类型的组件可以包括TransformList、TranslateTransform、RotateTransform、ScaleTransform、SkewTransform,及MatrixTransform。单独的属性可以做成动画,例如,一个程序开发者可以将一个RotateTransform的Angle属性做成动画。
用于2D计算的矩阵表示为一个3×3的矩阵。对所需的转换,只需要6个值而不是整个3×3的矩阵。这些的命名和定义如下所示。
当一个矩阵和一个点相乘的时候,它转换该点从新的坐标系统到原来的坐标系统:
转换可以嵌套任意层次。无论何时应用一个新的转换时,用它右乘当前的转换矩阵是相同的:
在API中的多数地方不直接取一个矩阵,而是使用支持动画的Transform类。
public struct System.Windows.Media.Matrix{//Construction and settingpublic Matrix();//defaults to identitypublic Matrix( |
double m00,double m01,double m10,double m11,double m20,double m21);//Identitypublic static readonly Matrix Identity;public void SetIdentity();public bool IsIdentity{get;}public static Matrix operator*(Matrix matrix1,Matrixmatrix2);public static Point operator*(Matrix matrix,Point point);//These function reinitialize the current matrix with//the specified transform matrix.public void SetTranslation(double dx,double dy);public void SetTranslation(Size offset);public void SetRotation(double angle);//degreespublic void SetRotation(double angle,Point center);//degreespublic void SetRotationRadians(double angle);public void SetRotationRadians(double angle,Point center);public void SetScaling(double sx,double sy);public void SetScaling(double sx,double sy,Point center);public void SetSkewX(double angle);//degreespublic void SetSkewY(double angle);//degreespublic void SetSkewXRadians(double angle);public void SetSkewYRadians(double angle);//These function post-multiply the current matrix//with the specified transformpublic void ApplyTranslation(double dx,double dy);public void ApplyTranslation(Size offApply);public void ApplyRotation(double angle);//degreespublic void ApplyRotation(double angle,Point center);//degreespublic void ApplyRotationRadian(double angle);public void ApplyRotationRadian(double angle,Point center);public void ApplyScaling(double sx,double sy);public void ApplyScaling(double sx,double sy,Point center);public void ApplySkewX(double angle);//degreespublic void ApplySkewY(double angle);//degreespublic void ApplySkewXRadians(double angle);public void ApplySkewYRadians(double angle);public void ApplyMatrix(Matrix matrix);//Inversion stuffpublic double Determinant{get;}public bool IsInvertible{get;}public voidInvert();//Throws ArgumentException if!IsInvertablepublic static MatrixInvert(Matrix matrix);//Individual memberspublic double M00{get;set;} |
public double M01{get;set;}public double M10{get;set}public double M11{get;set;}public double M20{get;set;}public double M21{get;set;}}; |
矢量图形的标记语言和对象模型
根据本发明的一个方面,提供一种标记语言和元素对象模型以使得用户程序和工具能够和场景图形数据结构216交互而不需要有API层212的细节的特定的知识(图2)。总的来说,提供了一种矢量图形标记语言,它包括一种交换格式,及一种简单的基于标记的通过元素对象模型表达矢量图形的创作格式。通过这种语言,标记(如HTML或XML类型的内容)可以被编程。然后,为了建立该场景图形,标记被解析并转换为适合的上述可视API层对象。在这个较高的操作层次,提供了一个元素树、属性系统及呈现器系统以处理大部分的复杂性,使场景设计者设计可能的复杂场景变得简单。
总的来说,矢量图形系统通常提供一组形状和其他元素,和一个通用属性系统、一个分组及合成系统集成,以及使得用户能够以符合灵活性和性能需要的方式编程的一种两层(元素层次和资源层次)方法。根据本发明的一个方面,处理矢量图形的元素对象模型和场景图形对象模型相关。换句话说,矢量图形系统和可视API层在元素对象模型层次共享一组资源,例如,当用可视API绘画并且它也是形状上填充属性的类型时使用Brush对象。因此,除了有和场景图形对象相关的元素,标记语言和可视API层共享一些图元资源(例如,画笔,转换等等)。矢量图形系统也暴露和扩充可视API层的动画能力,它很大程度上共享于层次之间。
进一步来说,如下所述,矢量图形系统可以编程到不同的配置、或层次,包括一个元素层次和一个资源层次。在元素层次,每个绘画形状都表示为一个元素,和一页/屏中余下的可编程元素在同一层次。这意味着形状以一种完全的方式和呈现器系统、事件及属性交互。在资源层次,矢量图形系统以一种纯粹的资源格式工作,类似于传统的图形元文件。资源层次是高效的,但对级联的属性、事件和细粒度的可编程性只有一些有限的支持。因此场景设计者要有能力根据需要平衡效率和可编程性。
根据本发明的一个方面,矢量图形系统在资源层次也和可视API相关,因为资源层次标记,在一个实现中,作为一个VisualBrush表达。当解析资源标记时,创建一个可视组件对象。该可视组件对象被放置到一个可以被形状、控件和其他元素在元素层次上使用的VisualBrush中。
图25展示元素类层次结构2500。本发明的标记语言对象模型的类通过有阴影的框表示,并且包括形状类2502、图像类2504、视频类2506及画布类2508。形状类的元素包括矩形2510、折线2512、多边形2514、路径2516、直线2518和椭圆2520。注意在一些实现中,如图25中的虚线框2522表示的那样,一个圆元素可以不出现,然而这里为了不同的例子的需要,圆元素2522将被说明。每个元素可以包括或关联于填充(属性)数据、笔触数据、剪切数据、转换数据、过滤效果数据及遮幅数据。
如下所述,形状对应于用继承的和级联的呈现属性画出的几何图形。呈现属性被用来构建画出形状所需的笔和画笔。在一个实现中,形状是完整的呈现器,象其他控件元素一样。然而,在其他实现中,一个画布类2508可以作为形状的一个容器提供,并且形状只能在一个画布元素中时被画出。例如,为了使形状为轻量级的,可以不允许形状有已连接的呈现器。相反,画布可以有已连接的呈现器并画出形状。画布元素在下面更详细地说明。
如下所述,图像类比一个形状更加具体,并例如可以包括可能很复杂的边界数据。例如,可以用可能不同的指定的厚度及其他设置的属性指定一个边界为一种在顶层的颜色、一种在边上的不同的颜色。可以对一个图像或类似的带框的元素,如文本或视频,设置位置、尺寸旋转和缩放。注意图像和视频元素可以存在于并且显示在一个画布元素之外,并且也是从BoxedElement集成而来,如从该元素得到背景、边界和装填支持。
视频元素允许视频(或类似的多媒体)播放在一个已显示的元素中。通过这种方式,矢量图形系统提供一种标记接口到API层,在媒体之间无缝地相容,包括文本、2D图形、3D图形、动画、视频、静态图像和音频。这使得设计者可以学习使用一种媒体而轻松地集成其他媒体到应用和文档中。矢量图形系统还使得多媒体能以和其他元素相同的方式做成动画,再次使得设计者能够象使用其他元素一样使用多媒体,而不牺牲每种单独的媒体类型的核心内在特性。例如,一个设计者可以在不同媒体类型之间对旋转、缩放、动画、绘画、合成及其他效果使用相同的命名方案,以此设计者可以轻松地创建很丰富的应用,也使得能够在底层建立很高效的呈现和合成的实现。
图26表示一个实现,在其中标记代码2602由一个解析器/翻译程序2604解释。通常,解析器/翻译程序2604增加元素到一个元素树/属性系统208(也如图2所示)并且连接呈现器到这些元素。然后呈现器系统210用已连接的呈现器取元素树210并转换该数据到对象并调用可视API层212。注意不是所有的元素都需要被转换,而只是那些有已连接的呈现器的元素。
通常,一个元素是在元素层中的一个参与属性系统、事件和布局/呈现系统的对象。解析器找到标签并确定这些标签是否有助于定义一个元素或一个资源对象。在这个VisualBrush的特例中,通样的标签可以被解释为元素或也可以被解释为资源对象,取决于这些标签出现的上下文,例如,取决于是否出现在复杂的属性语法中。
根据本发明的一个方面,标记语言提供独特的方法来描述一个资源,包括一种简单的字符串格式或一种复杂的对象符号。对一种简单的字符串格式,解析器/翻译程序2604使用一个类型转换器2608来转换一个字符串到一个适合的可视API对象。作为例子,在下面的标记行中,Fill属性值可以被转换为一个画笔对象,通过类型转换器2608:
<Circle CenterX=″10″CenterY=″10″Radius=″5″Fill=″Red″/> |
如可以马上理解的那样,这样一个基于标签的标记有简单的字符串参数内嵌行转换为一个画笔对象是简单的,并且为一个场景设计者提供了一种简单的方法来添加一个形状及其属性到一个场景。
然而有时填充属性会复杂得难以纳入一个字符串中。在这样的情况下,复杂的属性语法,可以内嵌在标记中,被用来设置这个属性。例如,下面复杂的属性语法用一个梯度而非一种实心的颜色填充一个圆,指定在不同的梯度站(范围从0到1)上的颜色:
<Circle CenterX=″10″CenterY=″10″Radius=″5″><Circle.Fill><LinearGradient><GradientStop Color=″Red″Offset=″0″/><GradientStop Color=″Blue″Offset=″0.33″/><GradientStop Color=″Green″Offset=″0.66″/><GradientStop Color=″Red″Offset=″1.0″/></LinearGradient></Circle.Fill></Circle> |
除了内嵌在标记中,一个资源实例可以放在其他地方(如,在标记或一个文件中,它可以是本地的或在一个远程网络上并适合地下载),并且通过一个名称引用,(例如,一个文本名称,引用或其他适合的标识)。通过这种方式,一个场景设计者可以在一个场景中重用一个在元素树中的元素,包括通过复杂的属性语法描述的元素。
解析器根据需要通过访问类型转换器2608处理在复杂的属性语法中的标记,并且也匹配指定的参数到对象属性,从而为场景设计者处理复杂度。因此,解析器不仅初始化对象,也设置对象的属性。注意解析器实际上实例化一个构建器来创建对象,因为对象是不可变的。
由于同样的呈现模型在元素层次和API层次之间共享,很多对象本质上是相同的。这使得解析/转换非常高效,并也允许不同类型的编程语言(例如,类似C#的语言)能够轻松地从标记转换到它自己的语法,反之亦然。注意如图26所示,另一种这样的编程语言可以增加元素到元素树208,或可以之间连接可视API层212。
如图26所示且根据本发明的一个方面,同样的标记2602可以被用来编程在一个元素层次和一个资源层次。如上所述,元素层次给场景设计者完整的可编程性,使用属性系统提供继承(例如,类似样式表的特性),及事件(例如,以此一个元素可以有连接的代码来改变它的外观、位置等等来响应一个用户输入事件)。然而,本发明也提供一种资源层次的机制,通过它场景设计者可以本质上跳过元素树和呈现器系统和程序直接到可视API层。对很多类型的静态形状、图像和其中不需要元素层次特性的类似组件,这通过了一种更高效并轻量级的方法来输出合适的对象。到这一端,解析器识别出当类型“可视组件画笔”的填充出现,并直接调用API层212以资源层次数据2612来创建对象。换句话说,如图22所示,元素层次矢量图形被解析为创建出的元素,它们需要后面的转换为对象,而资源层次矢量图形被解析并直接以一种高效的方式存储。
作为例子,下面的标记对LinearGradient对象直接从对象模型得到,并用一个VisualBrush填充一个外部的圆。那个VisualBrush的内容由内部的标记来定义。注意这种语法通常用来表达不同的画笔、转换和动画:
<Circle CenterX=″10″CenterY=″10″Radius=″5″><Circle.Fill><VisualBrush xmlns=″...″><Circle CenterX=″0.5″CenterY=″0.5″Radius=″0.25″Fill=″Blue″/><Circle CenterX=″0.6″CenterY=″0.6″Radius=″0.25″Fill=″Green″/><Circle CenterX=″0.7″CenterY=″0.7″Radius=″0.25″Fill=″Red″/><Circle CenterX=″0.8″CenterY=″0.8″Radius=″0.25″Fill=″LemonChiffon″/></VisualBrush></Circle.Fill></Circle> |
注意虽然这些可视组件画笔填充的对象是高效地存储的,资源层次的数据(或因此创建的对象)可以被元素和元素树208的部分引用,如图26总的所示。到这一端,这些可视组件画笔资源可以被重命名(例如,用一个名称、引用或其他适合的标识)并且象其他所述的资源那样通过复杂的属性语法来引用。
回到画布的解释,如上在一个作为其他选择的实现中所述,形状可以保持轻量级并且因此需要被包含在一个画布中。在这个作为其他选择的实现中,当呈现内容时,内容被呈现到一个无限的、独立于设备的有相关的坐标系统的画布。画布元素因此可以根据绝对坐标放置内容。画布元素可以选择性地定义一个视图孔,它指定剪切、转换、首选的高宽比和一种映射该视图孔到一个父空间的方法。如果没有建立视图孔,画布元素仅指定一组绘画图元并可以设置转换、不透明度和其他合成属性。
下面是样本画布的一个标记例子:
<Canvas Background=″black″Top=″100″Left=″100″Height=″600″Width=″800″><Rectangle Top=″600″Left=″100″Width=″100″Height=″50″Fill=″red″Stroke=″blue″StrokeWidth=″10″/><Line x1=″100″y1=″300″x2=″300″y2=″100″Stroke=″green″StrokeWidth=″5″/></Canvas> |
注意在一个实现中,当坐标被指定而没有单位那么它们被当作为96分之一英寸的“逻辑象素”,并且在上面的例子中,该直线将为200象素长。除了坐标,其他属性包括宽度、高度、水平和垂直对齐,及ViewBox(矩形类型的;缺省为未设置或(0,0,0,0),指不进行调整,并且伸展和对齐属性被忽略)。总的引用图18到20如上所述,其他属性包括伸展,当它未被指定时,保持初始的尺寸,或者可以1)指定一个填充,在其中高宽比未被保持并且内容被缩放到充满由顶/左/宽度/高度建立的边界,2)指定统一,它统一地缩放尺寸直到该图像适合由顶/左/宽度/高度建立的边界,或3)指定UniformToFill,它统一地缩放尺寸到充满由顶/左/宽度/高度建立的边界,并且根据需要剪切。
为了进一步关联低层次的对象模型,转换属性为该元素的子元素建立一个新的坐标框架,而剪切属性用定义为边界框的缺省剪切路径限制该区域到其中内容可以画在画布上的区域。ZIndex属性可以被用来在一个面板中为嵌套的画布元素指定呈现顺序。
ViewBox为内容指定一个新的坐标系统,例如,通过重定义视图孔的范围和原点。伸展帮助指定这些内容如何映射到视图孔。ViewBox属性的值为四个“无单位”数字<min-x>、<min-y>、<width>和<height>的一个列表,例如,通过空白字符和/或逗号分隔,并为矩形类型。ViewBox矩形指定映射到边界框的用户空间中的矩形。它和插入一个scaleX和scaleY是一样地工作。伸展属性(在该选择不为空时)为保持图形的高宽比提供附加的控制。一个附加的转换被应用到给定元素的后代来获得指定的效果。
在上面的例子中,在上面的标记样本中的矩形在每个伸展规则下的有效结果将为:
None-from(100,600)to(200,650)Fill-from(100,100)to(900,700)Uniform-from(100,?)to(900,?)-the new height will be400,and it will centered based on HorizontalAlign andVerticalAlign.UniformToFill-from(?,100)to(?,700)The new width is1200,and will again be centered based on Hori zontalAlign andVerticalAlign. |
如果在画布上有一个转换,它本质上应用上面的(例如,在树中)映射到ViewBox。注意这个映射将伸展一个画布中的任意元素,例如,框、文本等等,不仅是形状。进一步来说,如果指定了一个视图框,画布不再缩放到它的内容,而是有一个指定的尺寸。如果也指定了y宽度和y高度,那么伸展/对齐属性被用来适合视图框到指定的宽度和高度。
在对象模型中的元素每个可以有一个应用的‘剪切’属性。在一些元素上,特别是形状,这直接作为一种普通的语言运行时属性暴露,而在其他上(例如,多数控件)通过一个动态属性设置这个属性。
总的来说,剪切路径限制该区域到其中内容可以画在画布上的区域,如总的来说在图27中所示,其中一个按钮以未剪切的形式2702和在其中指定了一个剪切路径(其中虚线边上剪切路径)的形式2704展示。概念上来说,图形位于由当前活动的剪切路径界定的区域的任何部分都不被画出。一个剪切路径可以被看作一个遮幅,其中在剪切路径之外的那些象素为黑色,alpha值为0的并且那些在剪切路径中的象素为白色,alpha值为1(可能的例外为沿轮廓边缘的防锯齿)。
一个剪切路径由一个Geometry对象定义,或者内嵌或更典型地在一个资源部分中。一个剪切路径通过使用在一个元素上的“剪切”属性来使用和/或引用,如下面的例子所示:
<def:Resources><Geometry def:ID=″MyClip″><Path Data=″...″/><Rectangle.../></Geometry></def:Resources><Element Clip=″″%resource;MyClip″.../> |
注意把一个剪切作为动画和把转换作为动画类似:
<Element><Element.Clip><Circle..../><Rectangle....><FloatAnimation.../></Rectangle></Element.Clip>...children...</Element> |
一个路径通过指定在Path元素上的‘几何图形’数据和呈现属性,如填充、笔触和笔触宽度画出。一个路径的例子标记按如下指定:
<Path Data=″M 100 100 L 300 100 L 200 300 z″Fill=″red″Stroke=″blue″StrokeWidth=″3″/> |
路径‘Data’字符串为Geometry类型。指定一个画出的路径的更加详细并完整的方法是通过复杂的属性语法,如上所述。标记(如在下面的例子中)直接输送到上述的Geometry构建器类中:
<Path><Path.Data><CircleGeometry.../><RectangleGeometry.../><PathGeometry.../></Path.Data><Path.Fill value=″red″/><Path.Stroke value=″blue″/></Path> |
使用下面的符号来描述一个路径数据字符串的语法描述路径数据字符串:
*:0 or more+:1 or more?:0 or 1():grouping|:separates alternativesdouble quotes surround literals |
下面展示用这种符号描述的路径数据字符串信息(注意在一个实现中,FillMode可以被指定在这里,而非元素层次的一个属性):
wvg-path:wsp*moveto-drawto-command-groups?wsp*moveto-drawto-command-groups:moveto-drawto-command-group |
| moveto-drawto-command-group wsp*moveto-drawto-command-groupsmoveto-drawto-command-group:move to wsp*drawto-commands?drawto-commands:drawto-command| drawto-command wsp*drawto-commandsdrawto-command:closepath| lineto| horizontal-lineto| vertical-lineto| curveto| smooth-curveto| quadratic-bezier-curveto| smooth-quadratic-bezier-curveto| elliptical-arcmoveto:(″M″|″m″)wsp*moveto-argument-sequencemoveto-argument-sequence:coordinate-pair| coordinate-pair comma-wsp?lineto-argument-sequenceclosepath:(″Z″|″z″)lineto:(″L″|″l″)wsp*lineto-argument-sequencelineto-argument-sequence:coordinate-pair| coordinate-pair comma-wsp?lineto-argument-sequencehorizontal-lineto:(″H″|″h″)wsp*horizontal-lineto-argument-sequencehorizontal-lineto-argument-sequence:coordinate| coordinate comma-wsp?horizontal-lineto-argument-sequencevertical-lineto:(″V″|″v″)wsp*vertical-lineto-argument-sequencevertical-lineto-argument-sequence: |
coordinate| coordinate comma-wsp?vertical-lineto-argument-sequencecurveto:(″C″|″c″)wsp*curveto-argument-sequencecurveto-argument-sequence:curveto-argument| curveto-argument comma-wsp?curveto-argument-sequencecurveto-argument:coordinate-pair comma-wsp?coordinate-pair comma-wsp?coordinate-pairsmooth-curveto:(″S″|″s″)wsp*smooth-curveto-argument-sequencesmooth-curveto-argument-sequence:smooth-curveto-argument| smooth-curveto-argument comma-wsp?smooth-curveto-argument-sequencesmooth-curveto-argument:coordinate-pair comma-wsp?coordinate-pairquadratic-bezier-curveto:(″Q″|″q″)wsp*quadratic-bezier-curveto-argument-sequencequadratic-bezier-curveto-argument-sequence:quadratic-bezier-curveto-argument| quadratic-bezier-curveto-argument comma-wsp?quadratic-bezier-curveto-argument-sequencequadratic-bezier-curveto-argument:coordinate-pair comma-wsp?coordinate-pairsmooth-quadratic-bezier-curveto:(″T″|″t″)wsp*smooth-quadratic-bezier-curveto-argument-sequencesmooth-quadratic-bezier-curveto-argument-sequence:coordinate-pair| coordinate-pair comma-wsp?smooth-quadratic-bezier-curveto-argument-sequenceelliptical-arc:(″A″|″a″)wsp*elliptical-arc-argument-sequenceelliptical-arc-argument-sequence: |
elliptical-arc-argument| elliptical-arc-argument comma-wsp?elliptical-arc-argument-sequenceelliptical-arc-argument:nonnegative-number comma-wsp?nonnegative-number comma-wsp?number comma-wsp flag comma-wsp flag comma-wspcoordinate-paircoordinate-pair:coordinate comma-wsp?coordinatecoordinate:numbernonnegative-number:integer-constant| floating-point-constantnumber:sign?integer-constant| sign?floating-point-constantflag:″0″|″1″comma-wsp:(wsp+comma?wsp*)|(comma wsp*)comma:″,″integer-constant:digit-sequencefloating-point-constant:fractional-constant exponent?| digit-sequence exponentfractional-constant:digit-sequence?″.″digit-sequence| digit-sequence″.″exponent:(″e″|″E″)sign?digit-sequencesign:″+″|″-″ |
digit-sequence:digit| digit digit-sequencedigit:″0″|″1″|″2″|″3″|″4″|″5″|″6″|″7″|″8″|″9″wsp:(#x20|#x9|#xD|#xA) |
图像元素(图25)表明一个完整的文件的内容将被呈现到当前用户坐标系统中的一个给定的矩形中。图像(由image标签表明)可以引用扫描图像文件,如PNG或JPEG,或有MIME类型“image/wvg”的文件,如下面的例子所示:
<Image Top=″200″Left=″200″Width=″100px″Height=″100px″Source=″myimage.png″></Image> |
下面的表格提供图像的一些例子属性的信息:
名称 |
类型 |
读/读写 |
缺省值 |
说明 |
Top |
BoxUnit |
|
|
图像顶边的坐标 |
Left |
BoxUnit |
|
|
图像左边的坐标 |
Width |
BoxUnit |
|
|
图像宽度 |
Height |
BoxUnit |
|
|
图像高度 |
Source |
ImageData |
|
|
图像源 |
Dpi |
Float |
|
96(?) |
用于缩放的目标DPI |
HorizontalAlign |
enum{Left(?),Center(?),Right(?)} |
|
Center |
|
VerticalAlign |
enum{Top(?)Middle(?), |
|
Middle |
|
名称 |
类型 |
读/读写 |
缺省值 |
说明 |
|
Bottom(?)} |
|
|
|
Stretch |
enumStretch{None,Fill,Uniform,UniformToFill} |
|
None |
None:保持初始尺寸Fill:不保持高宽比并且缩放内容以充满tlbh建立的边界Unifrom:统一地缩放尺寸直到图像适合tlbh建立的边界UniformToFill:统一地缩放尺寸直到图像适合tlbh建立的边界,并剪切 |
ReadyState |
enum{MetaDataReady,Loading,Loaded,LoadError} |
|
|
|
LoadCounter |
Int |
Read |
Null |
当ReadyState为Loading时递增的计数器 |
名称 |
String |
|
|
图像的替换文本 |
如上所述,形状对应于用继承的和级联的呈现属性画出的几何图形。下表给出上述基本形状元素(Rectangle、Ellipse、Line、PolyLine、Polygon)的形状属性的例子。注意这些基本形状可以有笔触属性、填充属性,并作为剪切路径使用,有继承特性,并应用到元素和资源层次:
名称 |
类型 |
读/读写 |
缺省值 |
说明 |
Fill |
Brush |
RW |
null |
矩形顶边的坐标 |
FillOpacity |
Float |
RW |
1.0 |
矩形左边的坐标 |
名称 |
类型 |
读/读写 |
缺省值 |
说明 |
Stroke |
Brush |
RW |
null |
矩形的宽度 |
StrokeOpacity |
Float |
RW |
1.0 |
矩形的高度 |
StrokeWidth |
BoxUnit |
RW |
1px |
笔触的宽度。1px=1/96英寸 |
FillRule |
enum{EvenOdd,NonZero} |
RW |
EvenOdd |
FillRule指示用来确定画布的哪部分被包括在形状之中的算法 |
StrokeLineCap |
enumButt,Round,Square,Diamond} |
RW |
Butt |
StrokeLineCap指定当它们被画出时用在开放子路径(或其他矢量形状)的角上的形状 |
StrokeLineJoint |
enum{Butt,Round,Square,Diamond} |
RW |
Miter |
StrokeLineJoint指定用在开放子路径(或其他矢量形状)的角上,当它们被画出时也被画出的形状 |
StrokeMitterLimit |
Float |
RW |
4.0 |
对MiterLength与StrokeWidth比率的限制。取值应>=1 |
名称 |
类型 |
读/读写 |
缺省值 |
说明 |
StrokeDashArray |
PointList |
RW |
null |
StrokeDashArray控制用来画出路径的短划线和空隙的模式。<dasharray>包含空格或逗号分隔的以用户单位指定替换的短划线和空隙的长度的<nubmer>的一个列表。如果提供了奇数个值,那么值的列表被重复以产生偶数个值。因此笔触数组:532等价于笔触数组:532532。 |
StrokeDashOffset |
Point |
RW |
|
StrokeDashOffset指定进入虚线模式来开始虚线的距离。 |
Transform |
Transform |
RW |
null |
Transform为该元素的子 |
|
|
|
|
元素建立一个新的坐标框架。 |
Clip |
Geometry |
RW |
null |
Clip限制该区域到其中内容可以画在画布上的区域。缺省的剪切路径定义为边界框。 |
下面是矩形的标记语法的一个例子:
<Rectangle Top=″600″Left=″100″Width=″100″Height=″50″Fill=″red″Stroke=″blue″StrokeWidth=″10″/> |
一个矩形在对象模型中有下面的属性(注意矩形是可读/写的,有缺省值等于0,支持继承并应用于元素和资源层次):
名称 |
类型 |
说明 |
Top |
BoxUnit |
矩形顶边的坐标 |
Left |
BoxUnit |
矩形左边的坐标 |
Width |
BoxUnit |
矩形的宽度 |
Height |
BoxUnit |
矩形的高度 |
RadiusX |
BoxUnit |
对圆角矩形,用来修整矩形的角的椭圆的X轴半径。如果指定一个负的X轴半径,将使用该半径的绝对值。 |
RadiusY |
BoxUnit |
对圆角矩形,用来修整矩形的角的椭圆的Y轴半径。如果指定一个负的Y轴半径,将使用该半径的绝对值。 |
下面是圆的标记语法的一个例子:
<Circle CenterX=″600″CenterY=″100″Fill=″red″Stroke=″blue″StrokeWidth=″10″/> |
一个圆在对象模型中有下面的属性(注意圆是可读/写的,有缺省值等于0,支持继承并应用于元素和资源层次):
名称 |
类型 |
说明 |
CenterX |
BoxUnit |
圆中心的X坐标 |
CenterY |
BoxUnit |
圆中心的Y坐标 |
Radius |
BoxUnit |
圆的半径 |
下面是椭圆的标记语法的一个例子:
<Ellipse CenterX=″600″CenterY=″100″Fill=″red″Stroke=″blue″StrokeWidth=″10″/> |
一个椭圆在对象模型中有下面的属性(注意椭圆是可读/写的,有缺省值等于0,支持继承并应用于元素和资源层次):
名称 |
类型 |
说明 |
CenterX |
BoxUnit |
椭圆中心的X坐标 |
CenterY |
BoxUnit |
椭圆中心的Y坐标 |
RadiusX |
BoxUnit |
椭圆的X轴半径。如果指定一个负的X轴半径,将使用该半径的绝对值。 |
RadiusY |
BoxUnit |
椭圆的Y轴半径。如果指定一个负的Y轴半径,将使用该半径的绝对值。 |
下面是直线的标记语法的一个例子:
<Line x1=″100″y1=″300″x2=″300″y2=″100″StrokeWidth=″5″/> |
一个直线在对象模型中有下面的属性(注意矩形是可读/写的,有缺省值等于0,支持继承并应用于元素和资源层次):
名称 |
类型 |
说明 |
X1 |
BoxUnit |
直线起点的X轴坐标。缺省值为”0”。 |
Y1 |
BoxUnit |
直线起点的Y轴坐标。缺省值为”0”。 |
X2 |
BoxUnit |
直线终点的X轴坐标。缺省值为”0”。 |
Y2 |
BoxUnit |
直线终点的Y轴坐标。缺省值为”0”。 |
‘折线’定义一组互相连接的直线段。通常,一个‘折线’定义一个开放形状。
下面是折线的标记语法的一个例子:
<Polyline Fill=″None″Stroke=″Blue″StrokeWidth=″10cm″Points=″50,375150,375150,325250,325250,375350,375350,250450,250450,375550,375550,175650,175650,375750,375750,100850,100850,375950,375950,251050,251050,3751150,375″/> |
<Polyline Fill=”None”Stroke=”Blue”StrokeWidth=”10cm”Points”50,375150,375150,325250,325250,375350,375350,250450,250450,375550,375550,175650,175650,375750,375750,100850,100850,375950,375,950,25,1050,25,1050,375 |
一个折线在对象模型中有下面的属性(注意直线是可读/写的,有缺省值等于0,支持继承并应用于元素和资源层次):
名称 |
类型 |
说明 |
Points |
PointCollection |
组成折线的点。坐标值在用户坐标空间中。 |
多边形元素定义一个封闭的形状包括一组互相连接的直线段的。下面是多边形的标记语法的一个例子:
<Polygon Fill=″red″Stroke=″blue″StrokeWidth=″10″points=″350,75379,161469,161397,215423,301350,250277,301303,215231,161321,161″/> |
一个多边形在对象模型中有下面的属性(注意直线是可读/写的,有缺省值等于0,支持继承并应用于元素和资源层次):
名称 |
类型 |
说明 |
Points |
PointCollection |
组成多边形的点。坐标值在用户坐标空间中。如果提供了奇数个坐标,那么该元素有错误。 |
在‘折线’和‘多边形’元素中的点的详细说明的语法用下面的符号来描
*:0或更多+:1或更多?:0或1():分组|.分隔选择 |
述:
下面使用上面的符号说明在‘折线’和‘多边形’元素中的点的详细说明:
list-of-points:wsp*coordinate-pairs?wsp*coordinate-pairs:coordinate-pair| coordinate-pair comma-wsp coordinate-pairscoordinate-pair:coordinate comma-wsp coordinatecoordinate:numbernumber:sign?integer-constant| sign?floating-point-constantcomma-wsp:(wsp+comma?wsp*)|(comma wsp*)comma:″,″integer-constant:digit-sequencefloating-point-constant:fractional-constant exponent?| digit-sequence exponentfractional-constant:digit-sequence?″.″digit-sequence |
|digit-sequence″.″exponent:(″e″|″E″)sign?digit-sequencesign:″+″|″-″digit-sequence:digit|digit digit-sequencedigit:″0″|″1″|″2″|″3″|″4″|″5″|″6″|″7″|″8″|″9″wsp:(#x20|#x9|#xD |#xA)+ |
结论
如前面的详细说明所示,提供了一种提供程序代码不同的机制来连接一个场景图形的系统、方法和元素/对象模型。该系统、方法和对象模型使用简单,但很强大、灵活,并可扩充。
虽然本发明容许不同的改动和作为其他选择的构建,某些在其中展示的实例在附图中展示并且在上面详细说明。然而应理解并不意味着限制本发明为所揭示的具体的形式,而是相反,本发明将覆盖所有的改动、作为其他选择的构建,及在本发明精神和范围之内的等价物。