Android大图绘制——硬件加速限制分析与方案
Posted David-Kuper
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android大图绘制——硬件加速限制分析与方案相关的知识,希望对你有一定的参考价值。
最近在做PhotoView图片的效果定制时,在加载展示图片情境下,统一把图片按照屏幕宽度作为固定值,计算宽高的缩放比然后对Bitmap进行伸缩,这样可以避免一般情况下的大图加载产生——OOM和trying to draw too large(xxxbytes) bitmap的问题。
当即便这样,也还是会有加载的图片尺寸超过限制的时候,就经常会看到这个warning,图片显示不出来:
Bitmap too large to be uploaded into a texture (1080x9431, max=8192x8192)
Bitmap too large to be uploaded into a texture (1080x9431, max=8192x8192)
因为指定的比例的按照屏幕宽度进行宽高比缩放,宽度到了合适的范围,但是当图片太长的时候,还是会触发上面的问题,无法显示图片。
方案一:关闭硬件加速
上面的警告是由于OpenGL硬件加速造成的,可以在Activity、Application、Window、甚至View中把硬件加速关闭即刻。关闭的方式如下:
1、硬件加速的级别
Application
<application
android:hardwareAccelerated="false"
...>
</application>
Activity
<application
android:hardwareAccelerated="true">
<activity ... />
<activity android:hardwareAccelerated="false" />
</application>
Window
getWindow().setFlags(
WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED,
WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED);
View
myView.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
Note: 你可以关闭View级别的硬件加速,但是你不能在View级别开启硬件加速,因为它还依赖其他的设置
2、两种获取是否支持硬件加速的方式
View.isHardwareAccelerated() //returns true if the View is attached to a hardware accelerated window.
Canvas.isHardwareAccelerated() //returns true if the Canvas is hardware accelerated
如果必须进行这样的验证,建议你在draw的代码块中使用:Canvas.isHardwareAccelerated(),因为如果一个View被attach到一个硬件加速的Window上,即使没有硬件加速的Canvas,它也是可以被绘制的。比如:将一个View以bitmap的形式进行缓存通过关闭硬件加速可以避免上面的问题,但是会让使用软件变得卡顿。因此这不是一个好的解决方案,直接放弃。
方案二:获取硬件加速的最大限制值,然后再对将Bitmap进行裁剪、缩放
既然是超出了最大限制,那么只要我们将Bitmap缩放到合适的值即可。那么思路就是:
Step1:获取OpenGL硬件加速最大长宽限制
Step2:开启大图模式,阻止PhotoView默认情况下的ScaleType:FIT_CENTER
Step3:获取Bitmap进行缩放、裁剪处理
Step4:重新刷新绘制
获取OpenGL硬件加速最大限制方式如下:
/**
* 在Lollipop版本之前可以直接获取硬件加速值
*/
private void getGLESTextureLimitBelowLollipop()
GLES10.glGetIntegerv(GLES10.GL_MAX_TEXTURE_SIZE, maxTextureSize, 0);
/**
* 在Lollipop版本之后,需要用下面的方式获取
* 拿到OpenGL硬件加速所允许的最大长宽,用来做二次bitmap压缩
*/
private void getGLESTextureLimitEqualAboveLollipop()
EGL10 egl = (EGL10) EGLContext.getEGL();
EGLDisplay dpy = egl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);
int[] vers = new int[2];
egl.eglInitialize(dpy, vers);
int[] configAttr =
EGL10.EGL_COLOR_BUFFER_TYPE, EGL10.EGL_RGB_BUFFER, EGL10.EGL_LEVEL, 0,
EGL10.EGL_SURFACE_TYPE, EGL10.EGL_PBUFFER_BIT, EGL10.EGL_NONE
;
EGLConfig[] configs = new EGLConfig[1];
int[] numConfig = new int[1];
egl.eglChooseConfig(dpy, configAttr, configs, 1, numConfig);
if (numConfig[0] == 0) // TROUBLE! No config found.
EGLConfig config = configs[0];
int[] surfAttr =
EGL10.EGL_WIDTH, 64, EGL10.EGL_HEIGHT, 64, EGL10.EGL_NONE
;
EGLSurface surf = egl.eglCreatePbufferSurface(dpy, config, surfAttr);
final int EGL_CONTEXT_CLIENT_VERSION = 0x3098; // missing in EGL10
int[] ctxAttrib =
EGL_CONTEXT_CLIENT_VERSION, 1, EGL10.EGL_NONE
;
EGLContext ctx = egl.eglCreateContext(dpy, config, EGL10.EGL_NO_CONTEXT, ctxAttrib);
egl.eglMakeCurrent(dpy, surf, surf, ctx);
GLES10.glGetIntegerv(GLES10.GL_MAX_TEXTURE_SIZE, maxTextureSize, 0);
egl.eglMakeCurrent(dpy, EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_CONTEXT);
egl.eglDestroySurface(dpy, surf);
egl.eglDestroyContext(dpy, ctx);
egl.eglTerminate(dpy);
拿到了硬件加速值之后,就可以对当前的大图进行测量,然后决定是否需要剪裁、缩放。
方案三:分块显示大图Bitmap
对于超大的Bitmap可以进行分块展示。这种方案有两种不同实现:
(1)把Bitmap分块切割到List中,使用ListView或者RecyclerView中展示。
——这样做比较方便,容易实现,但是方法优点投机取巧。
实现步骤:
这种方式的实现步骤主要就在于Bitmap的分割,只要确定了每一个子View的大小,那么分割就完成了。方法很简单,就不在赘述了。
(2)使用BitmapRegionDecoder类,重写ImageView的Touch事件,保证在手指移动内容的时候将对应绘制区域的Bitmap进行加载绘制,也就是说只加载当前绘制区的Bitmap。
——这样做的方式最好,官方推荐,实现方式比第一种稍微复杂一点。
实现步骤:
step1:监听ImageView的onTouch事件。
step2:传入图片的输入流inputStream,解析Bitmap的大小,初始化可见绘制区Rect的坐标。
step3:在滑动过程中确定可见区Rect的坐标,然后通过BitmapRegionDecoder加载对应的内容至Rect中,生成可见区的Bitmap
step4:在onDraw中对可见区Bitmap进行绘制。
以上是关于Android大图绘制——硬件加速限制分析与方案的主要内容,如果未能解决你的问题,请参考以下文章