android 特效繪圖,Android繪圖機制與處理技巧——Android圖像處理之圖形特效處理...

Android變形矩陣——Matrix

對于圖像的圖形變換,Android系統是通過矩陣來進行處理的,每個像素點都表達了其坐標的X、Y信息。Android的圖形變換矩陣是一個3x3的矩陣,如下圖所示:

84b4bbb66136

72F0CAC1-14FB-40F8-A430-8F542B09DC4E.png

當使用變換矩陣去處理每一個像素點的時候,與顏色矩陣的矩陣乘法一樣,計算公式如下所示:

X1=aX+bY+c

Y1=dX+eY+f

1=gX+hY+i

通常情況下,會讓g=h=0,i=1,這樣就使1=gX+hY+i恒成立。因此,只需著重關注上面幾個參數即可。

與色彩變換矩陣的初始矩陣一樣,圖形變換矩陣也有一個初始矩陣。就是對角線元素a、e、i為1,其他元素為0的矩陣,如下圖所示:

84b4bbb66136

圖形變換初始矩陣

圖像的變形處理通常包含以下四類基本變換:

Translate——平移變換

Rotate——旋轉變換

Scale——縮放變換

Skew——錯切變換

平移變換

平移變換的坐標值變換過程就是將每個像素點都進行平移變換,當從P(x0,y0)平移到P(x1,y1)時,所需的平移矩陣如下所示:

84b4bbb66136

F8CD701F-4C5A-40DF-9B67-E50500B702DC.png

旋轉變換

旋轉變換即指一個點圍繞一個中心旋轉到一個新的點。當從P(x0,y0)點,以坐標原點O為旋轉中心旋轉到P(x1,y1)時,可以將點的坐標都表達成OP與X軸正方向夾角的函數表達式(其中r為線段OP的長度,α為OP(x0,y0)與X軸正方向夾角,θ為OP(x0,y0)與OP(x1,y1)之間夾角),如下所示:

x0=rcosα

y0=rsinα

x1=rcos(α+θ)=rcosαcosθ?rsinαsinθ=x0cosθ?y0sinθ

y1=rsin(α+θ)=rsinαcosθ+rcosαsinθ=y0cosθ+x0sinθ

矩陣形式如下圖所示:

84b4bbb66136

旋轉變換矩陣

前面是以坐標原點為旋轉中心的旋轉變換,如果以任意點O為旋轉中心來進行旋轉變換,通常需要以下三個步驟:

1.將坐標原點平移到O點

2.使用前面講的以坐標原點為中心的旋轉方法進行旋轉變換

3.將坐標原點還原

縮放變換

一個像素點是不存在縮放的概念的,但是由于圖像是由很多個像素點組成的,如果將每個點的坐標都進行相同比例的縮放,最終就會形成讓整個圖像縮放的效果,縮放效果的公式如下

x1=K1x0

y1=K2y0

矩陣形式如下圖所示:

84b4bbb66136

縮放變換矩陣

錯切變換

錯切變換(skew)在數學上又稱為Shear mapping(可譯為“剪切變換“)或者Transvection(縮并),它是一種比較特殊的線性變換。錯切變換的效果就是讓所有點的X坐標(或者Y坐標)保持不變,而對應的Y坐標(或者X坐標)則按比例發生平移,且平移的大小和該點到Y軸(或者X軸)的距離成正比。錯切變換通常包含兩種——水平錯切與垂直錯切。

錯切變換的計算公式如下:

水平錯切

x1=x0+K1y0

y1=y0

垂直錯切

x1=x0

y1=K2x0+y0

矩陣形式如下圖

84b4bbb66136

錯切變換矩陣

由上面的分析可以發現,這個圖形變換3x3的矩陣與色彩變換矩陣一樣,每個位置的元素所表示的功能是有規律的,總結如下:

84b4bbb66136

矩陣變換規律

可以發現,a、b、c、d、e、f這六個矩陣元素分別對應以下變換:

a和e控制Scale——縮放變換

b和d控制Skew——錯切變換

a和e控制Trans——平移變換

a、b、d、e共同控制Rotate——旋轉變換

通過類似色彩矩陣中模擬矩陣的例子來模擬變形矩陣。在圖形變換矩陣中,同樣是通過一個一維數組來模擬矩陣,并通過setValues()方法將一個一維數組轉換為圖形變換矩陣,代碼如下所示:

private float[] mImageMatrix = new float[9];

Matrix matrix = new Matrix();

matrix.setValues(mImageMatrix);````

當獲得了變換矩陣后,就可以通過以下代碼將一個圖像以這個變換矩陣的形式繪制出來。

