您的位置:首页 > 移动开发 > Android开发

安卓实现为图片添加滤镜功能

2016-09-09 19:44 218 查看
相机中有一个功能:滤镜。 我们一点击某种样式(怀旧色、反色、暖色等等)那么这个功能是如何实现的呢?

想要实现这个功能我们首先要了解什么是滤镜?现实中如何实现滤镜?

带着这个问题,我们开始思考。

在实际生活中,彩色透明玻璃相信大家都见过,其实这种玻璃就是一个滤镜,我们透过彩色玻璃看对面的画面,画面的整个色调就会变得跟玻璃的颜色贴近,这样就实现了滤镜功能。那么在代码中我们如何去设计和实现呢?

有了彩色玻璃这个例子,我们很容易就能想到,我们用代码写一个图层,图层用我们想要过滤的颜色填充,然后更改图层的透明度,这样不就是自己制作了一个“彩色玻璃”吗?

答案是肯定的,这样好像不能完全实现滤镜功能,比如:我们想要一个照片的底片效果,那我们用什么“彩色玻璃”去过滤呢?黑色?很明显这样不合适。那我们继续思考,“彩色玻璃”的实际原理是什么?想要弄清楚这个,我们就需要了解颜色原理了。我们这里不多提。(其实笔者也不是太懂T_T)我们只需要知道在每一个颜色值都是一个5*4的二维矩阵,例如:



这里第一行决定红色、第二行决定绿色、第三行决定蓝色、第四行决定了透明度。第五列是颜色的偏移量。

而在android中这个二维矩阵被处理成了一维矩阵!

颜色矩阵以一维数组的方式存储如下:

[ a, b, c, d, e, f, g, h, i, j, k, l, m, n, o, p, q, r, s, t ]

我们通过RGBA四个通道来直接操作对应颜色,如果会使用Photoshop就会知道有时处理图片通过控制RGBA各颜色通道来做出特殊的效果。

而图像的RGBA值则存储在一个5*1的颜色分量矩阵C中,由颜色分量矩阵C可以控制图像的颜色效果



要想改变一张图片的颜色效果,只需要改变图像的颜色分量矩阵即可。通过颜色矩阵可以很方便的修改图像的颜色分量矩阵。假设修改后的图像颜色分量矩阵为C1,则有如下图所示的颜色分量矩阵计算公式。



M是颜色矩阵,C矩阵是图片中包含的RGBA信息,R矩阵是用M应用于C之后的新的颜色分量。即通过这种形式我们可以通过改变C矩阵的值任意改变一个颜色。

过上图所示的运算可知,颜色矩阵M的第一行参数abcde决定了图像的红色成分,第二行参数fghij决定了图像的绿色成分,第三行参数klmno决定了图像的蓝色成分,第四行参数pqrst决定了图像的透明度,第五列参数ejot是颜色的偏移量。

一般情况下,我们都通过修改第5列的颜色偏移量来实现改变颜色,如下图所示的颜色矩阵M1,通过计算后可以得知该颜色矩阵的作用是使图像的红色分量和绿色分量均增加100,这样的效果就是图片泛黄(因为红色与绿色混合后得到黄色)。



除此之外,也可以通过直接对颜色值乘以某一系数而达到改变颜色分量的目的。如下图所示的颜色矩阵M2,将绿色分量放大了2倍,这样的效果就是图片泛绿色。



好了扯了这么多了,还是来点实在的,上代码:

这个是JavaActivity代码

package com.lx.androidday07;

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.ColorMatrix;
import android.graphics.ColorMatrixColorFilter;
import android.graphics.Paint;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.EditText;
import android.widget.GridLayout;
import android.widget.ImageView;
import android.widget.Toast;

public class MainActivity extends AppCompatActivity {

ImageView imageView;
GridLayout gridLayout;

//建立EditText数组用于存放创建的EditText
EditText[] editTexts;
//新建一个位图对象用于存放我们的图片源
Bitmap oriBitmap;

//定义editText的宽和高,方便动态获取
int editTextWidth, editTextHeight;

//定义一个系数数组
float[] coefficients;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);

