一、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就开始支持) - 1、 兼容性问题:由于是将绘制函数转换成OpenGL命令来绘制,定然会存在OpenGL并不能完全支持原始绘制函数的问题,所以这就会造成在打开GPU加速时,效果会失效的问题。
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是部分不支持的。
所以在使用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的声明如下:
它只有一个参数PorterDuff.Mode,它的可取值有如下几个:public PorterDuffXfermode(PorterDuff.Mode 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