引言
接android變形(transform)之matrix,來總結下camera的使用,camera主要實現3d的變形,有轉動,旋轉等,camera的源碼是由native(本地代碼)實現,提供的接口也比較簡單。官方的介紹:a camera instance can be used to compute 3d transformations and generate a matrix that can be applied, for instance, on a canvas
.
效果圖
原圖:
變形以后:
api使用
camera提供的方法如下:
save:保存當前狀態
restore:回復當前狀態
translate:在x,y,z三位控件內進行平移
rotatex:以(0.0)為中心,繞x軸進行選擇
rotatey:以(0.0)為中心,繞y軸進行選擇
rotatez:以(0.0)為中心,旋轉(此處和matrix旋轉原理一樣,只不過反向相反,為逆時針)
...
常用的就這么多
實踐
直接上代碼:
public class cameratransformview extends view {
private bitmap mbitmap;
private camera mcamera;
private matrix mmatrix;
private int deltax, deltay, deltaz, extraz;
private int centerx, centery;
public cameratransformview(context context, attributeset attrs) {
super(context, attrs);
}
public void setdrawable(int resid) {
mbitmap = bitmapfactory.decoderesource(getresources(), resid);
centerx = mbitmap.getwidth() / 2;
centery = mbitmap.getheight() / 2;
mcamera = new camera();
mmatrix = new matrix();
}
public void setdelta(int x, int y, int z, int extra) {
deltax += x;
deltay += y;
deltaz += z;
extraz += extra;
invalidate();
}
public void reset() {
deltax = 0;
deltay = 0;
deltaz = 0;
invalidate();
}
@override
protected void ondraw(canvas canvas) {
mcamera.save();
mcamera.translate(10, 10, extraz);
mcamera.rotatex(deltax);
mcamera.rotatey(deltay);
mcamera.rotatez(deltaz);
mcamera.getmatrix(mmatrix);
mcamera.restore();
mmatrix.pretranslate(-this.centerx, -this.centery);
mmatrix.posttranslate(this.centerx, this.centery);
canvas.drawbitmap(mbitmap, mmatrix, null);
super.ondraw(canvas);
}
}
其實camera的變化就是封裝了一個matrix矩陣,可以通過getmatrix方法來獲取這個坐標矩陣。在上面的demo中就用到了該方法做些額外的處理,下面具體看看:
@override
protected void ondraw(canvas canvas) {
mcamera.save();
mcamera.translate(10, 10, extraz);
mcamera.rotatex(deltax);
mcamera.rotatey(deltay);
mcamera.rotatez(deltaz);
mcamera.getmatrix(mmatrix);
mcamera.restore();
//mmatrix.pretranslate(-this.centerx, -this.centery);
//mmatrix.posttranslate(this.centerx, this.centery);
canvas.drawbitmap(mbitmap, mmatrix, null);
super.ondraw(canvas);
}
在ondraw方法中,可以通過camera的方法來完成變形。注意11,12行,如果在ondraw的時候不進行倆行設置的話,可以看到效果如下:
可以看到,其按照y軸旋轉中心點是(0,0),那么平常的應用而言,大多希望其中心點在圖片的中心點上。所以需要加入
mmatrix.pretranslate(-this.centerx, -this.centery);
mmatrix.posttranslate(this.centerx, this.centery);
其實這一節的重點就在于剖析這倆句話。
從camara的api中可以看出來其不提供變形中心點的設置方法,那么怎么辦呢,基本思路是:假設圖片中心點為(centerx,centery),既然camera始終以(0,0)為中心點,那么我先將圖形矩陣往左移動centerx,再往上移動centery,讓(centerx,centery)正好掐在初始的(0,0)上,這樣進行變形的話,中心點就變成了(centerx,centery),達到了目的,當然這還沒結束,你既然偏移了(-centerx,-centery),那么變形以后得移回來,然后再往右下方分別移動centerx,centery。
按照矩陣的變換,可以表達為:
1,0,-centerx 1,0,centerx
0,1,-centery * 變形矩陣 * 0,1,centery
0,0,1 0,0,1
那么具體就如此,思路和代碼結合起來怎么來解釋呢,接著看,我們需要回顧下matrix中的部分知識。
回顧
matrix提供的三種變形方式為:set,post,pre。
set就是先reset,然后進行變形
pre可以解釋為先乘,在矩陣原理中對應的右乘
post可以理解成后乘,在矩陣遠離中對應左乘
不著急,接下倆具體看什么是先乘,后乘,什么是左乘,右乘。
舉個例子:
原圖
讓一個圖形按照中心點放大至2倍
那么期望的效果是:中心點不變(圖片被邊緣截斷了)
那么按照之前提高的思路:假設中心點是(50,50)先左上移50,也即(-50,-50)再進行放大,再右下移50,也即(50,50)
api調用即為:setscale(2,2), pretranslate(-50,-50), posttranslate(50,50)
照例來說對應矩陣為:
1,0,-50 2,0,0 1,0,50 2,0,50
0,1,-50 * 0,0,2 * 0,1,50 = 0,2,50
0,0,1 0,0,1 0,0,1 0,0,1
可以看到結果是放大至2倍,但是卻往右下移動了(50,50),奇怪要是這樣的話,和預期的效果圖一樣預期的效果圖矩陣應該為(方法至2倍,往左上移動(-50,-50))
2,0,-50
0,2,-50,
0,0,1
好,揭曉下疑點:
此處api的執行順序為:pretranslate(-50,-50) -> setscale(2,2) -> posttranslate(50,50) 沒有問題
答案揭曉:矩陣符合變化的原則,如果圖形經過f1,f2...fn此變形,對應矩陣為t1,t2...tn,符合矩陣t = tn*tn-1...*t1
那么正確的矩陣算法應該為
1,0,50 2,0,0 1,0,-50 2,0,-50
0,1,50 * 0,0,2 * 0,1,-50 = 0,2,-50
0,0,1 0,0,1 0,0,1 0,0,1
此處也解釋了pre為右乘,post為左乘的原理了。
那么到此為止,一切都都得到了解釋。
回歸
回歸到camera的demo當中,既然camera的變形中心點是(0,0),而且camera的變形實際是對matrix的變形,我們可以通過getmatrix方法來獲取這個matrix,然后通過左移pre,變形后右移post來實現中心點的設置。