Android 圖片裁剪之三劍式(二)

1.PorterDuffXfermode 簡介

《Android 圖片裁剪之三劍式(一)》中講述了圖片裁剪的2種方式(clipPath/bitmaShader),本文探索最後一式裁剪:PorterDuffXfermode 。
PorterDuffXfermode 是畫布繪製的合成模式,也就是兩張圖像混合後的顯示模式,與setColorFilter很相似,我們可以使用它畫出各種神奇的效果。

2.PorterDuffXfermode 類型介紹

API中爲我們提供了18中模式:

  • Mode.CLEAR 清除圖像
  • Mode.SRC 只顯示源圖像(上層圖片)
  • Mode.DST 只顯示目標圖像(下層圖片)
  • Mode.SRC_IN 只在源圖像和目標圖像相交的地方繪製【源圖像】
  • Mode.DST_IN 只在源圖像和目標圖像相交的地方繪製【目標圖像】
  • Mode.SRC_OUT 只在源圖像和目標圖像不相交的地方繪製【源圖像】
  • Mode.DST_OUT 只在源圖像和目標圖像不相交的地方繪製【目標圖像】
  • Mode.SRC_ATOP 在源圖像和目標圖像相交的地方繪製【源圖像】,在不相交的地方繪製【目標圖像】
  • Mode.DST_ATOP 在源圖像和目標圖像相交的地方繪製【目標圖像】,在不相交的地方繪製【源圖像】
  • Mode.SRC_OVER 將源圖像放在目標圖像上方
  • Mode.DST_OVER 將目標圖像放在源圖像上方
  • Mode.XOR 在源圖像和目標圖像相交的地方之外繪製它們
  • Mode.DARKEN 變暗,較深的顏色覆蓋較淺的顏色,若兩者深淺程度相同則混合
  • Mode.LIGHTEN 變亮,與DARKEN相反
  • Mode.OVERLAY 疊加
  • Mode.SCREEN 濾色,色調均和,保留兩個圖層中較白的部分,較暗的部分被遮蓋
  • Mode.MULTIPLY 正片疊底,源圖像素顏色值乘以目標圖像素顏色值除以255得到混合後圖像像素顏色值
  • Mode.ADD 飽和相加,對圖像飽和度進行相加
    網上效果圖:

    我測試了常見的幾種:

3.PorterDuffXfermode 使用場景

圓形頭像

主要採用了SRC_IN模式來實現,圖標圖片是圓形bitmap,源頭像是具體圖片,先看效果:

主要代碼:

class XfermodeImage(context: Context?, attrs: AttributeSet?) : View(context, attrs), Runnable {

    private var mPaint = Paint()
    private var bitmap = BitmapFactory.decodeResource(resources, com.anan.testkotlin.R.mipmap.ic_launcher)

    private var innerRadius = 0
    private var halfHeight = height / 2

    private var mHandler = android.os.Handler()

    private var xfermode = PorterDuffXfermode(PorterDuff.Mode.SRC_IN)

    init {
        mPaint.isAntiAlias = true
        mPaint.style = Paint.Style.FILL
        //設定是否使用圖像抖動處理,會使繪製出來的圖片顏色更加平滑和飽滿,圖像更加清晰
        mPaint.isDither = true
        //加快顯示速度,本設置項依賴於dither和xfermode的設置
        mPaint.isFilterBitmap = true
    }

    /**
     * 設置圖片
     */
    fun setImageResource(drawable: Int) {
        bitmap = BitmapFactory.decodeResource(resources, drawable)
        invalidate()
    }

    override fun onDraw(canvas: Canvas?) {
        super.onDraw(canvas)

        /**
         * 設置View的離屏緩衝。在繪圖的時候新建一個“層”,所有的操作都在該層而不會影響該層以外的圖像
         * 必須設置,否則設置的PorterDuffXfermode會無效
         */
        val sc = canvas!!.saveLayer(0f, 0f, width.toFloat(), height.toFloat(), mPaint, Canvas.ALL_SAVE_FLAG)

        halfHeight = height / 2

        // 先畫目標圖,再畫原圖
        if (bitmap != null) {
            
            if (innerRadius > 0 && innerRadius < halfHeight) {
                canvas?.drawBitmap(getCircleBitmap(innerRadius), 0f, 0f, mPaint)
                mPaint.xfermode = xfermode
            }

            canvas?.drawBitmap(bitmap, 0f, 0f, mPaint)
            mPaint.xfermode = null
        }

        /**
         * 還原畫布,與canvas.saveLayer配套使用
         */
        canvas.restoreToCount(sc)
    }

    private fun getCircleBitmap(radius: Int): Bitmap {
        val bm = Bitmap.createBitmap(height, height, Bitmap.Config.ARGB_8888)
        val c = Canvas(bm)
        val p = Paint(Paint.ANTI_ALIAS_FLAG)
        p.color = Color.WHITE
        c.drawCircle(halfHeight.toFloat(), halfHeight.toFloat(), radius.toFloat(), p)
        return bm
    }


    /**
     * 開啓動畫
     */
    fun startScaleAnim(drawable: Int) {
        bitmap = BitmapFactory.decodeResource(resources, drawable)
        innerRadius = height / 10
        invalidate()

        mHandler.postDelayed(this, 100)
    }


    override fun run() {
        if (innerRadius < halfHeight) run {
            innerRadius = innerRadius + height / 10
            postInvalidate()
            handler.postDelayed(this, 100)
        } else {
            innerRadius = halfHeight
            postInvalidate()
        }
    }
}
疊加圖片還可以實現橡皮擦和加載效果等

這裏實現就不具體敘述了

4.PorterDuffXfermode 在Drawable中的應用

主要是setColorFilter(@ColorInt int color, @NonNull PorterDuff.Mode mode)方法,該方法給圖片添加一個顏色圖層,實現混合效果。
如:圖片變暗

BitmapDrawable bitmapDrawable = new BitmapDrawable(getResources(), coverImageBitmap);
 //進行對應像素的比較,取較暗值,如果色值相同則進行混合
bitmapDrawable.mutate().setColorFilter(ContextCompat.getColor(mContext, R.color.me_forground), PorterDuff.Mode.DARKEN);
 iv_head_bg.setBackground(bitmapDrawable);
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章