Android Matrix源碼詳解
Matrix的數(shù)學(xué)原理
在Android中,如果你用Matrix進(jìn)行過(guò)圖像處理,那么一定知道Matrix這個(gè)類。Android中的Matrix是一個(gè)3 x 3的矩陣,其內(nèi)容如下:

Matrix的對(duì)圖像的處理可分為四類基本變換:
- Translate 平移變換
- Rotate 旋轉(zhuǎn)變換
- Scale 縮放變換
- Skew 錯(cuò)切變換
從字面上理解,矩陣中的MSCALE用于處理縮放變換,MSKEW用于處理錯(cuò)切變換,MTRANS用于處理平移變換,MPERSP用于處理透視變換。實(shí)際中當(dāng)然不能完全按照字面上的說(shuō)法去理解Matrix。同時(shí),在Android的文檔中,未見(jiàn)到用Matrix進(jìn)行透視變換的相關(guān)說(shuō)明,所以本文也不討論這方面的問(wèn)題。
針對(duì)每種變換,Android提供了pre、set和post三種操作方式。其中
set用于設(shè)置Matrix中的值。
pre是先乘,因?yàn)榫仃嚨某朔ú粷M足交換律,因此先乘、后乘必須要嚴(yán)格區(qū)分。先乘相當(dāng)于矩陣運(yùn)算中的右乘。
post是后乘,因?yàn)榫仃嚨某朔ú粷M足交換律,因此先乘、后乘必須要嚴(yán)格區(qū)分。后乘相當(dāng)于矩陣運(yùn)算中的左乘。
除平移變換(Translate)外,旋轉(zhuǎn)變換(Rotate)、縮放變換(Scale)和錯(cuò)切變換(Skew)都可以圍繞一個(gè)中心點(diǎn)來(lái)進(jìn)行,如果不指定,在默認(rèn)情況下是圍繞(0, 0)來(lái)進(jìn)行相應(yīng)的變換的。
下面我們來(lái)看看四種變換的具體情形。由于所有的圖形都是有點(diǎn)組成,因此我們只需要考察一個(gè)點(diǎn)相關(guān)變換即可。
一、 平移變換
假定有一個(gè)點(diǎn)的坐標(biāo)是
,將其移動(dòng)到
,再假定在x軸和y軸方向移動(dòng)的大小分別為:

如下圖所示:

不難知道:

如果用矩陣來(lái)表示的話,就可以寫(xiě)成:

二、 旋轉(zhuǎn)變換
2.1 圍繞坐標(biāo)原點(diǎn)旋轉(zhuǎn):
假定有一個(gè)點(diǎn)
,相對(duì)坐標(biāo)原點(diǎn)順時(shí)針旋轉(zhuǎn)
后的情形,同時(shí)假定P點(diǎn)離坐標(biāo)原點(diǎn)的距離為r,如下圖:

那么,

如果用矩陣,就可以表示為:

2.2 圍繞某個(gè)點(diǎn)旋轉(zhuǎn)
如果是圍繞某個(gè)點(diǎn)
順時(shí)針旋轉(zhuǎn)
,那么可以用矩陣表示為:

可以化為:

很顯然,
1.
是將坐標(biāo)原點(diǎn)移動(dòng)到點(diǎn)
后,
的新坐標(biāo)。
2.
是將上一步變換后的
,圍繞新的坐標(biāo)原點(diǎn)順時(shí)針旋轉(zhuǎn)
。
3. 
經(jīng)過(guò)上一步旋轉(zhuǎn)變換后,再將坐標(biāo)原點(diǎn)移回到原來(lái)的坐標(biāo)原點(diǎn)。
所以,圍繞某一點(diǎn)進(jìn)行旋轉(zhuǎn)變換,可以分成3個(gè)步驟,即首先將坐標(biāo)原點(diǎn)移至該點(diǎn),然后圍繞新的坐標(biāo)原點(diǎn)進(jìn)行旋轉(zhuǎn)變換,再然后將坐標(biāo)原點(diǎn)移回到原先的坐標(biāo)原點(diǎn)。
三、 縮放變換
理論上而言,一個(gè)點(diǎn)是不存在什么縮放變換的,但考慮到所有圖像都是由點(diǎn)組成,因此,如果圖像在x軸和y軸方向分別放大k1和k2倍的話,那么圖像中的所有點(diǎn)的x坐標(biāo)和y坐標(biāo)均會(huì)分別放大k1和k2倍,即

