博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
【Android】解析Paint类中Xfermode的使用
阅读量:5079 次
发布时间:2019-06-12

本文共 15401 字,大约阅读时间需要 51 分钟。

Paint类提供了setXfermode(Xfermode xfermode)方法,Xfermode指明了原图像和目标图像的结合方式。谈到Xfermode就不得不谈它的派生类PorterDuffXfermode,PorterDuffXfermode类只有一个构造函数如下:

public PorterDuffXfermode (PorterDuff.Mode mode)

创建PorterDuffXfermode实例,需要指定porter-duff模式。porter-duff实际上是Thomas Porter和Tom Duff的简写,他们在1984年发表的“Compositing Digital Images”文章中指出了该项发现。

PorterDuff.Mode是一个枚举类,下面是不同模式的结合图:

观察上面的16种模式。我们可以把一副图像分成两部分透明度(Alpha)和颜色(Color),源图像透明度(Alphasrc)和目标图像透明度(Alphadst)按照某种合成关系就产生了结果图像透明度(Alphaout),源图像颜色(Colorsrc)和目标图像颜色(Colordst)按照某种合成关系就产生了结果图像颜色(Colorout),然后组合Alphaout和Colorout就可以得到图像的一个像素点值了。这里的合成关系依据不同的PorterDuff.Mode会规定不同的合成关系。

在官方文档中,对每种模式的合成关系有更为详细的描述:

 

图象组合实现的代码:

Paint paint = new Paint();canvas.drawBitmap(destinationImage, 0, 0, paint);PorterDuff.Mode mode = // choose a modepaint.setXfermode(new PorterDuffXfermode(mode));canvas.drawBitmap(sourceImage, 0, 0, paint);

接下来笔者将会展示这些模式的实现,希望能够对你所有帮助。

 

众所周知,Bitmap中保存着Canvas中绘制的数据。为了更好的展示Xfermode功能,我们首先应该创建一张透明的画布层Canvas,然后在该画布层上完成一些列的porter-duff模式,最后将该画布层中的数据Bitmap绘制到屏幕层的画布中。

//set hardware layersetLayerType(View.LAYER_TYPE_HARDWARE, null);Bitmap separate_bitmap=Bitmap.createBitmap(screenWidth,screenHeight,Config.ARGB_8888);Canvas separate_canvas=new Cavnas(bitmap);separate_canvas.drawBitmap(destinationImage,0,0,paint);PorterDuff.Mode mode=//choose a modepaint.setXfermode(new PorterDuffXfermode(mode));separate_canvas.drawBitmap(sourceImage,0,0,paint);//retore to software layersetLayerType(View.LAYER_TYPE_SOFTWARE,null);//drawing separate_bitmap to base canvascanvas.drawBitmap(separate_bitmap, 0, 0, paint);

首先用separate_bitmap创建一个separate_canvas,因为separate_bitmap上没有任何数据,所以separate_canvas这时候是完全透明的,这时候我们可以在separate_canvas上顺利地完成Xfermode的操作。之所以不在屏幕canvas上的进行这些操作,这是因为屏幕canvas不是无色的和透明的(默认是白色的和不透明的),也就是说屏幕的Bitmap在为Config.ARGB_8888的情况下,那么它的ARGB值就分别是255、255、255、255(上面提过屏幕的canvas默认是白色的和不透明的),这显然将会对Xfermode的合成造成影响。但如果这样的影响是在你的预期范围内的话,可以考虑直接绘制到屏幕canvas上。

 

然后将separate_canvas已经绘制完的separate_bitmap数据再绘制屏幕canvas的bitmap中。

canvas.drawBitmap(separate_bitmap, 0, 0, paint);

 

有一点不得不提就是hardware

//set hardware layersetLayerType(View.LAYER_TYPE_HARDWARE, null);//do Xfermode combine.....//retore to software layersetLayerType(View.LAYER_TYPE_SOFTWARE,null);

视图默认的图层类型不是hardware,而是software。设置图层类型类型为hardware,就会开启硬件加速,在渲染图形时就会强制使用android硬件渲染通道。在AndroidManifest.xml进行如下配置也会开启硬件加速:

android:hardwareAccelerated="true"

hardware layers对于复杂图形树的绘制更快、更高效。不仅仅是Xfermode,任何需要进行开发复杂视图或是快速动画,笔者都强烈建议开启此硬件加速。

Canvas还提供了方法saveLayer,利用这个方法可以达到分层开发,它的原理图:
但是笔者不建议在开发中使用saveLayer这个方法,尤其在复制界面或是动画中,因为saveLayer是一个非常“昂贵”的方法,它通常会占用更多的资源。
activity_main.xml

MainActivity.java

public class MainActivity extends Activity{    private int screenWidth=0;    private int screenHeight=0;    private PorterDuff.Mode porterduffmodel=null;    private ImageView imageView=null;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);                //initial porter-duff mode is Src        porterduffmodel=PorterDuff.Mode.SRC;                Point point=new Point();        getWindowManager().getDefaultDisplay().getSize(point);        //width of screen        screenWidth=point.x;        //height of screen        screenHeight=point.y;                        imageView= ((ImageView)findViewById(R.id.image));        setXfermodeImage(imageView);                //choose src        findViewById(R.id.radio0).setOnClickListener(new OnClickListener() {            @Override            public void onClick(View arg0) {                porterduffmodel=PorterDuff.Mode.SRC;                setXfermodeImage(imageView);            }        });                //choose Dst        findViewById(R.id.radio1).setOnClickListener(new OnClickListener() {            @Override            public void onClick(View arg0) {                porterduffmodel=PorterDuff.Mode.DST;                setXfermodeImage(imageView);            }        });                //choose SrcOver        findViewById(R.id.radio2).setOnClickListener(new OnClickListener() {            @Override            public void onClick(View arg0) {                porterduffmodel=PorterDuff.Mode.SRC_OVER;                setXfermodeImage(imageView);            }        });                //choose DstOver        findViewById(R.id.radio3).setOnClickListener(new OnClickListener() {            @Override            public void onClick(View arg0) {                porterduffmodel=PorterDuff.Mode.DST_OVER;                setXfermodeImage(imageView);            }        });                //choose SrcIn        findViewById(R.id.radio4).setOnClickListener(new OnClickListener() {            @Override            public void onClick(View arg0) {                porterduffmodel=PorterDuff.Mode.SRC_IN;                setXfermodeImage(imageView);            }        });                //choose DstIn        findViewById(R.id.radio5).setOnClickListener(new OnClickListener() {            @Override            public void onClick(View arg0) {                porterduffmodel=PorterDuff.Mode.DST_IN;                setXfermodeImage(imageView);            }        });                //choose SrcOut        findViewById(R.id.radio6).setOnClickListener(new OnClickListener() {            @Override            public void onClick(View arg0) {                porterduffmodel=PorterDuff.Mode.SRC_OUT;                setXfermodeImage(imageView);            }        });                //choose DstOut        findViewById(R.id.radio7).setOnClickListener(new OnClickListener() {            @Override            public void onClick(View arg0) {                porterduffmodel=PorterDuff.Mode.DST_OUT;                setXfermodeImage(imageView);            }        });                //choose SrcATop        findViewById(R.id.radio8).setOnClickListener(new OnClickListener() {            @Override            public void onClick(View arg0) {                porterduffmodel=PorterDuff.Mode.SRC_ATOP;                setXfermodeImage(imageView);            }        });                //choose DstATop        findViewById(R.id.radio9).setOnClickListener(new OnClickListener() {            @Override            public void onClick(View arg0) {                porterduffmodel=PorterDuff.Mode.DST_ATOP;                setXfermodeImage(imageView);            }        });                //choose Xor        findViewById(R.id.radio10).setOnClickListener(new OnClickListener() {            @Override            public void onClick(View arg0) {                porterduffmodel=PorterDuff.Mode.XOR;                setXfermodeImage(imageView);            }        });                //choose Darken        findViewById(R.id.radio11).setOnClickListener(new OnClickListener() {            @Override            public void onClick(View arg0) {                porterduffmodel=PorterDuff.Mode.DARKEN;                setXfermodeImage(imageView);            }        });                //choose lighten        findViewById(R.id.radio12).setOnClickListener(new OnClickListener() {            @Override            public void onClick(View arg0) {                porterduffmodel=PorterDuff.Mode.LIGHTEN;                setXfermodeImage(imageView);            }        });                //choose multiply        findViewById(R.id.radio13).setOnClickListener(new OnClickListener() {            @Override            public void onClick(View arg0) {                porterduffmodel=PorterDuff.Mode.MULTIPLY;                setXfermodeImage(imageView);            }        });                //choose screen        findViewById(R.id.radio14).setOnClickListener(new OnClickListener() {            @Override            public void onClick(View arg0) {                porterduffmodel=PorterDuff.Mode.SCREEN;                setXfermodeImage(imageView);            }        });                //clear        findViewById(R.id.radio15).setOnClickListener(new OnClickListener() {            @Override            public void onClick(View arg0) {                porterduffmodel=PorterDuff.Mode.CLEAR;                setXfermodeImage(imageView);            }        });    }        private void setXfermodeImage(ImageView imageView){        //open hardware accelerate rendering, it's software rendering default.        imageView.setLayerType(View.LAYER_TYPE_HARDWARE, null);                Bitmap separate_bitmap=Bitmap.createBitmap(screenWidth, screenHeight/2, Config.ARGB_8888);        Canvas separate_canvas=new Canvas(separate_bitmap);                        Bitmap destination_bitmap=Bitmap.createBitmap(screenWidth, screenHeight/2, Config.ARGB_8888);        //create destination canvas        Canvas destination_canvas=new Canvas(destination_bitmap);        Paint destination_paint=new Paint();        destination_paint.setColor(Color.YELLOW);        destination_paint.setStyle(Style.FILL);        //drawing destination circle to destination canvas        destination_canvas.drawCircle(screenWidth/2,screenHeight/4,screenHeight/8,destination_paint);                        Bitmap source_bitmap=Bitmap.createBitmap(screenWidth, screenHeight/2, Config.ARGB_8888);        //create source canvas        Canvas source_canvas=new Canvas(source_bitmap);        Paint source_paint=new Paint();        source_paint.setColor(Color.BLUE);        source_paint.setStyle(Style.FILL);        //drawing source rectangle to source canvas        source_canvas.drawRect(screenWidth/8, screenHeight/4, screenWidth/2, 7*screenHeight/16, source_paint);                        Paint separate_paint=new Paint();        separate_canvas.drawBitmap(destination_bitmap, 0, 0, separate_paint);        separate_paint.setXfermode(new PorterDuffXfermode(porterduffmodel));        separate_canvas.drawBitmap(source_bitmap, 0, 0,separate_paint);                //set separate_bitmap to target view        imageView.setImageBitmap(separate_bitmap);                //retrieve layer type to software layer        imageView.setLayerType(ImageView.LAYER_TYPE_SOFTWARE, null);    }}

