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.
在GPU加速时,实际是使用OpenGL的函数来完成绘制的。
所以使用GPU加速的优点显而易见:硬件加速提高了Android系统显示和刷新的速度;
它有缺点也显而易见:
禁用GPU硬件加速方法
那么问题就来了,如果你的APP跑在API 14版本以后,而你洽好要用那些不支持硬件加速的函数要怎么办?
那就只好禁用硬件加速喽,针对不同类型的东东,Android给我们提供了不同的禁用方法:
硬件加速分全局(Application)、Activity、Window、View 四个层级
1.在AndroidManifest.xml文件为application标签添加如下的属性即可为整个应用程序开启/关闭硬件加速:
2.在Activity 标签下使用 hardwareAccelerated 属性开启或关闭硬件加速:
在Window 层级使用如下代码开启硬件加速:(Window层级不支持关闭硬件加速)
getWindow().setFlags(
WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED,
WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED);
4.View 级别如下关闭硬件加速:(view 层级上不支持开启硬件加速)
setLayerType(View.LAYER_TYPE_SOFTWARE, null);
或者使用android:layerType=”software”来关闭硬件加速:比如
Xfermode国外有大神称之为过渡模式,这种翻译比较贴切但恐怕不易理解,大家也可以直接称之为图像混合模式,因为所谓的“过渡”其实就是图像混合的一种。查看API文档发现其果然有三个子类:AvoidXfermode, PixelXorXfermode和PorterDuffXfermode。
由于AvoidXfermode, PixelXorXfermode都已经被标注为过时了,所以这次主要研究的是仍然在使用的PorterDuffXfermode。
从上面可以看出,派生自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);
我们需要把绘制的核心代码放在saveLayer()和restoreToCount()之间即可。
PorterDuffXfermode类同样有且只有一个含参的构造方法PorterDuffXfermode(PorterDuff.Mode mode),虽说构造方法的签名列表里只有一个PorterDuff.Mode的参数,但是它可以实现很多酷毙的图形效果!!而PorterDuffXfermode就是图形混合模式的意思,其概念最早来自于SIGGRAPH的Tomas Proter和Tom Duff,混合图形的概念极大地推动了图形图像学的发展,延伸到计算机图形图像学像Adobe和AutoDesk公司著名的多款设计软件都可以说一定程度上受到影响,而我们PorterDuffXfermode的名字也来源于这俩人的人名组合PorterDuff,那PorterDuffXfermode能做些什么呢?我们先来看一张API DEMO里的图片:
这张图片从一定程度上形象地说明了图形混合的作用,两个图形一圆一方通过一定的计算产生不同的组合效果,在API中Android为我们提供了18种(比上图多了两种ADD和OVERLAY)模式:
自定义view(注意:这里的代码是有问题的,后面详细讲解)
public class XfermodeView extends View {
Paint paint;
int width;
int height;
public XfermodeView(Context context) {
super(context);
init();
}
public XfermodeView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public XfermodeView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init() {
//初始化画笔
paint = new Paint();
paint.setColor(Color.RED);
paint.setStyle(Paint.Style.FILL_AND_STROKE);
width = 200;
height = 200;
}
@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(createDstBitmap(width, height), 0, 0, paint);
canvas.drawBitmap(createDstBitmap2(width, height), width, 0, paint);
canvas.drawBitmap(createSrcBitmap(width, height), width / 2, height / 2, paint);
canvas.restoreToCount(layerID);
}
public Bitmap createDstBitmap(int width, int height) {
Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
Paint scrPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
scrPaint.setColor(0xFFFFCC44);
canvas.drawCircle(width / 2, height / 2, width / 2, scrPaint);
return bitmap;
}
public Bitmap createDstBitmap2(int width, int height) {
Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
Paint scrPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
scrPaint.setColor(0xFFFF0033);
canvas.drawCircle(width / 2, height / 2, width / 2, scrPaint);
return bitmap;
}
public Bitmap createSrcBitmap(int width, int height) {
Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
Paint dstPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
dstPaint.setColor(0xFF66AAFF);
canvas.drawRect(new Rect(0, 0, width, height), dstPaint);
return bitmap;
}
}
显示效果如下:
这是没有任何图像混合的原图。
下面修改关键代码:
canvas.drawBitmap(createDstBitmap(width, height), 0, 0, paint);
canvas.drawBitmap(createDstBitmap2(width, height), width, 0, paint);
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST));
canvas.drawBitmap(createSrcBitmap(width, height), width / 2, height / 2, paint);
paint.setXfermode(null);
首先要清楚,Xfermode操作的对象的是源图像,没有操作的是目标图像。
下面分别对各个模式进行试验:
canvas.drawBitmap(createDstBitmap(width, height), 0, 0, paint);
canvas.drawBitmap(createDstBitmap2(width, height), width, 0, paint);
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
canvas.drawBitmap(createSrcBitmap(width, height), width / 2, height / 2, paint);
paint.setXfermode(null);
问题就在drawBitmap的时候指定了left和top(canvas.translate()方法一样的问题),借用一张图:
Xfermode作用的是canvasBitmap与canvas构造中bitmap取出不同的交、并、补的区域作为最终的bitmap显示出来,我理解就是Xfermode之后作用于bitmap本身,对于额外的位置不能感知,所以导致上面的绘图出现问题。修改后的代码如下:
public class XfermodeView extends View {
Paint paint;
int width;
int height;
public XfermodeView(Context context) {
super(context);
init();
}
public XfermodeView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public XfermodeView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init() {
//初始化画笔
paint = new Paint();
// paint.setColor(Color.RED);
// paint.setStyle(Paint.Style.FILL_AND_STROKE);
paint.setFilterBitmap(false);
width = 200;
height = 200;
}
@Override
protected void onDraw(Canvas canvas) {
//禁用硬件加速
// setLayerType(View.LAYER_TYPE_SOFTWARE, null);
//使用离屏绘制
int layerID = canvas.saveLayer(0, 0, getWidth(), getHeight(), null, Canvas.ALL_SAVE_FLAG);
canvas.drawBitmap(createDstBitmap(width, height), 0, 0, paint);
canvas.drawBitmap(createDstBitmap2(width, height), 0, 0, paint);
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
canvas.drawBitmap(createSrcBitmap(width, height), 0, 0, paint);
paint.setXfermode(null);
canvas.restoreToCount(layerID);
}
public Bitmap createDstBitmap(int width, int height) {
Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
Paint scrPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
scrPaint.setColor(0xFFFFCC44);
canvas.drawOval(new RectF(0, 0, width , height), scrPaint);
return bitmap;
}
public Bitmap createDstBitmap2(int width, int height) {
Bitmap bitmap = Bitmap.createBitmap(2 * width, height, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
Paint scrPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
scrPaint.setColor(0xFFFF0033);
canvas.drawOval(new RectF(width, 0, 2 * width , height), scrPaint);
return bitmap;
}
public Bitmap createSrcBitmap(int width, int height) {
Bitmap bitmap = Bitmap.createBitmap(width / 2 + width, height / 2 + height, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
Paint dstPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
dstPaint.setColor(0xFF66AAFF);
canvas.drawRect(new Rect(width / 2, height / 2, width / 2 + width, height / 2 + height),
dstPaint);
return bitmap;
}
}
效果如下:
注意这个效果是没有毛病的,可能有人会说,右边圆多显示了半圆,其实不然,我把布局放出来:
为了显示出层级关系,我把部分框稍微画大了一点。因为源图像与目标图像2的右半部分没有相交,所以对那部分没有影响,该怎么样显示就怎么样显示。不信我们修改一行代码:
public Bitmap createSrcBitmap(int width, int height) {
// 把这个Bitmap的宽加大一点,包容所有的目标图像
Bitmap bitmap = Bitmap.createBitmap(width + width, height / 2 + height, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
Paint dstPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
dstPaint.setColor(0xFF66AAFF);
canvas.drawRect(new Rect(width / 2, height / 2, width / 2 + width, height / 2 + height),
dstPaint);
return bitmap;
}
Android Paint Xfermode 学习小结
Android高级进阶——绘图篇(五)setXfermode 设置混合模式