用矩陣表示就是:

縮放變換比較好理解,就不多說(shuō)了。
四、 錯(cuò)切變換
錯(cuò)切變換(skew)在數(shù)學(xué)上又稱為Shear mapping(可譯為“剪切變換”)或者Transvection(縮并),它是一種比較特殊的線性變換。錯(cuò)切變換的效果就是讓所有點(diǎn)的x坐標(biāo)(或者y坐標(biāo))保持不變,而對(duì)應(yīng)的y坐標(biāo)(或者x坐標(biāo))則按比例發(fā)生平移,且平移的大小和該點(diǎn)到x軸(或y軸)的垂直距離成正比。錯(cuò)切變換,屬于等面積變換,即一個(gè)形狀在錯(cuò)切變換的前后,其面積是相等的。
比如下圖,各點(diǎn)的y坐標(biāo)保持不變,但其x坐標(biāo)則按比例發(fā)生了平移。這種情況將水平錯(cuò)切。

下圖各點(diǎn)的x坐標(biāo)保持不變,但其y坐標(biāo)則按比例發(fā)生了平移。這種情況叫垂直錯(cuò)切。

假定一個(gè)點(diǎn)
經(jīng)過(guò)錯(cuò)切變換后得到
,對(duì)于水平錯(cuò)切而言,應(yīng)該有如下關(guān)系:

用矩陣表示就是:

擴(kuò)展到3 x 3的矩陣就是下面這樣的形式:

同理,對(duì)于垂直錯(cuò)切,可以有:

在數(shù)學(xué)上嚴(yán)格的錯(cuò)切變換就是上面這樣的。在Android中除了有上面說(shuō)到的情況外,還可以同時(shí)進(jìn)行水平、垂直錯(cuò)切,那么形式上就是:
五、 對(duì)稱變換
除了上面講到的4中基本變換外,事實(shí)上,我們還可以利用Matrix,進(jìn)行對(duì)稱變換。所謂對(duì)稱變換,就是經(jīng)過(guò)變化后的圖像和原圖像是關(guān)于某個(gè)對(duì)稱軸是對(duì)稱的。比如,
某點(diǎn) 經(jīng)過(guò)對(duì)稱變換后得到
,
如果對(duì)稱軸是x軸,難么,

用矩陣表示就是:

如果對(duì)稱軸是y軸,那么,

用矩陣表示就是:

如果對(duì)稱軸是y = x,如圖:

那么,

很容易可以解得:

用矩陣表示就是:

同樣的道理,如果對(duì)稱軸是y = -x,那么用矩陣表示就是:

特殊地,如果對(duì)稱軸是y = kx,如下圖:

那么,

很容易可解得:

用矩陣表示就是:

當(dāng)k = 0時(shí),即y = 0,也就是對(duì)稱軸為x軸的情況;當(dāng)k趨于無(wú)窮大時(shí),即x = 0,也就是對(duì)稱軸為y軸的情況;當(dāng)k =1時(shí),即y = x,也就是對(duì)稱軸為y = x的情況;當(dāng)k = -1時(shí),即y = -x,也就是對(duì)稱軸為y = -x的情況。不難驗(yàn)證,這和我們前面說(shuō)到的4中具體情況是相吻合的。
如果對(duì)稱軸是y = kx + b這樣的情況,只需要在上面的基礎(chǔ)上增加兩次平移變換即可,即先將坐標(biāo)原點(diǎn)移動(dòng)到(0, b),然后做上面的關(guān)于y = kx的對(duì)稱變換,再然后將坐標(biāo)原點(diǎn)移回到原來(lái)的坐標(biāo)原點(diǎn)即可。用矩陣表示大致是這樣的:

需要特別注意:在實(shí)際編程中,我們知道屏幕的y坐標(biāo)的正向和數(shù)學(xué)中y坐標(biāo)的正向剛好是相反的,所以在數(shù)學(xué)上y = x和屏幕上的y = -x才是真正的同一個(gè)東西,反之亦然。也就是說(shuō),如果要使圖片在屏幕上看起來(lái)像按照數(shù)學(xué)意義上y = x對(duì)稱,那么需使用這種轉(zhuǎn)換:

