Android高级进阶——绘图篇(五)setXfermode 设置混合模式

本文介绍了Android中GPU硬件加速的重要性及其优缺点,并详细探讨了PorterDuff的混合模式,包括SRC_IN、DST_IN、SRC_OUT和DST_OUT。通过实例分析了Google代码中的误导,以及颜色叠加相关模式,如ADD、LIGHTEN、DARKEN、MULTIPLY、OVERLAY和SCREEN。此外,还展示了如何利用这些模式实现Twitter标识的描边效果和橡皮擦效果。
摘要由CSDN通过智能技术生成

一、GPU硬件加速

  • 1、概述

GPU英文全称Graphic Processing Unit,中文翻译为“图形处理器”。与CPU不同,GPU是专门为处理图形任务而产生的芯片。
在GPU出现之前,CPU一直负责着所有的运算工作,CPU的架构是有利于X86指令集的串行架构,CPU从设计思路上适合尽可能快的完成一个任务。但当面对类似多媒体、图形图像处理类型的任务时,就显得力不从心。因为在多媒体计算中通常要求更高的运算密度、多并发线程和频繁地存储器访问;显然当你打游戏时,屏幕上的动画是需要实时刷新的,这些都需要频繁的计算、存取动作;如果CPU不能及时响应,那么屏幕就会显得很卡……你的队友可能会发一句……我等的花都谢了,你咋还不动呢……
为了专门处理多媒体的计算、存储任务,GPU就应运而生了,GPU中自带处理器和存储器,以用来专门计算和存储多媒体任务。
对于Andorid来讲,在API 11之前是没有GPU的概念的,在API 11之后,在程序集中加入了对GPU加速的支持,在API 14之后,硬件加速是默认开启的!我们可以显式地强制图像计算时使用GPU而不使用CPU.

在CPU绘制和GPU绘制时,在流程上是有区别的:

  • 在基于软件的绘制模型下,CPU主导绘图,视图按照两个步骤绘制:

    • 让View层次结构失效
    • 绘制View层次结构
  • 在基于硬件加速的绘制模式下,GPU主导绘图,绘制按照三个步骤绘制:

    • 让View层次结构失效
    • 记录、更新显示列表
    • 绘制显示列表

可以看到在GPU加速时,流程中多了一项“记录、更新显示列表”,它表示在第一步View层次结构失效后,并不是直接开始逐层绘制,而是首先把这些View的绘制函数作为绘制指令记录一个显示列表中,然后再读取显示列表中的绘制指令调用OpenGL相关函数完成实际绘制。所以在GPU加速时,实际是使用OpenGL的函数来完成绘制的。

所以使用GPU加速的优点显而易见:硬件加速提高了Android系统显示和刷新的速度;

  • 它有缺点也显而易见:
    • 1、 兼容性问题:由于是将绘制函数转换成OpenGL命令来绘制,定然会存在OpenGL并不能完全支持原始绘制函数的问题,所以这就会造成在打开GPU加速时,效果会失效的问题。
    • 2、内存消耗问题:由于需要OpenGL的指令,所以需要把系统中的OpenGL相关的包加载到内存中来,所以单纯OpenGL API调用就会占用8MB,而实际上会占用更多内存;
    • 3、电量消耗问题:多使用了一个部件,当然会更耗电……

    下图显示了一些特殊函数硬件加速开始支持的平台等级:(红叉表示任何平台都不支持,不在列表中的默认在API 11就开始支持)
