《Android群英传》图像处理之画笔特效处理--PorterDuffXfermode篇

不管是在我们的世界里,还是在Android的世界里,要想向神笔马良一样画出各种精彩绝伦的画,就必须得有一个前提–要有一支神奇的画笔。下面我们就来看看画笔的一些高级的一些高级属性,帮助我们实现更丰富的绘图效果。

PorterDuffXfermode

在学习这个东西之前,先来看看一张非常经典的图,出自 API Demo.

PorterDuffXfermode

图中列举了16种PorterDuffXfermode,有点像数学中集合的交集、并集这样的概念,相信配合图例应该很好理解,它控制的是两个图象之间的混合显示模式。

  • 这里要注意的是,PorterDuffXfermode设置的是两个图层交集区域的显示方式,dst 是先画的图形,而src 是后画的图形。当然,这些模式也不是经常使用的,用的最多的是,使用一张图片作为另一张图片的遮罩层,通过控制遮罩层的图形,来控制下面被遮罩图形的显示效果。其中最常用的就是通过DST_INSRC_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.ROUNDPaint.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时还有一点需要注意,那就是最好在绘图时,将硬件加速关闭,因为有些模式并不支持硬件加速。

刮刮卡效果

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
要实现在安卓应用中显示圆形头像图像的功能,你可以通过以下步骤来实现: 1. 创建一个 xml 文件,定义圆形 ImageView 控件。 ``` <shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval"> <size android:width="120dp" android:height="120dp" /> <solid android:color="#FFFFFF" /> <stroke android:width="2dp" android:color="#CCCCCC" /> </shape> ``` 2. 在布局文件中使用定义的圆形 ImageView 控件。 ``` <ImageView android:id="@+id/avatar" android:layout_width="120dp" android:layout_height="120dp" android:src="@drawable/avatar" android:background="@drawable/circle" /> ``` 3. 通过代码将图片显示为圆形。 ``` public static Bitmap getCircleBitmap(Bitmap bitmap) { Bitmap output = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), Bitmap.Config.ARGB_8888); Canvas canvas = new Canvas(output); Paint paint = new Paint(); Rect rect = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight()); paint.setAntiAlias(true); canvas.drawARGB(0, 0, 0, 0); canvas.drawCircle(bitmap.getWidth() / 2, bitmap.getHeight() / 2, bitmap.getWidth() / 2, paint); paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN)); canvas.drawBitmap(bitmap, rect, rect, paint); return output; } ``` 在调用这个方法之后,将返回的 Bitmap 对象设置为 ImageView 的背景即可。 ``` Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.avatar); ImageView imageView = findViewById(R.id.avatar); imageView.setImageBitmap(getCircleBitmap(bitmap)); ``` 这样,你就可以在你的安卓应用中实现圆形头像图像的显示了。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值