要使圖片在屏幕上看起來(lái)像按照數(shù)學(xué)意義上y = -x對(duì)稱,那么需使用這種轉(zhuǎn)換:
關(guān)于對(duì)稱軸為y = kx 或y = kx + b的情況,同樣需要考慮這方面的問(wèn)題。
第二部分 代碼驗(yàn)證
在第一部分中講到的各種圖像變換的驗(yàn)證代碼如下,一共列出了10種情況。如果要驗(yàn)證其中的某一種情況,只需將相應(yīng)的代碼反注釋即可。試驗(yàn)中用到的圖片:

其尺寸為162 x 251。
每種變換的結(jié)果,請(qǐng)見(jiàn)代碼之后的說(shuō)明。
package compattesttransformmatrix;
import androidappActivity;
import androidcontentContext;
import androidgraphicsBitmap;
import androidgraphicsBitmapFactory;
import androidgraphicsCanvas;
import androidgraphicsMatrix;
import androidosBundle;
import androidutilLog;
import androidviewMotionEvent;
import androidviewView;
import androidviewWindow;
import androidviewWindowManager;
import androidviewViewOnTouchListener;
import androidwidgetImageView;
public class TestTransformMatrixActivity extends Activity
implements
OnTouchListener
{
private TransformMatrixView view;
@Override
public void onCreate(Bundle savedInstanceState)
{
superonCreate(savedInstanceState);
requestWindowFeature(WindowFEATURE_NO_TITLE);
thisgetWindow()setFlags(WindowManagerLayoutParamsFLAG_FULLSCREEN, WindowManagerLayoutParamsFLAG_FULLSCREEN);
view = new TransformMatrixView(this);
viewsetScaleType(ImageViewScaleTypeMATRIX);
viewsetOnTouchListener(this);
setContentView(view);
}
class TransformMatrixView extends ImageView
{
private Bitmap bitmap;
private Matrix matrix;
public TransformMatrixView(Context context)
{
super(context);
bitmap = BitmapFactorydecodeResource(getResources(), Rdrawablesophie);
matrix = new Matrix();
}
@Override
protected void onDraw(Canvas canvas)
{
// 畫(huà)出原圖像
canvasdrawBitmap(bitmap, 0, 0, null);
// 畫(huà)出變換后的圖像
canvasdrawBitmap(bitmap, matrix, null);
superonDraw(canvas);
}
@Override
public void setImageMatrix(Matrix matrix)
{
thismatrixset(matrix);
supersetImageMatrix(matrix);
}
public Bitmap getImageBitmap()
{
return bitmap;
}
}
public boolean onTouch(View v, MotionEvent e)
{
if(egetAction() == MotionEventACTION_UP)
{
Matrix matrix = new Matrix();
// 輸出圖像的寬度和高度(162 x 251)
Loge("TestTransformMatrixActivity", "image size: width x height = " + viewgetImageBitmap()getWidth() + " x " + viewgetImageBitmap()getHeight());
// 平移
matrixpostTranslate(viewgetImageBitmap()getWidth(), viewgetImageBitmap()getHeight());
// 在x方向平移viewgetImageBitmap()getWidth(),在y軸方向viewgetImageBitmap()getHeight()
viewsetImageMatrix(matrix);
// 下面的代碼是為了查看matrix中的元素
float[] matrixValues = new float[9];
matrixgetValues(matrixValues);
for(int i = 0; i < 3; ++i)
{
String temp = new String();
for(int j = 0; j < 3; ++j)
{
temp += matrixValues[3 * i + j ] + "\t";
}
Loge("TestTransformMatrixActivity", temp);
}
// // 旋轉(zhuǎn)(圍繞圖像的中心點(diǎn))
// matrixsetRotate(45f, viewgetImageBitmap()getWidth() / 2f, viewgetImageBitmap()getHeight() / 2f);
//
// // 做下面的平移變換,純粹是為了讓變換后的圖像和原圖像不重疊
// matrixpostTranslate(viewgetImageBitmap()getWidth() * 5f, 0f);
// viewsetImageMatrix(matrix);
//
// // 下面的代碼是為了查看matrix中的元素
// float[] matrixValues = new float[9];
// matrixgetValues(matrixValues);
// for(int i = 0; i < 3; ++i)
// {
// String temp = new String();
// for(int j = 0; j < 3; ++j)
// {
// temp += matrixValues[3 * i + j ] + "\t";
// }
// Loge("TestTransformMatrixActivity", temp);
// }
// // 旋轉(zhuǎn)(圍繞坐標(biāo)原點(diǎn)) + 平移(效果同2)
// matrixsetRotate(45f);
// matrixpreTranslate(-1f * viewgetImageBitmap()getWidth() / 2f, -1f * viewgetImageBitmap()getHeight() / 2f);
// matrixpostTranslate((float)viewgetImageBitmap()getWidth() / 2f, (float)viewgetImageBitmap()getHeight() / 2f);
//
// // 做下面的平移變換,純粹是為了讓變換后的圖像和原圖像不重疊
// matrixpostTranslate((float)viewgetImageBitmap()getWidth() * 5f, 0f);
// viewsetImageMatrix(matrix);
//
// // 下面的代碼是為了查看matrix中的元素
// float[] matrixValues = new float[9];
// matrixgetValues(matrixValues);
// for(int i = 0; i < 3; ++i)
// {
// String temp = new String();
// for(int j = 0; j < 3; ++j)
// {
// temp += matrixValues[3 * i + j ] + "\t";
// }
// Loge("TestTransformMatrixActivity", temp);
// }
// // 縮放
// matrixsetScale(2f, 2f);
// // 下面的代碼是為了查看matrix中的元素
// float[] matrixValues = new float[9];
// matrixgetValues(matrixValues);
// for(int i = 0; i < 3; ++i)
// {
// String temp = new String();
// for(int j = 0; j < 3; ++j)
// {
// temp += matrixValues[3 * i + j ] + "\t";
// }
// Loge("TestTransformMatrixActivity", temp);
// }
//
// // 做下面的平移變換,純粹是為了讓變換后的圖像和原圖像不重疊
// matrixpostTranslate(viewgetImageBitmap()getWidth(), viewgetImageBitmap()getHeight());
// viewsetImageMatrix(matrix);
//
// // 下面的代碼是為了查看matrix中的元素
// matrixValues = new float[9];
// matrixgetValues(matrixValues);
// for(int i = 0; i < 3; ++i)
// {
// String temp = new String();
// for(int j = 0; j < 3; ++j)
// {
// temp += matrixValues[3 * i + j ] + "\t";
// }
// Loge("TestTransformMatrixActivity", temp);
// }
// // 錯(cuò)切 - 水平
// matrixsetSkew(5f, 0f);
// // 下面的代碼是為了查看matrix中的元素
// float[] matrixValues = new float[9];
// matrixgetValues(matrixValues);
// for(int i = 0; i < 3; ++i)
// {
// String temp = new String();
// for(int j = 0; j < 3; ++j)
// {
// temp += matrixValues[3 * i + j ] + "\t";
// }
// Loge("TestTransformMatrixActivity", temp);
// }
//
// // 做下面的平移變換,純粹是為了讓變換后的圖像和原圖像不重疊
// matrixpostTranslate(viewgetImageBitmap()getWidth(), 0f);
// viewsetImageMatrix(matrix);
//
// // 下面的代碼是為了查看matrix中的元素
// matrixValues = new float[9];
// matrixgetValues(matrixValues);
// for(int i = 0; i < 3; ++i)
// {
// String temp = new String();
// for(int j = 0; j < 3; ++j)
// {
// temp += matrixValues[3 * i + j ] + "\t";
// }
// Loge("TestTransformMatrixActivity", temp);
// }
// // 錯(cuò)切 - 垂直
// matrixsetSkew(0f, 5f);
// // 下面的代碼是為了查看matrix中的元素
// float[] matrixValues = new float[9];
// matrixgetValues(matrixValues);
// for(int i = 0; i < 3; ++i)
// {
// String temp = new String();
// for(int j = 0; j < 3; ++j)
// {
// temp += matrixValues[3 * i + j ] + "\t";
// }
// Loge("TestTransformMatrixActivity", temp);
// }
//
// // 做下面的平移變換,純粹是為了讓變換后的圖像和原圖像不重疊
// matrixpostTranslate(0f, viewgetImageBitmap()getHeight());
// viewsetImageMatrix(matrix);
//
// // 下面的代碼是為了查看matrix中的元素
// matrixValues = new float[9];
// matrixgetValues(matrixValues);
// for(int i = 0; i < 3; ++i)
// {
// String temp = new String();
// for(int j = 0; j < 3; ++j)
// {
// temp += matrixValues[3 * i + j ] + "\t";
// }
// Loge("TestTransformMatrixActivity", temp);
// }
// 錯(cuò)切 - 水平 + 垂直
// matrixsetSkew(5f, 5f);
// // 下面的代碼是為了查看matrix中的元素
// float[] matrixValues = new float[9];
// matrixgetValues(matrixValues);
// for(int i = 0; i < 3; ++i)
// {
// String temp = new String();
// for(int j = 0; j < 3; ++j)
// {
// temp += matrixValues[3 * i + j ] + "\t";
// }
// Loge("TestTransformMatrixActivity", temp);
// }
//
// // 做下面的平移變換,純粹是為了讓變換后的圖像和原圖像不重疊
// matrixpostTranslate(0f, viewgetImageBitmap()getHeight());
// viewsetImageMatrix(matrix);
//
// // 下面的代碼是為了查看matrix中的元素
// matrixValues = new float[9];
// matrixgetValues(matrixValues);
// for(int i = 0; i < 3; ++i)
// {
// String temp = new String();
// for(int j = 0; j < 3; ++j)
// {
// temp += matrixValues[3 * i + j ] + "\t";
// }
// Loge("TestTransformMatrixActivity", temp);
// }
// // 對(duì)稱 (水平對(duì)稱)
// float matrix_values[] = {1f, 0f, 0f, 0f, -1f, 0f, 0f, 0f, 1f};
// matrixsetValues(matrix_values);
// // 下面的代碼是為了查看matrix中的元素
// float[] matrixValues = new float[9];
// matrixgetValues(matrixValues);
// for(int i = 0; i < 3; ++i)
// {
// String temp = new String();
// for(int j = 0; j < 3; ++j)
// {
// temp += matrixValues[3 * i + j ] + "\t";
// }
// Loge("TestTransformMatrixActivity", temp);
// }
//
// // 做下面的平移變換,純粹是為了讓變換后的圖像和原圖像不重疊
// matrixpostTranslate(0f, viewgetImageBitmap()getHeight() * 2f);
// viewsetImageMatrix(matrix);
//
// // 下面的代碼是為了查看matrix中的元素
// matrixValues = new float[9];
// matrixgetValues(matrixValues);
// for(int i = 0; i < 3; ++i)
// {
// String temp = new String();
// for(int j = 0; j < 3; ++j)
// {
// temp += matrixValues[3 * i + j ] + "\t";
// }
// Loge("TestTransformMatrixActivity", temp);
// }
// // 對(duì)稱 - 垂直
// float matrix_values[] = {-1f, 0f, 0f, 0f, 1f, 0f, 0f, 0f, 1f};
// matrixsetValues(matrix_values);
// // 下面的代碼是為了查看matrix中的元素
// float[] matrixValues = new float[9];
// matrixgetValues(matrixValues);
// for(int i = 0; i < 3; ++i)
// {
// String temp = new String();
// for(int j = 0; j < 3; ++j)
// {
// temp += matrixValues[3 * i + j ] + "\t";
// }
// Loge("TestTransformMatrixActivity", temp);
// }
//
// // 做下面的平移變換,純粹是為了讓變換后的圖像和原圖像不重疊
// matrixpostTranslate(viewgetImageBitmap()getWidth() * 2f, 0f);
// viewsetImageMatrix(matrix);
//
// // 下面的代碼是為了查看matrix中的元素
// matrixValues = new float[9];
// matrixgetValues(matrixValues);
// for(int i = 0; i < 3; ++i)
// {
// String temp = new String();
// for(int j = 0; j < 3; ++j)
// {
// temp += matrixValues[3 * i + j ] + "\t";
// }
// Loge("TestTransformMatrixActivity", temp);
// }
// // 對(duì)稱(對(duì)稱軸為直線y = x)
// float matrix_values[] = {0f, -1f, 0f, -1f, 0f, 0f, 0f, 0f, 1f};
// matrixsetValues(matrix_values);
// // 下面的代碼是為了查看matrix中的元素
// float[] matrixValues = new float[9];
// matrixgetValues(matrixValues);
// for(int i = 0; i < 3; ++i)
// {
// String temp = new String();
// for(int j = 0; j < 3; ++j)
// {
// temp += matrixValues[3 * i + j ] + "\t";
// }
// Loge("TestTransformMatrixActivity", temp);
// }
//
// // 做下面的平移變換,純粹是為了讓變換后的圖像和原圖像不重疊
// matrixpostTranslate(viewgetImageBitmap()getHeight() + viewgetImageBitmap()getWidth(),
// viewgetImageBitmap()getHeight() + viewgetImageBitmap()getWidth());
// viewsetImageMatrix(matrix);
//
// // 下面的代碼是為了查看matrix中的元素
// matrixValues = new float[9];
// matrixgetValues(matrixValues);
// for(int i = 0; i < 3; ++i)
// {
// String temp = new String();
// for(int j = 0; j < 3; ++j)
// {
// temp += matrixValues[3 * i + j ] + "\t";
// }
// Loge("TestTransformMatrixActivity", temp);
// }
viewinvalidate();
}
return true;
}
}
下面給出上述代碼中,各種變換的具體結(jié)果及其對(duì)應(yīng)的相關(guān)變換矩陣
1. 平移