//初始化数组
editTexts = new EditText[20];
coefficients = new float[20];

//初始化位图
oriBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.a1);
imageView = (ImageView) findViewById(R.id.imageview);
gridLayout = (GridLayout) findViewById(R.id.gridlayout);

//为imageView设置图像
imageView.setImageBitmap(oriBitmap);

//在UI线程的消息队列的尾部添加一个任务,
gridLayout.post(new Runnable() {
@Override
public void run() {
//动态获取editText的宽和高
editTextWidth = gridLayout.getWidth() / 5;
editTextHeight = gridLayout.getHeight() / 4;

//调用初始化editText方法
initEditText();
//调用初始化矩阵的方法
initMatrix();
}
});

}

/**
* 初始化EditText
*/
private void initEditText() {
for (int i = 0; i < 20; i++) {
EditText editText = new EditText(this);
editTexts[i] = editText;
gridLayout.addView(editText, editTextWidth, editTextHeight);
}
}

/**
* 初始化矩阵
*/
private void initMatrix() {
for (int i = 0; i < 20; i++) {
if (i % 6 == 0) {
editTexts[i].setText("1");
} else {
editTexts[i].setText("0");
}
}
}

/**
* 获得得到的矩阵值(用户输入的值)
*/
public void getMatrix() {
for (int i = 0; i < 20; i++) {
coefficients[i] = Float.valueOf(editTexts[i].getText().toString());
}
}

/**
* 重新设置矩阵(为原图片添加滤镜)
*/
public void setMatrix() {
//新建一个位图,并设置宽高与原图相同
Bitmap layerBitmap = Bitmap.createBitmap(oriBitmap.getWidth(), oriBitmap.getHeight(), Bitmap.Config.ARGB_8888);
//新建颜色矩阵
ColorMatrix colorMatrix = new ColorMatrix();
//把刚刚获得的矩阵传入颜色矩阵中
colorMatrix.set(coefficients);

//新建画布
Canvas canvas = new Canvas(layerBitmap);
//新建画笔
Paint paint = new Paint();
//为画笔设置颜色选择器,并把刚才得到的颜色矩阵传进去
paint.setColorFilter(new ColorMatrixColorFilter(colorMatrix));
//从画布的0,0点开始画图
canvas.drawBitmap(oriBitmap, 0, 0, paint);
//重新设置imageView
imageView.setImageBitmap(layerBitmap);
}

/**
* 改变应用按钮响应事件
* @param view
*/
public void apply(View view) {
Toast.makeText(MainActivity.this, "应用改变", Toast.LENGTH_SHORT).show();
getMatrix();
setMatrix();
}

/**
* 重置按钮响应事件
* @param view
*/
public void reset(View view) {
Toast.makeText(MainActivity.this, "重置改变", Toast.LENGTH_SHORT).show();
initMatrix();
getMatrix();
setMatrix();
}
}


这个是布局文件:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:orientation="vertical"
android:layout_height="match_parent">

<ImageView
android:id="@+id/imageview"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="2.0"/>

<GridLayout
android:id="@+id/gridlayout"
android:layout_width="match_parent"
android:layout_weight="3.0"
android:columnCount="5"
android:rowCount="4"
android:layout_height="0dp">

</GridLayout>

<LinearLayout
android:layout_width="match_parent"
android:orientation="horizontal"
android:layout_height="wrap_content">
<Button
android:layout_width="0dp"
android:layout_weight="1.0"
android:text="应用"
android:onClick="apply"
android:textSize="24sp"
android:layout_height="wrap_content" />
<Button
android:layout_width="0dp"
android:text="重置"
android:onClick="reset"
android:textSize="24sp"
android:layout_weight="1.0"
android:layout_height="wrap_content" />
</LinearLayout>
</LinearLayout>


把这两个代码复制进项目里就能直接运行了,当然布局我没有怎么弄,只实现了两个按钮完成它的设置跟重置,如果你们有兴趣可以封装一些矩阵值,把它设置到按钮中,点击按钮,一键换装。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  android 图片