zoukankan      html  css  js  c++  java
  • 通过渲染器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个按钮的效果了。还有最后一个按钮“像素块实验”没有实现了,下一篇文章中,我们就来看看这个效果吧。然后这个图形变换基础系列文章就完结了。

     

  • 相关阅读:
    Codeforces Round #599 (Div. 2) B2. Character Swap (Hard Version) 构造
    Codeforces Round #598 (Div. 3) F Equalizing Two Strings(构造题)
    codeforces round # 384 div2 B Chloe and the sequence 神奇二进制找规律题
    codeforces round #384 div2 C Vladik and fractions(构造)
    线段树板子
    Codeforces Round #616 (Div. 2) D (找规律题)
    codeforces round #616 div2 A (Even but not even)水题
    2017的计划清单
    回顾2016,我的简单总结
    关于ubuntu下sublime text 3 的安装和中文配置问题
  • 原文地址:https://www.cnblogs.com/fuly550871915/p/4886651.html
Copyright © 2011-2022 走看看