zoukankan      html  css  js  c++  java
  • 【Android】解析Paint类中Xfermode的使用

    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会规定不同的合成关系。

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


    图象组合实现的代码:

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

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

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

    //set hardware layer
    setLayerType(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 mode
    paint.setXfermode(new PorterDuffXfermode(mode));
    separate_canvas.drawBitmap(sourceImage,0,0,paint);
    
    //retore to software layer
    setLayerType(View.LAYER_TYPE_SOFTWARE,null);
    
    //drawing separate_bitmap to base canvas
    canvas.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 layer
    setLayerType(View.LAYER_TYPE_HARDWARE, null);
    
    //do Xfermode combine.
    ....
    
    //retore to software layer
    setLayerType(View.LAYER_TYPE_SOFTWARE,null);

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

    android:hardwareAccelerated="true"

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

    Canvas还提供了方法saveLayer,利用这个方法可以达到分层开发,它的原理图:

    但是笔者不建议在开发中使用saveLayer这个方法,尤其在复制界面或是动画中,因为saveLayer是一个非常“昂贵”的方法,它通常会占用更多的资源。

    activity_main.xml

    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:paddingBottom="@dimen/activity_vertical_margin"
        android:paddingLeft="@dimen/activity_horizontal_margin"
        android:paddingRight="@dimen/activity_horizontal_margin"
        android:paddingTop="@dimen/activity_vertical_margin"
        tools:context=".MainActivity" >
    
        <ImageView
            android:id="@+id/image"
            android:background="@android:drawable/edit_text"
            android:src="@drawable/ic_launcher"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_centerHorizontal="true"
            android:layout_alignParentTop="true"/>
        
        <ScrollView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_below="@id/image"
             >
        <RadioGroup
            android:id="@+id/radioGroup1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:orientation="vertical" >
            <RadioButton
                android:id="@+id/radio0"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:checked="true"
                android:text="Src" />
            <RadioButton
                android:id="@+id/radio1"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="Dst" />
            <RadioButton
                android:id="@+id/radio2"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="SrcOver" />
            <RadioButton
                android:id="@+id/radio3"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="DstOver" />
            <RadioButton
                android:id="@+id/radio4"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="SrcIn" />
            <RadioButton
                android:id="@+id/radio5"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="DstIn" />
            <RadioButton
                android:id="@+id/radio6"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="SrcOut" />
            <RadioButton
                android:id="@+id/radio7"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="DstOut" />
            <RadioButton
                android:id="@+id/radio8"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="SrcATop" />
            <RadioButton
                android:id="@+id/radio9"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="DstATop" />
            <RadioButton
                android:id="@+id/radio10"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="Xor" />
            <RadioButton
                android:id="@+id/radio11"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="Darken" />
            <RadioButton
                android:id="@+id/radio12"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="Lighten" />
            <RadioButton
                android:id="@+id/radio13"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="Multiply" />
            <RadioButton
                android:id="@+id/radio14"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="Screen" />
            <RadioButton
                android:id="@+id/radio15"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:text="Clear" />
        </RadioGroup>
        </ScrollView>
    </RelativeLayout>

    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官方文档查阅。最后,希望这篇可以对你所有帮助。

  • 相关阅读:
    HDU 1358 Period (KMP)
    POJ 1042 Gone Fishing
    Csharp,Javascript 获取显示器的大小的几种方式
    css text 自动换行的实现方法 Internet Explorer,Firefox,Opera,Safar
    Dynamic Fonts动态设置字体大小存入Cookie
    CSS Image Rollovers翻转效果Image Sprites图片精灵
    CSS three column layout
    css 自定义字体 Internet Explorer,Firefox,Opera,Safari
    颜色选择器 Color Picker,Internet Explorer,Firefox,Opera,Safar
    CSS TextShadow in Safari, Opera, Firefox and more
  • 原文地址:https://www.cnblogs.com/HDK2016/p/9733560.html
Copyright © 2011-2022 走看看