paint的色彩矩阵以及滤镜效果

来源:互联网 发布:nginx windows 重启 编辑:程序博客网 时间:2024/06/05 18:03

色彩矩阵


对于色彩的存储,Bitmap类采用了一个32位的数值来存储。红、绿、蓝、透明各占8位,每一个色彩分量的取值范围为0-255,。透明度为0表示完全透明,为255表示完全可见。

1.色彩信息的矩阵表示:
四阶表示
由于一个色彩信息包含R、G、B、Alpha信息,所以,我们必然要使用一个4阶色彩变换矩阵来修改色彩的每一个分量值:

注意:对于色彩变换矩阵,这里的色彩顺序是R、G、B、A而不是A、R、G、B!!!
如果想将色彩(0,255,0,255)更改为半透明时,可以使用下面的的矩阵运算来表示:
这里写图片描述

为什么使用五阶矩阵
四阶矩阵固然可以改变RGBA的值,但是如果要在某个分量上增加一个值之类的操作,这就不是四阶矩阵能解决的了,这时需要五阶矩阵,多的一阶用于平移操作。
下面是一个变换要求:1,。红色分量值变为原来的两倍,绿色分量值在原有的基础上增加100,矩阵变换如下:
这里写图片描述

Android中的矩阵

1.简述
Android中的色彩矩阵是用ColorMatrics类来表示的,使用方式如下:

// 生成色彩矩阵    ColorMatrix colorMatrix = new ColorMatrix(new float[]{            1, 0, 0, 0, 0,            0, 1, 0, 0, 0,            0, 0, 1, 0, 0,            0, 0, 0, 0.5, 0,    });    mPaint.setColorFilter(new ColorMatrixColorFilter(colorMatrix));  

2.实例1(单个颜色的蓝色通道)

public class MyView8 extends View {    private Paint paint;    public MyView8(Context context) {        super(context);        init();    }    public MyView8(Context context, @Nullable AttributeSet attrs) {        super(context, attrs);        init();    }    public void init()    {        paint=new Paint();        paint.setARGB(255,200,100,100);        paint.setAntiAlias(true);    }    @Override    protected void onDraw(Canvas canvas) {        //super.onDraw(canvas);        canvas.drawRect(0,0,500,500,paint);        canvas.translate(550,0);        // 生成色彩矩阵        ColorMatrix colorMatrix = new ColorMatrix(new float[]{                0, 0, 0, 0, 0,                0, 0, 0, 0, 0,                0, 0, 1, 0, 0,                0, 0, 0, 1, 0,        });        paint.setColorFilter(new ColorMatrixColorFilter(colorMatrix));        canvas.drawRect(0,0,500,500,paint);    }}

效果如下:
这里写图片描述

2.实例2(图片多颜色的蓝色通道)

public class MyView8 extends View {    private Paint paint;    private Bitmap bitmap;    private Context context;    public MyView8(Context context) {        super(context);        this.context=context;        init();    }    public MyView8(Context context, @Nullable AttributeSet attrs) {        super(context, attrs);        this.context=context;        init();    }    public void init()    {        paint=new Paint();        paint.setARGB(255,200,100,100);        paint.setAntiAlias(true);        bitmap= BitmapFactory.decodeResource(context.getResources(),R.mipmap.banner_picture02);    }    @Override    protected void onDraw(Canvas canvas) {        //super.onDraw(canvas);        // 绘制原始位图        canvas.drawBitmap(bitmap, null, new Rect(0, 0, 500, 500 * bitmap.getHeight() / bitmap.getWidth()),paint);        canvas.translate(510, 0);        // 生成色彩矩阵        ColorMatrix colorMatrix = new ColorMatrix(new float[]{                0, 0, 0, 0, 0,                0, 0, 0, 0, 0,                0, 0, 1, 0, 0,                0, 0, 0, 1, 0,        });        paint.setColorFilter(new ColorMatrixColorFilter(colorMatrix));        canvas.drawBitmap(bitmap, null, new Rect(0, 0, 500, 500 * bitmap.getHeight() / bitmap.getWidth()),paint);    }}

效果如下:
这里写图片描述

色彩的几种运算方式

1.色彩的平移运算
色彩的平移运算,实际上就是色彩的加法运算。其实就是在色彩变换矩阵的最后一行加上某个值;这样可以增加特定色彩的饱和度。
例如:适度增加绿色的饱和度。