canvas.drawBitmap(mBitmap, mMatrix, null);

public class HandleImage1Activity extends BaseActivity {

private ImageView mImageView;

private GridLayout mGroup;

private float mHue, mSaturation, mLum;

private Bitmap mBitmap;

private int mEtWidth, mEtHeight;

private EditText[] mEts = new EditText[9];

private float[] mImageMatrix = new float[9];

@Override

protected void onCreate(@Nullable Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_handleimg1);

mBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.iu1);

mImageView = (ImageView) findViewById(R.id.img);

mGroup = (GridLayout) findViewById(R.id.group);

mGroup.post(new Runnable() {

@Override

public void run() {

// 獲取寬高信息

mEtWidth = mGroup.getWidth() / 3;

mEtHeight = mGroup.getHeight() / 3;

addEts();

initMatrix();

}

});

mImageView.setImageBitmap(mBitmap);

}

// 初始化顏色矩陣為初始狀態

private void initMatrix() {

for (int i = 0; i < 9; i++) {

if (i % 4 == 0)

mEts[i].setText(String.valueOf(1));

else

mEts[i].setText(String.valueOf(0));

}

}

// 添加EditText

private void addEts() {

for (int i = 0; i < 9; i++) {

EditText editText = new EditText(this);

editText.setInputType(InputType.TYPE_NUMBER_FLAG_DECIMAL);

mEts[i] = editText;

mGroup.addView(mEts[i], mEtWidth, mEtHeight);

}

}

// 獲取矩陣值

private void getMatrix() {

for (int i = 0; i < 9; i++) {

mImageMatrix[i] = Float.valueOf(mEts[i].getText().toString());

}

}

// 將矩陣值設置到圖像

private void setImageMatrix() {

Bitmap bmp = Bitmap.createBitmap(mBitmap.getWidth(), mBitmap.getHeight(), Bitmap.Config.ARGB_8888);

Canvas canvas = new Canvas(bmp);

Matrix matrix = new Matrix();

matrix.setValues(mImageMatrix);

canvas.drawBitmap(mBitmap,matrix,null);

mImageView.setImageBitmap(bmp);

}

// 作用矩陣效果

public void btnChange(View view) {

getMatrix();

setImageMatrix();

}

// 重置矩陣效果

public void btnReset(View view) {

initMatrix();

getMatrix();

setImageMatrix();

}

}````

Android系統同樣提供了一些API來簡化矩陣的運算,我們不必每次都去設置矩陣的每一個元素值。Android中使用Matrix類來封裝矩陣,并提供了以下幾個操作方法來實現上面的四中變換方式:

matrix.setRotate()——旋轉變換

matrix.setTranslate()——平移變換

matrix.setScale()——縮放變換

matrix.setSkew()——錯切變換

matrix.preX和matrix.postY——提供矩陣的前乘和后乘運算

Matrix類的set方法會重置矩陣中的值,而post和pre方法不會,這兩個方法常用來實現矩陣的混合作用。不過要注意的是,矩陣運算不滿足乘法的交換律,所以矩陣乘法的前乘和后乘是兩種不同的運算方式。舉例說明,比如需要實現以下效果:

先旋轉45度

再平移到(200, 200)

如果使用后乘運算,表示當前矩陣乘上參數代表的矩陣,代碼如下所示:

matrix.setRotate(45);

matrix.postTranslate(200, 200);

如果使用前乘運算,表示參數代表的矩陣乘上當前矩陣,代碼如下所示:

matrix.setTranslate(200, 200);

matrix.preRotate(45);

像素塊分析

圖像的特效處理有兩種方式,即使用矩陣來進行圖像變換和使用drawBitmapMesh()方法來進行處理。drawBitmapMesh()與操縱像素點來改變色彩的原理類似,只不過是把圖像分成了一個個的小塊,然后通過改變每一個圖像塊來修改整個圖像。

drawBitmapMesh()方法代碼如下:

public void drawBitmapMesh(Bitmap bitmap, int meshWidth, int meshHeight, float[] verts, int vertOffset, int[] colors, int colorOffset, Paint paint)

關鍵的參數如下:

bitmap:將要扭曲的圖像

meshWidth:需要的橫向網格數目

meshHeight :需要的縱向網格數目

verts:網格交叉點坐標數組

vertOffset:verts數組中開始跳過的(x, y)坐標對的數目

要使用drawBitmapMesh()方法就需先將圖片分割為若干個圖像塊。所以,在圖像上橫縱各畫N條線,而這橫縱各N條線就交織成了NxN個點,而每個點的坐標則以x1,y1,x2,y2,...,xn,yn的形式保存在verts數組中。也就是說verts數組的每兩位用來保存一個交織點,第一個是橫坐標,第二個是縱坐標。而整個drawBitmapMesh()方法改變圖像的方式,就是靠這些坐標值的改變來重新定義每一個圖像塊,從而達到圖像效果處理的功能。

drawBitmapMesh()方法的功能非常強大,基本上可以實現所有的圖像特效,但使用起來也非常復雜,其關鍵就是在于計算、確定新的交叉點的坐標。下面舉例說明如何使用drawBitmapMesh()方法來實現一個旗幟飛揚的效果。

要想達到旗幟飛揚的效果,只需要讓圖片中每個交叉點的橫坐標較之前不發生變化,而縱坐標較之前坐標呈現一個三角函數的周期性變化即可。

首先獲取交叉點的坐標,并將坐標保存到orig數組中,其獲取交叉點坐標的原理就是通過循環遍歷所有的交叉線,并按比例獲取其坐標,代碼如下所示:

mBitmap = BitmapFactory.decodeResource(context.getResources(), R.mipmap.test);

float bitmapWidth = mBitmap.getWidth();

float bitmapHeight = mBitmap.getHeight();

int index = 0;

for (int y = 0; y <= HEIGHT ; y++) {

float fy = bitmapHeight * y / HEIGHT;

for (int x = 0; x <= WIDTH; x++) {

float fx = bitmapWidth * x / WIDTH;

orig[index * 2] = verts[ index * 2] = fx;

//這里人為將坐標+100是為了讓圖像下移,避免扭曲后被屏幕遮擋

orig[index * 2 + 1] = verts[ index * 2 + 1] = fy + 100;

index++;

}

}

接下來,在onDraw()方法中改變交叉點的縱坐標的值,為了實現旗幟飄揚的效果,使用一個正弦函數sinx來改變交叉點縱坐標的值,而橫坐標不變,并將變化后的值保存到verts數組中,代碼如下所示:

@Override

protected void onDraw(Canvas canvas) {

super.onDraw(canvas);

flagWave();

K += 0.1f;//將K的值增加

canvas.drawBitmapMesh(mBitmap, WIDTH, HEIGHT, verts, 0, null, 0, null);

invalidate();

}

/**

* 按當前點所在的橫坐標的位置來確定縱坐標的偏移量,其中A代表正弦函數中的振幅大小

*/

private void flagWave() {

for (int j = 0; j <= HEIGHT; j++) {

for (int i = 0; i <= WIDTH; i++) {

//在獲取縱坐標的偏移量時,利用正弦函數的周期性給函數增加一個周期K * Math.PI,就是為了讓圖像能夠動起來

float offsetY = (float) Math.sin(2 * Math.PI * i / WIDTH + K * Math.PI);

verts[(j * (WIDTH + 1) + i) * 2 + 1] = orig[(j * (WIDTH + 1) + i) * 2 + 1] + offsetY * A;

}

}

}

這樣,每次在重繪時,通過改變相位來改變偏移量,從而造成一個動態的效果,就好象旗幟在風中飄揚一樣,效果圖如下。

使用drawBitmapMesh()方法可以創建很多復雜的圖像效果,但是對它的使用也相對復雜,需要我們對圖像處理有很深厚的功底。同時,對算法的要求也比較高,需要計算各種特效下不同的坐標點變化規律,從而設計出不同的特效。

代碼如下:

public class WaveView extends AppCompatImageView {

private static final int HEIGHT=200;//想要劃分的高

private static final int WIDTH=200;//想要劃分的寬

private int COUNT = (WIDTH + 1) * (HEIGHT + 1);

private float[] verts = new float[COUNT * 2];

private float[] orig = new float[COUNT * 2];

private float A = 50;//表示正弦函數中的振幅大小

private float K = 1;

private Bitmap mBitmap;

private int mWaveSrc;

public WaveView(Context context) {

this(context,null);

}

public WaveView(Context context, @Nullable AttributeSet attrs) {

this(context,attrs,0);

}

public WaveView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {

super(context, attrs, defStyleAttr);

TypedArray typedArray = context.obtainStyledAttributes(R.styleable.WaveView);

mWaveSrc=typedArray.getResourceId(R.styleable.WaveView_waveSrc,R.drawable.iu1);

mBitmap= BitmapFactory.decodeResource(getResources(),mWaveSrc);

float bitmapWidth = mBitmap.getWidth();

float bitmapHeight = mBitmap.getHeight();

int index = 0;

for (int y = 0; y <= HEIGHT ; y++) {

float fy = bitmapHeight * y / HEIGHT;

for (int x = 0; x <= WIDTH; x++) {

float fx = bitmapWidth * x / WIDTH;

orig[index * 2] = verts[ index * 2] = fx;

//這里人為將坐標+100是為了讓圖像下移,避免扭曲后被屏幕遮擋

orig[index * 2 + 1] = verts[ index * 2 + 1] = fy ;

index++;

}

}

typedArray.recycle();

}

@Override

protected void onDraw(Canvas canvas) {

super.onDraw(canvas);

flagWave();

K += 0.1f;//將K的值增加

canvas.drawBitmapMesh(mBitmap, WIDTH, HEIGHT, verts, 0, null, 0, null);

setImageBitmap(mBitmap);

invalidate();

}

/**

* 按當前點所在的橫坐標的位置來確定縱坐標的偏移量,其中A代表正弦函數中的振幅大小

*/

private void flagWave() {

for (int j = 0; j <= HEIGHT; j++) {

for (int i = 0; i <= WIDTH; i++) {

//在獲取縱坐標的偏移量時,利用正弦函數的周期性給函數增加一個周期K * Math.PI,就是為了讓圖像能夠動起來

float offsetY = (float) Math.sin(2 * Math.PI * i / WIDTH + K * Math.PI);

verts[(j * (WIDTH + 1) + i) * 2 + 1] = orig[(j * (WIDTH + 1) + i) * 2 + 1] + offsetY * A;

}

}

}

}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/282774.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/282774.shtml
英文地址,請注明出處:http://en.pswp.cn/news/282774.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

WPF 使用 DrawingContext 繪制刻度條

WPF 使用 DrawingContext 繪制刻度條控件名&#xff1a;Ruler作者&#xff1a;WPFDevelopersOrg原文鏈接&#xff1a; https://github.com/WPFDevelopersOrg/WPFDevelopers框架使用大于等于.NET40&#xff1b;Visual Studio 2022;項目使用 MIT 開源許可協議&#xff1b;定義I…

純css實現漂亮又健壯的tooltip

前言 tooltip的實現有多種方式&#xff0c;下面是一個tooltip成長史。 預覽 https://codepen.io/moddx/pen/... 原始版 最簡單的莫過于就用原始title屬性&#xff0c;像這樣&#xff1a; <button title"tips">button</button> 缺點是體驗是差了點&#x…

個人中心的html,個人中心.html

&#xfeff;個人中心$axure.utils.getTransparentGifPath function() { return resources/images/transparent.gif; };$axure.utils.getOtherPath function() { return resources/Other.html; };$axure.utils.getReloadPath function() { return resources/reload.html; };…

使用CMD命令修改Windows本地賬戶密碼

2019獨角獸企業重金招聘Python工程師標準>>> 一、以管理員身份運行cmd命令 二、在命令提示符窗口中輸入命令符&#xff1a;net user Administrator 123&#xff0c;然后按回車鍵“Enter”。(Administrator是你的win8用戶名&#xff0c;123是重新設置的密碼。) ? 三…

Android 編譯時:m、mm、mmm、mma、mmma的區別

m&#xff1a;編譯整個安卓系統 makes from the top of the tree mm&#xff1a;編譯當前目錄下的模塊&#xff0c;當前目錄下需要有Android.mk這個makefile文件&#xff0c;否則就往上找最近的Android.mk文件。 builds all of the moudles in the current directory mma&#…

java線程安全問題原因及解決辦法

1.為什么會出現線程安全問題 計算機系統資源分配的單位為進程&#xff0c;同一個進程中允許多個線程并發執行&#xff0c;并且多個線程會共享進程范圍內的資源&#xff1a;例如內存地址。當多個線程并發訪問同一個內存地址并且內存地址保存的值是可變的時候可能會發生線程安全問…

html語言怎么添加圖片,我想問你一下,你是怎么在html中插入本地圖片?非常感謝...

滿意答案小蜜蜂手工2013.10.03采納率&#xff1a;43% 等級&#xff1a;12已幫助&#xff1a;7929人img{float:right}在下面的段落中&#xff0c;我們添加了一個樣式為 float:right 的圖像。結果是這個圖像會浮動到段落的右側。This is some text. This is some text. This i…

數組實現矩陣逐層向內層加1

