Source Han Sans & FreeType

前陣子 Adobe 和 Google 聯手各自用不同的名字釋出了一款新的中文黑體字形,Source Han Sans 思源黑體,也叫做 Noto Sans (反腐字?)。開心試用之後,把系統字形都換過來,也開始想要怎麼樣才可以達到最佳的顯示效果,尤其是 Adobe 和 Google 曾經給 FreeType 貢獻了 CFF 字形引擎的改進,思源黑體也是以 CFF 技術設計,終於可以試試看中文的效果怎麼樣了啊!

我這邊使用 FreeType 的工具程式 ftview 來測試不同參數設定下的顯示效果,同時也附上相對應的 FontConfig 設定。可以發現不同設定下的顯示差距還蠻大的:

Auto-hinter, hintfull, grayscale anti-alias

首先是使用 FreeType 的 autohint, hintfull 其他用預設值,這應該是滿多人使用的設定,可以看到 autohinter 有很強烈的瘦身效果,把 Normal 的字變成幾乎是 Light 了。

Hintfull

Auto-hinter, hintslight, grayscale anti-alias

再來是使用 hintslight,許多人用以取得類似 Mac 上面的渲染效果,卻不知道 hintslight 會自動使用 autohint,這張的效果和上面類似但是略為粗了一點。思源黑體使用 hintslight 的缺點是 Normal 變細,導致和 Bold 的粗細相差太多。

Hintslight

Auto-hinter, hintslight, RGB LCD subpixel AA

這張的效果和上面一樣,只是啟用了 subpixel AA 所以在螢幕是 RGB 排列的 LCD 的時候會有比較銳利的感覺。

Hintslight, Subpixel

Adobe CFF native hinter, hintfull, grayscale anti-alias

這張則是使用 Adobe 去年貢獻的 CFF native hinting 引擎,這個引擎的特性是在小字的時候不但不會把字變細,反而還會加深筆劃讓字形看起來更清楚,於是 Normal 變回原本得粗細。

CFF Hinter, Grayscale

Adobe CFF native hinter, hintfull, RGB LCD subpixel AA

同上,只是啟用 subpixel AA 讓字形在 LCD 螢幕上看起來更銳利。

CFF Hinter, Subpixel

最後因為我選擇的設定是 TTF 字形使用 autohint + hintslight,CFF 字形則是使用 native hinter,因為雖然很多 TTF 字形有人工去 hint,可是一般來說 autohint + hintslight 比較可以保留細節,也不比人工 hint 差,而 CFF 字形因為有 Adobe 的加持,所以使用 native hinter 可以得到更好的效果。

    <match target="font">
      <edit name="rgba" mode="assign">
        <const>rgb</const>
      </edit>
      <edit name="hinting" mode="assign">
        <bool>true</bool>
      </edit>
      <edit name="lcdfilter" mode="assign">
        <const>lcddefault</const>
      </edit>
      <edit name="hintstyle" mode="assign">
        <const>hintslight</const>
      </edit>
    </match>
    <match target="font">
      <test name="fontformat"><string>CFF</string></test>
      <edit name="hintstyle" mode="assign">
        <const>hintfull</const>
      </edit>
    </match>

https://github.com/kanru/rcfiles/blob/fonts.conf

這是我目前的設定,至於你喜歡什麼樣的效果,可以再實驗看看各種不同的參數囉 ?

GCIN in Debian is seeking co-maintainer

尋找 GCIN co-maintainer

HIME 的事件鬧的沸沸揚揚,一切的起因之一就是有人認為 HIME 會取代 Debian
裡面的 GCIN,這是什麼狀況呢?因為在 Debian 裡面把 GCIN 棄養 (orphan)
的就是我,似乎也有必要說明一下。以下提到 GCIN 都是指 Debian 裡面的 GCIN。

最早的 GCIN 維護者是 caleb,後來他因為不再使用 Debian 所以開始找其他人
幫忙維護,而我成為 Debian Developer 的初衷就是要幫助本地的使用者有習慣
好用的套件可以用,所以就接下了這個位子。當時我也有使用 GCIN 所以就算劉
老大 (eliu) 每兩三天就有新版本我也還可以跟上,只是沒有辦法把套件整理的
乾淨點,後來不用 GCIN 了,套件的更新就漸漸脫節,因為有 Luna Archiver
的存在也讓我有偷懶的藉口,最後發現花在這個套件的時間與 Debian 的
Popcon 統計不成比例,就決定把套件給棄養了。

