March 29, 2009

「我所知道的 C 語言」簡報上線

週六到台南社區大學分享主題為 [我所知道的 C 語言] 的演講,相關的資料已上線,可參考: 作為設計 UNIX 作業系統而發展出來的程式語言,C 語言肩負了眾多歷史的包袱與極為罕見、對應於硬體的設計,所以今天試著花些篇幅探討歷史緣由,特別是 UNIX 設計哲學的議題,再回來觀察 C 語言的若干特色。貫串議程的核心概念,就是以 C 語言為基礎的物件導向程式設計,選用若干嶄新的軟體系統作為說明,最後則以 Linux Kernel 一些 pointer/macro 的應用作結。整體來說,我期望這能從「鑑賞」的角度出發,好似修辭學之於經典文學作品一般,不過,看來還是得斟酌內容的呈現與詮釋。

因為這個議題其實有點冷門,意外發現會場竟然爆滿,感謝前來指教的朋友捧場,至於議程提到若干進階議題,如 JavaScriptCore 與 C/C++ 程式整合、混用物件模型,希望能在日後陸續整理於 blog 上,謝謝!
由 jserv 發表於 01:00 AM | 迴響 (8)

March 21, 2009

淺談 Google Skia 圖形處理引擎

2008 年九月,Google 宣佈以改良過的 WebKit 為核心的網路瀏覽器 Chrome,揭露了眾多新特徵,比方說嶄新的 [V8] JavaScript (ECMAscript) 執行引擎,或許因為太亮眼,掩蔽了所使用另一個開放原始碼專案 [skia],後者是個 2D 向量圖形處理函式庫,包含字型、座標轉換,以及點陣圖都有高效能且簡潔的表現。不僅用於 Google Chrome 瀏覽器,新興的 Android 開放手機平台也採用 skia 作為繪圖處理,搭配 OpenGL/ES 與特定的硬體特徵,強化顯示的效果,本文簡介 Google Skia 的歷史背景、應用層面,並探討其程式設計模型。

Google 為了搭建 Open Handset Alliance (OHA) 的 Android 平台,布局極久,背後的百人研發團隊部份來自之前的併購案,其中兩項具指標性意義:
  • 2005 年八月 17 日,收購美國 Android 公司,業務是手機軟體開發,這當然就是現在開放源碼 Android 計畫的前身
  • 2005 年十一月,收購美國 Skia 公司,業務是向量繪圖軟體
被 Google 收購前的 Android 公司有著在 IT 產業為人所津津樂道的成果,本文就不多談,而 Skia 公司自然也不是省油的燈。Skia Inc. 設立於北卡羅萊納州的 Chapel HIll,由 Michael Reed (也稱為 Mike Reed) 所創辦,他在圖形技術領域是相當頂尖的人物,與 Benoit Schillings (BeOS 主要開發者, Be Inc. 第二位工程師,現為 Nokia CTO) 於專業手機軟體開發公司 OpenWave 共事時,即在該公司產品 OpenWave Phone Suite Version 7.0 (以下簡稱 V7) 引入精湛的向量圖形技術,在 50-300 kb 空間的實做中,提供了圖層 overlay 之間 alpha blended 預覽、全功能向量矩陣轉換等進階功能。在加入 OpenWave 之前,Mike Reed 服務於 Apple,代表專案為 QuickDraw GX,主導進階圖形與字型處理技術。Benoit Schillings 離開 OpenWave 轉任 Trolltech CTO 期間,Mike Reed 開創了 Skia Inc.,該公司第一個產品為 SGL (Skia Graphics Library),一個非常嚴謹的向量顯示引擎,能在低端設備比如手機、電視及其它手持設備之上,呈現高品質的 2D 圖形。根據 LocalTechWire 的描述:
    "Skia’s first product, SGL, is a portable graphics engine capable of rendering state-of-the-art 2D graphics on low-end devices such as mobile phones, TVs, and handhelds,” the Web site said. “SGL is feature-set compatible with existing 2D standards, making it ideal to serve as a back-end for public formats such as SVG, PDF, and OpenVG. SGL is licensed as source or binary, and can be customized to match specific HW/framebuffer requirements.”