package java1701;public class javaMain { public static void main(String[] args) { // 逐層加 // 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 // 1 2 2 2 2 1 1 2 2 2 1 1 2 2 1 1 2 1 // 1 2 3 3 2 1 1 2 3 2 1 1 2 2 1 1 1 1 // 1 2 3 …

EntityFrameworkCore上下文如何實現繼承?

【導讀】如果我們存在基礎設施服務和其他服務&#xff0c;我們會定義屬于基礎設施服務的上下文以及其他服務的上下文&#xff0c; 而且會獨立部署&#xff0c;此時其他服務需要使用基礎服務&#xff0c;我們都會暴露基礎服務接口給到其他服務調用&#xff0c;這也是常規操作若在…

Unity 游戲框架搭建 (九) 減少加班利器-QConsole

為毛要實現這個工具? 在我小時候,每當游戲在真機運行時,我們看到的日志是這樣的。 沒高亮啊,還有亂七八糟的堆棧信息,好干擾日志查看,好影響心情。 還有就是必須始終連著usb線啊&#xff0c;我想要想躺著測試。。。 以上種種原因,QConsole誕生了。 如何使用? 使用方式和QLog…

android藍牙多次后,android – 如何防止BluetoothGattCallback一次多次執行

我的服務有一個BluetoothGattCallback實例public class MyService extends Service {private BluetoothGattCallback callback;Overridepublic void onCreate() {super.onCreate();callback new BluetoothGattCallback() {Overridepublic synchronized void onConnectionState…

美觀又實用,10 款強大的開源 Javascript 圖表庫

2019獨角獸企業重金招聘Python工程師標準>>> 隨著發展&#xff0c;現代 Web 設計在改善體驗和功能的同時&#xff0c;對于美觀的追求也越來越高&#xff0c;可視化、交互式、動態等元素和效果似乎已成為標配。 以下是為開發者推薦的 10 款開源 Javascript 圖表庫&am…

EF CORE 7 RC1 發布

原文鏈接&#xff1a;https://devblogs.microsoft.com/dotnet/announcing-ef7-rc1/[1]原文作者&#xff1a;Jeremy Likness翻譯&#xff1a;沙漠盡頭的狼(谷歌翻譯加持)Entity Framework Core 7 (EF7) Release Candidate 1 已發布&#xff01;該團隊專注于解決缺陷、小幅改進以…

0 重新學習Ubuntu -- 這一段沒怎么學習

在完成了前面的幾個學習后&#xff0c;再沒有進行系統的學習。 雖然在真機上安裝系統&#xff0c;每天都打開&#xff0c;完成以下的工作&#xff1a; 升級軟件用來查看相關的網站在Ubuntu上&#xff0c;現在可以完成辦公、上網、娛樂。 但專業的學習&#xff0c;例如編程方面進…

自定義地圖怎么做成html,自定義html為谷歌地圖制作標記

好吧&#xff0c;似乎Custom Overlays會做我想要的。這是ping層&#xff1a;function PingLayer(bounds, map) {this.bounds bounds;this.setMap(map);}PingLayer.prototype new google.maps.OverlayView();PingLayer.prototype.onAdd function() {var div document.create…

HDU5248:序列變換(二分)

序列變換 Time Limit: 4000/2000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 1348 Accepted Submission(s): 593Problem Description給定序列A{A1,A2,...,An}, 要求改變序列A中的某些元素&#xff0c;形成一個嚴格單調的序列B&am…

微服務太分散?使用Fundebug集中式bug監控

摘要&#xff1a; 微服務日志分散&#xff0c;可以使用Fundebug的異常監控將它們集中起來。 當一個項目復雜到一定程度&#xff0c;功能越來越多&#xff0c;隨之對應的模塊也越來越多。 如果都放在一個大的項目下面&#xff0c;共同開發&#xff0c;整合發布&#xff0c;那么會…

html404頁面怎么添加,網站要如何設置自定義404頁面?

之前我們講述過網站設置404頁面對于優化或是用戶體驗的重要意義&#xff0c;大家可移步到《網站為什么要設置404頁面》查看&#xff0c;今天我們講解的是網站要如何設置自己的404頁面。現在大多數空間商都有了404設置的功能&#xff0c;我們可將404頁面上傳至空間里面&#xff…

設計模式之——工廠方法模式

1、工廠方法模式&#xff08;Factory Method&#xff09;工廠方法模式分為三種&#xff1a;11、普通工廠模式&#xff0c;就是建立一個工廠類&#xff0c;對實現了同一接口的一些類進行實例的創建。首先看下關系圖&#xff1a;舉例如下&#xff1a;&#xff08;我們舉一個發送郵…

記一次性能故障排查

最近一次公司服務出了一些性能的問題&#xff0c;主要是內存不釋放。領到任務后就開始展開工作。項目是用.net core 6寫的&#xff0c;在框上應該不會有什么問題&#xff0c;這是大背景。另外服務是部署在k8s上的&#xff0c;于是就和性能測試人員&#xff0c;開發人員搭測試環…