当前位置: 首页 > 科技观察

使用RenderScript实现高斯模糊(磨砂玻璃-磨砂)效果

时间:2023-03-14 12:36:20 科技观察

前言在浏览instagram的时候,无意间发现instagram的对话框设计很有意思,如下图:它的对话框背景是其实是磨砂玻璃效果,我觉得好美啊,嗯,戴乐格和迪丽热巴都很美#128514;。看到这么好的效果,我们当然要动手了,自己实现类似的效果。最终实现效果如下图所示:分别实现了对话框背景的虚化和虚化程度的手动调整。实现方法比较刚开始想实现毛玻璃效果的时候,一头雾水,不知如何下手。谢天谢地,有***谷歌。搜索了一下,发现常见的实现方式有4种,分别是:RenderScriptJava算法NDK算法OpenGL处理??整张图片这么大的计算量,openGL的性能是最好的,用java实现肯定最不好。RenderScript和NDK的性能不相上下,但是你知道吗,我和NDK和openGL没有任何关系。综合考虑,RenderScript应该是最合适的。但并不代表RenderScript就完全没有问题:模糊半径(radius)越大,对性能的要求就越高,模糊半径不能超过25,所以不可能得到很模糊的画面。ScriptIntrinsicBlur是在API17才引入的,如果需要在Android4.2以下的设备上实现,需要引入RenderScriptSupportLibrary。当然,安装包的大小也会相应增加。RenderScript实现首先在app目录下的build.gradle文件中添加如下代码:defaultConfig{applicationId"io.github.marktony.gaussianblur"minSdkVersion19targetSdkVersion25versionCode1versionName"1.0"renderscriptTargetApi19renderscriptSupportModeEnabledtrue}RenderScriptIntrinsics提供了一些操作类可以帮助我们快速实现各种图像处理,比如ScriptIntrinsicBlur,可以简单高效的实现高斯模糊效果。packageio.github.marktony.gaussianblur;importandroid.content.Context;importandroid.graphics.Bitmap;importandroid.support.annotation.IntRange;importandroid.support.annotation.NonNull;importandroid.support.v8.renderscript.Allocation;importandroid.support。v8.renderscript.Element;importandroid.support.v8.renderscript.RenderScript;importandroid.support.v8.renderscript.ScriptIntrinsicBlur;publicclassRenderScriptGaussianBlur{privateRenderScriptrenderScript;publicRenderScriptGaussianBlur(@NonNullContextcontext){this.renderScript=RenderScript.create(context);}publicBitmapgaussianBlur(@IntRange(from=1,to=25)intradius,Bitmaporiginal){Allocationinput=Allocation.createFromBitmap(renderScript,original);Allocationoutput=Allocation.createTyped(renderScript,input.getType());ScriptIntrinsicBlurscriptIntrinsicBlur=ScriptIntrinsicBlur.create(renderScript,Element.U8_4(renderScript));scriptIntrinsicBlur.setRadius(radius);scriptIntrinsicBlur.setInput(input);scriptIntrinsicBlur.forEach(output);output.copyTo(original);returnoriginal;}}然后就可以直接使用RenderScriptGaussianBlur了,开心的根据SeekBar的值实现不同程度的模糊packageio.github.marktony.gaussianblur;importandroid.content.DialogInterface;importandroid.graphics.Bitmap;importandroid.graphics.BitmapFactory;importandroid.support.v7.app.AlertDialog;importandroid.support.v7.app.AppCompatActivity;importandroid.os。捆绑包;importandroid.util.Log;importandroid.view.View;importandroid.view.Window;importandroid.view.WindowManager;importandroid.widget.FrameLayout;importandroid.widget.ImageView;importandroid.widget.LinearLayout;importandroid.widget.SeekBar;importandroid.widget.TextView;publicclassMainActivityextendsAppCompatActivity{privateImageViewimageView;privateImageViewcontainer;privateLinearLayoutlayout;privateTextViewtextViewProgress;privateRenderScriptGaussianBlurblur;@OverrideprotectedvoidonCreate(BundlesavedInstanceState){super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);imageView=(ImageView).imageView);container=(ImageView)findViewById(R.id.container);container.setVisibility(View.GONE);layout=(LinearLayout)findViewById(R.id.layout);layout.setVisibility(View.VISIBLE);SeekBarseekBar=(SeekBar)findViewById(R.id.seekBar);textViewProgress=(TextView)findViewById(R.id.textViewProgress);TextViewtextViewDialog=(TextView)findViewById(R.id.textViewDialog);blur=newRenderScriptGaussianBlur(MainActivity.this);seekBar.setMax(25);seekBar.setOnSeekBarChangeListener(newSeekBar.OnSeekBarChangeListener(){@OverridepublicvoidonProgressChanged(SeekBarseekBar,intprogress,booleanfromUser){textViewProgress.setText(String.valueOf(progress));}@OverridepublicvoidonStartTrackingTouch(SeekBarseekBar){}@OverridepublicvoidonStopTrackingTouch(SeekBarseekBar){intradius=seekBar.getProgress();if(radius<1){radius=1;}Bitmapbitmap=BitmapFactory.decodeResource(getResources(),R.drawable.image);imageView.setImageBitmap(blur.gaussianBlur(radius,bitmap));}});textViewDialog.setOnClickListener(newView.OnClickListener(){@覆盖publicvoidonClick(Viewv){container.setVisibility(View.VISIBLE);layout.setDrawingCacheEnabled(true);layout.setDrawingCacheQuality(View.DRAWING_CACHE_QUALITY_LOW);Bitmapbitmap=layout.getDrawingCache();container.setImageBitmap(blur.gaussianBlur(25,bitmap));layout.setVisibility(View.INVISIBLE);AlertDialogdialog=newAlertDialog.Builder(MainActivity.this).create();dialog.setTitle("标题");dialog.setMessage("消息");dialog.setButton(DialogInterface.BUTTON_POSITIVE,"OK",newDialogInterface.OnClickListener(){@OverridepublicvoidonClick(DialogInterfacedialog,intwhich){dialog.dismiss();}});dialog.setButton(DialogInterface.BUTTON_NEGATIVE,"Cancel",newDialogInterface.OnClickListener(){@OverridepublicvoidonClick(DialogInterfacedialog,intwhich){}});dialog.setOnCancelListener(newDialogInterface.OnCancelListener(){@OverridepublicvoidonCancel(DialogInterfacedialog){}});dialog.setOnCancelListener(newDialogInterface.OnCancelListener(){@OverridepublicvoiconCancel(DialogInterfacedialog){container.setVisibility(View.GONE);layout.setVisibility(View.VISIBLE);}});dialog.show();}});}}在代码中做了一些视图可见性操作相对简单的。我相信你能理解。和instagram中dialog的实现有点不同。我没有截取整个页面的位图,只截取了actionbar下面的内容。如果一定要达到同样的效果,调整页面布局即可。这里不多说。是不是很简单?轮子除了RenderScript,还有一些优秀的轮子:500px-android-blurBlurryandroid-stackblurFastBlur:JavaAlgorithmImplementationBlurTestAndroid对不同类库的实现,采用的算法和耗时做统计比较,各位也可以下载它的demoapp自己测试。此处的示例代码:GaussianBlur