連接:https://www.jianshu.com/p/11e062284491html
Matrix
主要用於對圖像的圖形處理。前面學習的ColorMatirx主要是圖像色彩的處理android
學習資料canvas
十分感謝 : )api
Matrix
是一個3 * 3
的矩陣,每一個像素點表達了其座標的X,Y
信息數組
圖形矩陣變換ide
處理每一個像素點的計算方法函數
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
post
Matrix初始化矩陣學習
Matrix
主要能夠對圖像作4種基本變換
Matrix
類中的方法,主要也是和這四個變換相關,只是對計算過程作了封裝
做用對象是Bitmap
而不是Canvas
平移變換
紅點p1
平移到白點p
時,座標值
x = x1 + x0 y = y1 + y0
矩陣的形式:
平移變換矩陣
爲了更好直觀表現,先看原始效果
原始效果
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
的大小是沒有控件大的,屏幕右側留下了一塊黃色的區域。此時並無用到matrix
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); }
平移後
根據小圓能夠看出,matrix
的平移對canvas
的座標系不會形成影響,不像canvas.traslate()
方法。
matrix.setTranslate(100f,100f)
,bitmap
在x,y
軸上移動了100
個px
。
代入到平移的公式中:
平移100個像素
最終
x = x1 + 100 y = y1 + 100
而超出了canvas
的部分,則再也不顯示
旋轉就是一個點圍繞一箇中心點旋轉到新的位置
以原點的爲旋轉中心過程學習:
Rotate旋轉圖示
白點p(x0,yo)
繞原點旋轉β°
後,獲得紅點p(x,y)
斜邊爲r
,角度爲α
,利用三角函數,獲得
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β
過程其實就是三角函數展開,矩陣的形式就是
旋轉矩陣變換
根據計算結果y = y0 * cosβ + x0 * sinβ
,須要注意sinβ
和cosβ
在矩陣的位置
上面的狀況是以原點爲旋轉中心,任意點O
爲旋轉中心進行旋轉變換,通常有3個步驟:
O
主要就是考慮任意點與原點的座標錯。然而使用setRotate()
方法時,並不用考慮過多,都進行了封裝
旋轉方法有兩個重載方法:
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); }
圍繞左上角旋轉15度
默認以左上角爲旋轉中心,bitmap
的寬爲r
進行旋轉
第2個方法能夠指定旋轉中心O
,float px
就是O
點的X
軸座標,float py
就是O
點的Y
軸座標
matrix.setRotate(15,bitmap.getWidth()/2,bitmap.getHeight()/2);
以Bitmap中心爲旋轉中心
以bitmap
中心爲旋轉中心進行旋轉15度
對於一個像素點來講,不存在縮放的概念,但一個圖像是由不少個像素點組成,將每一個點的座標進行相同比例的縮放後,整個圖像也就有了縮放的效果。
計算公式:
x = K1 * x0 y = k1 * y0
矩陣形式:
矩陣縮放變換
k1 就是要縮放的比例,負值無效,bitmap
會不顯示,0~1f
縮小,k1 > 1
爲放大
這個方法也有兩個重載方法
1. setScale(float sx, float sy) 2. setScale(float sx, float sy, float px, float py)
根據學習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
的中心進行縮放,整個Bitmap
的邊緣向中間靠攏
錯切變換skew
是一種比較特殊的線性變換,分爲水平錯切和垂直錯切
水平錯切效果就是讓全部像素點的Y
軸座標不變,X
軸座標按照比例進行平移,且平移的大小與該點到Y
軸的距離成成正比
在座標系中的效果:
水平錯切
計算公式:
x = x0 + k1 * y0 y = y0
矩陣形式:
矩陣水平錯切變換
X
軸平移的值,是k1 * y0
垂直錯切讓全部像素點的X
軸座標不變,Y
軸座標按照比例進行平移,且平移的大小與該點到X
軸的距離成成正比
在座標系中的效果:
垂直錯切
計算公式:
x = x0 y = y0+ k2 * x0
矩陣形式:
矩陣垂直錯切變換
當水平和垂直方向上都作錯切變換時
計算公式:
x = x0 + k1 * y0 y = k2 * x0 * y0
矩陣形式:
水平和垂直都錯切變換
不管水平還垂直錯切,最終的效果其實就是由矩形變做平行四邊形
這個方法也有兩個重載
1. setSkew(float kx, float ky) 2. setSkew(float kx, float ky, float px, float py)
第2個方法後面兩個參數也是爲了指定錯切的中心
水平錯切:
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
最右邊的區域不是錯切的效果,是由於bitmap
的寬沒有canvas
的寬大,留下的空白區域
兩個方向都錯切:
matrix.setSkew(0.25f,0.25f);
兩個方向錯切
此時能夠明顯看出,錯切的中心點爲bitmap
的左上角
簡單使用:
matrix.setSkew(0.1f,0.1f,bitmap.getWidth()/2,bitmap.getHeight()/2);
指定錯切中心點
以bitmap
的中心爲錯切中心點
這裏目前並非很理解指定中心點後錯切對座標系的影響
矩陣
變化過濾
第1行都是影響的X
軸,第2行影響的Y
軸
首先,矩陣的乘法不知足乘法的交換規律
在Matrix
類中,set
方法會重置矩陣中的全部值,而pre
和post
不會
前乘的代碼:
private void init() { bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.test); matrix = new Matrix(); matrix.setTranslate(100,100); matrix.preRotate(15); }
前乘效果
先進行平移,前乘旋轉15°
後乘,簡單修改代碼:
matrix.postRotate(15);
後乘效果
二者差異,看右下角的區域比較明顯
前乘旋轉源碼
旋轉後乘源碼
前乘
就是M * R(degrees)
,後乘
就是R(degrees) * M
前乘
就對應線性代數矩陣運算的右乘
後乘
就對應線性代數矩陣運算的左乘
在矩陣運算中:
M
右乘A
,就是A * M
M
左乘A
,就是M * A
簡單記法:右乘從右邊乘進來,左乘從左邊乘進來
7.1
中的矩陣:
矩陣分析
在7.1
中共有3個矩陣,首先平移b
,旋轉a
,像素c
在前乘或者後乘以前有一個setTranslate(100,100)
設置的矩陣b
,前乘後後乘也就是相對於b
來講
在7.1
前乘,計算過程就是:
a
右乘b
,計算就是b * a
,獲得一個新的矩陣N
,N * c
後乘的過程:
a
左乘b
,計算就是a * b
,獲得一個新的矩陣N
,N * c
總結:
在pre
或者post
方法前進行設置了哪一個矩陣M
,矩陣M
與M
以前全部的矩陣的運算獲得的新矩陣N
,N
就看作當前矩陣,前乘或者後乘就是相對於這個當前矩陣N
而言
根據總結,看下下面的兩個小練習:
//方式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
結果是否相同? 相同1
和方式3
結果是否相同? 不一樣有圖,有真相
3種方式的差異
在看問題前,先了解這樣一個矩陣的知識點,有助於理解問題:
有3個矩陣A
,B
,C
,相乘,N
= A * B * C
從左向右順序計算,第1步,X
= A * B
,而後N
= X * C
從右向左倒序計算,第1步,X
= B * C
,而後N
= A * X
這兩種計算方式是同樣的。
網上有人說,圖形處理時,矩陣的運算是從右向左計算的,這也就是爲啥有pre
能夠理解爲先進行計算的一說,但我的感受,從左開始和從右開始計算是同樣的。但從右開始計算更容易理解吧
之因此說矩陣不知足乘法的交換規律,是說A * B
≠ B * A
N
= A * B * C
,從左開始計算和從右開始計算結果同樣的前提就是,要按照矩陣排列時的順序來進行計算
N
= A * B
,但N * C
≠ C * N
下面看問題
問題1:
1
的矩陣的形式:方式1矩陣
2
的矩陣的形式:方式2
new Matrix()
或者mMatrix.reset()
獲得的就是一個原始矩陣
兩個矩陣最終形式其實就是一個矩陣,差異能夠經過括號的位置理解,括號內的就是先計算的。方式1
是能夠看做從右面開始計算,方式2
能夠看作從左面開始計算。但前面說了,計算的順序並會不影響最終的結果
問題2:
方式3
的矩陣形式與方式1,2
不同:
方式3矩陣
方式3
中與方式1,2
的差異就是旋轉和平移兩個矩陣交換了位置,而矩陣不知足乘法的交換律,因此方式1
和方式3
,最終結果就不一樣
Matrix
中,方法主要4大類,佔據了絕大部分,set
,pre
,post
,map
開頭的方法
這個方法很是強大,經過改變參數,除了能夠實現平移,旋轉,縮放,錯切
,還能夠實現透視
這個方法主要是利用肯定矩形4個頂點,根據4個頂點座標的變化來對bitmap
進行變換
setPolyToPoly方法
最終的效果主要由src
和dst
兩個數組進行控制,兩個數組控制4個頂點的座標,srcIndex,dstIndex
分別是src
和dst
的第一個值的角標,pointCount
是4個頂點中要使用的個數,最大爲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
中的值必定是成對的出現,由於一個點的座標由(x,y)
來肯定,兩兩一對控制對應的一個頂點的座標,最多有4對有效,超過的無效,由於再方法setPolyToPoly()
中,最後一個參數不能超過4
數組值和頂點座標點的對應關係:
float[] dst = {f0, f1, f3, f3, f4, f5,f6, f7}
座標和頂點的對應關係
爲了方便看,將bitmap
放在了畫布比較靠中心的位置
dst
能夠看作是底板,最終要顯示的效果;
src
能夠看作是要截取的bitmap
的要顯示的有效區域
控制不一樣的點的效果:
setRectToRect(RectF src, RectF dst, ScaleToFit stf)
第一個參數src
,截取資源Bitmap
的顯示區域
第二個參數dst
,底板,顯示的區域
第三個參數stf
,模式
谷歌api 給的Demo:
ScaleToFit
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); }
Matrix.ScaleToFit.END
結合上面的圖,也比較容易理解
方法名 | 做用 |
---|---|
reset() |
將矩陣恢復爲初始化矩陣 |
boolean invert(Matrix inverse) |
反轉當前矩陣 |
boolean isIdentity() |
是否爲初始化矩陣 |
boolean isAffine() |
是否爲仿射矩陣 |
boolean rectStaysRect() |
判斷該矩陣是否能夠將一個矩形依然變換爲一個矩形。當矩陣是單位矩陣,或者只進行平移,縮放,以及旋轉90度的倍數的時候,返回true |
仿射變換其實就是二維座標到二維座標的線性變換,保持二維圖形的「平直性」(即變換後直線仍是直線不會打彎,圓弧仍是圓弧)和「平行性」(指保持二維圖形間的相對位置關係不變,平行線仍是平行線,而直線上點的位置順序不變),能夠經過一系列的原子變換的複合來實現,原子變換就包括:平移、縮放、翻轉、旋轉和錯切。這裏除了透視能夠改變z軸之外,其餘的變換基本都是上述的原子變換,因此,只要最後一行是0,0,1則是仿射矩陣。
其餘的方法之後用到了再學習補充
本篇主要就學習4種基本變換操做的方法
本人很菜,有錯誤請指出
感謝學習資料中的大神前輩們
共勉 : )
做者:英勇青銅5 連接:https://www.jianshu.com/p/11e062284491 來源:簡書 簡書著做權歸做者全部,任何形式的轉載都請聯繫做者得到受權並註明出處。