概述
今天回来看书突然看到了一个很有意思的效果,就立马动手写了一下,结合贝塞尔曲线简介中捕捉手指路径的示例,我就继续敲了一下~话不多说,我们先来看效果
运行结果
图像有点大~耐心等待一下哦
ezgif.com-gif-maker.gif
最喜欢的两个新疆美女~
PorterDuff.Mode.SRC_OUT
这里我们主要用到了混合模式中的PorterDuff.Mode.SRC_OUT,因为混合模式比较多,等全部了解之后会写一篇完整的混合模式介绍,这里我们只介绍这个模式下的效果
SRC_OUT的特性可以概括为,这里我们把带有Path的空白Bitmap称为目标图像,迪丽热巴的图像称为源图像,当目标图像有图像时结果显示空白像素,当目标图像没有图像结果时显示源图像。因此这个效果用来作为橡皮擦效果是最完美不过了~利用该特性我们开始进行动手写代码
绘制步骤
- 首先我们需要准备好两张美女的图片
- 绘制底层的图片 - 哈尼克孜
- 调用saveLayer()保存图层状态并新增一个画布
- 新画布上绘制一个空白的带有Path路径的Bitmap
- 计算源图像区域,用画笔的混合模式中的SRC_OUT模式绘制出迪丽热巴的照片
简单来说,就是我们先绘制一张哈尼的图片,然后作为透明图层保存下来并创建一个新画布,接着在这个新画布上绘制一个空白的Bitmap,再绘制Path路径,最后在这个画布上利用混合模式绘制迪丽热巴的图片
- 从下至上依次为
- 带有哈尼克孜的画布
- 绘制有空白的带有Path的Bitmap和迪丽热巴图像混合模式的画布
当目标图像(绘制了路径的新建的Bitmap图像)有图像时结果显示空白像素(被path绘制到的部分,显示出了下层画布哈尼的图像) , 当目标图像(热巴的图像)没有图像结果时(没有被我们用path绘制过的部分)显示源图像(迪丽热巴的图像)。
上源码
package com.tx.camera.view
import android.content.Context
import android.graphics.*
import android.util.AttributeSet
import android.view.MotionEvent
import android.view.View
import com.tx.camera.R
/**
* create by xu.tian
* @date 2021/9/7
*/
class BezierView(context: Context?, attrs: AttributeSet?) : View(context, attrs) {
var paint : Paint = Paint()
var bitmapBg = BitmapFactory.decodeResource(resources, R.drawable.hanikezi)
var bitmapSrc = BitmapFactory.decodeResource(resources, R.drawable.dilireba)
var bitmapDst = Bitmap.createBitmap(bitmapSrc.width,bitmapSrc.height,Bitmap.Config.ARGB_8888)
var w = 0
var h = 0
init {
paint.style = Paint.Style.STROKE
paint.strokeWidth = 100f
setLayerType(LAYER_TYPE_SOFTWARE,null)
}
var preX = 0f
var preY = 0f
var path = Path()
override fun onDraw(canvas: Canvas?) {
super.onDraw(canvas)
// 绘制底层图片
canvas?.drawBitmap(bitmapBg,null,Rect(0,0,bitmapDst.width,bitmapDst.height),paint)
var layerId : Int = canvas?.saveLayer(0f,0f, w.toFloat(), h.toFloat(),null,Canvas.ALL_SAVE_FLAG)!!
// 把手势路径绘制到目标图像上
var c = Canvas(bitmapDst)
c.drawPath(path,paint)
// 把目标图像绘制到画布上
canvas?.drawBitmap(bitmapDst,0f,0f,paint)
// 计算源图像区域
paint.xfermode = PorterDuffXfermode(PorterDuff.Mode.SRC_OUT)
canvas?.drawBitmap(bitmapSrc,0f,0f,paint)
// 取消混合模式
paint.xfermode = null
// 恢复画布状态
canvas?.restoreToCount(layerId)
}
override fun onTouchEvent(event: MotionEvent?): Boolean {
when (event?.action){
MotionEvent.ACTION_DOWN -> actionDown(event)
MotionEvent.ACTION_MOVE -> bezierPathMove(event)
}
return true
}
private fun actionDown(event: MotionEvent){
preX = event.x
preY = event.y
path.moveTo(preX,preY)
}
private fun linePathMove(event: MotionEvent){
path.lineTo(event.x,event.y)
postInvalidate()
}
private fun bezierPathMove(event: MotionEvent){
path.quadTo(preX,preY,(event.x+preX)/2,(event.y+preY)/2)
preX = event.x
preY = event.y
postInvalidate()
}
override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
super.onSizeChanged(w, h, oldw, oldh)
this.w = w
this.h = h
}
}
saveLayer()方法
这里简单说一下,这个方法可以保存当前的绘制内容且会生成一个新的Canvas,调用这个函数后的绘制过程都会在这个Canvas上进行。在这个例子中,我们的保存路径的bitmap和迪丽热巴的图像就在同一个canvas中~
总结
今天有点小累~就写到这里了
网友评论