通過渲染器Shader實現圖像變換效果


      在上一篇文章中,一起學習了通過設定畫筆風格來實現圖形變換,沒讀過的朋友可以點擊下面鏈接:

http://www.cnblogs.com/fuly550871915/p/4886455.html

     是不是覺得自己學到的知識更多了呢?那么再多學一點總沒壞處。在本篇文章中,將會一起學習通過給畫筆設定Shader屬性,實現圖形變換。並帶領讀者一起實現兩個實際例子,圖片渲染器和線性渲染器。有沒有發現我們的畫筆特別強大呢??確實,我們曾經給它設置過顏色矩陣屬性,設置過xfermode風格屬性,現在又來設定Shader屬性。好了,廢話不多說了,進入本章的主要內容吧。

一、基礎知識

      渲染效果大家都應該見到過,比如下面的這張圖片。

 

      上面是一張中規中矩的線性渲染的圖片,從藍色渲染到紅色。但是在android中,有一個很奇葩的渲染方法,叫圖片渲染。什么是圖片渲染呢??沒法說清楚,到下面的實戰項目中大家看到效果就明白了。其他的渲染,比如線性漸變,矩形漸變等都是大家熟知的了。

       對於給畫筆設定圖片渲染,首先要實例化一個圖片渲染對象,然后再設定給畫筆。示例代碼如下:

//初始化圖片渲染器
mBitmapShader = new BitmapShader(bmp, Shader.TileMode.REPEAT, Shader.TileMode.REPEAT);
//將畫筆綁定圖片渲染器
paint.setShader(mBitmapShader);

      其中bmp是一個Bitmap圖片,而渲染的模式有Shader.TileMode.REPEAT,Shader.TileMode.CLAMP,Shader.TileMode.MIRROR。分別是重復模式,拉伸邊緣像素模式,鏡像模式。這幾種模式你現在無法理解,到實戰項目中,我們會演示效果,到時候你就明白了。上面的代碼是x方向和y方向都設定為重復渲染模式。

 

      對於給畫筆設定普通的渲染器,比如說線性渲染,一句代碼搞定,如下:

//方法new LinearGradient(x0, y0, x1, y1, color0, color1, tile)
//設置線性渲染器,(x0,y0)是起點坐標,(x1,y1)是終點坐標
//從color0渲染到color1,當然這里的color也可以改為透明度
//最后的tile表示選擇一種渲染模式
paint.setShader(new LinearGradient(0, 0, 300, 300, Color.BLUE, Color.RED, Shader.TileMode.CLAMP));

     注釋中說的很清楚了,至於渲染模式,上面也有解釋。

     好了,然我們快進入實戰,看看這些方法到底是怎么用的吧。

二、圖片渲染實戰

      這里我們需要一張圖片資源,讀者可以替換為自己的圖片,建議圖片選的小一點盡量。這樣效果看起來方便一些。在上一篇文章的代碼基礎上我們接着往下寫。首先新建MyShaderView繼承自view。代碼中的注釋很詳細,在代碼中用畫筆綁定了圖片渲染器然后畫了一個圓出來。如下:

 1 package com.fuly.image;
 2 
 3 import android.content.Context;
 4 import android.graphics.Bitmap;
 5 import android.graphics.BitmapFactory;
 6 import android.graphics.BitmapShader;
 7 import android.graphics.Canvas;
 8 import android.graphics.Paint;
 9 import android.graphics.Shader;
10 import android.util.AttributeSet;
11 import android.view.View;
12 /**
13  * 利用渲染器shader變換圖像
14  * @author fuly1314
15  *
16  */
17 public class MyShaderView extends View{
18     
19     private Bitmap bmp;
20     private Paint paint = new Paint();
21     private BitmapShader mBitmapShader;//圖片渲染器
22     
23 
24     public MyShaderView(Context context) {
25         super(context);    
26     }
27     public MyShaderView(Context context, AttributeSet attrs) {
28         super(context, attrs);
29 
30     }
31     public MyShaderView(Context context, AttributeSet attrs, int defStyleAttr) {
32         super(context, attrs, defStyleAttr);
33     }
34     
35 
36     protected void onDraw(Canvas canvas) {
37 
38         super.onDraw(canvas);
39         
40         bmp = BitmapFactory.decodeResource(getResources(), R.drawable.hudie2);
41         //初始化圖片渲染器
42         mBitmapShader = new BitmapShader(bmp, Shader.TileMode.REPEAT, Shader.TileMode.REPEAT);
43         //將畫筆綁定圖片渲染器
44         paint.setShader(mBitmapShader);
45         
46         canvas.drawCircle(150, 150, 300, paint);
47         
48     }
49 
50 }

       然后新建布局imageshader.xml把這個view裝進去。如下:

 1 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 2     xmlns:tools="http://schemas.android.com/tools"
 3     android:layout_width="match_parent"
 4     android:layout_height="match_parent"
 5     android:orientation="vertical" 
 6     android:gravity="center">
 7 
 8   <com.fuly.image.MyShaderView
 9       android:layout_marginLeft="20dp"
10       android:layout_marginTop="20dp"
11       android:layout_width="wrap_content"
12       android:layout_height="wrap_content"/>
13 
14 </LinearLayout>

     接着新建活動ShaderActivity顯示這個布局,此時別忘記給這個活動注冊。如下:

 1 package com.fuly.image;
 2 
 3 import android.app.Activity;
 4 import android.os.Bundle;
 5 
 6 public class ShaderActivity extends Activity{
 7     
 8     
 9     protected void onCreate(Bundle savedInstanceState) {
10     
11         super.onCreate(savedInstanceState);
12         setContentView(R.layout.imageshader);
13         
14     }
15 
16 }

    最后給MainActivity中的按鈕添加監聽事件,如下:

 1 package com.fuly.image;
 2 
 3 import android.os.Bundle;
 4 import android.view.View;
 5 import android.app.Activity;
 6 import android.content.Intent;
 7 
 8 
 9 public class MainActivity extends Activity {
10 
11    
12     protected void onCreate(Bundle savedInstanceState) {
13         super.onCreate(savedInstanceState);
14         setContentView(R.layout.activity_main);
15     }
16      
17     //下面是按鈕事件
18     public void btnMatrix(View v){
19         Intent intent = new Intent(this,MatrixActivity.class);
20         startActivity(intent);
21     }
22     public void btnXFermode(View v){
23         Intent intent = new Intent(this,XFermodeActivity.class);
24         startActivity(intent);
25     }
26     public void btnShader(View v){
27         Intent intent = new Intent(this,ShaderActivity.class);
28         startActivity(intent);
29     }
30     
31    
32 }

      好了,快運行程序看看效果。如下:

      注意在圖片渲染中,我們使用了REPEAT模式。從效果來看其實就是用我們的這張小蝴蝶的圖片來填充我們畫的圓,如果發現圖片太小,填充不完怎么辦,那就不斷重復畫這個圖片直到填充完為止。這下你明白什么是REPEAT模式了吧。下面我們將MyShaderView的第42行,即實例化圖片渲染的代碼改變一下,變成MIRROR模式,如下:

 //初始化圖片渲染器
42 mBitmapShader = new BitmapShader(bmp, Shader.TileMode.MIRROR, Shader.TileMode.MIRROR);

     只改這一個地方,然后重新運行程序,效果圖如下:

      有沒有發現跟REPEAT模式有什么不同?也就是說在填充的時候,發現圖片不夠大,那就畫它的鏡像(即在鏡子里的圖像,也就是倒影)來填充,如此反復,直到填充完為止。這既是MIRROR模式的效果了。那么CLAMP模式又是什么樣的呢?我們仍然修改第42行代碼,如下:

//初始化圖片渲染器
mBitmapShader = new BitmapShader(bmp, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);

      為了讓顯示效果更好一點,我們替換為另外一張圖片,此時要修改相應的獲取bmp的代碼。這個即修改第40的代碼即可。我就不貼出來了。然后運行程序,效果如下:

                        

      上圖中,左側是效果圖,右側就是我所使用的那張原圖。可以看到,CLAMP模式的意思其實當發現原來圖片不夠填充的時候,就拉伸原圖片邊緣的像素直到填充滿為止。

 

     好了。經過那上面的三輪實驗,相信你不經對着三種模式有所了解,而且對圖片渲染也很熟悉了。那么下面就讓我們來實現常規的渲染,比如線性漸變。

 

三、線性漸變實戰

      在這里簡單起見,我就簡單的單純的只實現一個線性漸變的效果吧。目的是為讓大家看看,普通的渲染是怎么綁定畫筆的。目前來說,掌握這個就達到這篇文章的目的了。首先老規矩,新建LinearShaderView繼承自view,在這里的代碼中,將為畫筆綁定一個線性漸變的渲染,並用它畫出一個矩形。代碼如下:

 1 package com.fuly.image;
 2 
 3 import android.content.Context;
 4 import android.graphics.Bitmap;
 5 import android.graphics.BitmapFactory;
 6 import android.graphics.BitmapShader;
 7 import android.graphics.Canvas;
 8 import android.graphics.Color;
 9 import android.graphics.LinearGradient;
10 import android.graphics.Paint;
11 import android.graphics.Shader;
12 import android.util.AttributeSet;
13 import android.view.View;
14 /**
15  * 利用線性渲染器shader變換圖像
16  * @author fuly1314
17  *
18  */
19 public class LinearShaderView extends View{
20     
21     private Paint paint = new Paint();
22 
23     
24 
25     public LinearShaderView(Context context) {
26         super(context);    
27     }
28     public LinearShaderView(Context context, AttributeSet attrs) {
29         super(context, attrs);
30 
31     }
32     public LinearShaderView(Context context, AttributeSet attrs, int defStyleAttr) {
33         super(context, attrs, defStyleAttr);
34     }
35     
36 
37     protected void onDraw(Canvas canvas) {
38 
39         super.onDraw(canvas);
40         //方法new LinearGradient(x0, y0, x1, y1, color0, color1, tile)
41         //設置線性渲染器,(x0,y0)是起點坐標,(x1,y1)是終點坐標
42         //從color0渲染到color1,當然這里的color也可以改為透明度
43         //最后的tile表示選擇一種渲染模式
44         paint.setShader(new LinearGradient(0, 0, 300, 300, Color.BLUE, Color.RED, 
45                 Shader.TileMode.CLAMP));
46         //初始化圖片渲染器
47 
48         canvas.drawRect(0, 0, 300, 300, paint);
49         
50     }
51 
52 }

         新建布局imagelinearshader.xml,將這個布局加進去,代碼如下:

 1 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 2     xmlns:tools="http://schemas.android.com/tools"
 3     android:layout_width="match_parent"
 4     android:layout_height="match_parent"
 5     android:orientation="vertical" 
 6     android:gravity="center">
 7 
 8   <com.fuly.image.LinearShaderView
 9       
10       android:layout_width="wrap_content"
11       android:layout_height="wrap_content"/>
12 
13 </LinearLayout>

      然后該新建活動了LinearShaderActivity用來顯示我們的布局,代碼如下:

 1 package com.fuly.image;
 2 
 3 import android.app.Activity;
 4 import android.os.Bundle;
 5 
 6 public class LinearShaderActivity extends Activity{
 7     
 8     
 9     protected void onCreate(Bundle savedInstanceState) {
10     
11         super.onCreate(savedInstanceState);
12         setContentView(R.layout.imagelinearshader);
13         
14     }
15 
16 }

      最后呢,就是MainActivity中的按鈕事件了,如下:

 1 package com.fuly.image;
 2 
 3 import android.os.Bundle;
 4 import android.view.View;
 5 import android.app.Activity;
 6 import android.content.Intent;
 7 
 8 
 9 public class MainActivity extends Activity {
10 
11    
12     protected void onCreate(Bundle savedInstanceState) {
13         super.onCreate(savedInstanceState);
14         setContentView(R.layout.activity_main);
15     }
16      
17     //下面是按鈕事件
18     public void btnMatrix(View v){
19         Intent intent = new Intent(this,MatrixActivity.class);
20         startActivity(intent);
21     }
22     public void btnXFermode(View v){
23         Intent intent = new Intent(this,XFermodeActivity.class);
24         startActivity(intent);
25     }
26     public void btnShader(View v){
27         Intent intent = new Intent(this,ShaderActivity.class);
28         startActivity(intent);
29     }
30     public void btnLShader(View v){
31         Intent intent = new Intent(this,LinearShaderActivity.class);
32         startActivity(intent);
33     }
34   
35    
36 }

     然后運行程序,效果如下:

      好了,至此,你有沒有學會怎么給畫筆綁定普通的渲染呢?上面實現的效果比較簡單,其實利用線性漸變結合xfermode風格可以實現常見的水面倒影效果,這個有興趣可以自己百度下相關案例。

     不知道你有沒有發現,不知不覺間我們已經實現了4個按鈕的效果了。還有最后一個按鈕“像素塊實驗”沒有實現了,下一篇文章中,我們就來看看這個效果吧。然后這個圖形變換基礎系列文章就完結了。

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM