当今是靠脸吃饭的时代,平时的人像、风景照、美食照等都需要加上一款优美的滤镜,才能让照片更加精致,更加吸引人,这样的照片能够让你在朋友圈更加出众,别具一格。
因此一个智能手机当然少不了一款美颜APP,在商店中有众多的美颜APP,例如我比较喜欢的:Snapseed、InterPhoto、美颜相机和美人相机等等。这些相机都拥有非常美和优秀的滤镜,正因为有这些优秀的滤镜,才能让你的照片变得更美,因此作为开发者的我们需要了解这些滤镜是怎么做出来的,一般都是通过Android自带的滤镜处理、OpenGL处理或者通过颜色RGB的滤镜处理。本文我们不介绍OpenGL,因为OpenGL确实是一个很强大的图像处理技术。
Android的滤镜效果就是对图像进行一定的过滤加工处理,一般的使用Paint设置滤镜效果分为两类:
1、Android自带的滤镜效果,而该滤镜效果可以分为:
1)模糊遮罩滤镜(BlurMaskFilter);
2)浮雕遮罩滤镜(EmbossMaskFilter)。
2、颜色RGB的滤镜处理,可以通过ColorMatrix设置。
通过Android的滤镜效果和颜色通道过滤这两种方式,可以对图像做出很好的滤镜效果,接下来学习下如何处理滤镜。
Android自带的滤镜效果
Android的滤镜处理使用paint.setMaskFilter(maskfilter)
方法设置,其中可以设置BlurMaskFilter和EmbossMaskFilter这两款滤镜,而这两款滤镜都是继承MaskFilter这个基类。
BlurMaskFilter
BlurMaskFilter:即模糊遮罩滤镜,通过该滤镜可以使图像呈现模糊效果。
使用:
public class MaskFilterView extends View {
private int progress = 10;
private Paint paint;
public MaskFilterView(Context context) {
super(context);
paint = new Paint(Paint.ANTI_ALIAS_FLAG);
}
public MaskFilterView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
paint = new Paint(Paint.ANTI_ALIAS_FLAG);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//需要关闭硬件加速(没有关闭则没效果)
setLayerType(View.LAYER_TYPE_SOFTWARE, null);
paint.setColor(Color.RED);
RectF r = new RectF(100, 100, 300, 300);
/**模糊遮罩滤镜效果
* BlurMaskFilter.Blur.INNER
* BlurMaskFilter.Blur.NORMAL
* BlurMaskFilter.Blur.OUTER
* BlurMaskFilter.Blur.SOLID
*/
paint.setMaskFilter(new BlurMaskFilter(progress, BlurMaskFilter.Blur.NORMAL));
canvas.drawRect(r , paint);
}
public void setProgress(int progress) {
if (progress <= 0){
return;
}
this.progress = progress;
postInvalidate();
}
}
复制代码
Activity:
public class MaskFilterActivity extends AppCompatActivity {
private SeekBar mSeekBar;
private MaskFilterView mFilterView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_mask_filter);
mSeekBar = (SeekBar)this.findViewById(R.id.seekBar);
mFilterView = (MaskFilterView)this.findViewById(R.id.my_view);
mSeekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
mFilterView.setProgress(progress);
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
}
});
}
}
复制代码
效果图:
在MaskFilterView的onDraw方法中,首先需要new一个BlurMaskFilter对象,我们看下其构造方法:
public BlurMaskFilter(float radius, Blur style)
radius:模糊的半径,值越大模糊就越扩散。
style:模糊滤镜使用的类型,总共有四种类型可以选择,它们分别是:
1)INNER:在图像内部产生模糊;
2)NORMAL:将整个图像模糊掉;
3)OUTER:在Alpha边界外产生一层模糊,而且将原本的图像变透明。
4)SOLID:在图像的Alpha外边界产生一层与Paint颜色一致的模糊效果,但不影响图像本身。
注意:这里我们需要需要关闭硬件加速,否则没效果。
上图效果中我们使用了INNER,接下来我们看看其他三种效果。
NORMAL:
paint.setMaskFilter(new BlurMaskFilter(progress, BlurMaskFilter.Blur.NORMAL));
canvas.drawRect(r , paint);
复制代码
OUTER:
paint.setMaskFilter(new BlurMaskFilter(progress, BlurMaskFilter.Blur.OUTER));
canvas.drawRect(r , paint);
复制代码
SOLID:
paint.setMaskFilter(new BlurMaskFilter(progress, BlurMaskFilter.Blur.SOLID));
canvas.drawRect(r , paint);
复制代码
以上便是模糊遮罩滤镜的使用了,下面我们看看浮雕遮罩滤镜的使用效果。
EmbossMaskFilter
EmbossMaskFilter(浮雕遮罩滤镜)让图像呈现一种凹凸不平的面具效果。
使用
paint.setMaskFilter(new EmbossMaskFilter(new float[]{30,30,30}, 0.2f, 20, progress));
canvas.drawBitmap(bitmap, 100, 300, paint);
复制代码
使用的时候,需要new一个EmbossMaskFilter对象,并通过paint.setMaskFilter方法设置,我们主要看EmbossMaskFilter的构造方法参数。
public EmbossMaskFilter(float[] direction, float ambient, float specular, float blurRadius)
复制代码
direction:指定长度为xxx的数组标量[x,y,z],用来指定光源的位置;
ambient:指定周边背景光源(0~1);
specular:指镜面反射系数;
blurRadius:指定模糊半径。
注意:使用EmbossMaskFilter同样需要关闭硬件加速,否则没效果。
颜色RGB的滤镜处理
颜色RGB的滤镜处理是通过ColorMatrix来实现的,即颜色矩阵,然后将ColorMatrix设置到ColorMatrixColorFilter,通过setColorFilter方法设置ColorMatrixColorFilter,这样就完成了设置颜色过滤器。setColorFilter还可以设置所有ColorFilter的子类,包括LightingColorFilter和PorterDuffColorFilter。
ColorMatrix顾名思义就是颜色矩阵,那么滤镜的所有处理效果都是通过颜色矩阵的变换实现的。所以需要读者能够熟悉基本的矩阵运算方法。如果读者目前还不认识矩阵,怎么办?没关系,接下来我们简单介绍下矩阵,以及基本的运算方法。读者可以自行参考百度百科。
矩阵
定义:
矩阵加法:
矩阵减法:
矩阵乘法:
第一个矩阵A的第一行,与第二个矩阵B的第一列的数字分别相乘,得到的结果相加,最终的值做为结果矩阵的第(1,1)位置的值(即第一行,第一列)。 同样,A矩阵的第一行与B矩阵的第二列的数字分别相乘然后相加,得到的值作为结果矩阵第(1,2)位置的值(即第一行第二列)。
色彩矩阵: 一般的色彩矩阵使用四阶表示,也就是RGBA
半透明的色彩矩阵,如:
Android色彩矩阵使用五阶矩阵
在四阶矩阵的基础上,增加一阶,而第五阶表示偏移量,上图表示红色分量值更改为原来的2倍,绿色分量增加100(1*100+100);
ColorMatrix的使用
调用红色(R),绿色增加两倍。
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//需要关闭硬件加速(没有关闭则没效果)
setLayerType(View.LAYER_TYPE_SOFTWARE, null);
paint.setColor(Color.RED);
ColorMatrix matrix = new ColorMatrix(new float[]{
0,0,0,0,0,
0,1,0,0,200,
0,0,1,0,0,
0,0,0,1,0,
});
paint.setColorFilter(new ColorMatrixColorFilter(matrix ));
canvas.drawBitmap(bitmap, null, new RectF(0, 0, 400, 400*bitmap.getHeight()/bitmap.getWidth()), paint);
}
复制代码
先new一个ColorMatrixColorFilter对象,并把ColorMatrix设置进去,setColorFilter方法把ColorMatrixColorFilter对象设置进去就可以完成设置颜色过滤器。
反相效果
ColorMatrix matrix = 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(matrix ));
canvas.drawBitmap(bitmap, null, new RectF(0, 0, 400, 400*bitmap.getHeight()/bitmap.getWidth()), paint);
复制代码
颜色增强
颜色增强:可以起到一个变亮的效果,通过矩阵缩放方式。
ColorMatrix matrix = 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(matrix ));
canvas.drawBitmap(bitmap, null, new RectF(0, 0, 400, 400*bitmap.getHeight()/bitmap.getWidth()), paint);
复制代码
黑白图片
去色原理:只要把RGB三通道的色彩信息设置成一样;即:R=G=B,那么图像就变成了灰色,并且,为了保证图像亮度不变,同一个通道中的R+G+B=1。 如:0.213+0.715+0.072=1;也就是RGB分别为:0.213,0.715,0.072; 三个数字是根据色彩光波频率及色彩心理学计算出来的。
ColorMatrix matrix = 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, 1f, 0,
});
paint.setColorFilter(new ColorMatrixColorFilter(matrix ));
canvas.drawBitmap(bitmap, null, new RectF(0, 0, 400, 400*bitmap.getHeight()/bitmap.getWidth()), paint);
复制代码
发色效果
发色效果:如红色和绿色交换——把第一行和第二行交换。
ColorMatrix matrix = new ColorMatrix(new float[]{
0,1f,0,0,0,
1f,0,0,0,0,
0,0,1f,0,0,
0,0,0,1f,0,
});
paint.setColorFilter(new ColorMatrixColorFilter(matrix ));
canvas.drawBitmap(bitmap, null, new RectF(0, 0, 400, 400*bitmap.getHeight()/bitmap.getWidth()), paint);
复制代码
复古风格
ColorMatrix matrix = 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,1f,0,
});
paint.setColorFilter(new ColorMatrixColorFilter(matrix ));
canvas.drawBitmap(bitmap, null, new RectF(0, 0, 400, 400*bitmap.getHeight()/bitmap.getWidth()), paint);
复制代码
色彩运算
ColorMatrix色彩矩阵,除了以上直接设置矩阵的方式,还可以使用ColorMatrix类的方法来设置进行色彩运算:
1)色彩的缩放运算(matrix.setScale):也就是四阶矩阵RGBA对应的乘法运算。
ColorMatrix matrix = new ColorMatrix();
//setScale(float rScale, float gScale, float bScale, float aScale)
matrix.setScale(2, 2, 2f, 1);
paint.setColorFilter(new ColorMatrixColorFilter(matrix ));
canvas.drawBitmap(bitmap, null, new RectF(0, 0, 400, 400*bitmap.getHeight()/bitmap.getWidth()), paint);
复制代码
上图中RGB都方法原来的两倍。
2)色彩的平移运算,也就是矩阵加法运算。
ColorMatrix的API
通过上面的学习我们知道ColorMatrix其强大之处在于通过矩阵运算可以实现颜色通道过滤,我们有必要学习ColorMatrix的API。
构造方法
三个构造方法,可以不需要参数,可以传一个float数组(矩阵),也可以ColorMatrix对象作为参数。
看下ColorMatrix()方法里面的reset()方法。
/**
* Set this colormatrix to identity:
* <pre>
* [ 1 0 0 0 0 - red vector
* 0 1 0 0 0 - green vector
* 0 0 1 0 0 - blue vector
* 0 0 0 1 0 ] - alpha vector
* </pre>
*/
public void reset() {
final float[] a = mArray;
Arrays.fill(a, 0);
a[0] = a[6] = a[12] = a[18] = 1;
}
复制代码
该方法主要是初始化一个矩阵,而且将RGBA都设置为1。
set方法
提供两个set方法,可以设置float数组(矩阵),也可以设置ColorMatrix对象,跟构造方法对应起来。
matrix.set(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,1f,0,
});
paint.setColorFilter(new ColorMatrixColorFilter(matrix ));
canvas.drawBitmap(bitmap, null, new RectF(0, 0, 400, 400*bitmap.getHeight()/bitmap.getWidth()), paint);
复制代码
setScale方法
设置色彩的缩放函数,上面已经介绍过该方法了.
setRotate
该方法是色彩旋转函数
axis:代表绕哪一个轴旋转,0,1,2 (0红色,1绿色,2蓝色);
degrees:旋转的度数。
该函数已经做好了矩阵的设置和运算了。
matrix.setRotate(0,progress);
paint.setColorFilter(new ColorMatrixColorFilter(matrix ));
canvas.drawBitmap(bitmap, null, new RectF(0, 0, 400, 400*bitmap.getHeight()/bitmap.getWidth()), paint);
复制代码
setSaturation
setSaturation:设置饱和度
方法内部已经运算好的了,我们只需要传一个float类型参数sat。
sat:1表示是原来不变,0表示灰色;如果大于1增加饱和度。
matrix.setSaturation(progress);
paint.setColorFilter(new ColorMatrixColorFilter(matrix ));
canvas.drawBitmap(bitmap, null, new RectF(0, 0, 400, 400*bitmap.getHeight()/bitmap.getWidth()), paint);
复制代码
setRGB2YUV
RGB转成YUV,对应的还有一个方法setYUV2RGB,也就是YUV转成RGB。
setConcat
setConcat(ColorMatrix matA, ColorMatrix matB):将颜色矩阵matA和matB复合,相当与对图片进行matA矩阵处理再进行矩阵matB处理。
matrixA.preConcat(ColorMatrix prematrix):等价于setConcat(matrixA, prematrix)
matrixA.postConcat(ColorMatrix postmatrix):等价于setConcat(prematrix,matrixA)
复制代码
ColorFilter
ColorFilter作为颜色过滤器,有三个子类来实现颜色过滤: 1)ColorMatrixColorFilter:即色彩矩阵的颜色过滤器,配合ColorMatrix使用。 2)LightingColorFilter:即光照颜色过滤器,能过滤颜色和增强色彩的方法。 3)PorterDuffColorFilter:即图形混合滤镜,该是图形学的一个理论飞跃。
上文中已经使用过了ColorMatrixColorFilter,并且配合ColorMatrix使用。接下来主要介绍LightingColorFilter和PorterDuffColorFilter。
LightingColorFilter
光照颜色过滤器,也就是ColorMatrixColorFilter的简化版本。
public LightingColorFilter(@ColorInt int mul, @ColorInt int add) {
mMul = mul;
mAdd = add;
}
复制代码
通过构造方法设置过滤颜色,参数解析:
mul:multiply,也就是乘法 ;
add:加法,也就是颜色偏移量。
paint.setColorFilter(new LightingColorFilter(0x00ff00, 0xff0000));
canvas.drawBitmap(bitmap, null, new RectF(0, 0, 400, 400*bitmap.getHeight()/bitmap.getWidth()), paint);
复制代码
PorterDuffColorFilter
public PorterDuffColorFilter(@ColorInt int color, @NonNull PorterDuff.Mode mode) {
mColor = color;
mMode = mode;
}
复制代码
color:源颜色, mode:色彩的混合模式:
1)PorterDuff.Mode.CLEAR:所绘制不会提交到画布上 ;
2)PorterDuff.Mode.SRC:显示上层绘制图片 ;
3)PorterDuff.Mode.DST:显示下层绘制图片 ;
4)PorterDuff.Mode.SRC_OVER:正常绘制显示,上下层绘制叠盖 ;
5)PorterDuff.Mode.DST_OVER:上下层都显示,下层居上显示 ;
6)PorterDuff.Mode.SRC_IN:取两层绘制交集,显示上层;
7)PorterDuff.Mode.DST_IN:取两层绘制交集。显示下层;
8)PorterDuff.Mode.SRC_OUT:取上层绘制非交集部分;
9)PorterDuff.Mode.DST_OUT:取下层绘制非交集部分;
10)PorterDuff.Mode.SRC_ATOP:取下层非交集部分与上层交集部分;
11)PorterDuff.Mode.DST_ATOP:取上层非交集部分与下层交集部分;
12)PorterDuff.Mode.XOR:异或:去除两图层交集部分;
13)PorterDuff.Mode.DARKEN:取两图层全部区域,交集部分颜色加深;
14)PorterDuff.Mode.LIGHTEN:取两图层全部,点亮交集部分颜色;
15)PorterDuff.Mode.MULTIPLY:取两图层交集部分叠加后颜色;
16)PorterDuff.Mode.SCREEN:取两图层全部区域,交集部分变为透明色。
paint.setColorFilter(new PorterDuffColorFilter(Color.BLUE, PorterDuff.Mode.SRC_IN));
canvas.drawBitmap(bitmap, null, new RectF(0, 0, 400, 400*bitmap.getHeight()/bitmap.getWidth()), paint);
复制代码