主要记录两件事,1.高斯模糊毛玻璃效果;2.图片水印的添加
什么是高斯模糊,高斯模糊(英语:Gaussian Blur),也叫高斯平滑,是在Adobe Photoshop、GIMP以及Paint.NET等图像处理软件中广泛使用的处理效果,通常用它来减少图像噪声以及降低细节层次。这种模糊技术生成的图像,其视觉效果就像是经过一个半透明屏幕在观察图像,这与镜头焦外成像效果散景以及普通照明阴影中的效果都明显不同。看不懂?我也看不懂,来看下成品gif看下吧
没错,就是毛玻璃,就是模糊,常见的就是网易云音乐播放的后面的那个背景。我们使用使用官方提供在Support Library中的一个工具来做,就是RenderScript,这玩意使用C99衍生语言进行脚本编写的,相较于Java性能是大大的提升,Google官方也已经给出了对应的解决方案,我们并不需要编写对应的脚本就可以使用了。
先不说动态的,我们先实现一个静态的模糊一张图。我们需要两个变量,一个是Bitmap对象,另一个就是模糊半径,模糊半径的范围在0-25,超过范围会报错,数值越大代表模糊程度越高。我们对其封装成一个方法,如下
private Bitmap blur(Bitmap bitmap, float radius) {
Bitmap output = Bitmap.createBitmap(bitmap); // 创建输出图片
RenderScript rs = RenderScript.create(this); // 构建一个RenderScript对象
ScriptIntrinsicBlur gaussianBlue = ScriptIntrinsicBlur.create(rs, Element.U8_4(rs)); // 创建高斯模糊脚本
Allocation allIn = Allocation.createFromBitmap(rs, bitmap); // 创建用于输入的脚本类型
Allocation allOut = Allocation.createFromBitmap(rs, output); // 创建用于输出的脚本类型
gaussianBlue.setRadius(radius); // 设置模糊半径,范围0f=23则使用rs.releaseAllContexts()
return output;
}
注释已经很详细了,也都是固定写法,CV大法就好,需要注意的就是模糊半径的范围是0-25,那如果25的模糊程度达不到你想要的效果怎么办,最优的解就是先压缩bitmap,然后再模糊,这样大大减小算法的速度。错误的写法就是循环模糊几次,这样只会徒增模糊算法的调用时长,代码简单,不再演示。然后我们将处理后得到的bitmap设置给ImageView就搞定了。
Bitmap srcBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.pic);
Bitmap blurBitmap = blur(srcBitmap, 25);
ivBlur.setImageBitmap(blurBitmap);
如果我们想通过一个滑动条控制图片的模糊程度,这时候最简单的办法就有了,我们根据seekbar的值去改变模糊半径,然后重新走一遍模糊算法,得到bitmap再去设置给ImageView,但是这有一个很大的弊端,那就是滑动条数值变化很快,而算法一遍一遍的加载这样会大大增加内存消耗,毕竟算法跑的那么频繁,会导致卡顿,于是有了一种更巧妙的办法,我们先得到一个模糊程度最大的图,然后在其上面覆盖一张原图,当seekbar拖动的时候,我们只需要修改原图的透明度,就ok啦,原图完全可见时就是原图,原图完全不可见的时候,就是模糊程度最高的时候。代码很简单,不作讲解,完整代码如下。
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_blur);
ivBlur = findViewById(R.id.iv_blur);
ivSrc = findViewById(R.id.iv_src);
sb = findViewById(R.id.sb);
Bitmap srcBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.pic);
Bitmap blurBitmap = blur(srcBitmap, 25);
ivBlur.setImageBitmap(blurBitmap);
sb.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
ivSrc.setImageAlpha(255 - progress);
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
}
});
}
private Bitmap blur(Bitmap bitmap, float radius) {
Bitmap output = Bitmap.createBitmap(bitmap); // 创建输出图片
RenderScript rs = RenderScript.create(this); // 构建一个RenderScript对象
ScriptIntrinsicBlur gaussianBlue = ScriptIntrinsicBlur.create(rs, Element.U8_4(rs)); // 创建高斯模糊脚本
Allocation allIn = Allocation.createFromBitmap(rs, bitmap); // 创建用于输入的脚本类型
Allocation allOut = Allocation.createFromBitmap(rs, output); // 创建用于输出的脚本类型
gaussianBlue.setRadius(radius); // 设置模糊半径,范围0f=23则使用rs.releaseAllContexts()
return output;
}
先上gif看效果
点击添加水印按钮后,会在bitmap右下角生成一个水印,这里以添加字符串为例,当然也可以添加其他bitmap,或者其他图形等等。原理也很简单,BitmapFactory解析出来的一系列Bitmap是不允许编辑的,所以我们首先利用Bitmap类新建一张空白Bitmap,大小与原来Bitmap大小相同,然后为这个空白Bitmap创建一个Canvas,首先绘制原来的Bitmap,然后绘制字符串,如果你的水印也是一个bitmap,那也可以再绘制一张bitmap。我们将整个添加水印抽成一个方法
private Bitmap addWatermark(Bitmap srcBitmap, String watermark) {
Paint paint = new Paint();
paint.setColor(Color.WHITE);
paint.setTextSize(50);
Bitmap resultBitmap = Bitmap.createBitmap(srcBitmap.getWidth(), srcBitmap.getHeight(), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(resultBitmap);
canvas.drawBitmap(srcBitmap, 0, 0, null);
float watermarkWidth = paint.measureText(watermark);
canvas.drawText(watermark, srcBitmap.getWidth() - watermarkWidth - 20, srcBitmap.getHeight() - 30, paint);
return resultBitmap;
}
当我们点击按钮后就去生成带水印的Bitmap,设置给Bitmap即可,如果想要保存Bitmap,可以使用compress保存图片,注意保存的格式,JPG是不支持透明度的有损格式,PNG是无损且带透明度的格式,根据具体需求保存不同的格式。
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_watermark);
ivWatermark = findViewById(R.id.iv_watermark);
final EditText etWatermark = findViewById(R.id.et_watermark);
Button btWatermark = findViewById(R.id.bt_watermark);
srcBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.pic);
Bitmap watermarkBitmap = addWatermark(srcBitmap, "水印");
ivWatermark.setImageBitmap(watermarkBitmap);
btWatermark.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
String string = etWatermark.getText().toString();
Bitmap watermarkBitmap = addWatermark(srcBitmap, string);
ivWatermark.setImageBitmap(watermarkBitmap);
}
});
}