这个夏天

改好 [DictUnifier](http://code.google.com/p/mac-dictionary-kit/) 的 [Lion](http://www.apple.com/macosx/) 支持已经将近九点,在奥斯陆的公寓窗外望去,天色已经深沉,地上依旧湿漉漉的,想是小雨还间或下着。这个时节的奥斯陆,气温已经在十度上下徘徊,虽然周围的树叶草坪还绿,冷风已经带上些寒意了。一看上次更新 blog 还是七月下旬,转眼一个多月过去,夏天已经结束了。

去年夏天我结束了三年的研究生学业,一个人跑到远隔万里的奥斯陆来工作,对于生活确实是个极大的变化,但诸般变化总还在计划里,慢慢也就适应了。然而今年夏天对于我生活的影响也许比去年还要大,既然如此,不妨暂且放下这个 blog 向来只更新技术内容的习惯,多记上一笔。

在这个夏天里,我邀请父母来奥斯陆,在我自己布置的公寓住了一个多月,一起游览了挪威的海边小镇 [Ålesund](http://www.flickr.com/photos/jjgod/tags/ålesund/)、乘船到 [Geiranger](http://www.flickr.com/photos/jjgod/tags/geiranger/) 峡湾、爬上 [Preikestolen](http://www.flickr.com/photos/jjgod/tags/stavanger/);还逛过了[巴黎](http://www.flickr.com/photos/jjgod/tags/paris/)和[布拉格](http://www.flickr.com/photos/jjgod/tags/prague/),自己则两次到[柏林](http://www.flickr.com/photos/jjgod/tags/berlin/)出差,虽然匆忙,也算走过了许多地方,最适合旅游的暑假没有浪费,当然更重要的是在自己安排下带父母出门,这还是平生头一次,其中也有遗憾,但总算走下来了。

这个夏天里我还遇上了自己喜欢的人,认真地开始一段感情,长时间一个人的生活,到现在这种时常牵挂另一个人的心境,其实还颇有一点不大适应,但总归要慢慢学习和成长。

如果要说最大的遗憾,应该是没有花更多时间陪父母,尤其是花在旅程上的时间上多了,计划得略显匆忙,交流其实反而少了。二是因为工作繁杂,生活变化也大,很多原来坚持做的开源项目都没有更新,随着 Lion 的推出都应该陆续完成的,目前已经完成了 TextEdit 和 DictUnifier 的更新,希望接下来继续。

套用《甲方乙方》里那句话,这个夏天过去了,我很怀念它。

To all my friends

昨天下午,奥斯陆市中心发生了大型爆炸事件,我经常经过的一条路两侧一片狼藉;随后在 Utøya 的青年夏令营发生了持枪屠杀事件。已有 87 人遇难。

我与家人目前都还安好,也在积极关注事态的发展,谢谢各位的关心。

可以想见的是,无论这两件史无前例的事件最终调查结果如何,都必然会对挪威造成非常深远的影响。作为旅居的华人,我也会在 blog 和 twitter 上继续介绍我了解到的情况。

Is Cocoa a legacy framework?

[Kyle Neath](http://warpspire.com/) wrote an [excellent piece](http://warpspire.com/posts/designing-github-mac/) about the design process of [GitHub for Mac](http://mac.github.com/). What’s more interesting is his concern about the Mac OS X/Cocoa framework as a whole, it’s the best criticize of Cocoa that I’ve seen for a while, I sincerely recommend any developer interested in Cocoa to read.

According to Kyle, a first-time Mac OS X designer/developer, Cocoa is dying for a framework. This argument feels a bit ironic since I came from the age when a bunch of Cocoa fanboys were labeled as the “[Delicious Generation](http://en.wikipedia.org/wiki/Delicious_Generation)” who only wrote fancy good looking apps with no actual functionalities, while the good ‘ol Carbon guys looked so damn unattractive. Has it now come to the downfall of us Cocoa developers?[^1]

Kyle’s main reasons for not liking Cocoa are as follows:

1. Drawing in code is slow and painful. Images are easier to work with and result in more performant code.
2. There is no layout engine for Cocoa. If you want two elements to rest side to side, you’ll need to calculate the pixel size of the text, padding, borders, margins — then manually position the next element.
3. There is no styling engine in Cocoa. (To change the background color of a button for instance will require significant changes)
4. Learning the differences between layer-backed views, layer-hosted views — understanding that you have to subclass _everything_ — balancing delegates, weak connections, strong connections, KVC, view controllers, and notifications — understanding little intricacies like how AppKit flips `.xib`s when it load them up or how hard it is to make one word in a sentence bold.

I will try to share some of my opinions about them, one by one.

#### Is draw in code slow and painful?

Being a low level graphics developer for so long, I know my opinion must be biased. But still, I’ve never felt drawing in Cocoa (or Cocoa Touch) to be slow, working with Core Graphics, Cocoa Drawing API and APIs like Core Text is quite pleasant actually. Drawing operations are always blazingly fast. I honestly couldn’t see a better way to solve these problems without resorting to low level. Yes, they are imperative APIs rather than declarative, yes, you have to do your `-drawRect:` code cautiously. But low level drawing itself has never been a bottleneck for my programs. The _real_ problems are **scheduling what to draw** and **finding out when stuff is drawn**.

So in my opinion, it’s not the drawing itself that’s slow and painful, but you need to have a **thorough understanding of the graphics stack** to be able to write efficient low level imperative drawing code. That’s a damn steep learning curve. That’s why very few people can write efficient yet complex drawing code even veterans like Loren Brichter recommend to “[do your own drawing](http://blog.atebits.com/2008/12/fast-scrolling-in-tweetie-with-uitableview/)”.

However, there is no silver bullet. Cocoa Touch makes life a little bit easier by simplifying the view hierarchy and build with Core Animation from ground up. But to have butter smooth scrolling, you still need to do your own drawing cautiously. Apple did provide layer-based and layer-hosted views in Cocoa, but they are too conservative to re-architect the entire Cocoa view stack with Core Animation, that’s a pity but luckily you can always roll your own (as we always did).

Anyway, the best framework I can imagine is the one using a declarative API to construct most of the UI and leverage the full power of GPU, with flexibility to do custom drawing with an imperative API.

#### No layout engine for Cocoa?

Apparently, Apple is solving this with [Cocoa Autolayout](http://www.scribd.com/doc/50426060/Mac-OS-X-v10-7-Lion). Can’t say much about this (it’s in NDA) but it does look promising.

#### No styling engine in Cocoa?

Well well, this is controversial. On one hand people are complaining that UI in Mac OS X apps are not consistent anymore, on the other hand people detest [Aqua](http://en.wikipedia.org/wiki/Aqua_(user_interface)) and dream for change from the bottom of their heart. I can understand Kyle since he is more of a designer than a developer. With no doubt I think that’s a fair judgement, but it’s hard to justify whether the styling difficulty contributes to [HIG](http://developer.apple.com/library/mac/#documentation/UserExperience/Conceptual/AppleHIGuidelines/XHIGIntro/XHIGIntro.html) consistency or drives people to the opposite side of it. As a user of Mac OS X I felt that UIs should be “semi-consistent”, or as [John Gruber](http://daringfireball.net) [puts](http://daringfireball.net/2011/01/uniformity_vs_individuality_in_mac_ui_design) it eloquently: [uniformity has been replaced by conformity](http://vimeo.com/21742166).

Apple is acting slow and they can definitely improve on this. The solution doesn’t have to be very innovative, adding styling flexibilities here and there is no groundbreaking change. Cocoa Touch is not far superior either, there is still a lot of work to do if you don’t like the standard controls from UIKit. In contrast, my biased opinion would say that [QML/Qt Quick](http://qt.nokia.com/qtquick/) appears to be a much more flexible solution, it’s [XUL](http://en.wikipedia.org/wiki/XUL)/[Air](http://www.adobe.com/products/air/)/[JavaFX](http://javafx.com/)/[WPF](http://en.wikipedia.org/wiki/Windows_Presentation_Foundation) **done right**. Though I am biased, who knows me well should know that I’m hard to convince as a low level graphics engineer.

#### Cocoa has complicated intricacies?

Yes, any framework survived more than 20 years will have some intricacies, that’s inevitable. Actually I’m surprised by the fact that so many of the [NeXTSTEP](http://en.wikipedia.org/wiki/NeXTSTEP) APIs are still in good use. Come on, it’s a fast changing industry and you really need to be a genius to predict the trend in more than 10 years.

However, most of Kyle’s concerns came from his expectations: he expects to fit UIKit model **as is** into Cocoa but it didn’t work. It probably never will, as I said previously, Apple is conservative in that sense, for stable platform like Mac OS X, they tend to provide _new options_ other than forcing everyone to _adapt to_ the new model. That inherently complicates the framework, of course. Changes that us Cocoa developers applauded for can be seen as too cautious by iOS developers.

Anyway, I think it’s fair to say that Cocoa isn’t the best API for modern, dynamic UI, alternatives like [Chameleon](https://github.com/BigZaphod/Chameleon) may eventually surpass it. Nevertheless, there is no easy path between both worlds, you either port the code to the new model (UIKit like) or adapt to the hybrid model (Cocoa like). For iOS developers, the former is definitely easier, but for us the latter seems to be a better option.

That’s it. I’m surprised that you can read my ramblings to this far 🙂

[^1]: I’ve never labeled myself as part of the “Delicious Generation”, I will rather do something useful if it can’t be good looking at the same time. But still, it’s sad.

X100 与 GF2

Fujifilm FinePix X100
Fujifilm FinePix X100

自从上个相机 (Nikon D90) 丢在马德里之后,就一直在寻找比较轻巧的替代,D90 的画质虽然不错,但平时几乎不愿意带出门去,其间关注了 Olympus E-P 系列、Panasonic Lumix GF 系列和 Fujifilm 的 FinePix X100,今年复活节假期之前还没等到 X100 就先买了台 GF2 (14mm f/2.8) 尝试,上周刚刚拿到 X100,可以说说对这两台相机的印象了。

之所以先选择 Panasonic GF2,主要是因为另外两个选择:Olympus E-PL2 和 Sony NEX-5 带的镜头都不令人满意,E-PL2 在我这里只有变焦套机卖而 NEX-5 的 16mm f/2.8 定焦画质收到很多批评,而 GF2 的 14mm f/2.8 正好非常适合旅游的风景照和到此一游照,后来[事实证明](http://www.flickr.com/photos/jjgod/tags/eastertrip/)效果确实也不错。

我对于 GF2 的印象是:

* 体积非常小,搭配 14mm/2.8 镜头,一只手就可以很方便的握持和操控,适合拍一些角度比较低的照片。
* 对焦奇快,习惯了单反的对焦速度,改用 GF2 基本不会有任何效率的损失,当然据说这也和镜头搭配有关,Panasonic 14mm f/2.8 镜头应该是目前最快的。
* GF2 的高感光度画质是软肋,但因为我主要是拍旅游风景照,基本上没拍什么夜景,所以把 ISO 控制在 800 以下时,画质还是很出色的,至少看不出比 APS 幅面的相机逊色。
* 操控比较简单,但因为去掉了大部分的拨盘,基本改用触摸加按钮操控,所以偶尔还是不够方便,尤其像切换对焦点、更改快门、增加曝光补偿之类的常用操作,我还不太习惯。
* 一个教训是如果让别人帮忙拍到此一游照,一定要把对焦模式改成面部识别。

这个相机总的说来还是很满意的,喜欢轻巧搭配的可以加上 Panasonic 20mm f/1.7 镜头,不管是风景还是人像都很方便。不过我因为还想等等看 X100,就先把 GF2 给退了。

X100 的评测其实目前已经很多了,论详尽可以看 [DPreview](http://dpreview.com/reviews/FujifilmX100/) 的,尤其是想解毒的可以专门看 [Conclusion](http://dpreview.com/reviews/FujifilmX100/page25.asp) 里的 Cons 部分,还是非常中肯的;如果想看看实际拍摄的照片,[Steve Huff 的评测](http://www.stevehuffphoto.com/2011/05/03/the-fuji-x100-digital-camera-real-world-review-by-steve-huff/)还不错,当然像 flickr 上搜索 “x100” 也能找到很多照片。

从去年 9 月公布的时候我就开始关注 X100,到今年 4 月发售却一直没有货,直到上周才刚刚拿到,总的印象还是没有令我失望 (目前拍的照片放在[这里](http://www.flickr.com/photos/jjgod/tags/x100/)),虽然前面使用 Panasonic GF2 的经验多少提高了期待程度。虽然优缺点网上也都说得很多了,我主要想重复一下我体会最多的几点:

* 体积比 GF2 稍大一点,毕竟加上了取景器,顶部拨盘也比较多,但还是一只手可以抓稳的。
* 画质一流,镜头在全开光圈时锐度也很高。最强大的是高 ISO 效果,1600 以下基本上不用担心任何明显的噪点,对比起来完全不逊色于 Nikon 这一代的单反。
* “微距 (Macro) 模式”其实应该称作“近摄 (Close up) 模式”,因为基本上一米以内的对象不用这个模式就很难合焦了,而到 10cm 以内则所谓的微距模式也不管用了,所以实际上是不可以和单反的微距镜头相比的。
* 手动对焦 (MF) 模式的对焦环不大好拧,但可以用 AF/AE Lock 按钮来预先自动合焦,然后再用对焦环细调,所以还算方便。
* 其他情况下对焦速度还是不错的,比 GF2 肯定是慢了半拍,抓拍运动的对象不那么方便,但还不算太困难,尤其是 OVF 提高了取景的效率。
* 背后的拨盘和按钮设置不太科学,像更改对焦点之类的操作不如 D90 方便。
* OVF 的视差要在两米以外才基本可以忽略,而且用 OVF 对近处的对象相比 EVF 也更难合焦,当然因为混合取景器的设计,切换起来并不困难。
* 内置的中灰滤镜 (ND Filter) 很管用,适合在阳光下拍摄。

总结起来 Fujifilm FinePix X100 不失为扫街 (street photography) 利器,至于是否值现在这个价格就见仁见智了,比如花 4000 就能买到的 GF1 套机在焦段上接近 (40mm vs. 35mm 等效)、画质也不差太多,但选择 X100 的多半是因为它复古的造型和操控方式,如果在以后的固件升级里能提高一下对焦效率,尤其是微距模式的对焦,就堪称完美了。

浏览器如何渲染文本

浏览器是我们最常用的软件之一,文本又是网页中最主要的元素,在浏览器显示文本的过程中有许多有趣的细节,值得展开来讲讲,或许能减少一些误解。这是一个比较粗略的,概括性的介绍,尽可能不涉及过多的技术细节和具体实现,而立足于给 Web 开发者和设计师提供一些正确的概念。

下面的介绍主要根据我对 WebKit 和 Gecko (Firefox) 的印象来谈,其他的浏览器也大致相同,如有阙漏之处欢迎指出。

解码

当浏览器收到来自 Web 服务器的网页数据之后,第一步是要把它解码成可以阅读的文本,因为历史原因,不同区域和语言的网页可能会使用不同的编码方式,而浏览器判断编码主要是依据以下方法:

  1. Web 服务器返回的 HTTP 头中的 Content-Type: text/html; charset= 信息,这一般有最高的优先级;
  2. 网页本身 meta header 中的 Content-Type 信息的 charset 部分,对于 HTTP 头未指定编码或者本地文件,一般是这么判断;
  3. 假如前两条都没有找到,浏览器菜单里一般允许用户强制指定编码;
  4. 部分浏览器 (比如 Firefox) 可以选择编码自动检测功能,使用基于统计的方法判断未定编码。

分段

编码确定后,网页就被解码成了 Unicode 字符流,可以进行进一步的处理,比如 HTML 解析了,不过我们这里跳过 HTML/XML 解析的细节,单讲得到了解析后的文本元素之后该怎么处理。

因为我们得到的文本可能是很多种语言混杂的,里面可能有中文、有英文,它们可能要用不同的字体显示;也可能有阿拉伯文、希伯来文这种从右到左书写的文字;也有可能涉及印度系文字这样涉及复杂布局规则的文字;另外,还可能有网页内自己指定的文本语言,比如 <span lang="jp">日本语</span> 这样的标记,使得日文汉字可以使用日文字体显示 (因为 Han Unification 导致这些汉字和中文里的汉字使用同样的代码点,尽管很多写法不同),"lang" 属性也可以在 HTTP 头、<meta> 或者 <html> 出现,用于标记整个文档的全局语言,通常这是一种好的习惯,方便浏览器进行字体匹配。

为了统一处理所有这些复杂的情况,我们要将文本分为由不同语言组成的小段,在有的文本布局引擎里,这个步骤称为“itemize”,分解后的文本段常被称作“text run”,但是具体划分的规则可能根据不同的引擎有所区别,比如 HarfBuzzICU 一般是根据要使用的不同排版类来划分 (常称作“shaper”),比如英语和法语可能使用同一个 shaper 排版,那么相邻的英语和法语文本就会划分到同一个 run 里,而希伯来文需要另一个 shaper,就划分到它自己的 run 里,以 HarfBuzz 为例,它有这样一些 shaper:

不少浏览器还会在这个划分下面,在确定具体使用的字体之后,根据使用字体的不同划分更细的 run,这种 run 可能称作“SimpleTextRun”,每个都会使用和相邻不同的字体,最后把它们逐一交给 shaper 进行排版得到要绘制的字形,这样一来,shaper 的工作就被简化为在确定的语言、确定的字体下排版确定的文本,生成对应的字形和它们应该放置的位置、占用的空间。下面先详细说说确定字体的步骤。

字体

说到字体,首先必须提到的就是 CSS 里的 fontfont-family 等规则。比如这样的规则:

p { font-family: Helvetica, Arial, sans-serif; }
strong { font-weight: bold; }

如果对于这样一段文本:

<p>A quick brown fox <strong>jumps</strong> over the lazy dog.</p>

表示这个段落里优先使用 Helvetica 这个 family 的字体,如果找不到,就找 Arial,如果还是找不到,就用浏览器设置的默认非衬线字体 (有的浏览器,比如 Safari 只给你一个设置,有的像 Firefox 则允许根据不同语言设置,这时可以根据前面分析得到的文本 run 语言信息来判断该用哪个),这个过程非常简单,大家都很好理解。稍微复杂一点的是“jumps”,它应该继承父元素的 font-family,也用 Helvetica,但不用默认的 Regular,而用 Bold 版本,假如找不到 Helvetica Bold,就找 Arial Bold,否则就找浏览器设置的那个字体的 Bold 版本,假如都没有呢?就要考虑用人工伪造的方式来显示粗体了,这个且按下不谈,先看对于中文常见的情况:CSS 指定的字体没有覆盖我们需要的文本时,该怎么做。比如还是上面的 CSS 规则,但对这样的文本:

<p>一只敏捷的狐狸...</p>

这里的“一只敏捷的狐狸”该用什么字体呢?假设 CSS 里具体指定了中文字体,比如 Helvetica, STHeiti, sans-serif,那很简单,按照英文字体一样的规则来判断:逐个字符尝试当前的字体是否提供了针对该字符的字形,如果没有则尝试下一个,要是到了最后都没找到匹配的字体呢?CSS 规范里只简单的说执行“system font fallback”,但这个过程在不同的浏览器下可能很不一样,比如 WebKit 会使用 font-family 列表里的第一个字体和这段文本所属的语言来寻找 fallback 字体,像 Times 这样的 serif 字体对应的中文 fallback 字体,在 Mac OS X 下是华文宋体 (STSong);而 Firefox 则会根据 sans-serif 这样的通用 font family 和对应的语言匹配到设置中针对对应语言的默认字体,比如在 Mac OS X 默认的中文非衬线字体是华文黑体 (STHeiti)。Linux 下一般通过 fontconfig 去根据语言、风格等参数来选择 fallback,但不同浏览器的实现还可能有区别;Windows 下则一般会使用系统的 Font Linking 机制,根据注册表内的 FontSubstitutes 信息来寻找。因为在这里不同的浏览器可能有不同的行为,所以建议在 CSS 中写明对应平台该用的字体

具体的字体选择还有一些不太容易注意的细节,也是各个浏览器差异比较大的一点,可能会出现这样一些问题:

  • 是否支持用字体的 PostScript name 选择:如 STHeiti 的 Light 版本又称作 STXihei,或者是否能用 full name 选择:有的浏览器不能正确地将 CSS 里对字体的 font-weight 或者 font-style 等要求映射到特定的字体上,尤其是在字体使用了非标准的 style 命名的情况下,考虑到很多厂商有自己的字体命名规则,这其实很容易出现,像 Helvetica Neue 的 UltraLight, Light, Regular, Medium, Bold 这些不同的 weight,是怎么对应到 CSS font-weight 的 100 到 900 数值上的?这就是特别容易出现 bug 的地方。
  • 是否支持按 localized name 选择:比如能不能用 "宋体" 来代表 "SimSun"。以 Mac OS X 下的浏览器为例,Firefox 支持这样的写法,但基于 WebKit 的浏览器一般不支持,这样的问题 CSS 规范没有限定,所以无论哪种情况都是允许的。

总的说来,如果要保证最大限度的兼容性,在 CSS 书写的时候应该尽可能选择明确、不容易出错的写法,尽量少隐式地让浏览器自己确定 (be explict instead of implict),虽然隐式写法通常比较简洁,但除非你 100% 确定想支持的浏览器在你想支持的平台下都能支持这个写法,否则还是不应该轻易用。

CSS3 新增的 @font-face 规则则是对于现有规则的扩展,提供了 web fonts 功能,但字体匹配算法的逻辑并没有改变,详细的算法可以看 CSS 规范里的说明

渲染

当确定了字体以后,就可以将文本、字体等等参数一起交给具体的排版引擎,生成字形和位置,然后根据不同的平台调用不同的字体 rasterizer 将字形转换成最后显示在屏幕上的图案,一般浏览器都会选择平台原生的 rasterizer,比如 Mac OS X 下用 Core Graphics,Linux/X11 下用 FreeType,Windows 下用 GDI/DirectWrite 等等。关于这个步骤,typekit 的这篇 blog 可以作为参考。各个浏览器的差异主要来自使用的排版引擎可能对不同的语言支持有差异,调用 rasterizer 使用的参数可能有差异 (比如是否启用 subpixel rendering、使用的 hinting 级别等等),但在同一个操作系统下的效果差别不会很大。

建议

基于以上的介绍,可以尝试提出一个在现有浏览器下,针对中文用户的,书写 CSS 字体选择规则的建议,如下:

  1. 首先确定要选择字体的元素应该使用的字体风格,比如是衬线字体、非衬线字体还是 cursive、fantasy 之类的;
  2. 确定了风格之后,先选择西文字体,优先把平台独特的、在该平台下效果更好的字体写上,比如 Mac OS X 下有 Helvetica 也有 Arial,但 Helvetica (可能) 效果更好,Windows 下则一般只有 Arial,那么写 Helvetica, Arial 就比 Arial, Helvetica 或者只有 Arial 更好;
  3. 然后列出中文字体,原则相同,多个平台共有的字体应该尽量放在后边,独有的字体放在前面,还需要照顾到 Mac OS X/Linux 下一般用户习惯用(细)黑体作为默认字体,Windows 下习惯以宋体作为默认字体的情况,比如 STXihei, SimSun 这样的写法比较常见,如果写作 SimSun, STXihei,但 Mac OS X 上装了 SimSun 效果就不会太好看。
  4. 最后还是应该放上对应的 generic family,比如 sans-serif 或者 serif
  5. 尽量用字体的基本名称 (比如 English locale 下显示的),而不要用本地化过的名称。除非特殊情况 (Windows 下“某些”浏览器在特定编码下只能支持本地化的字体名称)。Mac OS X 下字体名称可以用 Font Book 查到 (菜单 Preview -> Show Font Info),Windows 下字体信息在微软的网站可以得到,Linux/X11 下可以使用 fc-list 命令查到。
  6. 字体名称中包含空格时记住用引号扩起,比如 "American Typewritter""Myriad Pro"
  7. 文档开头最好指明语言,比如 <html lang="zh-CN">,可以使用的语言标记参见 <a href="http://www.w3 pharmacie du viagra.org/International/articles/language-tags/”>W3C 的说明。