前天在博客上說希望開發(fā)一個盡量獨立于GDI的圖形庫。這個圖形庫將不使用其他圖形庫例如GDI+、OpenGL以及DirectX等。圖形庫使用GDI的原因如下:
1:字體的邊框比較難獲得。直接讀TTF文件暫時還不想做,因此想借助GDI的API獲取文字的Bezier輪廓。
2:不使用GDI無法把圖片刷上窗口。
因此這個圖形庫使用的GDI的功能也僅限于此。當(dāng)然,開發(fā)出來的結(jié)果必然是GDI所不能達到的。GDI+的結(jié)構(gòu)也稍微有一點點不理想。
為什么GDI和GDI+的速度都不太理想呢?下面的分析將會給出一個可能的解釋。
今天早上考了軟件配置管理,也就是讓我們了解一下為什么需要Subversion這樣的軟件來幫助我們開發(fā)軟件。考完試回來的路上就構(gòu)思了這個圖形庫的結(jié)構(gòu)。讓我們考慮一下圖形庫所需的功能,也就是需求分析了。我們用慣的圖形庫都有繪制圖形、文字以及圖像的功能。圖形有畫刷和邊框,其中邊框是具有形狀的。
首先考慮一下文字。我們知道現(xiàn)在絕大多數(shù)的文字都是由Bezier邊框構(gòu)成的,雖然這種圖形是鑲嵌圖形(也就是有孔),不過孔不是什么大問題。我們拿到了文字的Bezier邊框之后,就可以將文字轉(zhuǎn)換為幾何圖形了。于是實際上只需要繪制圖形和圖像就夠了,外加一個獲取邊框的程序。
其次,繪制圖像實際上也是使用一個跟邊框有關(guān)系的畫刷去繪制一個矩形而已。所以,繪制圖像的時候,我們需要創(chuàng)建一個幾何圖形以及相匹配的設(shè)置的畫刷。
再者,讓我們考慮以下圖形。我們知道,圖形由邊框和內(nèi)部構(gòu)成,這兩個部分都是可選的。邊框有填充物、有線條類型(實線虛線等)、有線條的邊界形狀以及寬度等設(shè)置。那么,圖形的邊框通過這些設(shè)置就可以轉(zhuǎn)化為一個或多個幾何形狀和填充物。于是,我們只需要一個將邊框轉(zhuǎn)換為幾何圖形的程序就可以將邊框也去掉了,剩下的就是使用畫刷填充幾何圖形。
那么,幾何圖形就是由直線、弧線、Bezier曲線以及其他線條方程組成了。這個就跟架構(gòu)無關(guān)了,只需要解決相應(yīng)的數(shù)學(xué)問題就行了。剩下的就是畫刷的問題。我們知道畫刷有若干類型,譬如單調(diào)顏色、漸變以及圖像等。漸變分兩種,一種是跟繪制的幾何圖形有關(guān)的,另一種是跟繪制的幾何圖形無關(guān)的。于是我們在使用跟繪制的幾何圖形有關(guān)的畫刷的時候,我們可以創(chuàng)建一個根據(jù)某種幾何圖形漸變的畫刷,并且讓這個幾何圖形跟被繪制的幾何圖形相同,從而可以去掉『跟繪制的幾何圖形有關(guān)的畫刷』了。
于是畫刷就只有一種屬性了,也就是通過坐標(biāo)來獲取顏色。因為這個時候畫刷已經(jīng)跟被繪制的幾何圖形無關(guān)了。于是到了這里,我們很容易聯(lián)想到多態(tài)。使用一個接口包含『坐標(biāo)返回顏色』的函數(shù),就可以填充幾何圖形了。但是這樣做是不好的,因為一個幾何圖形有成千上萬個點,調(diào)用這么多次虛函數(shù)是會嚴重影響效率的。所以填充幾何圖形這種工作應(yīng)該完全由畫刷負責(zé)。那么我們?nèi)绾味鄳B(tài)呢?實際上可以這樣。我們定義一個接口,給出幾何圖形然后繪制。然后繼承一個類出來,這個類是模板類。模板類傳入一個參數(shù)用于決定如何通過坐標(biāo)返回顏色。因為畫刷跟被繪制的幾何圖形無關(guān),所以可以這么做。這個時候,我們就可以把填充跟獲取顏色分開了,但是編譯的時候仍然會讓他們結(jié)合在一起。所以無論幾何圖形有多大,調(diào)用的虛函數(shù)也就是一次。
好了,到了這里,一個圖形庫實際上就是由通過一定模式填充幾何圖形的函數(shù),加上構(gòu)造幾何圖形以及畫刷的各種各樣功能強大的周邊函數(shù)組成。
那么考慮到這里,我們?nèi)绾胃鶕?jù)一個點獲取顏色呢?單調(diào)顏色非常好處理,無論如何都返回這個顏色。漸變的話,根據(jù)復(fù)雜的幾何圖形漸變?nèi)孕杩紤]一下好用的算法,根據(jù)直線、方框以及橢圓等凸多邊形漸變實際上是相當(dāng)簡單的。剩下的就是根據(jù)圖像來獲取顏色了。根據(jù)圖像獲取顏色有幾個需要考慮的問題。第一個是超出圖像的部分如何處理,這個比較好辦,要么就返回一個顏色,要么就不填充,要么就讓圖像堆砌起來。第二個問題就是根據(jù)被繪制的點經(jīng)過變換到圖像上的點的時候,這個點可能不是整數(shù)。這個時候我們可以尋找臨近的點的顏色,或者根據(jù)縮放尺度來計算若干點的顏色的加權(quán)平均值。當(dāng)然第二種是比較逼真同時也比較慢的。
剩下的一個問題就是反鋸齒效果了。這個不用過多解釋,實際上也有非常多的辦法來解決,在框架沒有定下來之前討論這個是沒有意義的。因為圖形庫實際上是支持Alpha通道的,所以并沒有什么技術(shù)上過于困難的地方。
圖形我們就完全解決了,現(xiàn)在開始圖像的問題。我們?nèi)匀豢梢酝ㄟ^修改通過點獲取顏色的程序來實現(xiàn)調(diào)整一個圖像的對比度啊、亮度啊、甚至執(zhí)行一些模糊銳化邊緣化等處理。我們甚至可以借用OpenGL的Color Matrix這種概念來執(zhí)行一些比較簡單的線性顏色變換。這些效果實際上只需要慢慢添加進去就可以了,不過如何將多態(tài)的損耗減至最小仍然需要考慮。
如果以上的討論所涉及到的問題全部解決的話,我們可以得到一個跟GDI+一樣,甚至是更加強大的圖形庫。好了,現(xiàn)在討論一下為什么GDI和GDI+的速度都比較慢。我們可能經(jīng)常會重復(fù)繪制一些沒有任何變化的、具有復(fù)雜畫筆的幾何圖形。通過畫筆構(gòu)造邊框的輪廓是一件復(fù)雜的工作,我們使用GDI和GDI+就會在每一次繪制的時候重復(fù)計算這些東西了。不過由于GDI和GDI+的接口過于友好,我們無法干預(yù)這件事情。所以效率下降就是必然的了。而且繪制文字等價于填充幾何圖形,因此也是如此。GDI+唯一一個比較快的就是圖像處理了,因為它的圖像處理并不是通過本文講述的轉(zhuǎn)嫁到畫刷的辦法來實現(xiàn)的。當(dāng)然,如何打開和保存各種格式的圖片文件的事情就暫緩處理了,這根圖形庫本身是無關(guān)的。至于到時候這個圖形庫所展現(xiàn)出來的接口是如何的?我想仍然會有畫筆啊畫刷啊這種概念的,只不過會復(fù)雜一點,為了解決上面所說的問題也會繁瑣一點。
圖形庫,僅僅是數(shù)學(xué)問題而已。