Debian 對於被棄養的套件的處理方式是,如果沒有出什麼大問題就會由 QA
team 來管理,但是如果有人提出 RM: RoQA 的要求,因為使用者 (Popcon) 少
且無人管理,就有可能被移出 Debian,但無正當理由甚少有直接使用新專案取
代舊專案的狀況,因此 Debian 要使用 HIME 取代 GCIN 純粹是誤解而已。

後來想想也不能把 GCIN 就這樣晾著,所以我決定要再出點力,把 GCIN 納入
Debian IME Packaging Team 的管理,並誠徵對維護 GCIN 套件有興趣的人一起
來 co-maintain,讓 GCIN 的使用者可以直接用到最新、高品質的 GCIN 套件。

有興趣的人請與我聯絡,寄信到 koster@debian.org 或是
630484@bugs.debian.org 都可以。

Not Again, MingLiu!

長話短說,從 FreeType 2.4.4 (2010-11-28 release) 開始,只要編譯的時候有啟用 BCI (Byte Code Interpreter) ,新細明體就不應該會破掉了,只是因為一些原因 (下面詳述),一直到 2.4.5 (2011-06-24 release) 才算是完全解決。

這個問題牽扯到許多有趣知識,所以我決定作個筆記以免以後忘掉。

TrueType

首先是一些名詞解釋,TrueType 是常見的字型 (font) 格式,由 Apple 發展,後來被 Microsoft 採用並發揚光大;雖然沒有經過任何國際組織的標準化,在當時儼然已經成為業界標準 (de facto standard)。

TrueType 使用的檔案格式又稱為 sfnt 格式,由許多不同的表格 (table) 組成,每個表格由四個字的標籤 (tag) 表示,其中我們最關心的是 glyf 這個表格,因為所有的字符 (glyph) 都儲存在這個表格內。每個字符僅僅紀錄了形狀,要知道某個編碼的某個字元 (character) 是對應到哪個形狀,還要透過 cmap這個表格來查詢;也有一個字沒有相對應的字符,或是沒有任何字對應到某個字符的情況。

參考資料:

Hinting (Instructing)

TrueType 是一種外框字型 (Outline Font),也就是說每一個字符都是以向量圖形的方式呈現,理論上可以在各種不同解析度下呈現相同的形狀。

DejaVu.tiff O Rasterize
M M Rasterize

但是越是在低解析度的時候,由於可用的像素較少,越容易遇到邊界的表達能力問題,如右圖的 M 字,若沒有對齊像素點的話,呈現出來的圖案就會比較瘦長且左右不對稱,如果稍微向右移動並加寬一點,呈現出來的圖案就會比較接近原本向量圖形所預期的形狀;這個動作正是 Hinting 中的對齊格線 (gridfitting) 功能。

在 TrueType 檔案中,描述 Hinting 的方式其實是儲存在字型的 fpgmprep 表格中的小程式,或是內嵌在字符中。看起來像是這樣:

    PUSHB[000]   # Push one byte onto the stack.
    3            # Value for loop variable.
    SLOOP[]      # Set the loop count to 3.
    PUSHB[002]   # Push three bytes onto the stack.
    5            # Point number.
    46           # Point number.
    43           # Point number.
    ALIGNRP[]    # Align the points 5, 46 and 43 with rp0 (point 8). This will
                 # align the height of the two lower serifs. 

使用這個小程式的方法是 Apple 的專利,但是因為 TrueType 一開始只是業界標準,Apple 並沒有在規格書裡面提及專利的相關資訊,造成後來使用 FreeType啟用 BCI (Byte Code Interpreter) 可能會侵犯專利的問題。(相關專利已經在 2010 年 4 月全數過期)

對每個字符做 Hinting 是個耗時耗力的工作,就算是只有數百個字符的西文字型也要花上數倍於繪圖的時間來做 Hinting,因此除了少數高品質的字型外,多數的字型是沒有 Hinting 的。而隨著顯示裝置的解析度越來越高,對於Hinting 的需求也越來越少,在較新的作業系統上可能會忽略部份 Hinting 的資訊或是完全關閉 Hinting。而在自由軟體的世界中,大多使用 FreeType的 Auto-Hinting 技術來支援解析度較低的顯示裝置。

參考資料:

MingLiu

細明體 (MingLiu) 與新細明體 (PMingLiu) 是華康 (DynaLab) 設計的字型,內建於 Microsoft 的作業系統中,使用非常廣泛。華康的字型使用了特殊的組字方式,似乎是為了從自有的字型資料轉換到 TrueType 才會使用這樣的方法。

