顯示具有 Matrix 標籤的文章。 顯示所有文章
顯示具有 Matrix 標籤的文章。 顯示所有文章

2010年3月24日 星期三

幫圖片加上影像特效 (IV) - 3D 投射效果

Create simple effects for your images (IV) - 3D Projection

在 Android SDK 中有兩種 Camera classes。一個是 android.hardware.Camera,這是用來操控相機功能的類別。另一個是 android.graphics.Camera,這是什麼東西?今天我們就要來研究一下,這個類別能幫我們做些什麼事。

android.graphics.Camera 是一個可以讓你將 2D 物件在 3D 空間中移動,並將在其移動後的結果,畫在螢幕上的類別。

聽起來很有趣,是嗎?例如,最上面那張美女圖,你可以看出來,他是將原本的圖,以 Y 軸為軸心,旋轉約 30 度後,所畫出來的結果。

打開 android.graphics.Camera 的說明文件,你會發現,這個文件中只有下面這些 APIs 列表,什麼說明都沒有。

不過,從這些 APIs 的名稱與參數,你應該可以猜出一些端倪。沒錯,這個 Camera class,其實是個 helper class。他提供一些 APIs ,讓你控制要如何在 3D 空間中移動,最後再產生出合適的 Matrix ,讓你套用到 Canvas 的座標體系上。關於 Matrix,我在 幫圖片加上影像特效 (I) - 陰影特效 已經介紹過他的強大,還不知道的,先讀一下這篇。

用個例子來解釋這個 Camera 類別的用法,是最適當的。底下這個簡單的程式片段的結果,就是最上面那張圖。其中的 m_bmp 就是原本的美女圖。

從這個範例程式中,我們知道,你可以利用 Camera.translate() 及 Camera.rotateXXX() 等 APIs 在 3D 空間中移動。其實,這些 APIs 就是在操作 Matrix 的內容。最後,你只要呼叫 Camera.getMatrix() 即可將這 Matrix 的內容抓出,並套到 Canvas 上。

在 Android ,要做出 3D 的效果,你當然可以用 OpenGL 的函式。不過,如果你需要更快的顯示速度,或者只是要做個簡單的 3D 特效,那千萬不要忘了這個 android.graphics.Camera。


繼續閱讀全文...

2009年12月29日 星期二

幫圖片加上影像特效 (II) - 鏡像特效

Create simple effects for your images (II) - Reflection Effect

上次介紹過如何實現陰影特效,這次我將介紹如何實現 iMac 上常見的鏡像特效。

鏡像特效最近還蠻流行的,尤其在 HTC 的 Sense 介面上,常常都可以見到。最上面的那張圖,就是來自 HTC Sense 介面,所自帶瀏覽器程式中的『我的最愛』截圖。看到沒,加了個鏡像特效後,那感覺就很立體,感覺上好像是這些圖片擺立在一個玻璃桌面上。

我想你一定想知道,在 Android 上要幫圖片加上鏡像特效,會不會很麻煩?我告訴你,一點也不麻煩。和陰影特效相似,同樣地,只要幾行程式,就可以搞定。

因此,在開始看答案之前,我會建議你要先有 Photoshop 的使用經驗。想想,如果用 Photoshop 要幫圖片加上鏡像特效,要如何做?我想一般不外乎是先複製個圖片,並將其垂直翻轉,接著再對這翻轉的圖片,加個由灰到黑的漸層 mask 即可。

如果你沒 Photoshop 的使用經驗,自己上網找一下,應該不難找到說明,像 ,還有 都有。

好了,讓我們來看一下答案。底下就是幫圖片加上鏡像特效的程式範例。

_DrawMirror() 函式是關鍵。還記得我剛剛說,用 Photoshop 要如何做出鏡像特效?第一步是先畫出垂直翻轉的圖片。

Android 繪圖座標體系預設的原點在左上角,X 軸往右是越來越大的正值,而 Y 軸往下,則是越來越大的正值。要畫出垂直翻轉的圖片,其實也就是要垂直翻轉整個繪圖座標體系。在 Android 中,要如何做?答案就是 canvas.scale(1.0f, -1.0f)。很簡單吧,沒想到給 scale() 函式一個負值,就可以翻轉相對應的軸。

在 Photoshop 中,做鏡像特效的第二步是要對這翻轉的圖片,加個由灰到黑的漸層 mask。

在 Android 中,要畫漸層色,那就一定得用 LinearGradient 這個類別。至於要對背景圖加上個 mask,就請參考一下 Paint 的 setXfermode() 函式。_Init() 這個函式,就是負責生成一個由灰到黑漸層 mask 的 m_paint 物件。

將一個地球的圖片設給 m_dw,透過上面這個程式範例,畫出來的樣子,就像下面這樣。是不是感覺這地球,放在一塊玻璃桌面上?


繼續閱讀全文...

2009年12月15日 星期二

幫圖片加上影像特效 (I) - 陰影特效

Create simple effects for your images (I) - Shadow Effect

Android 從 1.0 開始,他提供給開發者的各式繪圖函式,就比同時期其他手機平台來的強大與好用。

有 Android 基礎的你,應該都知道,在 Android 中繪圖,必須得要透過 Canvas 這個物件。在 Canvas 中,我最欣賞的功能就是,他讓開發者可以透過 matrix,來操作整個繪圖座標體系的位移、旋轉、傾斜等等功能。另外就是 Android 的 2D 繪圖功能,也開始支援 alpha,alpha 也就是透明度,這是要做一張美圖時,不可或缺的功能。

有了 matrix,加上 alpha,再配合比 bitmap 更為抽象的 drawable 物件,那你要將圖形做各式各樣的變形,甚至於將上影像的特效,都將會是輕而易舉的事。回想在 Windwos 3.1 的那個年代,要做到我要介紹的這些特效,那真的很辛苦,你必須要將影像中的像素,一個一個抓出來,再透過一些影像處理的演算法,才有辦法做到。

我就舉兩個影像特效例子,來說明這 Android 的繪圖功能有多強大。首先,我先介紹陰影特效要如何做。

在寫地圖程式時,通常會加個 Overlay 物件在地圖上,用來標示某個景點的所在位置。要在地圖上畫個 Overlay,Overlay 這個類別已經提供一個很好用的 drawAt() 函式給你。你只要負責給他一個沒有陰影的 Drawable,他會自動幫你製造出陰影的效果。結果就像最上面這張圖一樣,每個圖案標示,都有個陰影,這讓這些標示,看起來就像立在地圖上一般。

drawAt() 這個函式的宣告如下:

從實際的使用中,我們可以發現當你傳入 false 給 shadow 時,他就會幫你製造出陰影的效果。

這個函式,怎麼這麼神奇!今天我將幫你們翻出他的原始碼,讓我們來看看這個 drawAt() 是如何實現的。

底下就是 drawAt() 的原始碼。你看,短短不到 20 行,就可以幫任何的圖片做出陰影的效果。

為了幫助大家理解他是如何做到的,我將他整理成下面的程式範例。

_DrawShadowImg() 就是負責製造出陰影效果的函式。

這畫出陰影的函式,主要就是運用 matrix 與 drawable 的 color filter 這兩個功能。首先是 m_dw.setColorFilter(),將整個圖片的顏色都轉成半透明的黑色。接著 canvas.translate() 將座標體系原點移到 nX, nY 的位置,而 canvas.skew() 與 canvas.scale() 則是將座標體系做個 x 軸方向的傾斜,與 y 軸方向的大小壓縮。最後,呼叫 m_dw.draw() 將圖片畫上,陰影就這麼產生出來了。

底下這張圖,就是上面這個程式片段的執行結果。

下回,我將介紹要如何實現 iMac 上常見的鏡像特效。


繼續閱讀全文...

2009年7月13日 星期一

Android 原始碼徹底研究系列 - 鬧鐘程式, AnalogClock (1)

Android Source Code Internals - Alarm Clock, AnalogClock (1)

鬧鐘的原始程式碼在 這裡

PS: 請不要向我要原始碼,所有Android原始碼都可已自行下載,詳情請見 https://source.android.com/source/building.html

一執行這個鬧鐘程式,畫面上第一眼看到的,就是一個大大的時鐘。因此我們今天,就先從這個時鐘下手。讓我們一探究竟,看他是如何實現的。

要了解這個時鐘,是用什麼元件做出來的。有兩個方法,第一個就是用 SDK 內自帶的 Hierarchy Viewer 工具;另一個,就是直接觀察 res/layout 中的畫面設計檔。這次,我們直接用第二個方法。

翻開 AlarmClock/res/layout 目錄的內容,你會發現有好幾個 clock_xxx.xml 的檔案。打開其中的 clock_droid2.xml 檔。內容如下所示:

嗯,看起來,這個時鐘就是用 android.widget.AnalogClock 這個元件直接實現的。只要透過 android:dial, android:hand_hour, android:hand_minute 這三個 XML 屬性,指定鐘面,時針與分針的圖檔,一個完整的時鐘,就會顯現出來。最上方的圖片,就是這個時鐘顯示的樣子。

都不需要任何額外的設定,這個時鐘元件就會自己依照目前的時間,將時、分針顯示在正確的位置,那他是如何定時更新時、分針位置?另外,注意到了嗎,時、分針的圖檔,只有一份。從這就猜到,這個時鐘還會自動將時、分針圖旋轉至正確的角度,貼在螢幕上。讓我們來看看,這個 AnalogClock 元件是如何實現這些功能的。

打開 AnalogClock 元件原始碼,扣掉最前面的註解不算,整個原始碼也才不到 230 行。這麼少的程式碼,卻能將這個時鐘功能做得這麼完整。這也是我常最推薦,一定要看的元件原始碼之ㄧ。

這次,我們要看的重點有兩個。

  1. 在元件程式中,要如何隨時都知道時間的改變?
  2. 在 Android 中,要如何將圖片做任意角度的旋轉,並貼在螢幕上?

在元件程式中,要如何隨時都知道時間的改變?

原來在 onAttachedToWindow() 中,AnalogClock 透過 registerReceiver() 註冊了三個由系統發出的 Actions,ACTION_TIME_TICK, ACTION_TIME_CHANGEDACTION_TIMEZONE_CHANGED

在 Android 中,系統會透過 Intent 傳送某些系統通知訊息,這些通知訊息又稱為 Action。在 Intent 類別的說明中,你可以找到所有的 Action 定義。

在你的程式中,想要收到這些系統通知訊息,有兩個方法。第一個方法就是在 AndroidManifest.xml 中定義 receiver 的類別,這個方法在日後的介紹中會提到。第二種方法就是利用 registerReceiver(),向系統註冊接收的類別。由於 AnaloClock 是以元件的方式存在,如果每次使用這個元件,都還要在 AndroidManifest.xml 中定義,那就太麻煩了。因此, AnaloClock 採用的是第二種方法。

其中,ACTION_TIME_TICK,就是由系統在每分整,傳送出來的 Action。ACTION_TIME_CHANGED 則是當使用者更改系統時間時,所送出的 Action。當你的手機跨越時區,系統則會送出 ACTION_TIMEZONE_CHANGED 給有註冊的程式。

一但這些 Actions 發生時,他會呼叫在程式最後 new 出來的 BroadcastReceiver 物件, mIntentReceiver,中的 onReceive() 回呼函式。

首先,在 onReceive() 中,先檢查如果是時區有改變的話,當然你要重新設定 mCalendar 變數。

onTimeChanged() 函式,只是重新抓出最新的時、分資料。

在 onReceive() 的最後,再呼叫 View.invalidate()。這個動作,會觸發 View.onDraw() 函式的呼叫,並在螢幕上畫上最新的時,分針。

在 Android 中,要如何將圖片做任意角度的旋轉,並貼在螢幕上?

Android 的 2D 繪圖功能,其實是很強的,尤其是 matrix 這部份。透過 matrix,你就可以輕易地,控制整個繪圖座標體系的位移、旋轉、傾斜,放大等功能。舉個例子來說,如果你要將圖片旋轉 30 度後,再貼在螢幕上。傳統的作法,都是直接先將圖片的內容,逐點旋轉 30 度。但是在 Android 平台上,你不用這麼麻煩。你只要將整個座標系統轉個 30 度,之後不管你是畫線,還是貼圖,最後呈現在螢幕上的都是轉 30 度的效果。

想要了解 AnalogClock 是如何選轉時、分針圖片,那看 onDraw() 這個函式就對了。我將 onDraw() 中,和這部份選轉時、分針圖片相關的,列在下面。其中的 canvas.rotate() 是關鍵,這個函式就是在將座標系統轉個角度。看完這個例子,有沒有發覺要在 Android 上旋轉、放大或縮小圖片,其實都是只要兩三行程式,就可搞定的事。

AnalogClock 就剩 constructors,與其他 onMeasure(), onSizeChanged(), onDetachedFromWindow() 等函式還沒提到,我想這部份就留給你自己去發掘。


繼續閱讀全文...