自 2005 年 Skia 被 Google 收購後,一直相當神秘低調,直到 2007 年初,Skia GL 相關的程式碼才被揭露,作為 Google Android 平台的圖形引擎,稍候的 Google Chrome 瀏覽器也採用 Skia 引擎。隨著 Android 與 Chrome (開放版本稱為 "Chromium") 兩大專案公佈程式碼後,skia 也一併公開原始程式碼,以 Apache License v2 釋出 (注意,這意味著與 GPLv2 授權不相容),而 Android 與 Chrome 的程式碼庫中都有一份 [skia] 的複製,因需求不同,做了部份的修改,比方說 Chrome 專案底下的 [chrome/trunk/src/skia],需要注意的是,Skia 本身是不涉及底層環境,如 Linux Framebuffer 或 Gtk+ 銜接的處理,這也是何以 Android (透過 Linux Framebuffer) 與 Chrome (開發中的 Linux 版本使用 Gtk+) 需要提供一份修改,以便系統接軌,關於這方面的資訊,可參照 Google Chromium 的開發日誌 [Graphics in Google Chrome]

相較於 Firefox 1.x,後繼的 Firefox 2.x/3.x 在圖形顯示方面有相當大的進展,很大層面歸功於引入 Cario 向量圖形程式庫來處理網頁繪製,而 Skia 就相當於扮演 Cairo 的角色,不過更輕量些。快速發展的 WebKit 儼然是從桌面應用跨足移動裝置之網頁引擎解決方案的首選,Apple 與 Google 都有為數可觀的全職工程師投入,拜網際網路的威力,也有其他廠商與團體個人積極投入開發,目前 WebKit 支持的圖形函式庫計有 Cairo, Gtk+, Qt4, WxWidgets, Cg (Mac 的非開放原始碼函式庫), Skia 等等,並以 WebKit 中 class GraphicsContext 處理前述圖形函式庫的實做,可針對不同平台的特性,規範不同平台所需的巨集與成員,詳情可參考程式碼 WebCore/platform/graphics/GraphicsContext.{h,cpp}。
Skia 以 C++ 實做,程式碼約八萬行,基本某些未知的因素,可參考的文件相當有限,但 Chromium 的 SVN log 與程式碼則是現在最完整的文件,以下是其特徵:
  • 高度優化的軟體 rasteriser (module sgl/)
  • 選擇性透過 OpenGL/ES,加速特定操作,如 shader 與 textures (module gl/)
  • 動畫處理能力(module animator/)
  • 內建 SVG 支援 (module (svg/)
  • 內建若干 image codec,如 PNG, JPEG, GIF, BMP (modules images/)
  • 內建文字處理,但缺乏泰文、藏文一類複雜文字處理的能力
  • 效能特性:
    • 對 image 與特定資料型態的 Copy-on-write
    • 內部記憶體管理,謹慎地被免 fragmentation
    • Thread-safety
Skia 實做所需的相依性:
  • 字型: FreeType (值得注意的是,FreeType 的維護者 David Turner 目前任職於 Google), Windows GDI
  • 多執行緒模型: pthread, Windows threads
  • XML: expat, tinyxml
理解歷史背景,我們終於可以來作點有趣的事。首先,自 Google Code 取得 Skia 原始程式碼:
# svn co http://skia.googlecode.com/svn/trunk skia-trunk
乍看這「清爽」的目錄架構,很難想像過去這是商業軟體,或許 Google 有些「不能說的秘密」,除了 samplecode/ 目錄若干的程式碼之外,就幾乎沒有充分的文檔了。用 svn log 可瀏覽 Skia 開發的紀錄,"reed@android.com" 就是 Mike Reed 本人,至今仍相當活躍地改良 Skia 的實做。編譯方式很單純,先看看說明:(本文對應於 svn r130)
# cd skia-trunk
# make help
可得到以下說明:
Targets:
    : out/libskia.a
    bench: out/bench/bench
    tests: out/tests/tests
    clean: removes entire out/ directory
    help: this text
Options: (after make, or in bash shell)
    SKIA_DEBUG=true for debug build
    SKIA_SCALAR=fixed for fixed-point build
    SKIA_BUILD_FOR=mac for mac build (e.g. CG for image decoding)
期望的編譯輸出就是靜態函式庫 out/libskia.a,而 Skia 的內部運算可選擇浮點數與定點 (fixed-point),不過筆者發現,目前尙未能透地選擇,但這不影響我們理解 Skia 的使用與體驗其威力。以筆者使用的 GNU/Linux 來說,可下達以下指令要求編譯:
# make SKIA_BUILD_FOR=linux
沒意外的話,系統就會乖乖的編譯:
compiling out/src/core/Sk64.o
compiling out/src/core/SkAlphaRuns.o
compiling out/src/core/SkBitmap.o
...
至於編譯 benchmark 程式,則可透過以下指令:
# make SKIA_BUILD_FOR=linux bench
benchmark 程式算是除了 Chromium 之外,最佳的「文件」了,不過 SKia API 本來就簡潔強大,這也不妨礙。執行 benchmark 程式:
./out/bench/bench -o `pwd`
陸續會有類似以下的輸出:
running bench          polygon
running bench            lines
running bench           points
running bench          rrects3
running bench          rrects1
running bench           ovals3
running bench           ovals1
running bench           rects3
running bench           rects1
running bench    bitmap_index8
running bench      bitmap_4444
running bench       bitmap_565
running bench      bitmap_8888
可大概窺知 Skia 涵蓋的範疇,接著筆者就寫個小程式,使用 Skia C++ API: [test-skia.c]
/* Simple vector graphics demo utilizing Skia toolkit.
 * Authored by Jim Huang <jserv.tw@gmail.com>
 */
 
#include "SkBitmap.h"
#include "SkDevice.h"
#include "SkPaint.h"

#include "SkRect.h"
#include "SkImageEncoder.h"

int main()
{
	// Declare a raster bitmap, which has an integer width and height,
	// and a format (config), and a pointer to the actual pixels.
	// Bitmaps can be drawn into a SkCanvas, but they are also used to

	// specify the target of a SkCanvas' drawing operations.
	SkBitmap bitmap;
	bitmap.setConfig(SkBitmap::kARGB_8888_Config, 200, 200);
	bitmap.allocPixels();

	// A Canvas encapsulates all of the state about drawing into a
	// device (bitmap).  This includes a reference to the device itself,
	// and a stack of matrix/clip values. For any given draw call (e.g.
	// drawRect), the geometry of the object being drawn is transformed
	// by the concatenation of all the matrices in the stack. The
	// transformed geometry is clipped by the intersection of all of the

	// clips in the stack.
	SkCanvas canvas(new SkDevice(bitmap));

	// SkPaint class holds the style and color information about how to
	// draw geometries, text and bitmaps.
	SkPaint paint;

	// SkIRect holds four 32 bit integer coordinates for a rectangle.

	SkRect r;

	paint.setARGB(255, 255, 0, 0);
	r.set(25, 25, 145, 145);
	canvas.drawRect(r, paint); /** Draw the specified rectangle using
				       the specified paint. The rectangle
				       will be filled or stroked based on
				       the Style in the paint. */

	paint.setARGB(255, 0, 255, 0);
	r.offset(20, 20);
	canvas.drawRect(r, paint);

	paint.setARGB(255, 0, 0, 255);
	r.offset(20, 20);
	canvas.drawRect(r, paint);

	// SkImageEncoder is the base class for encoding compressed images
	// from a specific SkBitmap.
	SkImageEncoder::EncodeFile("snapshot.png", bitmap,
		SkImageEncoder::kPNG_Type,
		/* Quality ranges from 0..100 */ 100);
	return 0;
}
編譯方式:
g++ \
        -I./include \
        -I./include/core \
        -I./include/images \                                                               
        -Wall -o test-skia test-skia.c \
        out/src/images/SkImageDecoder_libpng.o out/libskia.a \
        -lpng -lpthread -g
筆者做了簡要的註解,大概可知曉 Sk 開頭的這些 API 的功用,而上述的範例程式一開始就要求 Skia 配置畫布 (SkCanvas),接著透過一份 SkRect 物件 r,給定 ARGB 的描述,使其有著不同的顏色,再來就是調整向量物件的位移並繪製。正如前文提及,Skia 僅是繪圖引擎,並未如 Cairo 一般廣泛對應到 PDF, X11, GDI 等等底層繪圖裝置,所以為了方便觀察繪圖結果,我們透過 Skia 內建的 image codec 來輸出 PNG 圖檔,所以執行前述編譯後的執行檔 "test-skia",應該會得到以下圖檔:(本無外框與底色,但為了清楚於文章呈現,額外用繪圖軟體追加)

疊合的三個不同色的矩形物件,就是透過以下 API 呼叫達成:
	paint.setARGB(255, 0, 255, 0);
	r.offset(20, 20);
	canvas.drawRect(r, paint);
由於 Skia 與 Cairo 的同質性相當高,也可參照 [Cairo :: documentation] 建立所需的背景知識。
由 jserv 發表於 07:42 PM | 迴響 (10)

March 08, 2009

演講:我所知道的 C 語言


本月 (三月) 底,小弟將在 [酷!學園] 分享一個新主題「我所知道的 C 語言」,以賞析的角度去探討 C 程式語言。余光中教授曾對「知性」分析為兩端:一是知識,一是思考。有知識而無見解,只是一堆死資料;思想得多而知識不夠,又淪於空想。有幸得以在短暫的人生中,以半數的光陰透過程式設計,賦予冰冷的硬體一些新的生命,背後強大的工具,正是 C 程式語言,而廣泛接觸了多樣的作業系統與硬體架構後,更覺探索資訊系統「知性」的可貴。如何將「知識」與「思考」融合,並投射於以豐富面貌出現的資訊系統,正是本議程所要探討與分享的。以下摘錄酷!學園的 [活動資訊]:
  • 建議聽者背景: (任一)
    • 已熟悉 C 語言程式設計者,企求探索多元的開發
    • 粗淺知曉 C 程式語法,對現實資訊應用的知性,有高度興趣者
    • 具任何一種程式語言之實務經驗者
  • 講題簡介: C 語言雖然是一種通用的程式語言,但本質上是為了開發 UNIX 作業系統而提出,我們非但不可忽略這個事實,反而要適度從硬體的觀點去反思軟體設計,是的,透過 C 語言這個「不軟不硬」的美妙語言。本議程將以物件導向與資料表示等題材,探討遊覽 C 語言開發的知性美。
  • 預定提綱:
    • 海角 C 語言 -- 被遺忘的淒美故事
    • 高度物件導向的 C 語言
    • 窺探 C 程式、尋訪 C 程式的資料表示、奇妙的 pointer 與 macro
    • C 語言與硬體擦出的火花,呈現 C 設計的彈性
  • 軟硬體規格:x86/IA32, C99, gcc 4.3.3, Ubuntu Linux 9.04, kernel 2.6.28
  • 參考資料:
  • 時間:三月 28 日 / 13:30-17:00
  • 地點:台南市社區大學 3F 305教室 (台南市公園路 750 號)
  • 報名網頁: http://registrano.com/events/satn09031
物件導向程式設計大師 Martin Fowler 曾說:你永遠無法讓物件導向的新手們瞭解為什麼要採取這種分散式的設計,你只能要求他們如此做,幾年後,他們會突然頓悟,腦袋有如重生一般。而我們的問題則是,倘若已被動地接受 C 程式語言的陶冶多年後,卻遲遲不能「頓悟」,究竟是什麼環節錯了呢?所以小弟試著回顧對資訊系統的「知性」,並以 C 語言再詮釋,期待您的指教,謝謝!
由 jserv 發表於 04:05 PM | 迴響 (8)