 // 绘制原始位图        canvas.drawBitmap(bitmap, null, new Rect(0, 0, 500, 500 * bitmap.getHeight() / bitmap.getWidth()),paint);        canvas.translate(510, 0);        // 生成色彩矩阵 给绿色的饱和度增加50        ColorMatrix colorMatrix = new ColorMatrix(new float[]{                1, 0, 0, 0, 0,                0, 1, 0, 0, 50,                0, 0, 1, 0, 0,                0, 0, 0, 1, 0,        });        paint.setColorFilter(new ColorMatrixColorFilter(colorMatrix));        canvas.drawBitmap(bitmap, null, new Rect(0, 0, 500, 500 * bitmap.getHeight() / bitmap.getWidth()),paint);

效果图如下:
这里写图片描述

色彩平移除了增加饱和度这一简单应用之外,还有色彩反转。

 // 绘制原始位图        canvas.drawBitmap(bitmap, null, new Rect(0, 0, 500, 500 * bitmap.getHeight() / bitmap.getWidth()),paint);        canvas.translate(510, 0);        // 生成色彩矩阵 色彩反转        ColorMatrix colorMatrix = new ColorMatrix(new float[]{                -1,0,0,0,255,                0,-1,0,0,255,                0,0,-1,0,255,                0,0,0,1,0        });        paint.setColorFilter(new ColorMatrixColorFilter(colorMatrix));        canvas.drawBitmap(bitmap, null, new Rect(0, 0, 500, 500 * bitmap.getHeight() / bitmap.getWidth()),paint);

效果如下:
这里写图片描述

2.色彩的缩放操作

色彩的缩放运算其实就是色彩的乘法运算。在色彩矩阵对角线上的分别代表R、G、B、A的几个值,将其分别乘以指定的值。这就是所谓的缩放变换。
我们可以针对某个颜色进行缩放操作,如果对R、G、B、A同时进行缩放操作则是对亮度进行调节。

// 绘制原始位图        canvas.drawBitmap(bitmap, null, new Rect(0, 0, 500, 500 * bitmap.getHeight() / bitmap.getWidth()),paint);        canvas.translate(510, 0);        // 生成色彩矩阵 调节亮度        ColorMatrix colorMatrix = new ColorMatrix(new float[]{                1.2f, 0, 0, 0, 0,                0, 1.2f, 0, 0, 0,                0, 0, 1.2f, 0, 0,                0, 0, 0, 1.2f, 0,        });        paint.setColorFilter(new ColorMatrixColorFilter(colorMatrix));        canvas.drawBitmap(bitmap, null, new Rect(0, 0, 500, 500 * bitmap.getHeight() / bitmap.getWidth()),paint);

效果如下:
这里写图片描述

3.缩放变换的特殊应用(通道输出)
由于在色彩变换矩阵中,对角线上的数的取值范围是从0-1的,所以当取0时,这个色彩就完全不显示,所以当我们R、G都取0,而独有B取1时,就只显示了蓝色,所形成的图像也就是我们通常说的蓝色通道;

 // 绘制原始位图        canvas.drawBitmap(bitmap, null, new Rect(0, 0, 500, 500 * bitmap.getHeight() / bitmap.getWidth()),paint);        // 生成色彩矩阵 红色通道        ColorMatrix colorMatrix = new ColorMatrix(new float[]{                1, 0, 0, 0, 0,                0, 0, 0, 0, 0,                0, 0, 0, 0, 0,                0, 0, 0, 1, 0,        });        paint.setColorFilter(new ColorMatrixColorFilter(colorMatrix));        canvas.drawBitmap(bitmap, null, new Rect(510, 0, 1010, 500 * bitmap.getHeight() / bitmap.getWidth()),paint);        //色彩矩阵  绿色通道        ColorMatrix colorMatrix2= new ColorMatrix(new float[]{                0, 0, 0, 0, 0,                0, 1, 0, 0, 0,                0, 0, 0, 0, 0,                0, 0, 0, 1, 0,        });        paint.setColorFilter(new ColorMatrixColorFilter(colorMatrix2));        canvas.drawBitmap(bitmap, null, new Rect(0, 500, 500, 500+ 500* bitmap.getHeight() / bitmap.getWidth()),paint);        //色彩矩阵 蓝色通道        ColorMatrix colorMatrix3= new ColorMatrix(new float[]{                0, 0, 0, 0, 0,                0, 0, 0, 0, 0,                0, 0, 1, 0, 0,                0, 0, 0, 1, 0,        });        paint.setColorFilter(new ColorMatrixColorFilter(colorMatrix3));        canvas.drawBitmap(bitmap, null, new Rect(510, 500, 1010, 500+ 500* bitmap.getHeight() / bitmap.getWidth()),paint);

效果如下:
这里写图片描述

5.色彩的投射运算
这里写图片描述

以红色举例,红色标记的那几个元素a12,a13,a14,在运算中,是利用G、B、A的颜色值的分量来增加红色值的。
这里写图片描述

色彩投射最常见的应用就是黑白照片:

 // 绘制原始位图        canvas.drawBitmap(bitmap, null, new Rect(0, 0, 500, 500 * bitmap.getHeight() / bitmap.getWidth()),paint);        // 生成色彩矩阵  色彩投射        ColorMatrix colorMatrix = new ColorMatrix(new float[]{                0.213f, 0.715f, 0.072f, 0, 0,                0.213f, 0.715f, 0.072f, 0, 0,                0.213f, 0.715f, 0.072f, 0, 0,                0,       0,    0, 1, 0,        });        paint.setColorFilter(new ColorMatrixColorFilter(colorMatrix));        canvas.drawBitmap(bitmap, null, new Rect(510, 0, 1010, 500 * bitmap.getHeight() / bitmap.getWidth()),paint);

效果如下:
这里写图片描述

**首先了解一下去色原理:只要把RGB三通道的色彩信息设置成一样;即:R=G=B,那么图像就变成了灰色,并且,为了保证图像亮度不变,同一个通道中的R+G+B=1:如:0.213+0.715+0.072=1;
三个数字的由来:0.213, 0.715, 0.072;
按理说应该把RGB平分,都是0.3333333。三个数字应该是根据色彩光波频率及色彩心理学计算出来的(本人是这么认为,当然也查询了一些资料,目前尚未找到准确答案)。
在作用于人眼的光线中,彩色光要明显强于无色光。对一个图像按RGB平分理论给图像去色的话,人眼就会明显感觉到图像变暗了(当然可能有心理上的原因,也有光波的科学依据)另外,在彩色图像中能识别的一下细节也可能会丢失。
所以google最终给我们的颜色值就是上面的比例:0.213, 0.715, 0.072;**

色彩投射还可以应用在色彩反色上:

// 绘制原始位图        canvas.drawBitmap(bitmap, null, new Rect(0, 0, 500, 500 * bitmap.getHeight() / bitmap.getWidth()),paint);        // 生成色彩矩阵  色彩反色        ColorMatrix colorMatrix = new ColorMatrix(new float[]{                0,1,0,0,0,                1,0,0,0,0,                0,0,1,0,0,                0,0,0,1,0        });        paint.setColorFilter(new ColorMatrixColorFilter(colorMatrix));        canvas.drawBitmap(bitmap, null, new Rect(510, 0, 1010, 500 * bitmap.getHeight() / bitmap.getWidth()),paint);

效果如下:
这里写图片描述

除此,还有一个照片变旧效果:

ColorMatrix colorMatrix = new ColorMatrix(new float[]{          1/2f,1/2f,1/2f,0,0,          1/3f,1/3f,1/3f,0,0,          1/4f,1/4f,1/4f,0,0,          0,0,0,1,0  });  

效果如下:
这里写图片描述

ColorMatrix函数

前面学习的都是基于色彩矩阵的一些运算,很多情况下需要有一定的色彩设计基础,Android ColorMatrix本身自带了一些函数帮我们完成对应的操作。

1.构造函数
ColorMatrix类有三个构造函数:

ColorMatrix()  ColorMatrix(float[] src)  ColorMatrix(ColorMatrix src) 

之前的学习中,我们一直用的是第二种。第三种也可以很容易看出,是使用另一个ColorMatrix来复制一个一样的ColorMatrix实例。

2.设置、重置函数
如果我们使用的是第一种构造函数,那就得配合设置函数使用了:

public void set(ColorMatrix src)  public void set(float[] src)  public void reset() 

3.设置饱和度

上述的学习中,我们可以通过色彩矩阵局部或者整体增强饱和度,ColorMatrix类中提供了setSaturation()方法来整体设置饱和度,方法如下:

public void setSaturation(float sat) 

参数float sat:表示将当前的色彩饱和度放大的倍数,为0表示无色彩,即黑白图像;为1,饱和度不变;大于1,过度饱和状态。
下面是结合Seekbar动态改变图片饱和度的一个实例:

<?xml version="1.0" encoding="utf-8"?><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"    tools:context="com.android.ebeijia.draw1.MainActivity"    android:orientation="vertical">    <ImageView        android:id="@+id/img"        android:layout_width="fill_parent"        android:layout_height="wrap_content"        android:scaleType="centerCrop"        android:src="@mipmap/banner_picture02"        />    <LinearLayout        android:layout_marginTop="10dp"        android:layout_width="match_parent"        android:layout_height="match_parent"        android:orientation="horizontal">        <TextView            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:text="饱和度(0-20):"/>        <SeekBar            android:id="@+id/seekbar"            android:layout_width="match_parent"            android:layout_height="wrap_content"/>    </LinearLayout></LinearLayout>
public class MainActivity extends AppCompatActivity {    private SeekBar mSeekBar;    private ImageView mImageView;    private Bitmap mOriginBmp,mTempBmp;    @Override    public void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        mImageView = (ImageView) findViewById(R.id.img);        mSeekBar = (SeekBar)findViewById(R.id.seekbar);        mOriginBmp = BitmapFactory.decodeResource(getResources(), R.mipmap.banner_picture02);        mTempBmp = Bitmap.createBitmap(mOriginBmp.getWidth(), mOriginBmp.getHeight(),                Bitmap.Config.ARGB_8888);        mSeekBar.setMax(20);        mSeekBar.setProgress(1);        mSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {            @Override            public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {                Bitmap bitmap = handleColorMatrixBmp(progress);                mImageView.setImageBitmap(bitmap);            }            @Override            public void onStartTrackingTouch(SeekBar seekBar) {            }            @Override            public void onStopTrackingTouch(SeekBar seekBar) {            }        });    }    private Bitmap  handleColorMatrixBmp(int progress){        // 创建一个相同尺寸的可变的位图区,用于绘制调色后的图片        Canvas canvas = new Canvas(mTempBmp); // 得到画笔对象        Paint paint = new Paint(); // 新建paint        paint.setAntiAlias(true); // 设置抗锯齿,也即是边缘做平滑处理        ColorMatrix mSaturationMatrix = new ColorMatrix();        mSaturationMatrix.setSaturation(progress);        paint.setColorFilter(new ColorMatrixColorFilter(mSaturationMatrix));// 设置颜色变换效果        canvas.drawBitmap(mOriginBmp, 0, 0, paint); // 将颜色变化后的图片输出到新创建的位图区        // 返回新的位图,也即调色处理后的图片        return mTempBmp;    }}

这边着重需要handleColorMatrixBmp这个方法:mTempBmp是生成的一个跟原始的bitmap同样大小的空白图片,然后在设置的Paint的ColorMatrics之后,利用canvas.drawBitmap(mOriginBmp, 0, 0, paint);在原始图片的基础上应用Paint把生成的图像画在canvas上。drawBitmap()的第一个参数表示的是源图像;
效果图如下:
这里写图片描述

3.setScale色彩缩放
系统提供的方法为:

public void setScale(float rScale, float gScale, float bScale,float aScale)

四个参数分别为R、G、B、A的缩放倍数。
下面的图片以红色为主,所以试着将红色放大到1.3倍。

// 绘制原始位图        canvas.drawBitmap(bitmap, null, new Rect(0, 0, 500, 500 * bitmap.getHeight() / bitmap.getWidth()),paint);        // 生成色彩矩阵  将红色放大至1.5倍        ColorMatrix colorMatrix=new ColorMatrix();        colorMatrix.setScale(1.3f,1,1,1);        paint.setColorFilter(new ColorMatrixColorFilter(colorMatrix));        canvas.drawBitmap(bitmap, null, new Rect(510, 0, 1010, 500 * bitmap.getHeight() / bitmap.getWidth()),paint);

效果如下:
这里写图片描述

  1. setRotate——色彩旋转
    由于色彩旋转涉及到正余弦函数的计算,比较复杂,所以系统提供了方法:
/**  * 将旋转围绕某一个颜色轴旋转  * axis=0 围绕红色轴旋转  * axis=1 围绕绿色轴旋转  * axis=2 围绕蓝色轴旋转  */  public void setRotate(int axis, float degrees);

下面是一个简单的案例:

public class MainActivity extends AppCompatActivity {    private ImageView ivImg;    private SeekBar seekForRed,seekForGreen,seekForBlue;    private Bitmap bitmapOrigin,bitmapTemp;    @Override    protected void onCreate(@Nullable Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        ivImg=(ImageView)findViewById(R.id.ivImg);        seekForRed=(SeekBar)findViewById(R.id.seekForRed);        seekForGreen=(SeekBar)findViewById(R.id.seekForGreen);        seekForBlue=(SeekBar)findViewById(R.id.seekForBlue);        bitmapOrigin=BitmapFactory.decodeResource(getResources(),R.mipmap.banner_picture02);        bitmapTemp=Bitmap.createBitmap(bitmapOrigin.getWidth(), bitmapOrigin.getHeight(),                Bitmap.Config.ARGB_8888);        seekForRed.setMax(360);        seekForRed.setProgress(180);        seekForGreen.setMax(360);        seekForGreen.setProgress(180);        seekForBlue.setMax(360);        seekForBlue.setProgress(180);        seekForRed.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {            @Override            public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {                Bitmap bitmap=handleColorRotateBmp(progress,0);                ivImg.setImageBitmap(bitmap);            }            @Override            public void onStartTrackingTouch(SeekBar seekBar) {            }            @Override            public void onStopTrackingTouch(SeekBar seekBar) {            }        });        seekForGreen.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {            @Override            public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {                Bitmap bitmap=handleColorRotateBmp(progress,1);                ivImg.setImageBitmap(bitmap);            }            @Override            public void onStartTrackingTouch(SeekBar seekBar) {            }            @Override            public void onStopTrackingTouch(SeekBar seekBar) {            }        });        seekForBlue.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {            @Override            public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {                Bitmap bitmap=handleColorRotateBmp(progress,2);                ivImg.setImageBitmap(bitmap);            }            @Override            public void onStartTrackingTouch(SeekBar seekBar) {            }            @Override            public void onStopTrackingTouch(SeekBar seekBar) {            }        });    }    private Bitmap handleColorRotateBmp(int progress,int colorFlag){        // 创建一个相同尺寸的可变的位图区,用于绘制调色后的图片        Canvas canvas = new Canvas(bitmapTemp); // 得到画笔对象        Paint paint = new Paint(); // 新建paint        paint.setAntiAlias(true); // 设置抗锯齿,也即是边缘做平滑处理        ColorMatrix colorMatrix = new ColorMatrix();        colorMatrix.setRotate(colorFlag,progress-180);        paint.setColorFilter(new ColorMatrixColorFilter(colorMatrix));// 设置颜色变换效果        canvas.drawBitmap(bitmapOrigin, 0, 0, paint); // 将颜色变化后的图片输出到新创建的位图区        // 返回新的位图,也即调色处理后的图片        return bitmapTemp;    }}
<?xml version="1.0" encoding="utf-8"?><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"    tools:context="com.android.ebeijia.draw1.MainActivity"    android:orientation="vertical">    <ImageView        android:id="@+id/ivImg"        android:layout_width="wrap_content"        android:layout_height="wrap_content"        android:src="@mipmap/banner_picture02"/>    <!--Red-->    <LinearLayout        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:layout_marginTop="10dp">        <TextView            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:text="红色"/>        <SeekBar            android:id="@+id/seekForRed"            android:layout_width="match_parent"            android:layout_height="wrap_content" />    </LinearLayout>    <!--Green-->    <LinearLayout        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:layout_marginTop="10dp">        <TextView            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:text="绿色"/>        <SeekBar            android:id="@+id/seekForGreen"            android:layout_width="match_parent"            android:layout_height="wrap_content" />    </LinearLayout>    <!--Blue-->    <LinearLayout        android:layout_width="match_parent"        android:layout_height="wrap_content"        android:layout_marginTop="10dp">        <TextView            android:layout_width="wrap_content"            android:layout_height="wrap_content"            android:text="红色"/>        <SeekBar            android:id="@+id/seekForBlue"            android:layout_width="match_parent"            android:layout_height="wrap_content" />    </LinearLayout></LinearLayout>

效果如下:
这里写图片描述

使用了三个SeekBar,分别对R、G、B进行色彩进行旋转操作。