文章目錄
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);