輸出的結(jié)果:

請(qǐng)對(duì)照第一部分中的“一、平移變換”所講的情形,考察上述矩陣的正確性。
2. 旋轉(zhuǎn)(圍繞圖像的中心點(diǎn))

輸出的結(jié)果:

它實(shí)際上是
matrix.setRotate(45f,view.getImageBitmap().getWidth() / 2f, view.getImageBitmap().getHeight() / 2f); matrix.postTranslate(view.getImageBitmap().getWidth()* 1.5f, 0f);
這兩條語(yǔ)句綜合作用的結(jié)果。根據(jù)第一部分中“二、旋轉(zhuǎn)變換”里面關(guān)于圍繞某點(diǎn)旋轉(zhuǎn)的公式,
matrix.setRotate(45f,view.getImageBitmap().getWidth() / 2f, view.getImageBitmap().getHeight() / 2f);
所產(chǎn)生的轉(zhuǎn)換矩陣就是:

而matrix.postTranslate(view.getImageBitmap().getWidth()* 1.5f, 0f);的意思就是在上述矩陣的左邊再乘以下面的矩陣:

關(guān)于post是左乘這一點(diǎn),我們?cè)谇懊娴睦碚摬糠衷?jīng)提及過(guò),后面我們還會(huì)專門討論這個(gè)問(wèn)題。
所以它實(shí)際上就是:

出去計(jì)算上的精度誤差,我們可以看到我們計(jì)算出來(lái)的結(jié)果,和程序直接輸出的結(jié)果是一致的。
3. 旋轉(zhuǎn)(圍繞坐標(biāo)原點(diǎn)旋轉(zhuǎn),在加上兩次平移,效果同2)

根據(jù)第一部分中“二、旋轉(zhuǎn)變換”里面關(guān)于圍繞某點(diǎn)旋轉(zhuǎn)的解釋,不難知道:
matrix.setRotate(45f,view.getImageBitmap().getWidth() / 2f, view.getImageBitmap().getHeight() / 2f);
等價(jià)于
matrix.setRotate(45f); matrix.preTranslate(-1f* view.getImageBitmap().getWidth() / 2f, -1f *view.getImageBitmap().getHeight() / 2f); matrix.postTranslate((float)view.getImageBitmap().getWidth()/ 2f, (float)view.getImageBitmap().getHeight() / 2f);
其中matrix.setRotate(45f)對(duì)應(yīng)的矩陣是:

matrix.preTranslate(-1f* view.getImageBitmap().getWidth() / 2f, -1f * view.getImageBitmap().getHeight()/ 2f)對(duì)應(yīng)的矩陣是:

由于是preTranslate,是先乘,也就是右乘,即它應(yīng)該出現(xiàn)在matrix.setRotate(45f)所對(duì)應(yīng)矩陣的右側(cè)。
matrix.postTranslate((float)view.getImageBitmap().getWidth()/ 2f, (float)view.getImageBitmap().getHeight() / 2f)對(duì)應(yīng)的矩陣是:

這次由于是postTranslate,是后乘,也就是左乘,即它應(yīng)該出現(xiàn)在matrix.setRotate(45f)所對(duì)應(yīng)矩陣的左側(cè)。
所以綜合起來(lái),
matrix.setRotate(45f); matrix.preTranslate(-1f* view.getImageBitmap().getWidth() / 2f, -1f *view.getImageBitmap().getHeight() / 2f); matrix.postTranslate((float)view.getImageBitmap().getWidth()/ 2f, (float)view.getImageBitmap().getHeight() / 2f);
對(duì)應(yīng)的矩陣就是:

這和下面這個(gè)矩陣(圍繞圖像中心順時(shí)針旋轉(zhuǎn)45度)其實(shí)是一樣的:

因此,此處變換后的圖像和2中變換后的圖像時(shí)一樣的。
4. 縮放變換

程序所輸出的兩個(gè)矩陣分別是:

其中第二個(gè)矩陣,其實(shí)是下面兩個(gè)矩陣相乘的結(jié)果:

大家可以對(duì)照第一部分中的“三、縮放變換”和“一、平移變換”說(shuō)法,自行驗(yàn)證結(jié)果。
5. 錯(cuò)切變換(水平錯(cuò)切)

代碼所輸出的兩個(gè)矩陣分別是:

其中,第二個(gè)矩陣其實(shí)是下面兩個(gè)矩陣相乘的結(jié)果:

大家可以對(duì)照第一部分中的“四、錯(cuò)切變換”和“一、平移變換”的相關(guān)說(shuō)法,自行驗(yàn)證結(jié)果。
6. 錯(cuò)切變換(垂直錯(cuò)切)

代碼所輸出的兩個(gè)矩陣分別是:

其中,第二個(gè)矩陣其實(shí)是下面兩個(gè)矩陣相乘的結(jié)果:

大家可以對(duì)照第一部分中的“四、錯(cuò)切變換”和“一、平移變換”的相關(guān)說(shuō)法,自行驗(yàn)證結(jié)果。
7. 錯(cuò)切變換(水平+垂直錯(cuò)切)

代碼所輸出的兩個(gè)矩陣分別是:

其中,后者是下面兩個(gè)矩陣相乘的結(jié)果:

大家可以對(duì)照第一部分中的“四、錯(cuò)切變換”和“一、平移變換”的相關(guān)說(shuō)法,自行驗(yàn)證結(jié)果。
8. 對(duì)稱變換(水平對(duì)稱)

代碼所輸出的兩個(gè)各矩陣分別是:

其中,后者是下面兩個(gè)矩陣相乘的結(jié)果:

大家可以對(duì)照第一部分中的“五、對(duì)稱變換”和“一、平移變換”的相關(guān)說(shuō)法,自行驗(yàn)證結(jié)果。
9. 對(duì)稱變換(垂直對(duì)稱)

代碼所輸出的兩個(gè)矩陣分別是:

其中,后者是下面兩個(gè)矩陣相乘的結(jié)果:

大家可以對(duì)照第一部分中的“五、對(duì)稱變換”和“一、平移變換”的相關(guān)說(shuō)法,自行驗(yàn)證結(jié)果。
10. 對(duì)稱變換(對(duì)稱軸為直線y = x)

代碼所輸出的兩個(gè)矩陣分別是:

其中,后者是下面兩個(gè)矩陣相乘的結(jié)果:
大家可以對(duì)照第一部分中的“五、對(duì)稱變換”和“一、平移變換”的相關(guān)說(shuō)法,自行驗(yàn)證結(jié)果。
11. 關(guān)于先乘和后乘的問(wèn)題
由于矩陣的乘法運(yùn)算不滿足交換律,我們?cè)谇懊嬖?jīng)多次提及先乘、后乘的問(wèn)題,即先乘就是矩陣運(yùn)算中右乘,后乘就是矩陣運(yùn)算中的左乘。其實(shí)先乘、后乘的概念是針對(duì)變換操作的時(shí)間先后而言的,左乘、右乘是針對(duì)矩陣運(yùn)算的左右位置而言的。以第一部分“二、旋轉(zhuǎn)變換”中圍繞某點(diǎn)旋轉(zhuǎn)的情況為例:

越靠近原圖像中像素的矩陣,越先乘,越遠(yuǎn)離原圖像中像素的矩陣,越后乘。事實(shí)上,圖像處理時(shí),矩陣的運(yùn)算是從右邊往左邊方向進(jìn)行運(yùn)算的。這就形成了越在右邊的矩陣(右乘),越先運(yùn)算(先乘),反之亦然。
當(dāng)然,在實(shí)際中,如果首先指定了一個(gè)matrix,比如我們先setRotate(),即指定了上面變換矩陣中,中間的那個(gè)矩陣,那么后續(xù)的矩陣到底是pre還是post運(yùn)算,都是相對(duì)這個(gè)中間矩陣而言的。
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
- Android中利用matrix 控制圖片的旋轉(zhuǎn)、縮放、移動(dòng)
- 深入理解Android Matrix理論與使用的詳解
- Android變形(Transform)之Matrix用法
- Android中Matrix用法實(shí)例分析
- android高仿小米時(shí)鐘(使用Camera和Matrix實(shí)現(xiàn)3D效果)
- Android中使用Matrix控制圖形變換和制作倒影效果的方法
- android Matrix實(shí)現(xiàn)圖片隨意放大縮小或拖動(dòng)
- Android 矩陣ColorMatrix
- Android使用Matrix旋轉(zhuǎn)圖片模擬碟片加載過(guò)程
- 詳談Android中Matrix的set、pre、post的區(qū)別
- android.graphics.Matrix類用法分析
相關(guān)文章
Android編程之EditText常見(jiàn)操作示例
這篇文章主要介紹了Android編程之EditText常見(jiàn)操作,結(jié)合實(shí)例形式分析了Android EditText光標(biāo)與文本操作相關(guān)技巧,需要的朋友可以參考下2017-03-03
android監(jiān)控sim卡有沒(méi)有服務(wù)示例(sim卡管理)
android監(jiān)聽(tīng)SIM卡有沒(méi)有服務(wù),可以使用android.telephony.PhoneStateListener類來(lái)實(shí)現(xiàn),下面是一個(gè)簡(jiǎn)單的小例子,大家參考使用吧2014-01-01
Android 桌面Widget開(kāi)發(fā)要點(diǎn)解析(時(shí)間日期Widget)
總的來(lái)說(shuō),widget主要功能就是顯示一些信息。我們今天編寫(xiě)一個(gè)很簡(jiǎn)單的作為widget,顯示時(shí)間、日期、星期幾等信息。需要顯示時(shí)間信息,那就需要實(shí)時(shí)更新,一秒或者一分鐘更新一次2013-07-07
Android網(wǎng)絡(luò)通信基礎(chǔ)類源碼分析講解
這篇文章主要介紹了Android網(wǎng)絡(luò)通信基礎(chǔ)類源碼,包括了Handler、Looper、Thread的分析講解,對(duì)日常開(kāi)發(fā)學(xué)習(xí)很有幫助,需要的朋友可以參考下2024-05-05
Android Color顏色過(guò)度計(jì)算實(shí)現(xiàn)代碼
這篇文章主要介紹了Android Color顏色過(guò)度計(jì)算實(shí)現(xiàn)代碼的相關(guān)資料,需要的朋友可以參考下2017-06-06
android實(shí)現(xiàn)okHttp的get和post請(qǐng)求的簡(jiǎn)單封裝與使用
這篇文章主要介紹了android實(shí)現(xiàn)okHttp的get和post請(qǐng)求的簡(jiǎn)單封裝與使用,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-05-05
Kotlin協(xié)程之Flow基礎(chǔ)原理示例解析
這篇文章主要為大家介紹了Kotlin協(xié)程之Flow基礎(chǔ)原理示例解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-09-09