![](https://upload-images.jianshu.io/upload_images/11455341-72cdf224fbe0260d.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) ![](https://upload-images.jianshu.io/upload_images/11455341-b1cda5ae66bb1340.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) ![](https://upload-images.jianshu.io/upload_images/11455341-56fab787fef40b17.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 图片摘自[《google官方文档:硬件加速》 ](http://developer.android.com/guide/topics/graphics/hardware-accel.html) 我再重复一遍,上面我们涉及了两个API等级,在API 11以后,在程序集中加入了对GPU加速的支持,在API 14之后,硬件加速是默认开启的!也就是说在API 11——API 13虽然是支持硬件加速的,但是默认是关闭的。
  • 2、禁用GPU硬件加速方法
    那么问题就来了,如果你的APP跑在API 14版本以后,而你洽好要用那些不支持硬件加速的函数要怎么办?
    那就只好禁用硬件加速喽,针对不同类型的东东,Android给我们提供了不同的禁用方法:
    硬件加速分全局(Application)、Activity、Window、View 四个层级

  • 1.在AndroidManifest.xml文件为application标签添加如下的属性即可为整个应用程序开启/关闭硬件加速:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"  
    android:layout_width="fill_parent"  
    android:layout_height="fill_parent"  
    android:orientation="vertical"  
    android:paddingLeft="2dp"  
    android:layerType="software"  
    android:paddingRight="2dp" >  
二、setXfermode(Xfermode xfermode)之PorterDuffXfermode 这个函数是图像混合里最难的一个了,它的功能也是相当强大的,这个模式叫做图形混合模式。 与setColorFilter一样,派生自Xfermode的有三个类: ![image.png](https://upload-images.jianshu.io/upload_images/11455341-d205f06c14c3e563.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
  • 1、概述——基本流程
    从上面可以看出,派生自Xfermode的有AvoidXfermode,PixelXorXfermode,PorterDuffXfermode;
    从硬件加速不支持的函数列表中,我们可以看到AvoidXfermode,PixelXorXfermode是完全不支持的,而PorterDuffXfermode是部分不支持的。

image.png

所以在使用Xfermode时,为了保险起见,我们需要做两件事:

  • 1、禁用硬件加速:
    setLayerType(View.LAYER_TYPE_SOFTWARE, null);
  • 2、使用离屏绘制
//新建图层  
int layerID = canvas.saveLayer(0,0,width,height,mPaint,Canvas.ALL_SAVE_FLAG);  

//TODO 核心绘制代码  

//还原图层  
canvas.restoreToCount(layerID);  
有关离屏绘制的原因,这节就先不给大家引申了,后面会单独拉出来一篇文章讲离屏绘制(Canvas图层相关),大家只需要知道,我们需要把绘制的核心代码放在saveLayer()和restoreToCount()之间即可。 下面我们先简单讲解PorterDuffXfermode的用法,然后写个例子,看下SetXfermode()的使用方法和效果 PorterDuffXfermode的声明如下:

public PorterDuffXfermode(PorterDuff.Mode mode)

它只有一个参数PorterDuff.Mode,它的可取值有如下几个:
Mode.CLEAR  
Mode.SRC  
Mode.DST  
Mode.SRC_OVER  
Mode.DST_OVER  
Mode.SRC_IN  
Mode.DST_IN  
Mode.SRC_OUT  
Mode.DST_OUT  
Mode.SRC_ATOP  
Mode.DST_ATOP  
Mode.XOR  
Mode.DARKEN  
Mode.LIGHTEN  
Mode.MULTIPLY  
Mode.SCREEN  
Mode.OVERLAY  
Mode.ADD  
上面每一个模式都对应着一个算法: ![image.png](https://upload-images.jianshu.io/upload_images/11455341-5471f7aaf8af8589.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 摘自[《google:PorterDuff.Mode》 ](http://developer.android.com/reference/android/graphics/PorterDuff.Mode.html) 比如LIGHTEN的计算方式为[Sa + Da - Sa*Da, Sc*(1 - Da) + Dc*(1 - Sa) + max(Sc, Dc)],其中Sa全称为Source alpha表示源图的Alpha通道;Sc全称为Source color表示源图的颜色;Da全称为Destination alpha表示目标图的Alpha通道;Dc全称为Destination color表示目标图的颜色,在每个公式中,都会被分为两部分[……,……],其中“,”前的部分为“Sa + Da - Sa*Da”这一部分的值代表计算后的Alpha通道而“,”后的部分为“Sc*(1 - Da) + Dc*(1 - Sa) + max(Sc, Dc)”这一部分的值代表计算后的颜色值,图形混合后的图片就是依据这个公式来对DST和SRC两张图像中每一个像素进行计算,得到最终的结果的。  Google给我们了一张图,显示的是两个图形一圆一方通过一定的计算产生不同的组合效果,其中圆形是底部的目标图像,方形是上方的源图像。 ![](https://upload-images.jianshu.io/upload_images/11455341-bb5f1599fee93468.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240) 在上面的公式中涉及到一个概念,目标图DST,源图SRC。那什么是源图,什么是目标图呢?我们简单举例子来说明一下:
    private void init() {
        //初始化画笔
        paint = new Paint();
        paint.setColor(Color.RED);
        paint.setStyle(Paint.Style.FILL_AND_STROKE);
    }


    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        //禁用硬件加速
        setLayerType(View.LAYER_TYPE_SOFTWARE, null);
        //使用离屏绘制
        int layerID = canvas.saveLayer(0, 0, getWidth(), getHeight(), paint, Canvas.ALL_SAVE_FLAG);

        canvas.drawBitmap(createDstBigmap(width, height), 0, 0, paint);
        paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
        canvas.drawBitmap(createSrcBigmap(width, height), width / 2, height / 2, paint);
        paint.setXfermode(null);

        canvas.restoreToCount(layerID);

    }

    public Bitmap 
  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值