Matrix 是什么
Matrix 擁有一個(gè) 3 * 3 矩陣,這個(gè)矩陣用于坐標(biāo)變換劝赔。這個(gè)矩陣定義如下
Matrix 有個(gè)方法 isAffine()誓焦,判斷矩陣是否為仿射矩陣。那么是什么仿射矩陣呢着帽?下面一段話來自百度百科
仿射變換:它是一種二維坐標(biāo)到二維坐標(biāo)之間的線性變換杂伟,保持二維圖形的“平直性”(變換后,直線還是直線仍翰,圓弧還是圓缓罩唷)、“平行性”(保持二維圖形間的相對位置不變予借,平等線還是平等線越平,但是向量夾角可以發(fā)生變化)。仿射變換可以通過一系列的原子變換的復(fù)合來實(shí)現(xiàn)灵迫,包括平移(Translate)秦叛、縮放(Scale)、翻轉(zhuǎn)(Flip)瀑粥,旋轉(zhuǎn)(Rotation)和錯(cuò)切(Skew)挣跋。此類變換可以用一個(gè) 3*3的矩陣來表示,其最后一行為(0狞换,0避咆,1)舟肉。
Matrix 操作的仿射變換操作有平移(Translate),縮放(Scale)查库,旋轉(zhuǎn)(Rotate)路媚,錯(cuò)切(Skew),所以這些操作對應(yīng)的矩陣最后 一行永遠(yuǎn)是(0,0,1)樊销。
當(dāng)然 Matrix 并不只是操作仿射變換整慎,它還可以操作透視變換(Perspective),也就是矩陣最后一行操作的就是透視變換 现柠。所以仿射變換其實(shí)就是透視變換的一種特殊情況院领。Matrix.setPolyToPoly() 提供了透視變換的操作,但是畢竟不是專業(yè)處理圖像的够吩,所以只能帶大家入個(gè)門吧比然。
下面先從數(shù)學(xué)角度推導(dǎo)下 Matrix 操作的四種彷射變換對應(yīng)的矩陣是什么。
平移的矩陣
一個(gè)點(diǎn) P 平移到 P' 周循,在坐標(biāo)系表示如下
用數(shù)學(xué)公式表示
轉(zhuǎn)換為坐標(biāo)系表示
中間的3 * 3 矩陣就是平移對應(yīng)的 Matix 强法, ?x 就是 x 軸的增量,?y 就是 y 軸的增量湾笛。
旋轉(zhuǎn)矩陣
旋轉(zhuǎn)是有中心點(diǎn)的饮怯,如果把 Matrix 應(yīng)用到圖片,默認(rèn)旋轉(zhuǎn)點(diǎn)是圖片的左上角嚎研,也就是圖片的坐標(biāo)原點(diǎn)(0蓖墅,0)。
繞坐標(biāo)原點(diǎn)旋轉(zhuǎn)矩陣
用數(shù)學(xué)公式表示
用矩陣表示
中間的 3 * 3 矩陣就是繞坐標(biāo)原點(diǎn)旋轉(zhuǎn)的 Matrix临扮。
繞非坐標(biāo)原點(diǎn)旋轉(zhuǎn)矩陣
如果圖片并非繞它的坐標(biāo)原點(diǎn)(左上角)旋轉(zhuǎn)论矾,而是繞任意點(diǎn)旋轉(zhuǎn),例如 圖片中心點(diǎn) (x1, y1)杆勇,那么這個(gè)又如何計(jì)算呢贪壳?
這里我們先用數(shù)學(xué)思維思考,我們可以把(x1,y1) 作為坐標(biāo)原點(diǎn)蚜退,那么 P 的表示應(yīng)該為 (x0-x1, y0-y1)闰靴,P' 的坐標(biāo)為 (x-x1, y-y1),那么我們再應(yīng)用旋轉(zhuǎn)點(diǎn)是坐標(biāo)原點(diǎn)的矩陣公式钻注,就應(yīng)該這樣寫
再簡化下
是不是現(xiàn)在感覺比較明朗蚂且,等式右邊第一個(gè)和第三個(gè)矩陣就是平移矩陣,我們在后面會(huì)看到如何生成這三個(gè)矩陣的結(jié)果幅恋。
我們再來理解下這個(gè)矩陣膘掰,最后兩個(gè)矩陣相乘代表坐標(biāo)原點(diǎn)移動(dòng)到(x1,y1),最后三個(gè)矩陣代表以(x1, y1)為原點(diǎn)旋轉(zhuǎn),最后四個(gè)矩陣代表坐標(biāo)原點(diǎn)從(x1, y1)移動(dòng)到(0识埋,0),這樣是不是好記一些零渐。
縮放矩陣
圖片都是由像素點(diǎn)構(gòu)成的窒舟,如果一個(gè)圖片放大 k 倍,可以看作每個(gè)點(diǎn)的 x 和 y 坐標(biāo)值放大 k 倍诵盼。當(dāng)然縮放也有中心點(diǎn)的惠豺。圖片默認(rèn)的縮放中心為圖片的左上角,也就是圖片的原點(diǎn)(0风宁,0)洁墙。
原點(diǎn)為中心的縮放
用數(shù)學(xué)表示如下
用矩陣表示如下
中間的矩陣就是繞原點(diǎn)綻放的 Matrix
非原點(diǎn)的縮放
這與非原點(diǎn)的旋轉(zhuǎn)的數(shù)學(xué)思維是一樣的,這里直接給出矩陣
中間的三個(gè)矩陣相乘就是非原點(diǎn)縮放 Matrix
錯(cuò)切矩陣
錯(cuò)切分為 x 軸和 y 軸的錯(cuò)切戒财。
x 軸錯(cuò)切矩陣
x 軸錯(cuò)切热监,是保持坐標(biāo)的 y 軸值不變,x 軸值的做線性變換 饮寞,表示如下
斜率為 1/k
矩陣表示如下
矩陣表示用到的是 k孝扛,而不是斜率 1 / k,因此 k 越大幽崩,圖形錯(cuò)切的越大苦始。
y 軸錯(cuò)切矩陣
y軸錯(cuò)切,就是 x 軸的值不變慌申,y 軸的值做線性變換陌选,矩陣就不用我再推理吧,表示如下
x 軸 y 軸的錯(cuò)切矩陣
綜合 x 軸 和 y 軸錯(cuò)切蹄溉,統(tǒng)一表示如下
kx 表示 x 軸的錯(cuò)切值咨油,ky 表示 y 軸的錯(cuò)切值。kx类缤,ky 越大臼勉,圖形錯(cuò)切的越大。
看完了可惡的數(shù)學(xué)公式餐弱,我們就懂得了原理 宴霸,現(xiàn)在用 Matrix 的 API 來測試測試吧。
每種變換都有 setXx() 膏蚓,postXx()瓢谢,preXx() 方法來設(shè)置相應(yīng)的變換 。如 Translate驮瞧,有 Matrix.setTranslate()氓扛,Matirx.postTranslate(),Matrix.preTranslate()。我將會(huì)在代碼中解釋這些到底怎么用采郎,請大家多注意千所,因?yàn)榇蟛糠秩藭?huì)用錯(cuò)。
默認(rèn)顯示一個(gè) launcher 圖標(biāo)
我們先顯示一個(gè)不做 Matrix 處理的圖標(biāo)
/**
* Created by David Chow on 2016/12/6.
*/
public class MatrixView extends View {
private Matrix mMatrix;
private Bitmap mBitmap;
public MatrixView(Context context) {
this(context, null);
}
public MatrixView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
private void init() {
mBitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher);
mMatrix = new Matrix();
Log.d("david", mMatrix.toString());
}
@Override
protected void onDraw(Canvas canvas) {
canvas.drawBitmap(mBitmap, mMatrix, null);
}
}
我們看看這個(gè)初始的 Matrix 矩陣是什么
Matrix{[1.0, 0.0, 0.0][0.0, 1.0, 0.0][0.0, 0.0, 1.0]}
這就是一個(gè) 3 * 3 單位矩陣 蒜埋,因此它不論前乘還是后乘都無所謂淫痰。
平移
@Override
protected void onDraw(Canvas canvas) {
mMatrix.setTranslate(200, 200); // 單位 pixel
canvas.drawBitmap(mBitmap, mMatrix, null);
Log.d("david", "translate matrix : " + mMatrix.toString());
}
我們看到了圖像平移了,這個(gè)時(shí)候整份,我們打印 Log待错,可以看到矩陣的值為
translate matrix : Matrix{[1.0, 0.0, 200.0][0.0, 1.0, 200.0][0.0, 0.0, 1.0]}
我們可以看到 Matrix.setTranslate() 把單位矩陣重置為 平移的矩陣(上面數(shù)學(xué)分析得出的平移矩陣)
那么用理論的的矩陣這樣表示
這樣與我們上面理論是不是就一致了,這個(gè)時(shí)候我們心情是不是稍微好點(diǎn)了烈评,因?yàn)槲覀兝碚摻K于在實(shí)際中得到驗(yàn)證了火俄。后面我們將不再去這個(gè)用矩陣來驗(yàn)證理論,我們只打印相應(yīng)的 Log 即可讲冠。
縮放
@Override
protected void onDraw(Canvas canvas) {
mMatrix.setTranslate(200, 200); // 單位 pixel
mMatrix.setScale(3, 3);
canvas.drawBitmap(mBitmap, mMatrix, null);
Log.d("david", mMatrix.toString());
}
Log打印如下
Matrix{[3.0, 0.0, 0.0][0.0, 3.0, 0.0][0.0, 0.0, 1.0]}
就算我們先用了一個(gè)無關(guān)的 Matrix.setTranslate() 瓜客, 后面的 Matrix.setScale() 還是會(huì)重置矩陣為縮放的矩陣。這點(diǎn)大家要記住沟启,免得以后沒有達(dá)到效果忆家,卻不知道問題在哪里了。
默認(rèn)的縮放中心是圖片的左上角德迹,也就是(0,0)芽卿,當(dāng)然我們也可以調(diào)整縮放中心位置
@Override
protected void onDraw(Canvas canvas) {
mMatrix.setScale(3, 3,mBitmap.getWidth()/2,mBitmap.getHeight()/2);
canvas.drawBitmap(mBitmap, mMatrix, null);
Log.d("david", mMatrix.toString());
}
現(xiàn)在縮放中心的位置為 Bitmap 的中心位置,現(xiàn)在看下效果
從效果看胳搞,確實(shí)是根據(jù)中心點(diǎn)縮放卸例,以致圖片的左上角超出了屏幕顯示。
我們打印Log看下
Matrix{[3.0, 0.0, -126.0][0.0, 3.0, -126.0][0.0, 0.0, 1.0]}
那么與我們上面理論分析是不是相符肌毅,就留給大家去驗(yàn)證了筷转。
Matrix.setRotate()旋轉(zhuǎn)
旋轉(zhuǎn)也是有中心點(diǎn)的,先看看個(gè)繞圖片中心旋轉(zhuǎn)的情況
@Override
protected void onDraw(Canvas canvas) {
mMatrix.setRotate(45, mBitmap.getWidth() / 2, mBitmap.getHeight() / 2);
canvas.drawBitmap(mBitmap, mMatrix, null);
}
Log打印如下
Matrix{[0.70710677, -0.70710677, 63.0][0.70710677, 0.70710677, -26.095451][0.0, 0.0, 1.0]}
驗(yàn)證理論還是交給大家了~~
Matrix.setSinCos() 也是設(shè)置旋轉(zhuǎn)的悬而,只是它不是設(shè)置角度呜舒,而是設(shè)置角度的 sin 和 cos 值。
當(dāng)然默認(rèn)的旋轉(zhuǎn)中心也是圖片的左上角
@Override
protected void onDraw(Canvas canvas) {
mMatrix.setTranslate(200, 200); // 單位 pixel
mMatrix.setScale(3, 3);
mMatrix.setRotate(30);
canvas.drawBitmap(mBitmap, mMatrix, null);
Log.d("david", mMatrix.toString());
}
同樣笨奠,setRotate() 覆蓋了前面的 setScale(), setTranslate()
打印下矩陣信息
Matrix{[0.8660254, -0.5, 0.0][0.5, 0.8660254, 0.0][0.0, 0.0, 1.0]}
根據(jù)我們上面講的袭蝗,這個(gè)里面的小數(shù)值 ,對應(yīng)角度為 30 的 sin 或者 cos 值般婆,大家可以自己用計(jì)算器算算~~
現(xiàn)在看下效果圖
我們發(fā)現(xiàn)這個(gè)移出了屏幕了到腥,因此現(xiàn)在我們來平移下
@Override
protected void onDraw(Canvas canvas) {
mMatrix.setTranslate(200, 200); // 單位 pixel
mMatrix.setScale(3, 3);
mMatrix.setRotate(30);
mMatrix.preTranslate(200, 200);
canvas.drawBitmap(mBitmap, mMatrix, null);
Log.d("david", mMatrix.toString());
}
一個(gè)奇怪的現(xiàn)象就產(chǎn)生了,X 方向的平移呢蔚袍?其實(shí)這是因?yàn)榫仃嚦朔ú粷M足交換律的原因乡范。mMatrix.preTranslate()
意思是用 mMatrix 矩陣前乘 translate 矩陣,數(shù)據(jù)表示如下
這個(gè)結(jié)果就不用我算了吧,很顯然實(shí)際偏移的并不是200晋辆,200渠脉。那么我們?nèi)绾巫屗刃D(zhuǎn)30°,又正常偏移200,200呢栈拖。 我們可以讓 mMatrix 后乘 translate
@Override
protected void onDraw(Canvas canvas) {
canvas.drawLine(0, 0, 200, 200, mPaint);
mMatrix.setRotate(30);
mMatrix.postTranslate(200, 200);
canvas.drawBitmap(mBitmap, mMatrix, null);
}
為了看到我們確實(shí)是偏移了200,200连舍,我畫了一條紅色的線
那么我們用數(shù)學(xué)來表達(dá)下
大家計(jì)算下,是不是既旋轉(zhuǎn)了又平移了200,200涩哟?
我用這個(gè)例子是為了讓大家理解 preXx() 和 postXx() 的區(qū)別,希望大家自己動(dòng)手試試加深理解盼玄。
再給大家一個(gè) Tip贴彼,如果想要平移達(dá)到效果,最后調(diào)用 postTranslate()埃儿。
Matrix.setSkew() 錯(cuò)切
先用默認(rèn)中心點(diǎn)器仗,即圖片的左上角進(jìn)行錯(cuò)切。為了看到效果童番,我們先平移200,200精钮,再進(jìn)行錯(cuò)切變換
mMatrix.setTranslate(200, 200);
mMatrix.preSkew(1, 0);
canvas.drawBitmap(mBitmap, mMatrix, null);
這里我并沒有遵循上面的例子最后用 postTranslate(),這是因?yàn)楦鶕?jù)矩陣的特性剃斧,這樣剛好不影響平移轨香,我們可以打印Log看看矩陣
Matrix{[1.0, 1.0, 200.0][0.0, 1.0, 200.0][0.0, 0.0, 1.0]}
我沒說錯(cuò)吧,但是如果你再操作下幼东,就有問題了臂容,例如例如 postScale(3,3),就會(huì)平移 600,600了根蟹。當(dāng)然如果你最后還是調(diào)用 postTranslate(200脓杉,200) ,會(huì)正常平移400简逮,400球散。
大家別看我說的很簡單,自動(dòng)寫的時(shí)候就會(huì)遇到各種問題散庶,因此大家在看的時(shí)候還是多動(dòng)手蕉堰。
再看看中心點(diǎn)不是原點(diǎn)的錯(cuò)切,例如用圖片的左下角來錯(cuò)切督赤。
@Override
protected void onDraw(Canvas canvas) {
mMatrix.setTranslate(200, 200);
mMatrix.preSkew(1, 0, 0, mBitmap.getHeight());
canvas.drawBitmap(mBitmap, mMatrix, null);
Log.d("david", mMatrix.toString());
}
打印Log
Matrix{[1.0, 1.0, 74.0][0.0, 1.0, 200.0][0.0, 0.0, 1.0]}
理論驗(yàn)證繼續(xù)留給大家了~~
思考
我們使用 Matrix API 的時(shí)候嘁灯,我們只是一名 Developer,但是我們要把自己當(dāng)做一個(gè) Designer躲舌,我們思考下丑婿,其實(shí)我們可以利用矩陣做很多想要的效果,因?yàn)槲覀兛梢杂?Matrix.setValues() 來設(shè)置自己想要的矩陣 ,因此我們可以設(shè)計(jì)對稱效果羹奉,倒影效果等等秒旋,這就要大家去發(fā)掘啦~~
結(jié)束
這篇文章大家入門 Matrix,當(dāng)然這是為了我后面文章打基礎(chǔ)的诀拭,還是那句話迁筛,多動(dòng)手,如果遇到問題解決不了耕挨,歡迎大家留言討論细卧。 如果大家覺得還不錯(cuò),可以點(diǎn)個(gè)贊筒占,甚至來一波關(guān)注不惜留戀_贪庙, what's a nice day~~
參考文章
http://blog.csdn.net/cquwentao/article/details/51445269
http://baike.baidu.com/link?url=D4AHXGFs4yjlXg74jY1xU5shk1z--hyAe28ynSWaadyI0IrVQSYh6ueJpgpHbJwk8mGdrWEscclauzzMo81vv6qb__77JQdpSmgcgF6S1HnYJDHDdEGRquy3sCYf7UPa
http://baike.baidu.com/link?url=sqH8yi74VwnyBq3Uhr_tn6pCF9lHNZWmRF0j_4hc3gLy45vPBFM9eGnN3BrpUou8jiPaXtDxg_B7WvNGEXep8UuQr7q8tHk8DTWv34FcKKK-1iQy5W7-2IBPGxsab5Wr