首先,定義一組筆劃的字符,只用來組字,不對應到任何的編碼:

PMingLiu Strokes

然後在每一個個別的字符中用參照的方式把筆劃匯入,這時筆劃間的相對位置是亂的,最後再用 Hinting 的功能把筆劃移動到對的位置去:

PMingLiu uni65B0
PMingLiu Hinting

這樣濫用 Hinting 的方式,也導致如果不開啟 Hinting 就會看到破碎的字符:

PMingLiu Broken Glyph

為什麼說是濫用?因為只要沒有打開 Hinting 就會顯示錯誤,表示用錯地方了;有些時候是不需要,或是沒辦法使用 Hinting 的。

參考資料:

Tricky Fonts

除了新細明體,華康還有一些字型也是使用類似的方式組字,同樣很多人使用的標楷體就是其中之一,還有一些日本字型也是濫用 Hinting 的功能來繪製字符,所以 FreeType 其實有內建黑名單來對付這樣的字型,遇到了就強制開啟Hinting。一開始這個名單是用字型名稱來列表的:

    static const char trick_names[TRICK_NAMES_COUNT]
                                 [TRICK_NAMES_MAX_CHARACTERS + 1] =
    {
      "DFKaiSho-SB",     /* dfkaisb.ttf */
      "DFKaiShu",
      "DFKai-SB",        /* kaiu.ttf */
      "HuaTianKaiTi?",   /* htkt2.ttf */
      "HuaTianSongTi?",  /* htst3.ttf */
      "MingLiU",         /* mingliu.ttf & mingliu.ttc */
      "PMingLiU",        /* mingliu.ttc */
      "MingLi43",        /* mingli.ttf */
    };

但是這樣還不夠,因為很多 PDF 輸出軟體會把字型的名稱亂改:

    % pdffonts ~/pdf31yNwCi71d.pdf
    name                            type              emb sub uni object ID
    ------------------------------- ----------------- --- --- --- ---------
    AAAAAA+TimesNewRoman            TrueType          yes yes yes      9  0
    AAAAAB+___                      TrueType          yes yes yes     16  0
    AAAAAC+___                      TrueType          yes yes yes     17  0
    AAAAAE+____                     TrueType          yes yes yes     18  0
    AAAAAD+____                     TrueType          yes yes yes     19  0

