Canvas類中drawBitmap(Bitmap bitmap, Matrix matrix, Paint paint)方法中有個參數類型是Matrix,從字面上理解是矩陣的意思,而實際上它也確實是個3x3的矩陣。Matrix在android中的主要作用是圖像變換,如平移、旋轉、縮放、扭曲等。 關於圖像如何通過矩陣進行變化可參考這篇文章圖像處理—關於像素坐標矩陣變換(平移,旋轉,縮放,錯切)
Matrix內部通過維護一個float[9]的數組來構成3x3矩陣的形式,而實際上所有的變換方法說到底就是通過更改數組中某個或某幾個位置的數值。Matrix提供了setValues()和getValues()方法來操作數組。
顯然這兩個方法使用起來很不方便,如果只有這樣,那Matrix估計就不會有人使用了。Google輕易不會辜負我們的信任,Matrix提供了若幹簡單易用的變換方法和映射方法供開發者使用。
Matrix變換方法
1、Translate(平移)、Scale(縮放)、Rotate(旋轉)、Skew(扭曲)
Matrix提供了Translate(平移)、Scale(縮放)、Rotate(旋轉)、Skew(扭曲)四中變換操作,這四種操作實質上是調用了setValues()方法來設置矩陣數組來達到變換效果。
除Translate(平移)外,Scale(縮放)、Rotate(旋轉)、Skew(扭曲)都可以圍繞一個中心點來進行,如果不指定,在默認情況下是圍繞(0, 0)來進行相應的變換的。
Translate操作中
- 在x軸上使用正數進行平移將向右移動圖像,而使用負數將向左移動圖像。
- 在y軸上使用正數進行平移將向下移動圖像,而使用負數將向上移動圖像。
Scale操作中,
- 在x軸上使用正數進行縮放將在中心點的右邊縮放圖像,而使用負數將在中心點的左邊縮放圖像。
- 在y軸上使用正數進行縮放將在中心點的下邊縮放圖像,而使用負數將在中心點的上邊縮放圖像。
2、pre、set、post
Matrix提供的四種操作,每一種都有pre、set、post三種形式。原因是矩陣乘法不滿足乘法交換律,因此左乘還是右乘最終的效果都不一樣。左乘或者右乘是針對變換矩陣的,得到最終的變換矩陣後,與圖像矩陣想成,圖像矩陣在右邊。
pre方法表示矩陣左乘,例如:變換矩陣為A,原始矩陣為B,pre方法的含義即是A*B
post方法表示矩陣右乘,例如:變換矩陣為A,原始矩陣為B,post方法的含義即是B*A
我們可以把Matrix變換想象成一個隊列,隊列裏面包含了若幹個變換操作,隊列中每個操作按照先後順序操作變換目標完成變換,pre相當於向隊首增加一個操作,post相當於向隊尾增加一個操作,set相當於清空當前隊列重新設置。
///這段代碼只有translate(100, 100)生效,因為第二個set會把之前隊列中的操作清除。 Matrix m = new Matrix(); m.setRotate(100); m.setTranslate(100, 100);
//這段代碼先執行translate(100, 100),後執行rotate(100) Matrix m = new Matrix(); m.setTranslate(100, 100); m.postRotate(100);
///這段代碼先執行rotate(100),後執行translate(100, 100) Matrix m = new Matrix(); m.setTranslate(100, 100); m.preRotate(100);
///這段代碼的執行順序: translate(100f, 100f) -> scale(2f, 2f) -> scale(0.5f, 0.5f) -> translate(50f, 50f) Matrix m = new Matrix(); m.preScale(2f, 2f); m.preTranslate(100f, 100f); m.postScale(0.5f, 0.5f); m.postTranslate(50f, 50f);
//這段代碼的執行順序:translate(50f, 50f) -> scale(0.8f, 0.8f) -> scale(3f, 3f) Matrix m = new Matrix(); m.postTranslate(100, 100); m.preScale(0.5f, 0.5f); m.setScale(0.8f, 0.8f); m.postScale(3f, 3f); m.preTranslate(50f, 50f);
3、Matrix 映射方法
Matrix提供了mapPoints(),mapRects(),mapVectors()等映射方法,用來獲取經Matrix映射後的值
//這段代碼的作用是獲取經過平移後該bitmap四個點的坐標 Matrix m = new Matrix(); m.postTranslate(100f, 100f); float[] src = http://blog.csdn.net/xx326664162/article/details/{ 0, 0, 0, bitmap.getHeight(), bitmap.getWidth(), 0, bitmap.getWidth(), bitmap.getHeight() }; float[] dst = new float[8]; m.mapPoints(dst, src);
常用的矩陣變化方法
下面是常用變換的例子,除了變換參數,其他代碼也有一些差異,可以多認識幾種實現方法
平移Translate
/** * 圖片移動 */ protected void bitmapTranslate(float dx, float dy) { // 需要根據移動的距離來創建圖片的拷貝圖大小 Bitmap afterBitmap = Bitmap.createBitmap( (int) (baseBitmap.getWidth() + dx), (int) (baseBitmap.getHeight() + dy), Bitmap.Config.ARGB_8888); Canvas canvas = new Canvas(afterBitmap); Matrix matrix = new Matrix(); // 設置移動的距離 matrix.setTranslate(dx, dy); canvas.drawBitmap(baseBitmap, matrix, paint); iv_after.setImageBitmap(afterBitmap); }
縮放Scale
/** * 縮放圖片 */ protected void bitmapScale(float x, float y) { // 因為要將圖片放大,所以要根據放大的尺寸重新創建Bitmap Bitmap afterBitmap = Bitmap.createBitmap( (int) (baseBitmap.getWidth() * x), (int) (baseBitmap.getHeight() * y), baseBitmap.getConfig()); Canvas canvas = new Canvas(afterBitmap); // 初始化Matrix對象 Matrix matrix = new Matrix(); // 根據傳入的參數設置縮放比例 matrix.setScale(x, y); // 根據縮放比例,把圖片draw到Canvas上 canvas.drawBitmap(baseBitmap, matrix, paint); iv_after.setImageBitmap(afterBitmap); }
註意觀察下面兩個鏡像的實現方法,
X軸鏡像
/** * x軸鏡像 */ protected void bitmapXMirror() { // 因為要將圖片放大,所以要根據放大的尺寸重新創建Bitmap Bitmap afterBitmap = Bitmap.createBitmap( baseBitmap.getWidth() , baseBitmap.getHeight() , baseBitmap.getConfig()); Canvas canvas = new Canvas(afterBitmap); // 初始化Matrix對象 Matrix matrix = new Matrix(); // 根據傳入的參數設置縮放比例 matrix.postScale(-1, 1); matrix.postTranslate(baseBitmap.getWidth(), 0); // 根據縮放比例,把圖片draw到Canvas上 canvas.drawBitmap(baseBitmap, matrix, paint); iv_after.setImageBitmap(afterBitmap); }
Y軸鏡像
/** * y軸鏡像 */ protected void bitmapYMirror() { // 初始化Matrix對象 Matrix matrix = new Matrix(); // 根據傳入的參數設置縮放比例 matrix.postScale(1, -1); //根據變換矩陣,繪制新的圖片 Bitmap afterBitmap = Bitmap.createBitmap(baseBitmap, 0, 0,baseBitmap.getWidth(),baseBitmap.getHeight(), matrix, true); iv_after.setImageBitmap(afterBitmap); }
旋轉Rotate
/** * 圖片旋轉 */ protected void bitmapRotate(float degrees) { // 創建一個和原圖一樣大小的圖片 Bitmap afterBitmap = Bitmap.createBitmap(baseBitmap.getWidth(), baseBitmap.getHeight(), baseBitmap.getConfig()); Canvas canvas = new Canvas(afterBitmap); Matrix matrix = new Matrix(); // 根據原圖的中心位置旋轉 matrix.setRotate(degrees, baseBitmap.getWidth() / 2, baseBitmap.getHeight() / 2); canvas.drawBitmap(baseBitmap, matrix, paint); iv_after.setImageBitmap(afterBitmap); }
扭曲Skew
/** * 傾斜圖片 */ protected void bitmapSkew(float dx, float dy) { // 根據圖片的傾斜比例,計算變換後圖片的大小, Matrix matrix = new Matrix(); // 設置圖片傾斜的比例 matrix.setSkew(dx, dy); //根據變換矩陣,繪制新的圖片 Bitmap afterBitmap = Bitmap.createBitmap(baseBitmap, 0, 0,baseBitmap.getWidth(),baseBitmap.getHeight(), matrix, true); iv_after.setImageBitmap(afterBitmap); }
源碼
完整的Demo
MainActivity.Java
public class MainActivity extends AppCompatActivity { private Button btn_scale, btn_rotate, btn_translate, btn_skew,btn_XMirror,btn_YMirror; private ImageView iv_base, iv_after; private Bitmap baseBitmap; private Paint paint; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); btn_scale = (Button) findViewById(R.id.btn_scale); btn_rotate = (Button) findViewById(R.id.btn_rotate); btn_translate = (Button) findViewById(R.id.btn_translate); btn_skew = (Button) findViewById(R.id.btn_skew); btn_XMirror = (Button) findViewById(R.id.btn_x_mirror); btn_YMirror = (Button) findViewById(R.id.btn_y_mirror); btn_scale.setOnClickListener(click); btn_rotate.setOnClickListener(click); btn_translate.setOnClickListener(click); btn_skew.setOnClickListener(click); btn_XMirror.setOnClickListener(click); btn_YMirror.setOnClickListener(click); iv_base = (ImageView) findViewById(R.id.iv_base); iv_after = (ImageView) findViewById(R.id.iv_after); baseBitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher); iv_base.setImageBitmap(baseBitmap); // 設置畫筆,消除鋸齒 paint = new Paint(); paint.setAntiAlias(true); } private View.OnClickListener click = new View.OnClickListener() { @Override public void onClick(View v) { switch (v.getId()) { case R.id.btn_scale: bitmapScale(2.0f, 4.0f); break; case R.id.btn_rotate: bitmapRotate(180); break; case R.id.btn_translate: bitmapTranslate(20f, 20f); break; case R.id.btn_skew: bitmapSkew(0.2f, 0.4f); break; case R.id.btn_x_mirror: bitmapXMirror(); break; case R.id.btn_y_mirror: bitmapYMirror(); break; default: break; } } }; /** * 縮放圖片 */ protected void bitmapScale(float x, float y) { // 因為要將圖片放大,所以要根據放大的尺寸重新創建Bitmap Bitmap afterBitmap = Bitmap.createBitmap( (int) (baseBitmap.getWidth() * x), (int) (baseBitmap.getHeight() * y), baseBitmap.getConfig()); Canvas canvas = new Canvas(afterBitmap); // 初始化Matrix對象 Matrix matrix = new Matrix(); // 根據傳入的參數設置縮放比例 matrix.setScale(x, y); // 根據縮放比例,把圖片draw到Canvas上 canvas.drawBitmap(baseBitmap, matrix, paint); iv_after.setImageBitmap(afterBitmap); } /** * x軸鏡像 */ protected void bitmapXMirror() { // 因為要將圖片放大,所以要根據放大的尺寸重新創建Bitmap Bitmap afterBitmap = Bitmap.createBitmap( baseBitmap.getWidth() , baseBitmap.getHeight() , baseBitmap.getConfig()); Canvas canvas = new Canvas(afterBitmap); // 初始化Matrix對象 Matrix matrix = new Matrix(); // 根據傳入的參數設置縮放比例 matrix.postScale(-1, 1); matrix.postTranslate(baseBitmap.getWidth(), 0); // 根據縮放比例,把圖片draw到Canvas上 canvas.drawBitmap(baseBitmap, matrix, paint); iv_after.setImageBitmap(afterBitmap); } /** * y軸鏡像 */ protected void bitmapYMirror() { // 初始化Matrix對象 Matrix matrix = new Matrix(); // 根據傳入的參數設置縮放比例 matrix.postScale(1, -1); //根據變換矩陣,繪制新的圖片 Bitmap afterBitmap = Bitmap.createBitmap(baseBitmap, 0, 0,baseBitmap.getWidth(),baseBitmap.getHeight(), matrix, true); iv_after.setImageBitmap(afterBitmap); } /** * 傾斜圖片 */ protected void bitmapSkew(float dx, float dy) { // 根據圖片的傾斜比例,計算變換後圖片的大小, Matrix matrix = new Matrix(); // 設置圖片傾斜的比例 matrix.setSkew(dx, dy); //根據變換矩陣,繪制新的圖片 Bitmap afterBitmap = Bitmap.createBitmap(baseBitmap, 0, 0,baseBitmap.getWidth(),baseBitmap.getHeight(), matrix, true); iv_after.setImageBitmap(afterBitmap); } /** * 圖片移動 */ protected void bitmapTranslate(float dx, float dy) { // 需要根據移動的距離來創建圖片的拷貝圖大小 Bitmap afterBitmap = Bitmap.createBitmap( (int) (baseBitmap.getWidth() + dx), (int) (baseBitmap.getHeight() + dy), Bitmap.Config.ARGB_8888); Canvas canvas = new Canvas(afterBitmap); Matrix matrix = new Matrix(); // 設置移動的距離 matrix.setTranslate(dx, dy); canvas.drawBitmap(baseBitmap, matrix, paint); iv_after.setImageBitmap(afterBitmap); } /** * 圖片旋轉 */ protected void bitmapRotate(float degrees) { // 創建一個和原圖一樣大小的圖片 Bitmap afterBitmap = Bitmap.createBitmap(baseBitmap.getWidth(), baseBitmap.getHeight(), baseBitmap.getConfig()); Canvas canvas = new Canvas(afterBitmap); Matrix matrix = new Matrix(); // 根據原圖的中心位置旋轉 matrix.setRotate(degrees, baseBitmap.getWidth() / 2, baseBitmap.getHeight() / 2); canvas.drawBitmap(baseBitmap, matrix, paint); iv_after.setImageBitmap(afterBitmap); } }
xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" tools:context=".MainActivity" > <LinearLayout android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="horizontal" > <Button android:id="@+id/btn_scale" android:layout_height="wrap_content" android:layout_width="0dp" android:layout_weight="1" android:text="縮放" /> <Button android:id="@+id/btn_rotate" android:layout_weight="1" android:layout_width="0dp" android:layout_height="wrap_content" android:text="旋轉" /> <Button android:id="@+id/btn_translate" android:layout_weight="1" android:layout_width="0dp" android:layout_height="wrap_content" android:text="平移" /> <Button android:id="@+id/btn_skew" android:layout_width="0dp" android:layout_weight="1" android:layout_height="wrap_content" android:text="傾斜" /> <Button android:id="@+id/btn_x_mirror" android:layout_width="0dp" android:layout_weight="1" android:layout_height="wrap_content" android:text="X軸鏡像" /> <Button android:id="@+id/btn_y_mirror" android:layout_width="0dp" android:layout_weight="1" android:layout_height="wrap_content" android:text="Y軸鏡像" /> </LinearLayout> <!-- 原始圖片 --> <ImageView android:id="@+id/iv_base" android:layout_width="wrap_content" android:layout_height="wrap_content" /> <!-- 處理之後的圖片 --> <ImageView android:id="@+id/iv_after" android:layout_width="wrap_content" android:layout_height="wrap_content" /> </LinearLayout>
參考:
Android Matrix
Android—Matrix類的使用
Android中Matrix的pre post set方法理解
Android–Matrix圖片變換處理
android bitmap翻轉180,鏡像的簡單實現方法
圖像處理—關於像素坐標矩陣變換(平移,旋轉,縮放,錯切)
Tags: Android Google Matrix matrix 開發者
文章來源: