Matrix
主要用于對圖像的圖形處理痕惋。前面學(xué)習(xí)的ColorMatirx主要是圖像色彩的處理
學(xué)習(xí)資料
- Android 群英傳
- Android Matrix
- android matrix最全方法詳解與進階(完整篇)
十分感謝 : )
1.Martrix 變形矩陣
Matrix
是一個3 * 3
的矩陣钻蹬,每個像素點表達了其坐標的X,Y
信息
處理每個像素點的計算方法
X1 = a * X + b * Y + c
Y1 = d * X + e * Y + f
L = g * X + h * Y + i
一般葫笼,g = h = 0 , i = 1
祝闻,這時L = g * X + h * Y + i
恒成立,也就是L = i = 1
Matrix
的初始化矩陣,對角線為1
,其余為0
Matrix
主要可以對圖像做4種基本變換
- Translate 平移變換
- Rotate 旋轉(zhuǎn)變換
- Scale 縮放變換
- Skew 錯切變換
Matrix
類中的方法豆瘫,主要也是和這四個變換相關(guān),只是對計算過程做了封裝
作用對象是Bitmap
而不是Canvas
2. Translate 平移變換
紅點p1
平移到白點p
時左痢,坐標值
x = x1 + x0
y = y1 + y0
矩陣的形式:
為了更好直觀表現(xiàn)靡羡,先看原始效果
private void init() {
bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.test);
//畫筆
paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setColor(Color.parseColor("#FF4081"));
//矩陣
matrix = new Matrix();
matrix.setTranslate(100f,100f);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawColor(Color.YELLOW);
canvas.drawBitmap(bitmap,0,0,null);
}
在布局文件中系洛,控件的寬為match_parent
俊性,高為200dp
,onDraw()
方法中描扯,canvas
繪制底色為黃色定页,又繪制了原始了的bitmap
。bitmap
的大小是沒有控件大的绽诚,屏幕右側(cè)留下了一塊黃色的區(qū)域典徊。此時并沒有用到matrix
2.1 setTranslate()方法
Matrix
中提供了一個setTranslate()
方法,很容易就做到平移
簡單修改代碼
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawColor(Color.YELLOW);
canvas.drawBitmap(bitmap,matrix,null);
//在(100恩够,100)處畫一個圓
//用來輔助查看matrix作用后的坐標系
canvas.drawCircle(100,100,30,paint);
}
根據(jù)小圓可以看出卒落,matrix
的平移對canvas
的坐標系不會造成影響,不像canvas.traslate()
方法蜂桶。
matrix.setTranslate(100f,100f)
儡毕,bitmap
在x,y
軸上移動了100
個px
。
代入到平移的公式中:
最終
x = x1 + 100
y = y1 + 100
而超出了canvas
的部分扑媚,則不再顯示
3. Rotate 旋轉(zhuǎn)變換
旋轉(zhuǎn)就是一個點圍繞一個中心點旋轉(zhuǎn)到新的位置
以原點的為旋轉(zhuǎn)中心過程學(xué)習(xí):
白點p(x0,yo)
繞原點旋轉(zhuǎn)β°
后腰湾,得到紅點p(x,y)
斜邊為r
,角度為α
疆股,利用三角函數(shù)费坊,得到
x0 = r * cosα
y0 = r * sinα
同理,可以得出
x = r * cos(α + β) = r * cosα * cosβ - r * sinα * sinβ = x0 * cosβ - y0 * sinβ
y = r * sin(α + β) = r * sinα * cosβ + r * cosα * sinβ = y0 * cosβ + x0 * sinβ
過程其實就是三角函數(shù)展開旬痹,矩陣的形式就是
根據(jù)計算結(jié)果y = y0 * cosβ + x0 * sinβ
附井,需要注意sinβ
和cosβ
在矩陣的位置
上面的情況是以原點為旋轉(zhuǎn)中心,任意點O
為旋轉(zhuǎn)中心進行旋轉(zhuǎn)變換两残,一般有3個步驟:
- 將坐標原點移動到任意點
O
- 使用上面的以坐標系原點為中心的旋轉(zhuǎn)方法進行旋轉(zhuǎn)
- 將坐標原點還原
主要就是考慮任意點與原點的坐標錯羡忘。然而使用setRotate()
方法時,并不用考慮過多磕昼,都進行了封裝
3.1 setRotate()方法
旋轉(zhuǎn)方法有兩個重載方法:
1. setRotate(float degrees)
2. setRotate(float degrees, float px, float py)
第一個方法簡單使用卷雕,簡單修改2.1中的代碼
private void init() {
bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.test);
matrix = new Matrix();
matrix.setRotate(15);
}
默認以左上角為旋轉(zhuǎn)中心,bitmap
的寬為r
進行旋轉(zhuǎn)
第2個方法可以指定旋轉(zhuǎn)中心O
票从,float px
就是O
點的X
軸坐標漫雕,float py
就是O
點的Y
軸坐標
matrix.setRotate(15,bitmap.getWidth()/2,bitmap.getHeight()/2);
以bitmap
中心為旋轉(zhuǎn)中心進行旋轉(zhuǎn)15度
4.Scale 縮放變換
對于一個像素點來說滨嘱,不存在縮放的概念,但一個圖像是由很多個像素點組成浸间,將每個點的坐標進行相同比例的縮放后太雨,整個圖像也就有了縮放的效果。
計算公式:
x = K1 * x0
y = k1 * y0
矩陣形式:
k1 就是要縮放的比例魁蒜,負值無效囊扳,bitmap
會不顯示,0~1f
縮小兜看,k1 > 1
為放大
4.1 setScale() 縮放方法
這個方法也有兩個重載方法
1. setScale(float sx, float sy)
2. setScale(float sx, float sy, float px, float py)
根據(jù)學(xué)習(xí)setRotate()
方法锥咸,這個方法的兩個重載方法比較好理解
第1個方法,簡單使用
private void init() {
bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.test);
matrix = new Matrix();
matrix.setScale(0.5f,0.5f);
}
此時的縮放中心為bitmap
的坐上角
第2個方法细移,簡單使用
matrix.setScale(0.5f,0.5f,bitmap.getWidth()/2,bitmap.getHeight()/2);
此時就是以Bitmap
的中心進行縮放搏予,整個Bitmap
的邊緣向中間靠攏
5. Skew 錯切變換
錯切變換skew
是一種比較特殊的線性變換,分為水平錯切和垂直錯切
5.1 水平錯切
水平錯切效果就是讓所有像素點的Y
軸坐標不變弧轧,X
軸坐標按照比例進行平移雪侥,且平移的大小與該點到Y
軸的距離成成正比
在坐標系中的效果:
計算公式:
x = x0 + k1 * y0
y = y0
矩陣形式:
X
軸平移的值,是k1 * y0
5.2 垂直錯切
垂直錯切讓所有像素點的X
軸坐標不變精绎,Y
軸坐標按照比例進行平移速缨,且平移的大小與該點到X
軸的距離成成正比
在坐標系中的效果:
計算公式:
x = x0
y = y0+ k2 * x0
矩陣形式:
5.3 兩個方向都進行錯切
當(dāng)水平和垂直方向上都做錯切變換時
計算公式:
x = x0 + k1 * y0
y = k2 * x0 * y0
矩陣形式:
無論水平還垂直錯切,最終的效果其實就是由矩形變作平行四邊形
5.3 setSkew() 錯切方法
這個方法也有兩個重載
1. setSkew(float kx, float ky)
2. setSkew(float kx, float ky, float px, float py)
第2個方法后面兩個參數(shù)也是為了指定錯切的中心
5.3.1 setSkew(float kx, float ky)第一個方法
水平錯切:
private void init() {
bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.test);
matrix = new Matrix();
matrix.setSkew(0.25f,0f);
}
kx
就是k1
代乃,負值旬牲,向左切;正值向右切
垂直錯切:
private void init() {
bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.test);
matrix = new Matrix();
matrix.setSkew(0f,0.25f);
}
ky
就是k2
襟己,負值引谜,向上切;正值擎浴,向下切
bitmap
最右邊的區(qū)域不是錯切的效果员咽,是因為bitmap
的寬沒有canvas
的寬大,留下的空白區(qū)域
兩個方向都錯切:
matrix.setSkew(0.25f,0.25f);
此時可以明顯看出贮预,錯切的中心點為bitmap
的左上角
5.3.2 setSkew(float kx, float ky, float px, float py) 指定錯切中心
簡單使用:
matrix.setSkew(0.1f,0.1f,bitmap.getWidth()/2,bitmap.getHeight()/2);
以bitmap
的中心為錯切中心點
這里目前并不是很理解指定中心點后錯切對坐標系的影響
6.矩陣中的元素與四種變換效果的對應(yīng)關(guān)系
- a和e 控制縮放變換
- b和d 控制錯切變換
- c和f 控制平移變換
- a贝室,b,d仿吞,e 共同控制旋轉(zhuǎn)變換
第1行都是影響的X
軸滑频,第2行影響的Y
軸
7.關(guān)于前乘和后乘
首先,矩陣的乘法不滿足乘法的交換規(guī)律
在Matrix
類中唤冈,set
方法會重置矩陣中的所有值峡迷,而pre
和post
不會
7.1 簡單對比
前乘的代碼:
private void init() {
bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.test);
matrix = new Matrix();
matrix.setTranslate(100,100);
matrix.preRotate(15);
}
先進行平移,前乘旋轉(zhuǎn)15°
后乘,簡單修改代碼:
matrix.postRotate(15);
兩者差別绘搞,看右下角的區(qū)域比較明顯
7.2 嘗試分析
前乘
就是M * R(degrees)
彤避,后乘
就是R(degrees) * M
前乘
就對應(yīng)線性代數(shù)矩陣運算的右乘
后乘
就對應(yīng)線性代數(shù)矩陣運算的左乘
在矩陣運算中:
M
右乘A
,就是A * M
M
左乘A
夯辖,就是M * A
簡單記法:右乘從右邊乘進來琉预,左乘從左邊乘進來
7.1
中的矩陣:
在7.1
中共有3個矩陣,首先平移b
蒿褂,旋轉(zhuǎn)a
圆米,像素c
在前乘或者后乘之前有一個setTranslate(100,100)
設(shè)置的矩陣b
,前乘后后乘也就是相對于b
來說
在7.1
前乘啄栓,計算過程就是:
a
右乘b
娄帖,計算就是b * a
,得到一個新的矩陣N
谴供,N * c
后乘的過程:
a
左乘b
块茁,計算就是a * b
齿坷,得到一個新的矩陣N
桂肌,N * c
總結(jié):
在pre
或者post
方法前進行設(shè)置了哪個矩陣M
,矩陣M
與M
之前所有的矩陣的運算得到的新矩陣N
永淌,N
就看做當(dāng)前矩陣崎场,前乘或者后乘就是相對于這個當(dāng)前矩陣N
而言
7.3 補充 2016.09.30 09:09 <p>
根據(jù)總結(jié),看下下面的兩個小練習(xí):
//方式1
matrix = new Matrix();
matrix.preRotate(30);
matrix.postTranslate(100f, 100f);
//方式2
matrix = new Matrix();
matrix.postTranslate(100f, 100f);
matrix.preRotate(30);
//方式3
matrix = new Matrix();
matrix.postRotate(30);
matrix.preTranslate(100f, 100f);
- 方式
1
和方式2
結(jié)果是否相同遂蛀? 相同 - 方式
1
和方式3
結(jié)果是否相同谭跨? 不同
有圖,有真相
在看問題前李滴,先了解這樣一個矩陣的知識點螃宙,有助于理解問題:
有3個矩陣A
,B
所坯,C
谆扎,相乘,N
= A * B * C
從左向右順序計算芹助,第1步堂湖,X
= A * B
,然后N
= X * C
從右向左倒序計算状土,第1步无蜂,X
= B * C
,然后N
= A * X
這兩種計算方式是一樣的蒙谓。
網(wǎng)上有人說斥季,圖形處理時,矩陣的運算是從右向左計算的累驮,這也就是為啥有pre
可以理解為先進行計算的一說酣倾,但個人感覺渊迁,從左開始和從右開始計算是一樣的。但從右開始計算更容易理解吧
之所以說矩陣不滿足乘法的交換規(guī)律灶挟,是說A * B
≠ B * A
N
= A * B * C
琉朽,從左開始計算和從右開始計算結(jié)果一樣的前提就是,要按照矩陣排列時的順序來進行計算
N
= A * B
稚铣,但N * C
≠ C * N
下面看問題
問題1:
- 方式
1
的矩陣的形式:
- 方式
2
的矩陣的形式:
new Matrix()
或者mMatrix.reset()
得到的就是一個原始矩陣
兩個矩陣最終形式其實就是一個矩陣箱叁,差別可以通過括號的位置理解,括號內(nèi)的就是先計算的惕医。方式1
是可以看作從右面開始計算耕漱,方式2
可以看做從左面開始計算。但前面說了抬伺,計算的順序并會不影響最終的結(jié)果
問題2:
方式3
的矩陣形式與方式1螟够,2
不一樣:
方式3
中與方式1,2
的差別就是旋轉(zhuǎn)和平移兩個矩陣交換了位置峡钓,而矩陣不滿足乘法的交換律妓笙,所以方式1
和方式3
,最終結(jié)果就不同
8.其他的方法
Matrix
中能岩,方法主要4大類寞宫,占據(jù)了絕大部分,set
拉鹃,pre
辈赋,post
,map
開頭的方法
8.1 setPolyToPoly()
這個方法非常強大膏燕,通過改變參數(shù)钥屈,除了可以實現(xiàn)平移,旋轉(zhuǎn)坝辫,縮放篷就,錯切
,還可以實現(xiàn)透視
這個方法主要是利用確定矩形4個頂點阀溶,根據(jù)4個頂點坐標的變化來對bitmap
進行變換
最終的效果主要由src
和dst
兩個數(shù)組進行控制腻脏,兩個數(shù)組控制4個頂點的坐標,srcIndex,dstIndex
分別是src
和dst
的第一個值的角標银锻,pointCount
是4個頂點中要使用的個數(shù)永品,最大為4,0表示不進行操作變換
透視效果,簡單使用:
private void init() {
bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.test);
matrix = new Matrix();
float bWidth = bitmap.getWidth();
float bHeight = bitmap.getHeight();
float[] src = {0, 0, 0, bHeight, bWidth, bHeight,bWidth, 0};
float[] dst = {0 + 150,0, 0, bHeight, bWidth, bHeight, bWidth - 150, 0};
matrix.setPolyToPoly(src, 0, dst, 0, 4);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawColor(Color.YELLOW);
canvas.drawBitmap(bitmap,matrix,null);
}
float[] src
和float[] dst
中的值一定是成對的出現(xiàn)击纬,因為一個點的坐標由(x,y)
來確定鼎姐,兩兩一對控制對應(yīng)的一個頂點的坐標,最多有4對有效,超過的無效炕桨,因為再方法setPolyToPoly()
中饭尝,最后一個參數(shù)不能超過4
數(shù)組值和頂點坐標點的對應(yīng)關(guān)系:
float[] dst = {f0, f1, f3, f3, f4, f5,f6, f7}
為了方便看,將bitmap
放在了畫布比較靠中心的位置
dst
可以看做是底板献宫,最終要顯示的效果钥平;
src
可以看做是要截取的bitmap
的要顯示的有效區(qū)域
控制不同的點的效果:
- 1個點,平移
- 2個點姊途,縮放或者旋轉(zhuǎn)
- 3個點涉瘾,錯切
- 4個點,透視
8.2 setRectToRect()
setRectToRect(RectF src, RectF dst, ScaleToFit stf)
第一個參數(shù)src
捷兰,截取資源Bitmap
的顯示區(qū)域
第二個參數(shù)dst
立叛,底板,顯示的區(qū)域
第三個參數(shù)stf
贡茅,模式
谷歌api 給的Demo:
FILL: 可能會變換矩形的長寬比秘蛇,保證變換和目標矩陣長寬一致。
START:保持坐標變換前矩形的長寬比顶考,并最大限度的填充變換后的矩形赁还。至少有一邊和目標矩形重疊。左上對齊村怪。
CENTER: 保持坐標變換前矩形的長寬比秽浇,并最大限度的填充變換后的矩形浮庐。至少有一邊和目標矩形重疊甚负。
END:保持坐標變換前矩形的長寬比,并最大限度的填充變換后的矩形审残。至少有一邊和目標矩形重疊梭域。右下對齊。
圖和文字說明從androidmatrix最全方法詳解與進階(完整篇)摘抄
簡單使用:
private void init() {
bitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher);
matrix = new Matrix();
int screenWidth = getResources().getDisplayMetrics().widthPixels;
int screenHeight = getResources().getDisplayMetrics().heightPixels;
float bWidth = bitmap.getWidth();
float bHeight = bitmap.getHeight();
RectF src = new RectF(0,0,bWidth/2,bHeight/2 );
RectF dst = new RectF(0,0,screenWidth,screenHeight);
matrix.setRectToRect(src,dst, Matrix.ScaleToFit.END);
}
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawColor(Color.YELLOW);
canvas.drawBitmap(bitmap,matrix,null);
}
結(jié)合上面的圖搅轿,也比較容易理解
8.3 其他的一些方法
方法名 | 作用 |
---|---|
reset() |
將矩陣恢復(fù)為初始化矩陣 |
boolean invert(Matrix inverse) |
反轉(zhuǎn)當(dāng)前矩陣 |
boolean isIdentity() |
是否為初始化矩陣 |
boolean isAffine() |
是否為仿射矩陣 |
boolean rectStaysRect() |
判斷該矩陣是否可以將一個矩形依然變換為一個矩形病涨。當(dāng)矩陣是單位矩陣,或者只進行平移璧坟,縮放既穆,以及旋轉(zhuǎn)90度的倍數(shù)的時候,返回true |
仿射變換其實就是二維坐標到二維坐標的線性變換雀鹃,保持二維圖形的“平直性”(即變換后直線還是直線不會打彎幻工,圓弧還是圓弧)和“平行性”(指保持二維圖形間的相對位置關(guān)系不變黎茎,平行線還是平行線囊颅,而直線上點的位置順序不變),可以通過一系列的原子變換的復(fù)合來實現(xiàn),原子變換就包括:平移踢代、縮放盲憎、翻轉(zhuǎn)、旋轉(zhuǎn)和錯切胳挎。這里除了透視可以改變z軸以外饼疙,其他的變換基本都是上述的原子變換,所以慕爬,只要最后一行是0,0,1則是仿射矩陣宏多。
其他的方法以后用到了再學(xué)習(xí)補充
9. 最后
本篇主要就學(xué)習(xí)4種基本變換操作的方法
本人很菜,有錯誤請指出
感謝學(xué)習(xí)資料中的大神前輩們
共勉 : )