CSDN博客
img cquwentao
android matrix 最全方法詳解與進(jìn)階(完整篇)
發(fā)表于2016/5/18 16:43:27 ?1385人閱讀
分類: android繪制
1 概述
這里我們會詳細(xì)講解matrix的各個(gè)方法,以及它的用法蓉驹。matrix叫做矩陣瞻润,在前面講解 ColorFilter 的文章中章贞,我們講解了ColorMatrix兜喻,他是一個(gè)4*5的矩陣。而這里戚扳,我們講解的Matrix不是用于處理顏色的,而是處理圖形的。他是一個(gè)3*3的矩陣桃焕。
2 原理
先看看matrix的矩陣是什么樣子的:
這里寫圖片描述
這里可以查看Matrix的代碼得到。那么這個(gè)矩陣分別代表了什么呢胰坟,這里通過他們的名字可以看出,scale是縮放礁叔,skew是錯(cuò)切(canvas變換中有講過),trans是平移,persp代表透視(官方文檔中禀晓,也沒有詳細(xì)講解顷级,透視在這里只做簡單介紹)删掀。這里需要把矩陣根據(jù)他們的作用劃分為4塊:
這里寫圖片描述
如上圖所示搬瑰,這四塊區(qū)域各有作用鹦赎。后面會詳細(xì)講解各個(gè)作用胀莹,先來看看這個(gè)矩陣是如何影響圖像的。先看看屏幕的坐標(biāo)系:
這里寫圖片描述
看上圖吃媒,這里表示了屏幕的坐標(biāo)系瓤介,其中的x吕喘,y軸是大家所熟知的,但是其實(shí)刑桑,一個(gè)物體他是存在于一個(gè)三維空間的氯质,所以必然會有z軸。我們的屏幕祠斧,就像是一個(gè)窗口闻察,透過它,我們看到了屏幕后面的世界梁肿,那里面有各種物體蜓陌,我們看到的是映射在x觅彰,y平面上的一個(gè)投射圖像吩蔑。屏幕就像是一個(gè)鏡頭一樣,將里面的物體映射到x填抬,y平面上烛芬,成為一個(gè)二維的圖像。那么如果飒责,我們把屏幕這個(gè)鏡頭沿著z軸赘娄,拉遠(yuǎn)或者拉進(jìn),那么圖像會有什么變化呢宏蛉,肯定會變小或者變大遣臼。就好比坐在飛機(jī)上透過窗口看地面的汽車,和在地面上看到的大小是不同的拾并。
結(jié)論就是揍堰,在屏幕上顯示的像素,不僅僅有x嗅义,y坐標(biāo)屏歹,其實(shí)還有z軸的影響。所以這里對應(yīng)的像素描述由一個(gè)3行一列的矩陣來表示:
這里寫圖片描述
x之碗,y分別代表x蝙眶,y軸上的坐標(biāo),而1代表屏幕在z軸上的坐標(biāo)為默認(rèn)的褪那。如果將1變大幽纷,那么屏幕會拉遠(yuǎn), 圖形會變小博敬。
現(xiàn)在我們來看看matrix怎么作用于每個(gè)像素的值友浸。這里需要用到矩陣的乘法,首先需要明確的是冶忱,矩陣的前乘和后乘是不相同的尾菇,也就是說不滿足乘法交換律境析。
這里我們通過一個(gè)旋轉(zhuǎn)變換來看看原理,其實(shí)一張圖片圍繞一個(gè)點(diǎn)旋轉(zhuǎn),也就是所有的點(diǎn)都圍繞一個(gè)點(diǎn)旋轉(zhuǎn)派诬,所以只需要關(guān)注一個(gè)點(diǎn)的情況即可:
假定有一個(gè)點(diǎn) 劳淆,相對坐標(biāo)原點(diǎn)順時(shí)針旋轉(zhuǎn)后的情形,同時(shí)假定P點(diǎn)離坐標(biāo)原點(diǎn)的距離為r默赂,如下圖:
這里寫圖片描述
那么就有:
這里寫圖片描述
換做矩陣運(yùn)算就如下圖:
這里寫圖片描述
從這里就可以看出沛鸵,矩陣中的值,是如何作用于像素點(diǎn)的x缆八,y坐標(biāo)以及z軸遠(yuǎn)近曲掰。
同時(shí),可以看到奈辰,上面的矩陣四塊區(qū)域的切分也是因?yàn)榫仃嚦朔ǖ牟僮鳑Q定的栏妖,由于這里的乘法運(yùn)算中,左上角的四個(gè)值奖恰,可以和x吊趾,y值做乘法運(yùn)算,所以可以影響到旋轉(zhuǎn)等操作瑟啃,而右上角的模塊论泛,只能做加法,所以只能影響到平移蛹屿。右下角的模塊主要管z軸屁奏,自然就可以進(jìn)行等比的縮放了蜈出,左下角的模塊一般不去動他爷速,否則會把x喧兄,y值加入到z軸中來醋界,會不可控请唱。
3 基本方法解析
講解完了matrix作用于像素點(diǎn)的原理之后艘虎,我們逐個(gè)講解它的方法繁扎。
(1) 構(gòu)造函數(shù)
public Matrix()
public Matrix(Matrix src)
構(gòu)造函數(shù)有兩個(gè)辙培,第一個(gè)是直接創(chuàng)建一個(gè)單位矩陣油航,第二個(gè)是根據(jù)提供的矩陣創(chuàng)建一個(gè)新的矩陣(采用deep copy)
單位矩陣如下:
這里寫圖片描述
(2) isIdentity與isAffine
public boolean isIdentity()//判斷是否是單位矩陣
public boolean isAffine()//判斷是否是仿射矩陣
是否是單位矩陣很簡單崭庸,就不做講解了,這里是否是仿射矩陣可能大家不好理解谊囚。
首先來看看什么是仿射變換怕享。仿射變換其實(shí)就是二維坐標(biāo)到二維坐標(biāo)的線性變換,保持二維圖形的“平直性”(即變換后直線還是直線不會打彎镰踏,圓弧還是圓缓睢)和“平行性”(指保持二維圖形間的相對位置關(guān)系不變,平行線還是平行線奠伪,而直線上點(diǎn)的位置順序不變)跌帐,可以通過一系列的原子變換的復(fù)合來實(shí)現(xiàn)首懈,原子變換就包括:平移、縮放谨敛、翻轉(zhuǎn)究履、旋轉(zhuǎn)和錯(cuò)切。這里除了透視可以改變z軸以外脸狸,其他的變換基本都是上述的原子變換最仑,所以,只要最后一行是0,0,1則是仿射矩陣炊甲。
(3) rectStaysRect
public boolean rectStaysRect()
判斷該矩陣是否可以將一個(gè)矩形依然變換為一個(gè)矩形泥彤。當(dāng)矩陣是單位矩陣,或者只進(jìn)行平移卿啡,縮放吟吝,以及旋轉(zhuǎn)90度的倍數(shù)的時(shí)候,返回true牵囤。
(4) reset
public void reset()
重置矩陣為單位矩陣爸黄。
(5) setTranslate
public void setTranslate(float dx, float dy)
設(shè)置平移效果,參數(shù)分別是x揭鳞,y上的平移量。
效果圖如下:
這里寫圖片描述
代碼如下:
Matrix matrix = new Matrix();
canvas.drawBitmap(bitmap, matrix, paint);
matrix.setTranslate(100, 1000);
canvas.drawBitmap(bitmap, matrix, paint);
1
2
3
4
5
(6) setScale
public void setScale(float sx, float sy, float px, float py)
public void setScale(float sx, float sy)
兩個(gè)方法都是設(shè)置縮放到matrix中梆奈,sx野崇,sy代表了縮放的倍數(shù),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)
這個(gè)方法乍一看可能有點(diǎn)蒙扶镀,其實(shí)在前面的原理中,我們講解了一個(gè)旋轉(zhuǎn)的例子焰轻,他最終的矩陣效果是這樣的:
這里寫圖片描述
其實(shí)旋轉(zhuǎn)臭觉,就是使用了這樣的matrix,顯而易見辱志,這里的參數(shù)就清晰了蝠筑。
sinValue:對應(yīng)圖中的sin值
cosValue:對應(yīng)cos值
px:中心的x坐標(biāo)
py:中心的y坐標(biāo)
看一個(gè)示例,我們把圖像旋轉(zhuǎn)90度,那么90度對應(yīng)的sin和cos分別是1和0揩懒。
這里寫圖片描述
看代碼如下:
Matrixmatrix = new Matrix();
matrix.setSinCos(1, 0, bitmap.getWidth() / 2, bitmap.getHeight() / 2);
canvas.drawBitmap(bitmap, matrix, paint);
1
2
3
(9) setSkew
public void setSkew(float kx, float ky, float px, float py)
public void setSkew(float kx, float ky)
錯(cuò)切什乙,這里kx,ky分別代表了x已球,y上的錯(cuò)切因子臣镣,px辅愿,py代表了錯(cuò)切的中心。不了解錯(cuò)切了在前面canvas變換中去查看忆某,這里不再講解渠缕。
(10) setConcat
public boolean setConcat(Matrix a,Matrix b)
將當(dāng)前matrix的值變?yōu)閍和b的乘積,它的意義在下面的 進(jìn)階方法中來探討。
4 進(jìn)階方法解析
上面的基本方法中褒繁,有關(guān)于變換的set方法都可以帶來不同的效果亦鳞,但是每個(gè)set都會把上個(gè)效果清除掉,例如依次調(diào)用了setSkew,setTranslate棒坏,那么最終只有setTranslate會起作用燕差,那么如何才和將兩種效果復(fù)合呢。Matrix給我們提供了很多方法坝冕。但是主要都是2類:
preXXXX:以pre開頭徒探,例如preTranslate
postXXXX:以post開頭,例如postScale
他們分別代表了前乘喂窟,和后乘测暗。看一段代碼:
Matrix matrix = new Matrix();
matrix.setTranslate(100, 1000);
matrix.preScale(0.5f, 0.5f);
1
2
3
這里matrix前乘了一個(gè)scale矩陣磨澡,換算成數(shù)學(xué)式如下:
這里寫圖片描述
從上面可以看出碗啄,最終得出的matrix既包含了縮放信息也有平移信息。
后乘自然就是matrix在后面稳摄,而縮放矩陣在前面稚字,由于矩陣前后乘并不等價(jià),也就導(dǎo)致了他們的效果不同厦酬。我們來看看后乘的結(jié)果:
這里寫圖片描述
可以看到胆描,結(jié)果跟上面不同,并且這也不是我們想要的結(jié)果仗阅,這里縮放沒有更改昌讲,但是平移被減半了,換句話說减噪,平移的距離也被縮放了短绸。所以需要注意前后乘法的關(guān)系。
來看看他們對應(yīng)的效果圖:
前乘:
這里寫圖片描述
后乘:
這里寫圖片描述
可以明顯看到旋廷,后乘的平移距離受了影響鸠按。
了解清除了前后乘的意義,在使用的過程中饶碘,多個(gè)效果的疊加時(shí)目尖,一樣要注意,否則效果達(dá)不到預(yù)期扎运。
5 其他方法解析
matrix除了上面的方法外瑟曲,還有一些其他的方法饮戳,這里依次解析
(1) setRectToRect
public boolean setRectToRect(RectF src, RectF dst, ScaleToFit stf)
將rect變換成rect,上面的rectStaysRect已經(jīng)說過洞拨,要保持rect只能做縮放平移和選擇90度的倍數(shù)扯罐,那么這里其實(shí)也是一樣,只是這幾種變化烦衣,這里通過stf參數(shù)來控制歹河。
ScaleToFit 有如下四個(gè)值:
FILL: 可能會變換矩形的長寬比,保證變換和目標(biāo)矩陣長寬一致花吟。
START:保持坐標(biāo)變換前矩形的長寬比秸歧,并最大限度的填充變換后的矩形。至少有一邊和目標(biāo)矩形重疊衅澈。左上對齊键菱。
CENTER: 保持坐標(biāo)變換前矩形的長寬比,并最大限度的填充變換后的矩形今布。至少有一邊和目標(biāo)矩形重疊经备。
END:保持坐標(biāo)變換前矩形的長寬比,并最大限度的填充變換后的矩形部默。至少有一邊和目標(biāo)矩形重疊侵蒙。右下對齊。
這里使用谷歌的api demo的圖片作為例子:
這里寫圖片描述
(2) setPolyToPoly
public boolean setPolyToPoly(float[] src, int srcIndex,float[] dst, int dstIndex,int pointCount)
通過指定的0-4個(gè)點(diǎn)甩牺,原始坐標(biāo)以及變化后的坐標(biāo)蘑志,來得到一個(gè)變換矩陣。如果指定0個(gè)點(diǎn)則沒有效果贬派。
下面通過例子分別說明1到4個(gè)點(diǎn)的可以達(dá)到的效果:
這里寫代碼片##### 1個(gè)點(diǎn),平移
只指定一個(gè)點(diǎn)澎媒,可以達(dá)到平移效果:
這里寫圖片描述
代碼如下:
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);
1
2
3
4
5
2個(gè)點(diǎn)搞乏,旋轉(zhuǎn)或者縮放
兩個(gè)點(diǎn),可以達(dá)到旋轉(zhuǎn)效果或者縮放效果戒努,縮放比較簡單请敦,這里我們來看旋轉(zhuǎn)效果,一個(gè)點(diǎn)指定中心储玫,一點(diǎn)指出旋轉(zhuǎn)的效果
這里寫圖片描述
代碼如下
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);
1
2
3
4
5
6
圖片的中心點(diǎn)作為旋轉(zhuǎn)的中心侍筛,前后不變,右上角變化到了下方撒穷,所以導(dǎo)致圖片旋轉(zhuǎn)了90度匣椰。
3個(gè)點(diǎn),錯(cuò)切
使用3個(gè)點(diǎn)端礼,可以產(chǎn)生錯(cuò)切效果禽笑,指定3個(gè)頂點(diǎn)入录,一個(gè)固定,另外兩個(gè)移動佳镜。
看圖:
這里寫圖片描述
代碼如下:
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);
1
2
3
4
5
6
7
4個(gè)點(diǎn)僚稿,透視
透視就是觀察的角度變化了。導(dǎo)致投射到平面上的二維圖像變化了蟀伸。
我們看下面的例子蚀同,更容易理解:
這里寫圖片描述
圖片看起來好像傾斜了,實(shí)現(xiàn)特別簡單:
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);
1
2
3
4
5
6
7
8
可以看到啊掏,只是把左右兩個(gè)頂點(diǎn)往里面收攏了蠢络,這樣就得出了一個(gè)有3d效果的透視圖。
(3) invert
public boolean invert(Matrix inverse)
反轉(zhuǎn)當(dāng)前矩陣脖律,如果能反轉(zhuǎn)就返回true并將反轉(zhuǎn)后的值寫入inverse谢肾,否則返回false。當(dāng)前矩陣*inverse=單位矩陣小泉。
反轉(zhuǎn)前后有什么效果芦疏,我們來看看示例:
這里寫圖片描述
可以看到,反轉(zhuǎn)之后微姊,其實(shí)是對效果的一種反轉(zhuǎn)酸茴。
(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)
映射點(diǎn)的值到指定的數(shù)組中,這個(gè)方法可以在矩陣變換以后兢交,給出指定點(diǎn)的值薪捍。
dst:指定寫入的數(shù)組
dstIndex:寫入的起始索引,x配喳,y兩個(gè)坐標(biāo)算作一對酪穿,索引的單位是對,也就是經(jīng)過兩個(gè)值才加1
src:指定要計(jì)算的點(diǎn)
srcIndex:要計(jì)算的點(diǎn)的索引
pointCount:需要計(jì)算的點(diǎn)的個(gè)數(shù)晴裹,每個(gè)點(diǎn)有兩個(gè)值被济,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基本類似涧团,這里是將一個(gè)矩陣作用于一個(gè)向量只磷,由于向量的平移前后是相等的,所以這個(gè)方法不會對translate相關(guān)的方法產(chǎn)生反應(yīng)泌绣,如果只是調(diào)用了translate相關(guān)的方法钮追,那么得到的值和原本的一致。
(6) mapRect
public boolean mapRect(RectF dst, RectF src)
public boolean mapRect(RectF rect)
返回值即是調(diào)用的rectStaysRect()阿迈,這個(gè)方法前面有講過元媚,這里把src中指定的矩形的左上角和右下角的兩個(gè)點(diǎn)的坐標(biāo),寫入dst中。
(7) mapRadius
public float mapRadius(float radius)
返回一個(gè)圓圈半徑的平均值惠毁,將matrix作用于一個(gè)指定radius半徑的圓犹芹,隨后返回的平均半徑。
以上基本解析完畢了所有matrix的方法鞠绰,以及一些高階用法腰埂,本篇文章就到這里
上一篇 下一篇
評論(1)
philipy_meiphilipy_mei1樓
博主好,我現(xiàn)在在做一個(gè)功能蜈膨,就是想等比例縮放手機(jī)屏幕屿笼,我看到里面有 mSurfaceControl.setMatrix,這個(gè)矩陣的參數(shù)和你講的不太一樣翁巍,我要實(shí)現(xiàn)縮放的話能怎么做呢驴一?
mSurfaceControl.setMatrix(
mDsDx * w.mHScale, mDtDx * w.mVScale,
mDsDy * w.mHScale, mDtDy * w.mVScale);
2016-08-23 14:52回復(fù)
發(fā)表評論
我的熱門文章
android Path 和 PathMeasure 進(jìn)階
android matrix 詳解與進(jìn)階(一)
android intentFilter 匹配規(guī)則
android matrix 最全方法詳解與進(jìn)階(完整篇)
android canvas layer (圖層)詳解與進(jìn)階
相關(guān)博文
我的Android進(jìn)階之旅------Android通過使用Matrix旋轉(zhuǎn)圖片來模擬碟片加載過程
Android基礎(chǔ)入門教程8318 Canvas API詳解Part 3Matrix和drawBitmapMash
Android仿人人客戶端v571新鮮事之完整篇
android activity 應(yīng)該知道的一切完整篇
android開發(fā)筆記之不同機(jī)型的適配的解決方案完整篇
可動態(tài)布局的Android抽屜之完整篇
Android環(huán)境資源完整篇
可動態(tài)布局的Android抽屜之完整篇拖拽
可動態(tài)布局的Android抽屜之完整篇
Coco2dx 20 android開發(fā)搭建完整篇
未登錄
?首頁
?移動開發(fā)
?Web前端
?架構(gòu)設(shè)計(jì)
?編程語言
?互聯(lián)網(wǎng)
?數(shù)據(jù)庫
?系統(tǒng)運(yùn)維
?云計(jì)算
?研發(fā)管理
?綜合
2 0 1 分享