遇到這樣的情況就破功了,所以很多人在看 PDF 的時候會遇到字破掉的問題,我先在中提出可能的解法,後來發現 Suzuki Toshiya 在 2010-11-22 的時候加入用 cvt/fpgm/prep 表格的加總檢查來比對黑名單,因為這三個表格是只要是有使用 Hinting 的字型都會有,也不太會改變的。

    static const tt_sfnt_id_rec sfnt_id[TRICK_SFNT_IDS_NUM_FACES]
                                       [TRICK_SFNT_IDS_PER_FACE] = {
      ...
      { /* MingLiU 1995 */
        { 0x05bcf058, 0x000002e4 }, /* cvt  */
        { 0x28233bf1, 0x000087c4 }, /* fpgm */
        { 0xa344a1ea, 0x000001e1 }  /* prep */
      },
      { /* MingLiU 1996- */
        { 0x05bcf058, 0x000002e4 }, /* cvt  */
        { 0x28233bf1, 0x000087c4 }, /* fpgm */
        { 0xa344a1eb, 0x000001e1 }  /* prep */
      },
      ...

只是原先這個方法只有在字型沒有名稱的時候才會啟用,我加了 patch 改成無條件檢查黑名單,從此就應該不會有漏網之魚了。

參考資料:

Sign Extension

只是後來我在我的機器上面仍然發現某些 PDF 仍然顯示破碎字型,難道是加總檢查也會出錯?因為牽扯到加總的問題,第一個想到的就是可能跟機器的整數長度有關,立刻改用 32-bit 的環境測試同一個 PDF,果然就好了。但是計算檢查碼的演算法很簡單,也沒有溢位的疑慮,那是哪裡出了問題?好在 IRC上有大大給了提示:

    kanru> hum.. checksum *: 0xa344a1eb
    kanru> checksum #: 0xffffffffa344a1eb
    j5g> kanru: 哈,因為 init checksum = 0xffffffff, sign extension 變
                成 0xffffffffffffffff?
    j5g> 然後之後都只處理後面 32bit
    kanru> j5g: 不過有可能是 font 裡面是錯的
    kanru> 0xffffffffa344a1eb 是直接從 font table 讀出來的
    j5g> 喔,那就是 0xa344a1eb sign extended to 0xffffffffa344a1eb

原來是 FreeType 在讀入字型資訊的時候,強制把 int 轉型為 uint 結果發生sign extension 錯誤,之後比對的時候就跟著錯了。送了 patch 之後,應該天下太平了吧?

參考資料:

PDF correctness

有人問說:那為什麼不乾脆隨時打開 Hinting 就好?據我所知目前的自由 PDFViewer 都是預設關閉 Hinting 的,除了上述的理由外,我猜大概是因為Hinting 有可能大幅度的改變一個字符的寬度,如下圖:

FtDiff DejaVu

可以發現三種顯示方式的寬度都不一樣,而 PDF 是一種接近印刷品質的檔案格式,有可能在輸出 PDF 的時候已經丟失動態改變寬度的能力,kern 的修正也已經沒辦法修改,這時候如過還讓 Hinting 去改變字符的寬度的話,很有可能得到不理想的結果。不過以上只是我的猜想,還沒有找各 PDF Viewer 的作者證實。至於封閉的商業 PDF Viewer 又是怎麼做的就更不得而知了。

結論:快升級到最新的 FreeType 吧!如果還有遇到顯示錯誤的 PDF 可以寄給我,如果以上筆記有疏漏錯誤的地方歡迎指正 ?

Speed comparison C vs Common Lisp

昨天看到有人轉噗一則 StackOverflow 上面的問題,內容是問他分別用 C,
python, erlang 與 haskell 寫了 Project Euler 的第十二題,但是
haskell 版本慢得不像話,該如何改進?

以下是苦主的原始 C 語言版:

    #include <stdio.h>
    #include <math.h>
    int factorCount (long n)
    {
        double square = sqrt (n);
        int isquare = (int) square;
        int count = isquare == square ? -1 : 0;
        long candidate;
        for (candidate = 1; candidate <= isquare; candidate ++)
            if (0 == n % candidate) count += 2;
        return count;
    }
    int main ()
    {
        long triangle = 1;
        int index = 1;
        while (factorCount (triangle) < 1001)
        {
            index ++;
            triangle += index;
        }
        printf ("%ld\n", triangle);
    }

我的執行時間約為 5.48s

    % gcc -lm -o euler12.bin euler12.c
    % time ./euler12.bin 
    842161320
    ./euler12.bin  5.48s user 0.00s system 99% cpu 5.484 total

手癢想看看據說可以編譯出不錯的機器碼的 Common Lisp 速度怎麼樣,於是第
一個版本如下 (用 sbcl 執行):

    (defun factor-count (n)
      (let* ((square (sqrt n))
             (squaref (floor square))
             (count (if (eql squaref square) -1 0)))
          (loop
             for cand from 1 to squaref
             count (eql 0 (rem n cand)) into c
             finally (return (+ count c c)))))
    (defun first-triangle-over (n)
        (loop
           for idx from 1
           sum idx into triangle
           until (>= (factor-count triangle) n)
           finally (return triangle)))
    ;; (time (first-triangle-over 1000))
    ;; Evaluation took:
    ;;   11.192 seconds of real time
    ;;   11.184699 seconds of total run time (11.184699 user, 0.000000 system)
    ;;   99.94% CPU
    ;;   30,135,882,489 processor cycles
    ;;   32,912 bytes consed
    ;;   
    ;; 842161320

還不錯,11.192s,這個版本採用原文中給 haskell 的建議,使用 rem 而不是 mod,可以
加快一點速度。再給一點關於型別的提示,然後把第一個 function inline,第
二版可以跑出和 C 版本差不多的成績 5.563s 🙂

    (declaim (inline factor-count)) ;; <----
    (defun factor-count (n)
      (let* ((square (sqrt n))
             (squaref (floor square))
             (count (if (eql squaref square) -1 0)))
          (loop
             for cand from 1 to squaref
             count (eql 0 (rem n cand)) into c
             finally (return (+ count c c)))))
    (defun first-triangle-over (n)
        (loop
           for idx of-type fixnum from 1 ;; <----
           sum idx into triangle of-type fixnum  ;; <----
           until (>= (factor-count triangle) n)
           finally (return triangle)))
    ;; (time (first-triangle-over 1000))
    ;; Evaluation took:
    ;;   5.563 seconds of real time
    ;;   5.556347 seconds of total run time (5.556347 user, 0.000000 system)
    ;;   99.87% CPU
    ;;   14,970,270,903 processor cycles
    ;;   32,768 bytes consed
    ;;
    ;; 842161320

純粹湊個熱鬧而已,有興趣的人可以試試看把 loop 改成和原文其他語言一樣使
用遞迴來實做,大部分 Lisp 都有做 TCO,速度應該差不多…

結論:Common Lisp is awesome 😉

Literate Programming

Let us change our traditional attitude to the construction of
programs: Instead of imagining that our main task is to instruct a
computer what to do, let us concentrate rather on explaining to human
beings what we want a computer to do.

― Donald Knuth. “Literate Programming (1984)”

Thinker 的一篇心靈與程式碼的協奏曲提到程式由後往前寫似乎比較符
合思考的方向,不禁讓我想到 Knuth 提出的 Literate Programming
法;其實我們思考的順序有時是跳躍式的,用 LP 的方法可以完全跳脫先後關係,
用自己喜歡的順序來寫,我覺得也可以稱為碎碎唸寫法。

用 Thinker 的同一個例子,改作 noweb 的格式,用 LP 的方法來寫,會變
成怎樣?首先定義問題:

假設問題是,function 接受一字串,裡面的每一行有兩個欄位,皆是整數。欄
位以一個空白相隔。function 必把每一行的兩個欄位的數字相乘,然後再將每
一行的結果加總。

我們需要一個接受一個字串的 function :

    <<接受字串的 function>>=
    def mul_n_sum(data):
        <<function 內容>>
    @

這個 function 必須把每一行的兩欄數字相乘,然後相加;假設我們取得每一行
的兩個數字之後照順序存放在 field_lines 這個變數裡面,則用 sum 就可
以把兩兩相乘之後的數字加總:

    <<function 內容>>=
    <<取得每一行的兩個數字>>
    return sum([field1 * field2 for field1, field2 in field_lines])
    @

要怎麼取得每一行的兩個數字呢?首先要把每一行的字串切開:

    <<取得每一行的兩個數字>>=
    lines = data.strip().split('\n')
    @

然後被切開的每一行字串經過分割後,會變成依照

    [field1, field2],
    [field1, field2],
    [field1, field2],
    ...

順序排列的字串,所以我們還要轉換成數字才能運算:

    <<取得每一行的兩個數字>>=
    <<分割欄位>>
    field_lines = [[int(field1), int(field2)] for field1, field2 in txt_field_lines]
    @

最後,分割欄位有很多方法,因為我們已經知道輸入的格式不會錯誤,所以用簡
單的 str.split() 來切割就可以了:

    <<分割欄位>>=
    txt_field_lines = [line.split() for line in lines]
    @

這篇 blog 經過 notangle 的處理,會變成以下的 python 程式:

    # notangle -R'接受字串的 function' literate-programming.mdwn
    def mul_n_sum(data):
        lines = data.strip().split('\n')
        txt_field_lines = [line.split() for line in lines]
        field_lines = [[int(field1), int(field2)] for field1, field2 in txt_field_lines]
        return sum([field1 * field2 for field1, field2 in field_lines])

可以跟 Thinker 的結果比比看 🙂

這篇是故意使用類似的思考邏輯來寫,所以會產生相似的程式,且為了展示 LP
我最後還刻意調整了一下順序。只要換一下思考的方向,要寫出 loop 的版本也
是有可能的。重點在於隨想隨寫,邊寫程式邊紀錄思考過程。

碎碎唸的好處是不怕跳躍式的思考,想寫什麼就寫什麼,邊想邊寫,壞處是最後
組合出來的程式一般人可能沒辦法直接看懂,但是這樣寫還真的滿有趣的,有機
會一定要試試!

其他參考資料:

Android: Wakelocks and TuxOnIce

最近為了把 TuxOnIce 整到 Android 上面,著實把 Linux 的電源管
理系統中關於 suspend 與 hibernation 的部份研究了一下。而 Android 為了增
加待機時間加入了 wakelock 的機制,讓情況變得更加複雜。

TuxOnIce

TOI Patch

TuxOnIce 簡稱 TOI,前身是 Software Suspend 2,是一個長期在 linux
upstream 以外耕耘的一個休眠補釘,相較於早期整合到 linux kernel 內的
swsusp,TOI 的功能非常豐富,可以指定把記憶體內容儲存到多種不同的位置,
如檔案或置換空間,還可以選擇不同的壓縮方法。

首先幫 ARM 平台加上基本的休眠功能,使用 Hiroshi DOYU 的 patch:

http://article.gmane.org/gmane.linux.power-management.general/20543

然後到 TOI 網站下載最新的 3.2 補釘,打上去的時候會有一些衝突但是都還滿
容易解決,要注意的是某些衝突是看不出來的,例如因為函式移動位置而造成重
複定義,要小心檢查。

然後因為 TOI 對於非 x86 平台有一些假設現在已經不適用,所以需要修改

https://gitorious.org/0xlab-kernel/kernel/commit/e551caf

OMAPFB

使用 Pandaboard 加上 Android 測試才發現從休眠醒來後 LCD 不會自動點亮,
通常在 x86 平台上有 pm-utils 或是 hibernate script 來幫忙處理這種關閉打
開 LCD 的問題,這次我是先在 kernel 內幫 OMAPFB 加上一個電源管理的呼叫,
讓 kernel 醒來的時候自己重新設定 LCD。

https://gitorious.org/0xlab-kernel/kernel/commit/21e0f14

Pandaboard & Beagleboard xM

在 Pandaboard 或 Beagleboard xM 上面沒有 NAND 可以當作儲存空間,所以一
般都是把檔案放在MMC 上面,第一個 patch 讓 kernel 在開機的時候不要初始化
不存在的 NAND 空間

https://gitorious.org/0xlab-kernel/kernel/commit/3ae6b4c

然後因為 MMC 的 probe 需要時間,第二個 patch 讓 TOI 等待指定的 resume
device 已經偵測到之後才開始進行 resume

https://gitorious.org/0xlab-kernel/kernel/commit/1078bd7

Wakelock

wakelock 是 Android 為了讓裝置可以更積極的休眠又不妨礙正常工作的進行而
加入的機制,究竟是好是壞已經在 LKML 與 LWN 上面討論過非
常多次,可以參考:

失眠症

開始測試 TOI 的效果時遇到的第一個問題就是失眠症,因為 wakelock 從中作
梗,導致每次休眠到一半就被取消掉。原來是因為 TOI 的休眠途徑會先強制檔
案系統把內容寫回,而 MMC 子系統會在寫入/讀取一段時間後嘗試進入低耗電模
式,偏偏 Android 在這裡安插了一個 wakelock,導致之後要凍結所有執行緒的
時候失敗。

解法,首先因為休眠的後半段路徑其實就是要準備進行關機,而一般來說休眠都
是人為啟動,因此可以假設此時使用者了解工作可能還未完成,可以忽略
wakelock,所以第一個 patch 就是讓休眠的路徑不去檢查 wakelock,避免像
MMC 這樣的問題。

https://gitorious.org/0xlab-kernel/kernel/commit/3bb5d6f

接下來是一般的待機路徑,似乎是因為新的 kernel 會積極的嘗試把 MMC 變成
低耗電模式,但 OMAP 預設的 MMC timeout 是 100 毫秒,把工作放在 Queue
裡面反而不斷鎖住 wakelock 讓 Android 無法休眠,第二個 patch 就是讓 MMC
子系統在準備休眠的時候就不要作無謂的掙扎,直接進入低耗電模式。

https://gitorious.org/0xlab-kernel/kernel/commit/88823b9

嗜睡症

解決了失眠症沒想到接下來遇到嗜睡症,因為手機插著 USB 的時候是不會真正
睡著的,所以一直沒機會觀察 Android 睡著是怎麼樣,原來只把 kernel 叫醒
是沒用的,Android 還會檢查目前的 input event 是否屬於 user activity 的
範圍,是的話才會把整個系統帶醒,不然因為 wakelock 的機制,馬上就會又睡
著。

參考:

Android 2.3 Introduction & Statistics

如果你有收看 LWN.net 的話,大概知道每次 linux kernel 有新的 release 前
都會有一次針對這次 release 的統計資料,看看主要的貢獻是由哪些人,哪些
公司完成。藉由這次跟 0xlab 分享 Android 2.3 新特性的時候,我學 LWN.net
做了一些小統計,還滿有趣的。

Most active Android 2.3 organizations (by changesets)

4204 google.com
1354 android.com
98 sonyericsson.com
71 gmail.com
39 codeaurora.org
39 samsung.com
38 intel.com
32 nokia.com
32 holtmann.org
29 0xlab.org
25 trusted-logic.com
17 openbossa.org
11 nxp.com
11 linux.org.tw
10 ti.com
10 acer.com.tw
8 themaw.net
8 garmin.com
7 snpe.rs
7 motorola.com
7 mc.pp.se
7 googlemail.com
5 invensense.com
4 mirbsd.org
3 windriver.com
3 realvnc.com
2 teleca.com
2 sharp.co.jp
2 nvidia.com
2 motoya.co.jp
2 lge.com
2 broadcom.com
1 yahoo-inc.com
1 xs4all.nl
1 wdsglobal.com
1 uwevoelker.de
1 strongswan.org
1 stericsson.com
1 signove.com
1 saftware.de
1 richlowe.net
1 pv.com
1 promwad.com
1 pcc.me.uk
1 padovan.org
1 osbeck.com
1 nii.net
1 martin.st
1 ilovelinux.de
1 happydroid.com
1 droidmod.org
1 cpeterso.com
1 big.or.jp
1 arm.com

Most active Android 2.3 developers (by lines changed)

Jesse Wilson
Eric Laurent
Dianne Hackborn
David 'Digit' Turner
Nick Pelly
Mathias Agopian
Nick Kralevich
Brian Carlstrom
Eric Fischer
Andreas Huber
386588
364359
260033
205494
160707
124119
103004
83492
61646
61233
12.8708%
12.13%
8.65%
6.84% 
5.35%
4.13%
3.42%
2.77%
2.05%
2.03%

投影片中還研究了一下各主要開發者主要研究的方向,看 log 的時候依照這個
順序去看,有一定的脈絡可尋 🙂

Understanding UUID

通用唯一識別碼 (Universally Unique IDentifier, UUID) 或是全域唯一識別
碼 (Globally Unique IDentifier, GUID) 是一個 128 bits 的整數,並保證其
在時間與空間的分佈都是獨一無二的。UUID 由開放軟體基金會(OSF) 標準化後
用在他們的 DCE 系統上,後來在微軟的 COM 系統上發揚光大。除此之外在許多
地方也都可以看到 UUID 的身影,如 Linux 上的分割表/區塊裝置就是以 UUID
來標示,或是RSS 的 <guid> 標籤也可以使用 UUID,實際上 UUID 是標準的
URN 表示法之一,你可以在任何需要標示單一物件的地方使用 UUID。

UUID 的文字形式為一個 8-4-4-4-12 的十六進位表示,共有 16 個 bytes,有
人說使用 UUID 不方便人類辨識,但了解 UUID 的組成後你還是可以從這個表示
法看出一些端倪來。本文參考的是 IETF 版本的 RFC4122。

UUID

UUID 共有四個版本,第 13 個字元的位置就是表示版本號。第一種是以時間和網
路卡號組成,時間是以一百奈秒為單位,網路卡號理論上是不會重複的,再加上
clock_seq 這個每次開機重設一次的亂數欄位,就算時間回朔了也不會重複,
代號是 1。第二種和第四種是以命名空間加上一個 hash 組成的,分別可以使
用MD5 或是 SHA1 演算法,算出來後就填到空位中,代號是 35。第三種
是全亂數組成,代號是 4

因此我們可以在不同的情境選用不同的 UUID,也可以從 UUID 看出版本跟時間等
資訊,如 4ef17586-f187-11df-8xxx-xxxxxxxxxxxx 看到第三個區塊是
11df 就可以知道是最近產生的以時間卡號為基礎的 UUID,時間
1dff1874ef17586 解出來就是 2010-11-16 13:42:09.173031.0 UTC。而
7c0fdbe4-1b09-4278-9fc9-5f0c6a1f2ae2 就是純亂數的 UUID,沒有任何
意義。

uuid-el

在轉換 Blog 到 ikiwiki 的時候,為了要整合 Disqus 系統,每篇都需要一個唯
一的識別碼,這樣 Disqus 就只認 UUID 而不是網址,將來網址變了也可以對得
上,而此 ID 也可以用在 RSS, ATOM 上面。研究了 RFC 4122 後,用 elisp 寫
了產生 UUID 的工具,uuid-el,使用只需要:

(require 'uuid)
;; Generate UUIDv1
(uuid-1)
;; Generate UUIDv4
(uuid-4)
;; Generate UUIDv3, v5
(uuid-3 uuid-ns-url "http://example.com")
(uuid-4 uuid-ns-url "http://example.com")

搭配以下 snippet 就可以快速展開需要的 metadata:

# -*- mode: snippet -*-
# name: meta
# key: meta
# --
\[[!meta title="$1"]]
\[[!meta guid="${1:$(uuid-urn (uuid-5 uuid-ns-kanru text))}"]]
\[[!meta date="${2:`(current-time-string)`}"]]
$0

最後補上影片:

下一集:用 ffmpeg 製作桌面錄影

Blog Redesigned

最近又把 Blog 整頓了一番,目標是改用靜態網頁。使用靜態網頁搭配一些網頁
樣板程式不禁讓人想起最初使用 MT 來寫 Blog 的時候,不同的是現在有像
Disqus 或是 IntenseDebate 這樣的動態留言服務,讓網頁可以完全
的只提供靜態內容,不用多一個 cgi 程式來提供留言功能,這隱然成為最近流行
的網頁設計方法之一。

轉換的過程中學到一些新東西,紀錄於此。

網頁編譯器

現在網頁是用
IkiWiki
產生的,它是大師 Joey Hess 的作品,大師稱它是一個
wiki compiler
可以透過各種規則把網頁的原始碼 (可能是 markdown 的格式) 轉成 HTML 網頁。
因為強大的 inline 規則以及各式各樣的外掛程式,也有許多人把 ikiwiki 當作
Blog 的平台。

轉換的過程其實滿順利的,因為舊的 Blog 都是以 Markdown 寫成,換到
ikiwiki 的 Markdown 引擎只需要一點點修改。要感謝 WordPress 可以把資料
庫匯出成 XML 格式,轉換只需要簡單的 XSLT 就完成了,我用的
XSL 是參考這裡修改,去掉
Comment 以及更新 meta的格式而成。

改用文字編輯器來寫 Blog 後,會習慣在 72 行的地方換行,但是在瀏覽器中只
要換行就會多一個空白,以前寫網頁的時候遇到中文都要小心的換行才不會讓不
該空白的地方出現空白,也不能打開 Emacs 的 auto-fill 模式。拜 ikiwiki
強大的外掛程式架構以及 Perl 的 Unicode 正規表示式支援,寫個
外掛把不該空白的地方黏起來只要幾分鐘
時間!而 Blog 原始碼則維持容易閱讀的格式。

screenshot

HTML5

既然都換了 Blog 系統,也打算換新的版面,乾脆就升級到網頁標準的新世代
HTML5。在收集 HTML5 相關資訊時發現了 HTML5 Boilerplate 這個網頁樣
板計畫,包含了許多目前 HTML5 的 Best Practice,基於這個樣板設計網頁讓
你馬上就 HTML5 Ready ☺

我認為 HTML5 最有趣的一點是帶有語意的新標籤,如 <article>,
<section> 等等,搭配新的大綱演算法可以自動產生大綱,運用得當
的話,對於機器或是身體不便的人都能更方便的取得資訊。

ikiwiki 原本就有支援實驗性的 HTML5 模式,但是預設的樣板不是很完美,例如
多層嵌套的 <section><nav><article>,都會產生多餘的大綱節
點,若只是需要群組與排版功能的話,最好還是使用 <div> 就好。如我現在
用的 template 長得就像這樣:

    <article>
        <header>
            <h1>TITLE</h1>
            <time>TIME</time>
        </header>
        <div class="content">
            CONTENT
        </div>
        <footer></footer>
    </article>

這裡推薦使用 HTML 5 Outliner 來檢查網頁的大綱,或是用專門的
Chrome 擴展,如果網頁設計無誤,應該會看到如下乾淨的大綱:

screenshot

Web Fonts

使用網路字體可以讓網頁在不同的作業系統、不同的瀏覽器上看起來都是一樣的,
現在有很多提供網路字體的服務,像是 Google 也推出
Google Font Directory,甚至 Fonts.com 還有免費的中文網路字體,
參考 Type Is Beautiful測試頁,速度還不錯呢。

我現在用的是從 Font Squirrel 下載的自由字體,這個網站上不但有大
量的自由字體可以挑選,還有預先整理好的 @font-face Kits 可以直接使用,
或是用 @font-face Generator 產生最佳化的網路字體。

結語

這篇是心得筆記的性質,因為如果不紀錄一下可能很快就忘了…有許多細節都省
略過去,若是要深入探討則每個主題都可以發展成長篇大論,需要更詳細資料的
網友可以參考引用的連結。

一些眉眉角角的東西希望之後有機會可以再單獨拿出來分享囉 ☻