《Android群英传》图像处理之画笔特效处理--PorterDuffXfermode篇
2016-06-02 17:19
465 查看
不管是在我们的世界里,还是在Android的世界里,要想向神笔马良一样画出各种精彩绝伦的画,就必须得有一个前提–要有一支神奇的画笔。下面我们就来看看画笔的一些高级的一些高级属性,帮助我们实现更丰富的绘图效果。
图中列举了16种PorterDuffXfermode,有点像数学中集合的交集、并集这样的概念,相信配合图例应该很好理解,它控制的是两个图象之间的混合显示模式。
这里要注意的是,PorterDuffXfermode设置的是两个图层交集区域的显示方式,dst 是先画的图形,而src 是后画的图形。当然,这些模式也不是经常使用的,用的最多的是,使用一张图片作为另一张图片的遮罩层,通过控制遮罩层的图形,来控制下面被遮罩图形的显示效果。其中最常用的就是通过DST_IN、SRC_IN 模式来实现将一个矩形图片变成圆角图片或者圆形图片的效果。
下面让我们来将一张图片处理成圆角图片,先上图:
我们先用一个普通画笔画一个圆角矩形遮罩层,再用带PorterDuffXfermode的画笔将图像画在遮罩层上,这样就可以通过上面所说的效果来混合两个图像了,完整的代码如下所示:
下面我们再来看一个稍微复杂点的刮刮卡效果。我们都知道,刮刮卡一般有两个图层,即上面的用来刮掉的图层和下面隐藏的图层。在初始状态下,上面的图层会将下面整个图层覆盖,当你用手刮上面的图层的时候,下面的图层会慢慢显示出来,这也类似很多画图工具中的橡皮擦效果。这个效果可以使用PorterDuffXfermode来实现。
在上面的代码中,给 Paint 设置了一些属性,让它的笔触和连接外能更加圆滑一些,即Paint.Join.ROUND 和 Paint.Cap.ROUND属性。
PorterDuffXfermode
在学习这个东西之前,先来看看一张非常经典的图,出自 API Demo.图中列举了16种PorterDuffXfermode,有点像数学中集合的交集、并集这样的概念,相信配合图例应该很好理解,它控制的是两个图象之间的混合显示模式。
这里要注意的是,PorterDuffXfermode设置的是两个图层交集区域的显示方式,dst 是先画的图形,而src 是后画的图形。当然,这些模式也不是经常使用的,用的最多的是,使用一张图片作为另一张图片的遮罩层,通过控制遮罩层的图形,来控制下面被遮罩图形的显示效果。其中最常用的就是通过DST_IN、SRC_IN 模式来实现将一个矩形图片变成圆角图片或者圆形图片的效果。
下面让我们来将一张图片处理成圆角图片,先上图:
我们先用一个普通画笔画一个圆角矩形遮罩层,再用带PorterDuffXfermode的画笔将图像画在遮罩层上,这样就可以通过上面所说的效果来混合两个图像了,完整的代码如下所示:
package com.ajun.canvas.view; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.PorterDuff; import android.graphics.PorterDuffXfermode; import android.util.AttributeSet; import android.view.View; import com.ajun.canvas.R; public class CircleCornerImageView extends View { private Paint mPaint; private Bitmap newBitmap; private Bitmap srcBitmap; public CircleCornerImageView(Context context) { super(context); init(); } public CircleCornerImageView(Context context, AttributeSet attrs) { super(context, attrs); init(); } public CircleCornerImageView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(); } private void init() { srcBitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.test1); //创建一个和原图大小相同的图片 newBitmap = Bitmap.createBitmap(srcBitmap.getWidth(), srcBitmap.getHeight(), Bitmap.Config.ARGB_8888); Canvas canvas = new Canvas(newBitmap); mPaint = new Paint(); mPaint.setAntiAlias(true); canvas.drawRoundRect(0,0, srcBitmap.getWidth(), srcBitmap.getHeight(), 30,30,mPaint); mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN)); canvas.drawBitmap(srcBitmap,0,0,mPaint); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); canvas.drawBitmap(newBitmap,0,0,null); } }
下面我们再来看一个稍微复杂点的刮刮卡效果。我们都知道,刮刮卡一般有两个图层,即上面的用来刮掉的图层和下面隐藏的图层。在初始状态下,上面的图层会将下面整个图层覆盖,当你用手刮上面的图层的时候,下面的图层会慢慢显示出来,这也类似很多画图工具中的橡皮擦效果。这个效果可以使用PorterDuffXfermode来实现。
首先开始做一些初始化工作,准备好图片,设置好Paint的一些属性,代码如下所示:
mPaint = new Paint(); //设置Paint的颜色为透明 mPaint.setAlpha(0); mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN)); mPaint.setStyle(Paint.Style.STROKE); mPaint.setStrokeJoin(Paint.Join.ROUND);//圆状 mPaint.setStrokeWidth(50f); mPaint.setStrokeCap(Paint.Cap.ROUND); //初始化 Path mPath = new Path(); mSrcBitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.test1); //创建一张和原图一样大小的图片 mNewBitmap = Bitmap.createBitmap(mSrcBitmap.getWidth(), mSrcBitmap.getHeight(), Bitmap.Config.ARGB_8888); //设置上层图片颜色为灰色 mCanvas = new Canvas(mNewBitmap); mCanvas.drawColor(Color.GRAY);
在上面的代码中,给 Paint 设置了一些属性,让它的笔触和连接外能更加圆滑一些,即Paint.Join.ROUND 和 Paint.Cap.ROUND属性。
接下来看一下如何获取用户手指滑动所产生的路径,代码如下所示。使用Path保存用户手指划过的路径。
switch (event.getAction()) { case MotionEvent.ACTION_DOWN: //清除Path设置的所有属性 mPath.reset(); //坐标的起始位置 mPath.moveTo(event.getX(), event.getY()); break; case MotionEvent.ACTION_MOVE: //开始划路径 mPath.lineTo(event.getX(), event.getY()); break; }
最后,只需要使用 DST_IN 模式将路径绘制到前面覆盖的图层上面即可。不过,还需要做关键的一步,那就是将画笔的透明度设置为0,这样才能显示出擦出后的效果。完整代码如下:
package com.ajun.canvas.view; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.Path; import android.graphics.PorterDuff; import android.graphics.PorterDuffXfermode; import android.util.AttributeSet; import android.view.MotionEvent; import android.view.View; import com.ajun.canvas.R; public class ScratchCardView extends View { private Bitmap mSrcBitmap, mNewBitmap; private Paint mPaint; private Path mPath; private Canvas mCanvas; public ScratchCardView(Context context) { super(context); init(); } public ScratchCardView(Context context, AttributeSet attrs) { super(context, attrs); init(); } public ScratchCardView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(); } private void init() { mPaint = new Paint(); //设置Paint的颜色为透明 mPaint.setAlpha(0); mPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN)); mPaint.setStyle(Paint.Style.STROKE); mPaint.setStrokeJoin(Paint.Join.ROUND);//圆状 mPaint.setStrokeWidth(50f); mPaint.setStrokeCap(Paint.Cap.ROUND); //初始化 Path mPath = new Path(); mSrcBitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.test1); mNewBitmap = Bitmap.createBitmap(mSrcBitmap.getWidth(), mSrcBitmap.getHeight(), Bitmap.Config.ARGB_8888); mCanvas = new Canvas(mNewBitmap); mCanvas.drawColor(Color.GRAY); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); canvas.drawBitmap(mSrcBitmap, 0, 0, null); canvas.drawBitmap(mNewBitmap, 0, 0, null); } @Override public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: //清除Path设置的所有属性 mPath.reset(); //坐标的起始位置 mPath.moveTo(event.getX(), event.getY()); break; case MotionEvent.ACTION_MOVE: //开始划路径,(笔是透明的) mPath.lineTo(event.getX(), event.getY()); break; } mCanvas.drawPath(mPath, mPaint);//将划过的路径设置为透明 invalidate();//通知系统重绘 return true; } }
程序运行效果看下面gif图,当用户用手指滑动时,就会擦出上面的图层,形成刮刮卡的效果。
Tips:在使用PorterDuffXfermode时还有一点需要注意,那就是最好在绘图时,将硬件加速关闭,因为有些模式并不支持硬件加速。相关文章推荐
- 使用C++实现JNI接口需要注意的事项
- Android IPC进程间通讯机制
- Android Manifest 用法
- [转载]Activity中ConfigChanges属性的用法
- Android之获取手机上的图片和视频缩略图thumbnails
- Android之使用Http协议实现文件上传功能
- Android学习笔记(二九):嵌入浏览器
- android string.xml文件中的整型和string型代替
- i-jetty环境搭配与编译
- android之定时器AlarmManager
- android wifi 无线调试
- Android Native 绘图方法
- Android java 与 javascript互访(相互调用)的方法例子
- android 代码实现控件之间的间距
- android FragmentPagerAdapter的“标准”配置
- Android"解决"onTouch和onClick的冲突问题
- android:installLocation简析
- android searchView的关闭事件
- SourceProvider.getJniDirectories