效果图:

利用这些功能,我们可以轻易的完成一些较为复杂的功能。

接下来笔者实现一个绘制圆形头像的功能,DstIn只会绘制目标图形中与源图像相交的部分。因此我们只需要设定源图像为圆圈,目标图像设置为图片,然后再使用DstIn便可以完成显示圆头像的功能了。

public class MainActivity extends Activity {    int screenWidth=0;    int screenHeight=0;        @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        Point point=new Point();        getWindowManager().getDefaultDisplay().getSize(point);        //initialize screen width        screenWidth=point.x;        //initialize screen height        screenHeight=point.y;                setContentView(new MyView(this));    }    class MyView extends View{        Bitmap destination_bitmap=null;        public MyView(Context context){            super(context);            destination_bitmap=BitmapFactory.decodeResource(getResources(), R.drawable.scene);        }        @Override        protected void onDraw(Canvas canvas) {            super.onDraw(canvas);                                    //create an empty separate_bitmap and a separate_canvas            Bitmap separate_bitmap=Bitmap.createBitmap(screenWidth, screenHeight, Config.ARGB_8888);            Canvas separate_canvas=new Canvas(separate_bitmap);                                    //create an empty source canvas            Bitmap source_bitmap=Bitmap.createBitmap(screenWidth, screenHeight, Config.ARGB_8888);            Canvas source_canvas=new Canvas(source_bitmap);            //drawing a circle on source_bitmap            Paint source_paint=new Paint();            source_paint.setStyle(Style.FILL);            source_canvas.drawCircle(screenWidth/2,screenHeight/4,200,source_paint);                                    Paint paint=new Paint();            //drawing destination_bitmap to separate_canvas            separate_canvas.drawBitmap(destination_bitmap, 0, 0, paint);            //set porter-duff mode is DST_IN            paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));            //drawing source_bitmap to separate_canvas            separate_canvas.drawBitmap(source_bitmap, 0, 0, paint);                                    //drawing separate_bitmap to screen canvas            canvas.drawBitmap(separate_bitmap, 0, 0, new Paint());                    }    }}

效果图:

最后笔者简单展示一下,如何利用DST_OUT模式,实现刮刮乐的功能。

public class MainActivity extends Activity {    int screenWidth=0;    int screenHeight=0;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        Point point=new Point();        getWindowManager().getDefaultDisplay().getSize(point);        //screen width        screenWidth=point.x;        //screen height        screenHeight=point.y;                setContentView(new MyView(this));    }    class MyView extends View{        private Bitmap resultBitmap=null;        private Bitmap showBitmap=null;        private Bitmap tempBitmap=null;        private Canvas tempCanvas=null;        private float start_x = 0;        private float start_y = 0;         private float end_x=0;         private float end_y=0;         private Paint paint=null;         private Path path=null;        public MyView(Context context){            super(context);            //create result bitmap with R.drawable.result which representing the answer.            resultBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.result);            //create show bitmap with R.drawable.show which used to cover answer.            showBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.show);                        tempBitmap=Bitmap.createBitmap(screenWidth, screenHeight, Config.ARGB_8888);            tempCanvas=new Canvas(tempBitmap);            tempCanvas.drawBitmap(showBitmap, 0, 0, new Paint());                        path=new Path();            paint=new Paint();            paint.setStrokeWidth(20);            paint.setStyle(Style.STROKE);            paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT));            paint.setStrokeCap(Paint.Cap.ROUND);        }        @Override        protected void onDraw(Canvas canvas) {            super.onDraw(canvas);            canvas.drawBitmap(resultBitmap, 0, 0,new Paint());                        canvas.drawBitmap(tempBitmap, 0, 0, new Paint());        }                @Override        public boolean onTouchEvent(MotionEvent event) {            if(event.getAction()==MotionEvent.ACTION_DOWN){                start_x=event.getX();                start_y=event.getY();                path.moveTo(start_x, start_y);//setting start point            }else if(                    event.getAction()==MotionEvent.ACTION_MOVE                    ||                    event.getAction()==MotionEvent.ACTION_UP){                end_x=event.getX();                end_y=event.getY();                path.lineTo(end_x,end_y);                tempCanvas.drawPath(path,paint);                start_x=end_x;                start_y=end_y;                                postInvalidate();            }            return true;        }    }}

效果图:

 

这篇Blog并未详细的讲解每种模式的详细合成模式过程,关于这些读者可以到Google官方文档查阅。最后,希望这篇可以对你所有帮助。

 

转载于:https://www.cnblogs.com/HDK2016/p/9733560.html

你可能感兴趣的文章
HDU 5458 Stability
查看>>
左手坐标系和右手坐标系
查看>>
solr后台操作Documents之增删改查
查看>>
http://yusi123.com/
查看>>
文件文本的操作
查看>>
Ubuntu linux下gcc版本切换
查看>>
记一次Web服务的性能调优
查看>>
jQuery.form.js使用
查看>>
(转)linux sort,uniq,cut,wc命令详解
查看>>
关于ExecuteNonQuery执行的返回值(SQL语句、存储过程)
查看>>
UVa540 Team Queue(队列queue)
查看>>
mysql数据增删改查
查看>>
shell中下载最新版本或指定版本的办法(Dockerfile 中通用)
查看>>
极客时间-左耳听风-程序员攻略-分布式架构工程设计
查看>>
akka之种子节点
查看>>
不知道做什么时
查看>>
matlab 给某一列乘上一个系数
查看>>
密码学笔记——培根密码
查看>>
Screening technology proved cost effective deal
查看>>
MAC 上升级python为最新版本
查看>>