android matrix

1 概述

這裏咱們會詳細講解matrix的各個方法,以及它的用法。matrix叫作矩陣,在前面講解 ColorFilter 的文章中,咱們講解了ColorMatrix,他是一個4*5的矩陣。而這裏,咱們講解的Matrix不是用於處理顏色的,而是處理圖形的。他是一個3*3的矩陣。html

2 原理

先看看matrix的矩陣是什麼樣子的:canvas

這裏寫圖片描述

這裏能夠查看Matrix的代碼獲得。那麼這個矩陣分別表明了什麼呢,這裏經過他們的名字能夠看出,scale是縮放,skew是錯切(canvas變換中有講過),trans是平移,persp表明透視(官方文檔中,也沒有詳細講解,透視在這裏只作簡單介紹)。這裏須要把矩陣根據他們的做用劃分爲4塊:api

這裏寫圖片描述

如上圖所示,這四塊區域各有做用。後面會詳細講解各個做用,先來看看這個矩陣是如何影響圖像的。先看看屏幕的座標系:數組

這裏寫圖片描述

看上圖,這裏表示了屏幕的座標系,其中的x,y軸是你們所熟知的,可是其實,一個物體他是存在於一個三維空間的,因此必然會有z軸。咱們的屏幕,就像是一個窗口,透過它,咱們看到了屏幕後面的世界,那裏面有各類物體,咱們看到的是映射在x,y平面上的一個投射圖像。屏幕就像是一個鏡頭同樣,將裏面的物體映射到x,y平面上,成爲一個二維的圖像。那麼若是,咱們把屏幕這個鏡頭沿着z軸,拉遠或者拉進,那麼圖像會有什麼變化呢,確定會變小或者變大。就比如坐在飛機上透過窗口看地面的汽車,和在地面上看到的大小是不一樣的。app

結論就是,在屏幕上顯示的像素,不只僅有x,y座標,其實還有z軸的影響。因此這裏對應的像素描述由一個3行一列的矩陣來表示:ide

這裏寫圖片描述

x,y分別表明x,y軸上的座標,而1表明屏幕在z軸上的座標爲默認的。若是將1變大,那麼屏幕會拉遠, 圖形會變小。函數

如今咱們來看看matrix怎麼做用於每一個像素的值。這裏須要用到矩陣的乘法,首先須要明確的是,矩陣的前乘和後乘是不相同的,也就是說不知足乘法交換律。post

這裏咱們經過一個旋轉變換來看看原理,其實一張圖片圍繞一個點旋轉,也就是全部的點都圍繞一個點旋轉,因此只須要關注一個點的狀況便可:spa

假定有一個點 ,相對座標原點順時針旋轉後的情形,同時假定P點離座標原點的距離爲r,以下圖:.net

這裏寫圖片描述

那麼就有:

這裏寫圖片描述

換作矩陣運算就以下圖:

這裏寫圖片描述

從這裏就能夠看出,矩陣中的值,是如何做用於像素點的x,y座標以及z軸遠近。

同時,能夠看到,上面的矩陣四塊區域的切分也是由於矩陣乘法的操做決定的,因爲這裏的乘法運算中,左上角的四個值,能夠和x,y值作乘法運算,因此能夠影響到旋轉等操做,而右上角的模塊,只能作加法,因此只能影響到平移。右下角的模塊主要管z軸,天然就能夠進行等比的縮放了,左下角的模塊通常不去動他,不然會把x,y值加入到z軸中來,會不可控。

3 基本方法解析

講解完了matrix做用於像素點的原理以後,咱們逐個講解它的方法。

(1) 構造函數

public Matrix()
public Matrix(Matrix src)

構造函數有兩個,第一個是直接建立一個單位矩陣,第二個是根據提供的矩陣建立一個新的矩陣(採用deep copy)

單位矩陣以下:

這裏寫圖片描述

(2) isIdentity與isAffine

public boolean isIdentity()//判斷是不是單位矩陣
public boolean isAffine()//判斷是不是仿射矩陣

是不是單位矩陣很簡單,就不作講解了,這裏是不是仿射矩陣可能你們很差理解。

首先來看看什麼是仿射變換。仿射變換其實就是二維座標到二維座標的線性變換,保持二維圖形的「平直性」(即變換後直線仍是直線不會打彎,圓弧仍是圓弧)和「平行性」(指保持二維圖形間的相對位置關係不變,平行線仍是平行線,而直線上點的位置順序不變),能夠經過一系列的原子變換的複合來實現,原子變換就包括:平移、縮放、翻轉、旋轉和錯切。這裏除了透視能夠改變z軸之外,其餘的變換基本都是上述的原子變換,因此,只要最後一行是0,0,1則是仿射矩陣。

(3) rectStaysRect

public boolean rectStaysRect()

判斷該矩陣是否能夠將一個矩形依然變換爲一個矩形。當矩陣是單位矩陣,或者只進行平移,縮放,以及旋轉90度的倍數的時候,返回true。

