Bitmap之1.動態高斯模糊 2.水印添加

主要記錄兩件事,1.高斯模糊毛玻璃效果;2.圖片水印的添加

1.高斯模糊

什麼是高斯模糊,高斯模糊(英語:Gaussian Blur),也叫高斯平滑,是在Adobe Photoshop、GIMP以及Paint.NET等圖像處理軟件中廣泛使用的處理效果,通常用它來減少圖像噪聲以及降低細節層次。這種模糊技術生成的圖像,其視覺效果就像是經過一個半透明屏幕在觀察圖像,這與鏡頭焦外成像效果散景以及普通照明陰影中的效果都明顯不同。看不懂?我也看不懂,來看下成品gif看下吧

1.1靜態高斯模糊

沒錯,就是毛玻璃,就是模糊,常見的就是網易雲音樂播放的後面的那個背景。我們使用使用官方提供在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<radius<=25f
        gaussianBlue.setInput(allIn); // 設置輸入腳本類型
        gaussianBlue.forEach(allOut); // 執行高斯模糊算法,並將結果填入輸出腳本類型中
        allOut.copyTo(output); // 將輸出內存編碼爲Bitmap,圖片大小必須注意
        rs.destroy(); // 關閉RenderScript對象,API>=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);

1.2動態高斯模糊

如果我們想通過一個滑動條控制圖片的模糊程度,這時候最簡單的辦法就有了,我們根據seekbar的值去改變模糊半徑,然後重新走一遍模糊算法,得到bitmap再去設置給ImageView,但是這有一個很大的弊端,那就是滑動條數值變化很快,而算法一遍一遍的加載這樣會大大增加內存消耗,畢竟算法跑的那麼頻繁,會導致卡頓,於是有了一種更巧妙的辦法,我們先得到一個模糊程度最大的圖,然後在其上面覆蓋一張原圖,當seekbar拖動的時候,我們只需要修改原圖的透明度,就ok啦,原圖完全可見時就是原圖,原圖完全不可見的時候,就是模糊程度最高的時候。代碼很簡單,不作講解,完整代碼如下。

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout 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=".BlurActivity">

	<ImageView
		android:id="@+id/iv_blur"
		android:layout_width="match_parent"
		android:layout_height="match_parent" />

	<ImageView
		android:id="@+id/iv_src"
		android:layout_width="match_parent"
		android:layout_height="match_parent"
		android:src="@drawable/pic" />

	<SeekBar
		android:id="@+id/sb"
		android:layout_width="match_parent"
		android:layout_height="wrap_content"
		android:layout_alignParentBottom="true"
		android:layout_gravity="bottom"
		android:layout_margin="30dp"
		android:max="255"
		android:min="0" />

</FrameLayout>
    @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<radius<=25f
        gaussianBlue.setInput(allIn); // 設置輸入腳本類型
        gaussianBlue.forEach(allOut); // 執行高斯模糊算法,並將結果填入輸出腳本類型中
        allOut.copyTo(output); // 將輸出內存編碼爲Bitmap,圖片大小必須注意
        rs.destroy(); // 關閉RenderScript對象,API>=23則使用rs.releaseAllContexts()
        return output;
    }

2.水印的實現

先上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);
            }
        });
    }

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章