Updates from 六月, 2011 Toggle Comment Threads | 键盘快捷键

  • Roger 10:06 am on June 2, 2011 固定链接 | 回复
    Tags: character encoding, charset, unicode   

    Character Encoding – Concepts and Practices 

     
  • Roger 8:44 pm on May 2, 2011 固定链接
    Tags: , , Java 2D   

    把大象放入冰箱与设计抽象性的思考 

    Q:如何把大象放入冰箱

    A:把冰箱打开,然后把大象放进去,再把冰箱关上

    当然这是一个脑筋急转弯的笑话,但是对于系统设计者的启示是,如果你设计了一个足够抽象的接口,理论上这个接口可以代表近似于无限的实现,简单的说越抽象的接口,它可以衍生出来的实现就越多。

    如果我们把上面的问题当作一个软件功能上的需求并对它进行建模,我们可能会推导出下面的UML模型:

    Elephant

    在上图中,接口Containable和Placeable表达了两个非常抽象的概念,一个代表可以放置Placeable的容器,一个代表可以放入Containable的物件,而类IceBox和Elephant 分别实现了这两个接口。

    如果用代码来表示把大象放入冰箱的过程,可能的代码如下:

     1: void placeElephantInIceBox(IceBox box, Elephant e)
     2: {
     3: 
     4:     box.open();
     5:     box.place(e);
     6:     box.close();
     7: }

    上面的思考主要是受最近在看的Java 2D Graphics一书的启发,Java 2D是Java提供的2D Graphics Library,它是一个全功能,桌面操作系统级别的2D绘图库,为了能够在实现复杂功能的同时保有一个尽可能简单的编程接口,并且允许用户继续对它进行扩充,Java 2D提供了一个高抽象度的模型,很多接口的抽象度基本就跟上面的Containable和Placeable类似。

    比如说Stroke接口代表了笔画,但是它实际上是将一个输入的Shape转换成根据该笔画描绘出来的轮廓的Shape(Java 2D只处理图形填充,所谓描绘图形的轮廓实际上是先生成图形的轮廓图形,然后再对该轮廓图形进行填充)。

    下面的代码基本等同于直接调用Graphics2D中的draw(graphics.draw(shape);):

     1: void drawShape(Graphics2D graphics, Shape shape)
     2: {
     3:     Stroke stroke = graphics.getStroke();
     4:     Shape strokedOutline = stroke.createStrokedShape(shape);
     5:     graphics.fill(strokedOutline);
     6: }

    其中createStrokedShape就是Stroke接口的唯一的方法。

     
  • Roger 11:12 am on April 30, 2011 固定链接 | 回复
    Tags: ,   

    段子 – Java,C++,C语言的不同 

    Java和C++的最大区别在于,Java程序员再烂,写的代码烂起来还是有底线的,而C++程序员写的代码,烂起来简直毫无底线可言,轻易就把整个系统crash 100遍啊100遍…

    而C和C++程序员的最大差别在于,C++程序员可以把自己的烂隐藏在类与对象的汪洋大海中,难以发现,而C程序员的烂如漆黑中的萤火虫,无处藏身。

     
  • Roger 7:00 pm on April 20, 2011 固定链接 | 回复
    Tags: , ,   

    Java 2D Graphics Reading Notes [3] 

    第三章 Geometry 几何图形

     

    这一章主要讲述的内容

    • 表示点的类
    • 两个用于描述几何图形的重要接口:Shape和PathIterator
    • java.awt.geom包里面各种表示不同几何图形的类
    • 多个图形组合后的新图形

     

    点(Points)

     

    在Java 2D中,几何图形都有类似的类阶层模式,一个抽象类代表一个二维平面的一个几何图形,它的若干内部类派生自这个抽象类,分别提供不同的坐标精度(整数,单精度浮点数,双精度浮点数)。

     

    image

    代表一个2D平面上的点的类阶层结构

     

    图形和路径(Shapes and Paths)

     

    回顾第二章的内容,Java 2D的图形渲染引擎Graphics2D的两种最基本的操作是图形填充和图形轮廓的绘制,对于Graphics2D而言,所有图形都是一个Shape(java.awt.Shape),一个Shape可能包括轮廓和内部区域,Graphics2D的draw方法用于绘制Shape的轮廓,fill方法用于填充Shape的内部区域。

     

    Shape的getBounds和getBounds2D方法可以返回一个矩形,它是图形的外廓矩形,刚好能够包裹Shape。

     

    image

    不同Shape的Bounds

     

    Shape还有一系统方法用于判断包括,相交等状况,不过它最重要的方法还是getPathIterator,用于返回一个描述图形轮廓的路径(PathIterator)。

     

    image

    Java 2D 内置提供的所有的Shape

     

    Shape的边界被称为路径(Path)。路径是由一系列基本指令组成,用于从一个点移动到另外一个点。比如下面的指令描述了一个正方形的轮廓。

    1. 移动到0,0
    2. 画一条线到72,0
    3. 画一条线到72,72
    4. 画一条线到0,72
    5. 画一条线回0,0

     

    在Java 2D中,PathIterator封装了一个路径的全部片段(segments),用来描述图形的轮廓。Shape的getPathIterator方法用来返回该Shape的PathIterator,PathIterator跟Enumeration类似,程序可以从开始逐个访问每一个片段直到路径的结束。

     

    Java 2D定义了5种可能的片段类型:

    1. SEG_MOVETO 用于移动当前的位置,不进行任何绘制操作
    2. SEG_LINETO 绘制一条直线
    3. SEG_QUADTO 绘制二次曲线
    4. SEG_CUBICTO 绘制三次曲线
    5. SEG_CLOSE 绘制一条直线回最后SEG_MOVETO的位置,用于结束当前的子路径

     

    image

    二次曲线包含一个控制点

     

     

    image

    三次曲线包含两个控制点

     

    PathIterator的currentSegment方法可以用来查询当前的路径片段,next方法可以用来移动到下一个片段,isDone用于查询是否到了路径的终点。

     

    image

    用于打印一个图形的轮廓路径的示例代码

     

    如果图形是一个比较复杂的轮廓路径相互缠绕的图形,如何对重叠区域进行判断是否属于图形的内部区域,Java 2D定义了两种缠绕规则(winding rules),WIND_EVEN_ODD和WIND_NON_ZERO用于图形内部区域的判断。PathIterator的getWindingRule方法可以用于返回该路径使用的缠绕规则。

     

    image

    WIND_EVEN_ODD规则

     

    WIND_EVEN_ODD规则定义了:画一条直线穿越整个图形,每次跟图形的边界相交,则计数器加1(从1开始计数),当交点的计数器是奇数的时候,以它为起点到下一个交点的线段处于图形的内部。

     

    image

    WIND_NON_ZERO规则

     

    WIND_NON_ZERO规则定义了:画一条直线穿越整个图形,每次跟图形的边界相交时,如果相交的边是从左向右的,则计数器加1(从0开始计数),如果相交的边是从右向左的,则计数器减1。当交点的计数器为0时,以它为起点到下一个交点的线段处于图形的内部。在这种规则下,图形反过来画,结果也是一样的。

     

    Java 2D除了提供一系列内置的图形外,GeneralPath可以用于产生自定义的图形的轮廓(GeneralPath派生自Shape)。

     

    image

    通过程序创建一个自定义图形的示例代码

     

    直线和曲线(Lines and Curves)

     

    Java 2D里面已经包括了一组表示直线和曲线的类,并且都实现了Shape接口,虽然程序可以使用GeneralPath来创建,不过直接使用现成的类会更方便。

     

    image

    Line2D的类阶层

     

    QuardCurve2D和CubicCurve2D可以用来构造二次曲线和三次曲线。

     

    矩状图形(Rectangles)

     

    RectangularShape是一个抽象类,用来表示那些被矩形包裹的图形,包括矩形,圆角矩形,椭圆形,弧形等。

     

    image

    圆角矩形

     

     

    image

    弧形

     

    构造性区域图形(Constructive Area Geomerey)

     

    在Java 2D中,我们可以把多个图形用不同的方式组合形成一个新的图形,新的图形类型为Area,它用来表示多个图形的组合。

     

    image

    不同的图形组合方式

     
  • Roger 2:06 pm on April 17, 2011 固定链接 | 回复
    Tags: , synchronized, thread   

    synchronized, wait and notify 

    最近在J2ME平台上写一个UI框架库,因为自己之前Java的经验并不是很多,比较熟悉的是Qt的那一套,搞了半天才发现Java的Object本身就可以作为互斥体和简单的信号量使用,利用sychronized关键字和Object的wait和notify方法(wait的语义非常的复杂,直接从字面上去理解很难明白它实际的作用),就已经可以完成大部分多线程编程的需要(在这里不得不膜拜一下Java语言的设计者…)。

     

    synchronized

    synchronized在Java中可以用来为当前运行的线程获得一个对象的监视器(monitor),或者简单地说获得一个对象的所有权。因为一个时间只能有一个线程可以获得对象的所有权,假设对象A已经被线程A获得,那么其它试图获得对象A的线程将会被阻塞,等待对象A被线程A释放。

    如果直接用synchronized来修饰方法,那么某个时刻调用该方法的线程所要获得的对象就是当前对象(this),如果synchronized用来修饰静态方法,那么线程所要获得的对象就是这个类的类对象(Class)。

    如果线程已经获得对象的所有权,它再次对该对象发起请求是可以的,但不会有任何效果也不会有什么副作用,可能只是简单增加一个计数值用于处理嵌套的状况。当线程退出一个synchronized块的时候,它进入这个块所需要获得的那个对象的所有权将会被自动释放,从而允许其它竞争该对象的线程可以获得对象的所有权。

     

    Object.wait

    如果一个线程已经获得某个对象的所有权时,它也可以暂时选择放弃而不用等到退出对应的synchronized块,线程可以调用对象的wait方法放弃对象的所有权并把自己置于等待状态,在该状态下线程无法被执行和调度直到被其它线程通知,或者被中断,或者指定的timeout时间已经耗尽。

    当线程从等待状态中被唤醒重新参加调度和执行时,并不意味着它可以马上跳过wait运行下去,实际上,它需要参与之前放弃的对象的竞争直到它重新获得该对象的所有权为止,当它重新获得时,对于该线程而言,一切又恢复到了之前的状态,这个时候它从wait方法中返回,并带着对象的所有权继续执行下去直到对应的synchronized块的结束。

    所以理解wait方法的关键在于,它是用于暂时放弃对象的所有权,并在等待某段时间后重新参与该对象所有权的竞争。而timeout参数可以用来控制等待的时间,如果timeout为0,线程会被置于等待状态直到被其它线程通知或者线程被系统中断,这里潜在的危险在于如果没有一个别的线程去获得这个被原线程放弃的对象并通过它去通知原线程,原线程可能永远处于等待状态而不会被唤醒,而处于等待状态的线程不会被系统调度,等于是一个dead thread。如果存在上述的状况,指定一个非0的timeout的时间是绝对必要的,这时即使没有其它线程来通知,在timeout时间后,原线程也会被自动唤醒,如果此时对象的所有权没有被别的线程占有,原线程可以马上获得这个对象并继续执行下去。

     

    Object.notify and notifyAll

    有了上面对wait的理解,要理解notify就比较简单了。对象的notify方法可以被一个拥有该对象的线程用来唤醒其它因为这个对象而处于等待状态的线程(它们调用了对象的wait方法),被唤醒的线程需要重新参与对这个对象所有权的竞争,所以一般notify调用都是在调用线程准备放弃该对象所有权的时刻发生(synchronized块的末尾),这样被唤醒的线程马上就能够得到对象的所有权并正常执行下去。

    假设在对象上等待的线程有多个,notifyAll可以唤醒全部等待的线程,它们一起参与对对象所有权的竞争,而notify则随机唤醒其中的一个线程,其它线程仍然处于等待状态直到一个新的线程调用notify或者notifyAll。

     
  • Roger 7:58 am on April 13, 2011 固定链接 | 回复
    Tags: , Josh Bloch, Puzzle   

    Geek Time with Josh Bloch(Josh Bloch访谈录) 

    原文:http://google-opensource.blogspot.com/2011/04/geek-time-with-josh-bloch.html?utm_source=feedburner&utm_medium=feed&utm_campaign=Feed%3A+GoogleOpenSourceBlog+%28Google+Open+Source+Blog%29

     

    视频搬到了优酷:http://v.youku.com/v_show/id_XMjU4NDczMDI0.html

     

    In addition to being known as “The Mother of Java,” Josh Bloch is also the Chief Java Architect at Google. Josh sat down with fellow Open Source Programs Office colleague Jeremy Allison to chat about APIs, the importance of openness, and the successes and failures of Java.

    “Java之母”Josh Bloch目前是Google的首席Java架构师(之母,汗囧… 不过之父现在也去了Google)。Josh日前接受了Google开源计划办公室的同事Jeremy的采访,就APIs设计,开放的重要性和Java成功与不足之处等问题展开亲切友好的会谈…

    Some highlights from their conversation(对话中的亮点):

    (0:45) Josh describes what he does for Java and at Google.(Josh描述了他之前关于Java的工作和现在Google的工作)
    (1:59) Jeremy expresses his disappointments with Java, based on the early potential that it showed. Josh responds with an explanation of some of the difficulties that Java faced.(Jeremy表达了对Java的失望之情,觉得它没能达到早期潜力显示出来的高度。Josh回应,解释了一些Java面临的困难。)
    (4:48) Josh and Jeremy talk about some of the factors that contributed to Java’s successes.(Josh和Jeremy讨论了一些Java成功的因素)
    (9:51) Josh’s explains his philosophy towards creating APIs.(Josh解释了他设计APIs的哲学)
    (14:30) Josh talks about the APIs that he’s most proud of.(Josh谈及了他最感自豪的那些APIs)
    (19:45) Josh and Jeremy discuss the importance of reading other people’s code, and the impact of Sun’s decision to put the code and bug database for Java on the web.(Josh和Jeremy讨论了阅读别人代码的重要性,Sun决定把Java代码和bug数据库在网上公布的影响。)
    (24:00) Josh explains how he came to be in his current position and gives advice for others who are looking for ways to get started programming.(Josh解释了他如何取得目前的成就,并对刚开始学习编程的工程师给出一些建议(编程毁一生,还是赶紧去做点别的什么吧…))
    (27:32) Josh wrote the standard Java best-practices guide, Effective Java, and co-authored two other Java books: Java Puzzlers, and Java Concurrency in Practice. As a special treat for this blog’s readers, Josh is offering signed copies of his books for the first two people with correct responses to the following puzzle. Submit your answer as a comment on this post, then use the same Blogger ID to leave a separate comment with your contact info and inscription request (for your privacy, the comment with your contact info will not be published).(Josh是Java编程最佳实践指南Effective Java一书的作者,也是Java Puzzlers和Java Coconrrency in Practive的合著者。为了表示对博客读者的优待,Josh会为能够正确回答下面的谜题的前两位读者提供他的签名书籍。有意者请在评论中提交答案,并使用同样的Blogger ID在另外一个单独的评论中留下联系方式和希望的题词)

    Josh’s Puzzle: “The Story of O”(O的故事)

    The following Java program is not quite complete; it’s missing a parameter declaration for o. Can you provide a declaration that makes the program print “O noes!”? (The program must compile without generating any warnings.)
    下面的Java程序还不完整,方法story中的o参数缺少类型声明,请为o提供类型声明使得程序打印出“O noes!”(程序必须编译通过并且不会产生任何警告 Object…

    
    public class Story {
       public static void main(String[] args) {
           Object o = null;
           story(o);
       }
    
       private static void story(<you provide the declaration> o) {
           if (o != null)
               System.out.println("O noes!");
       }
    }
    
    

    Remember to leave your answer and contact info as two separate comments!
    记得把答案和联系方式分开提交!

    By Ellen Ko, Open Source Team

     
  • Roger 12:01 am on April 12, 2011 固定链接 | 回复
    Tags: , , Operator overload   

    CSDN旧文 – Java 为什么没有操作符重载 

    最近一直在看Herb 的 Exceptional C++ 系列书籍 ,Scott Meyer 在书的序中写到,Exceptional C++是一本常常会”make me surprise” 的书,的确,即使使用了C++ 已经很多年,但看这样的书仍然让自己觉得对C++的理解还十分浅薄。

    但是正如不断有人提出来的这个问题 - “是系统设计的复杂程度本身需要那么多的知识,经验和技巧还是C++ 自己本身太过复杂” (嗯,我个人的理解是二者皆是 ^_^),Bruce 在 Thinking In Java 4 的序中写到,随着自己不断加深的对Java的理解,越来越了解到Java语言的设计目标就是使得描述和构建复杂系统的工作变得更容易,一个即使只有有限的知识和经验的程序员也能够使用Java来进行某种复杂程度的系统建构工作,Java就是一门更容易,能够在更高的抽象层次上描述系统的语言,也许它不那么高效,有时也显得不够灵活,但是它不会经常让你 surprise…

    回到题目的话题,Java为什么没有操作符重载,C++ 的操作符重载是其语言强大与灵活的一个重要特征, 我今年因为工作原因接触过Quatro DSP的模拟器,Quatro DSP是一个用于打印机,扫描仪的数字图象处理器,模拟器本身其实是一个C++库,通过库的支持能够把Quatro DSP的汇编语言转化为合法的C++ 函数调用,从而在C++的集成开发环境(如 VC)中模拟DSP汇编语言的执行,主要工作原理无非是将寄存器定义为某个类类型对象,从而使用宏替换和操作符重载将汇编语言变成函数调用。

    C++不但有操作符重载,还有自定义类型转换 (嗯,大多数的C++书籍都会劝你在没有正当理由的情况下千万不用去使用后者),Exceptional C++书中在讲到异常安全代码的章节中有这么一段话 ——

    “In particular, it helps to develop a habit of eyeing with mild suspicion anything that might turn out to be a function call — including user-defineed operators, user-defined conversions, and silent temporary objects among the more subtle cluprits – because any function call might throw.”

    “实际上,它会帮你养成这样的习惯,对所有语句都带着怀疑的眼光,猜测这有可能会是一个函数调用 - 包括自定义操作符,自定义类型转换,和一些更是难以捉摸的语境中悄悄产生的临时对象 - 因为函数调用就有可能抛出异常。”

    Herb写这段话的目的是为了说明在C++中写异常安全或者异常中立的代码,你必须小心分析那些语句有可能抛出异常,而一个模版代码中的大部分语句都十分有可能是一个函数调用,而函数调用就有可能抛出异常,最后剩下给你的确定不会抛出异常的语句会非常的少… … 如果你没有做好这样的心理预期,想当然的认为大多数语句都不会抛出异常,那么你被 surprise的几率就会非常之高 … …

     
  • Roger 11:48 pm on April 11, 2011 固定链接 | 回复
    Tags: , , Minijoe   

    MiniJoe UI架构和样式标注部分代码分析 



    image

    上图显示一个较为完整的HtmlBrowser的UI的部分类,其中:

    1. 在uibase包下面的xxxWidget是一组基本UI组件
    2. 在html包下面的xxxWidget是页面元素对应的UI组件
    3. css包下面的类用于CSS解析,生成解析后的结果

    一些类的具体职责如下:

    Widget

    1. 所有UI组件的根类
    2. 也包含了容器部分的逻辑

    TextWidget

    1. 用于显示文本的Widget

    ScrollWidget

    1. 在Widget的基础上提供了滚动支持的逻辑

    Window

    1. 定义了一个顶层窗口的接口,由具体的窗口类实现
    2. (minijoe的Window的实现跟其它GUI框架稍微有些不一样,Window不是一个继承自Widget的UI组件,而是一个接口,具体的实现类需要在自己内部定义一个默认的UI组件布局和对应的事件路由逻辑)

    HtmlScreen

    1. 实现了Window接口,作为顶层窗口
    2. 继承Canvas,提供屏幕输出和事件处理相关的逻辑
    3. 包含一个HtmlWidget,用于展现一个HTML/XML页面

    HtmlBrowser

    1. 继承MIDlet
    2. 处理命令

    TextFragmentWidget

    1. 用于显示文本片段的UI组件
    2. 包含一个Element的引用回溯对应的文档树的Element节点

    BlockWidget

    1. 对应页面的Block元素
    2. 可以包含其它BlockWidgets(包括InputWidget 和 TableWidget)和TextFragmentWidget
    3. 包含一个Element的引用回溯对应的文档树的Element节点

    InputWidget

    1. 对应页面的Input元素,包含输入框,组合框,按钮等

    TableWidget

    1. 对应页面的Table元素,展现一个表格

    HtmlWidget

    1. 对应整个页面(可以看作是对应Body元素)
    2. 包含一个Body元素的Element成员,该Element展开构成了一颗文档树(Body Element还不是根节点,它的父亲为Html Element)
    3. 包含一个样式树的根节点styleSheet,该成员用来读取样式表数据生成完整的全局样式树
    4. 负责整个页面的资源加载和管理,如图像资源,CSS/JavaScript脚本等
    5. 包含一颗由BlockWidgets/TextFragmentWidget组成的UI组件树
    6. 负责处理用户跟页面的交互(可能委托外部的处理器进行处理)
    7. 负责处理JavaScript,提供一个JavaScript执行的环境

    Element

    1. 作为文档树的一个节点,包含该节点的内容和属性(如class, id 等)
    2. 可以包含子节点,在minijoe里面只区分两种节点,最简单的文本直接用String表示,其它则用Element表示,Element的子节点可以是其它Element或者String,所以文档树任意一个非叶子节点一定是一个Element,而叶子节点可能是一个String也可能是一个Element
    3. 同时包含样式数据,所以Element构成的树,即做为文档树,在进行样式标注后也做为作为渲染树而存在,在minijoe里面没有像DOM一样的单独的文档树

    CSSTokenizer

    1. 识别CSS文本中的标记的解析器

    Style

    1. 对一个规则集合块(Rule Set Block)或者仅仅一条声明进行解析后产生的结果
    2. 包含一条或者多条声明的数据,每条声明对应一个属性和值对
    3. 可以通过set方法将两个Style按覆盖模式进行合并
    4. 可以通过inherit方法将两个Style按继承模式进行合并

    StyleSheet

    1. 包含多个Style,对应同一个选择符(Selector)
    2. 包含多个StyleSheet的哈希表,对应不同的选择符和不同类型的选择符,形成一颗样式树
    3. HtmlWidget里面的styleSheet成员为该页面的样式表数据解析后所产生的样式树的根节点

    HtmlWidget页面解析和渲染的一个简略过程:

    1. 解析页面数据生成文档树
    2. 解析CSS数据生成样式树(1,2可以交错进行)
    3. 使用样式树对文档树进行节点样式标注,自顶向下,先根遍历,最后产生的完整节点的样式数据保存在computedStyle成员中(minijoe支持多遍样式标注,每次更新样式树时都会重新标注,并根据一些条件判断是否需要重新生成UI组件树)
    4. 使用样式标注后的渲染树(样式化的文档树)生成UI组件树,每个UI组件都包括一个Element的引用回溯对应的文档树节点
    5. 生成UI组件树之后,HtmlWidget需要重新计算布局,确定每个UI组件的位置和大小
    6. 在HtmlWidget内部显示这颗对应页面渲染树(样式化的文档树)的UI组件树
    7. 在UI组件绘制自身的过程中,它需要从对应的Element的computedStyle取出绘制所需要的样式数据

    HtmlWidget生成样式树的过程:

    1. 在解析页面数据生成文档树的过程中,如果碰到STYLE元素,并且媒体类型(media type)匹配 ,Element会请求HtmlWidget将这段样式直接读入它的styleSheet成员
    2. 在解析页面数据生成文档树的过程中,如果碰到LINK元素,并且媒体类型(media type)匹配 ,Element会请求HtmlWidget加载该外部资源
      1. HtmlWidget会请求资源读取线程读取该资源文件(异步)
      2. 当资源读取完毕,资源读取线程会调用HtmlWidget的addResource方法加载资源
      3. 当资源是CSS样式表时,HtmlWidget会将该资源读入styleSheet成员
      4. 如果这时文档树已经生成完毕,则进行样式标注,否则等待文档树生成完毕后进行样式标注
      5. 如果文档树已经标注过,则重新进行样式标注,并且根据需要选择是否重新生成UI组件树
    3. 如果在解析样式表的时候碰到at规则 import,并且媒体类型(media type)匹配,StyleSheet会请求HtmlWidget加载该外部资源,执行第2步的操作

    HtmlWidget对文档树进行样式标注的过程(实际上对文档树的样式标注可以从任意元素节点进行,不过对于HtmlWidget来说,要对整个文档树进行完整的样式标注是从Body元素节点开始):


    1. 当文档树生成完毕或者styleSheet成员所指向的样式树更新时,则进行样式标注(applyStyle),支持多遍标注
    2. 调用Body Element的apply方法进行样式标注,对文档树的样式标注是一个对树进行先根遍历的过程
    3. Element的apply方法接收applyHere和applyAnywhere两个参数,它们都是StyleSheet的队列,applyHere表示可用于当前元素的样式表,applyAnywhere表示可用于当前元素和它的所有子元素的样式表,HtmlWidget在调用Body Element的apply方法时,就把styleSheet成员放入applyAnywhere中作为参数
    4. 元素节点的样式标注最重要的一个步骤就是从通过参数传递的可用样式表集合中收集样式(StyleSheet的collectStyles方法):
      1. 可以应用于当前元素的样式集合(queue)
      2. 可用于子节点和后代节点的样式表集合(childStyles和descendantStyles)
      3. 其中applyAnywhere里面的样式表会直接放入descendantStyles中传给子节点
      4. 在collectStyles方法里面,需要对元素的name,id,class和其它属性进行查询,以收集到匹配的样式,如果不需要支持其它属性,理论上可以传name,id,class Strings来代替Element实例
    5. 产生当前元素的最终样式
      1. 对收集的可以应用于当前元素的样式集合进行覆盖式合并,按照权重由低到高的优先序(权重高的样式覆盖权重低的样式)
      2. 如果当前元素有style属性,用该属性的样式数据对第1步结果得到的样式进行覆盖式合并(style优先)
      3. 使用第2步的结果跟父元素的计算后样式computedStyle进行继承式合并
      4. 如果当前元素有其它inline的样式数据,将这部分数据抽取合并到第3步的样式结果里面(applyHtmlAttributes)
      5. 把最后的结果作为当前元素的计算后样式computedStyle,当前元素的样式标注就结束了
      6. computedStyle里面的数据会用于UI组件树的布局和渲染,如果没有UI组件树,则需要直接从computedStyle里面把样式数据逐个取出来放到确定的地方
      7. 其中还有一些值的计算可能需要依赖于外部传入的参数,如使用的字体的高度,宽度,盒子的大小,使用的DPI值等
    6. 继续标注当前元素的子元素,childStyles变量作为applyHere参数,descendantStyles变量作为applyAnywhere参数
    7. 决定是否需要重新生成UI组件树
     
  • Roger 7:04 pm on April 10, 2011 固定链接 | 回复
    Tags: , ,   

    Java 2D Graphics Reading Notes [2] 

    渲染引擎(Rendering Engine)

    所谓渲染是这样一个过程,它处理一组图形,文本和图像的集合,然后计算出在屏幕或者打印机上的像素应该是什么颜色。图形,文本和图像被称为图元(graphics primitives),屏幕和打印机被称为输出设备(output devices),而负责处理这一绘制过程的就是所谓的渲染引擎,在Java 2D API中,其中的2D渲染引擎就是类Graphics2D,Graphics2D同时还表示一个绘制表层(drawing surface),该绘制表层可以是窗口的表面,可以是打印中的页面,还可以是离屏位图。

     

    image

    渲染引擎把图元绘制到输出设备上

     

    渲染流水线(Rendering Pipeline)

    渲染引擎的状态

    渲染引擎使用内部的状态来决定如何将图元转换成像素颜色,Graphics2D的内部状态一共包括7种元素:

    1. paint 笔刷决定了图形如何填充
    2. stroke 笔画决定了图形的轮廓如何绘制
    3. font 字体决定了文本中的字符生成的图形的形状
    4. transformation 所有的图元在真正渲染前都会先执行几何变换
    5. composting rule 混合规则决定当前绘制的图元的颜色如何跟绘制表层的现有颜色进行混合
    6. clipping shape 裁剪区域决定了有效的绘制区域,如果为空,则整个绘制表层都可以被绘制
    7. rendering hints 渲染示意决定了渲染过程中的可能采用的不同演算法
      所谓渲染流水线就是渲染引擎处理图元绘制的一系列过程,下图显示了Graphics2D内部的7种状态如何影响渲染过程:

      image

      渲染流水线

      在Java 2D API中:

      • fill()方法用于图形的填充
      • draw()方法用于图形轮廓的绘制
      • drawString()方法用于绘制文本
      • drawImage()方法用于绘制图像

      整个绘制过程可分为5步,第一步主要取决于要绘制的图元:

      1. 对于绘制图形轮廓或者绘制文本,实际上都是生成一个轮廓的图形或者文本的图形,然后对生成的图形进行填充,所以实际上只有两种最基本的绘制,图形填充和图像绘制。而第一步就是对图形或者图像进行几何变换;
      2. 对图形进行光栅化,根据图形生成一个像素覆盖值(pixel coverage values)集合,值的大小由该像素跟图形区域的相交状况决定,光栅化的算法通常由渲染示意决定;
      3. 使用裁剪区域设置进行裁剪;
      4. 根据当前的笔刷设置进行图形填充(图像则直接跳过这一步);
      5. 跟绘制表层已有的颜色进行混合,最后输出混合后的颜色;

        Alpha, Alpha, Alpha!!!

        渲染是一个近似的过程。当你希望填充一个理想的图形时,实际上渲染引擎只能试图计算出输出设备的像素应该如何着色来得到一个最逼近绘制图形的结果。举例来说,假设程序要用纯色填充一个图形,有快速但是绘制质量不高的算法,也有绘制质量高但是速度较慢的算法。

        锯齿和反锯齿

        快速的算法就是只填充完全位于图形之内的像素。下图的R则采用了这种算法进行填充:

        image

        狗齿遍布的R

        好的算法则根据像素跟图形交叠的区域计算出一组像素覆盖值,然后以覆盖值作为比例系数来对像素进行着色,这种技术被称为反锯齿。

        image

        经过反锯齿算法渲染的R有着更平滑的边缘

        光栅器(Rasterizer)

        在渲染流水线中,光栅器负责将理想的图形转换成一组像素覆盖值,也就是所谓的alpha值,图像所有alpha值的集合被称为alpha通道。

        像素的alpha值决定了它的透明度,如果把alpha值看作是颜色的一部分,它就是颜色的透明程度。Alpha值位于0.0~1.0之间,0.0代表像素跟图形完全不交叠,1.0则代表像素完全位于图形之内,而反锯齿算法则为部分交叠的像素产生一个0.0~1.0之间的值。

        image

        光栅器使用反锯齿算法产生的alpha值

        然而最终像素的颜色的决定还不仅仅取决于绘制的图形,它还取决于如何跟原有的像素颜色进行混合。

         

        混合(Compositing)

        当光栅器为绘制的图形产生alpha值后,混合规则决定了待绘制的颜色如何跟绘制表层原有的颜色进行混合产生最终的输出颜色,混合规则其实就是一个方程式对每个像素进行计算,最简单的算法就是待绘制的颜色完全取代原有的颜色。

        image

        绘制新的图形到绘制表层

         

        坐标空间(Coordinate Space)

        Java 2D的图形被绘制一个笛卡尔坐标平面上,这个平面被称为所谓的用户空间(User Space)。当实际输出到显示器或者打印机时,用户空间坐标需要被转换成设备空间(Device Space)坐标,通常情况下,设备空间以1像素为1单位。一般而言,用户空间和设备空间是对齐的,默认情况下72个用户空间单位为1英寸,而显示器的DPI值默认刚好是72DPI。也就是说当输出设备是显示器,并且在默认设置下时,1用户空间单位刚好是1像素,并且用户空间到设备空间的转换比例刚好是1:1。(书中的最后一段比较难理解,只能是看到后面的章节再重新回顾这一段…)

         
      1. Roger 3:12 pm on April 10, 2011 固定链接 | 回复
        Tags: , ,   

        Java 2D Graphics Reading Notes [1] 

        image

        Java 2D Demo

        Java 2D Graphics

        Java 2D Graphics是一本介绍Java 2D API的书,但是它可贵之处在于并不仅仅告诉你这些API的用法,而是透过Java 2D API的使用讲解2D绘图的基本原理和概念,甚至包括Java 2D API内部的一些实现细节。通过这本书,不但可以学习到Java 2D API的使用方法,还能学习到很多关于2D绘图和2D绘图引擎实现的知识,从而使得要掌握其它GUI工具库的2D绘图引擎的使用也变得十分简单,甚至还可以自己实现一个完整2D绘图引擎或者其中部分的功能。

        一个2D绘图引擎可以做什么

        1. shapes 图形,可以创建由直线或者曲线组成的任意形状的图形
        2. stroking 笔画,可以使用不同的笔画绘制线段和图形的轮廓
        3. filling 填充,可以使用纯色,图案,渐变色等各种方式来填充一个图形
        4. transfromation 几何变换,可以对绘制的图元进行拉伸,挤压,旋转等几何变换
        5. alpha compositing alpha混合,可以支持多种alpha混合算法
        6. clipping 裁剪,可以对绘制设置任意形状的裁剪区域
        7. antialiasing 反锯齿,可以使用反锯齿技术避免参差不齐的边缘
        8. text 文本绘制,支持TureType和Type 1字体加载,根据加载的字体生成字符的图形,然后进行绘制
        9. color 颜色校正,支持对颜色进行校正,符合设备的特性和当前的照明条件
        10. images 图像,支持图像的绘制,包括几何变换,裁剪,alpha混合,还支持不同图像格式的编码和解码
        11. image processing 图像处理,可以对图像做简单的特效处理
        12. printing 打印,支持绘制到打印机
         
      c
      Compose new post
      j
      Next post/Next comment
      k
      Previous post/Previous comment
      r
      回复
      e
      编辑
      o
      Show/Hide comments
      t
      返回顶部
      l
      Go to login
      h
      Show/Hide help
      shift + esc
      取消