(4) reset

public void reset()

重置矩陣爲單位矩陣。

(5) setTranslate

public void setTranslate(float dx, float dy)

設置平移效果,參數分別是x,y上的平移量。 
效果圖以下:

這裏寫圖片描述

代碼以下:

Matrix matrix = new Matrix();
canvas.drawBitmap(bitmap, matrix, paint);

matrix.setTranslate(100, 1000);
canvas.drawBitmap(bitmap, matrix, paint);

(6) setScale

public void setScale(float sx, float sy, float px, float py)
public void setScale(float sx, float sy)

兩個方法都是設置縮放到matrix中,sx,sy表明了縮放的倍數,px,py表明縮放的中心。這裏跟上面比較相似不作講解了。

(7) setRotate

 public void setRotate(float degrees, float px, float py)
 public void setRotate(float degrees)

和上面相似,再也不講解。

(8) setSinCos

public void setSinCos(float sinValue, float cosValue, float px, float py)
public void setSinCos(float sinValue, float cosValue)

這個方法乍一看可能有點蒙,其實在前面的原理中,咱們講解了一個旋轉的例子,他最終的矩陣效果是這樣的:

這裏寫圖片描述

其實旋轉,就是使用了這樣的matrix,顯而易見,這裏的參數就清晰了。 
sinValue:對應圖中的sin值 
cosValue:對應cos值 
px:中心的x座標 
py:中心的y座標

看一個示例,咱們把圖像旋轉90度,那麼90度對應的sin和cos分別是1和0。

這裏寫圖片描述

看代碼以下:

Matrixmatrix = new Matrix();
matrix.setSinCos(1, 0, bitmap.getWidth() / 2, bitmap.getHeight() / 2);
canvas.drawBitmap(bitmap, matrix, paint);

(9) setSkew

public void setSkew(float kx, float ky, float px, float py)
public void setSkew(float kx, float ky)

錯切,這裏kx,ky分別表明了x,y上的錯切因子,px,py表明了錯切的中心。不瞭解錯切了在前面canvas變換中去查看,這裏再也不講解。

(10) setConcat

public boolean setConcat(Matrix a,Matrix b)

將當前matrix的值變爲a和b的乘積,它的意義在下面的 進階方法中來探討。

4 進階方法解析

上面的基本方法中,有關於變換的set方法均可以帶來不一樣的效果,可是每一個set都會把上個效果清除掉,例如依次調用了setSkew,setTranslate,那麼最終只有setTranslate會起做用,那麼如何才和將兩種效果複合呢。Matrix給咱們提供了不少方法。可是主要都是2類:

preXXXX:以pre開頭,例如preTranslate 
postXXXX:以post開頭,例如postScale

他們分別表明了前乘,和後乘。看一段代碼:

Matrix matrix = new Matrix();
matrix.setTranslate(100, 1000);
matrix.preScale(0.5f, 0.5f);

這裏matrix前乘了一個scale矩陣,換算成數學式以下:

這裏寫圖片描述

從上面能夠看出,最終得出的matrix既包含了縮放信息也有平移信息。 
後乘天然就是matrix在後面,而縮放矩陣在前面,因爲矩陣先後乘並不等價,也就致使了他們的效果不一樣。咱們來看看後乘的結果:

這裏寫圖片描述

能夠看到,結果跟上面不一樣,而且這也不是咱們想要的結果,這裏縮放沒有更改,可是平移被減半了,換句話說,平移的距離也被縮放了。因此須要注意先後乘法的關係。

來看看他們對應的效果圖:

前乘:

這裏寫圖片描述

後乘:

這裏寫圖片描述

能夠明顯看到,後乘的平移距離受了影響。

瞭解清除了先後乘的意義,在使用的過程當中,多個效果的疊加時,同樣要注意,不然效果達不到預期。

5 其餘方法解析

matrix除了上面的方法外,還有一些其餘的方法,這裏依次解析

(1) setRectToRect

public boolean setRectToRect(RectF src, RectF dst, ScaleToFit stf)

將rect變換成rect,上面的rectStaysRect已經說過,要保持rect只能作縮放平移和選擇90度的倍數,那麼這裏其實也是同樣,只是這幾種變化,這裏經過stf參數來控制。

ScaleToFit 有以下四個值:

FILL: 可能會變換矩形的長寬比,保證變換和目標矩陣長寬一致。 
START:保持座標變換前矩形的長寬比,並最大限度的填充變換後的矩形。至少有一邊和目標矩形重疊。左上對齊。 
CENTER: 保持座標變換前矩形的長寬比,並最大限度的填充變換後的矩形。至少有一邊和目標矩形重疊。 
END:保持座標變換前矩形的長寬比,並最大限度的填充變換後的矩形。至少有一邊和目標矩形重疊。右下對齊。

這裏使用谷歌的api demo的圖片做爲例子:

這裏寫圖片描述

(2) setPolyToPoly

public boolean setPolyToPoly(float[] src, int srcIndex,float[] dst, int dstIndex,int pointCount)

經過指定的0-4個點,原始座標以及變化後的座標,來獲得一個變換矩陣。若是指定0個點則沒有效果。

下面經過例子分別說明1到4個點的能夠達到的效果:

這裏寫代碼片##### 1個點,平移 
只指定一個點,能夠達到平移效果:

這裏寫圖片描述

代碼以下:

float[] src = {0, 0};
int DX = 300;
float[] dst = {0 + DX, 0 + DX};
matrix.setPolyToPoly(src, 0, dst, 0, 1);
canvas.drawBitmap(bitmap, matrix, paint);
2個點,旋轉或者縮放

兩個點,能夠達到旋轉效果或者縮放效果,縮放比較簡單,這裏咱們來看旋轉效果,一個點指定中心,一點指出旋轉的效果

這裏寫圖片描述

代碼以下

int bw = bitmap.getWidth();
int bh = bitmap.getHeight();
float[] src = {bw / 2, bh / 2, bw, 0};
float[] dst = {bw / 2, bh / 2, bw / 2 + bh / 2, bh / 2 + bw / 2};
matrix.setPolyToPoly(src, 0, dst, 0, 2);
canvas.drawBitmap(bitmap, matrix, paint);

圖片的中心點做爲旋轉的中心,先後不變,右上角變化到了下方,因此致使圖片旋轉了90度。

3個點,錯切

使用3個點,能夠產生錯切效果,指定3個頂點,一個固定,另外兩個移動。

看圖:

這裏寫圖片描述

代碼以下:

Matrix matrix = new Matrix();
int bw = bitmap.getWidth();
int bh = bitmap.getHeight();
float[] src = {0,0, 0, bh,bw,bh};
float[] dst = {0, 0, 200, bh, bw + 200, bh};
matrix.setPolyToPoly(src, 0, dst, 0, 3);
canvas.drawBitmap(bitmap, matrix, paint);
4個點,透視

透視就是觀察的角度變化了。致使投射到平面上的二維圖像變化了。

咱們看下面的例子,更容易理解:

這裏寫圖片描述

圖片看起來好像傾斜了,實現特別簡單:

Matrix matrix = new Matrix();
int bw = bitmap.getWidth();
int bh = bitmap.getHeight();
float[] src = {0, 0, 0, bh, bw, bh, bw, 0};
int DX = 100;
float[] dst = {0 + DX, 0, 0, bh, bw, bh, bw - DX, 0};
matrix.setPolyToPoly(src, 0, dst, 0, 4);
canvas.drawBitmap(bitmap, matrix, paint);

能夠看到,只是把左右兩個頂點往裏面收攏了,這樣就得出了一個有3d效果的透視圖。

(3) invert

public boolean invert(Matrix inverse)

反轉當前矩陣,若是能反轉就返回true並將反轉後的值寫入inverse,不然返回false。當前矩陣*inverse=單位矩陣。

反轉先後有什麼效果,咱們來看看示例:

這裏寫圖片描述

能夠看到,反轉以後,實際上是對效果的一種反轉。

(4) mapPoints

public void mapPoints(float[] dst, int dstIndex, float[] src, int srcIndex,int pointCount)
public void mapPoints(float[] dst, float[] src)
public void mapPoints(float[] pts)

映射點的值到指定的數組中,這個方法能夠在矩陣變換之後,給出指定點的值。 
dst:指定寫入的數組 
dstIndex:寫入的起始索引,x,y兩個座標算做一對,索引的單位是對,也就是通過兩個值才加1 
src:指定要計算的點 
srcIndex:要計算的點的索引 
pointCount:須要計算的點的個數,每一個點有兩個值,x和y。

(5) mapVectors

public void mapVectors(float[] dst, int dstIndex, float[] src, int srcIndex,int vectorCount)
public void mapVectors(float[] dst, float[] src)
public void mapVectors(float[] vecs)

與上面的mapPoionts基本相似,這裏是將一個矩陣做用於一個向量,因爲向量的平移先後是相等的,因此這個方法不會對translate相關的方法產生反應,若是隻是調用了translate相關的方法,那麼獲得的值和本來的一致。

(6) mapRect

public boolean mapRect(RectF dst, RectF src)
public boolean mapRect(RectF rect)

返回值便是調用的rectStaysRect(),這個方法前面有講過,這裏把src中指定的矩形的左上角和右下角的兩個點的座標,寫入dst中。

(7) mapRadius

public float mapRadius(float radius)

返回一個圓圈半徑的平均值,將matrix做用於一個指定radius半徑的圓,隨後返回的平均半徑。

以上基本解析完畢了全部matrix的方法,以及一些高階用法,本篇文章就到這裏

相關文章
相關標籤/搜索