图形、解决大图OOM、绘画工具的使用和练习

计算机图形表示的原理

首先要明确的一点是,一张图片的在内存中存储所需的大小和图片在屏幕设备上完整显示所需的内存大小是有非常大的差异的。

如下图,从中我们可以清晰的看出这张图片,在硬盘上所占的存储空间是303KB,也就是310272个字节。但是如果想把这样图片完整的展示到屏幕设备上,所需的内存空间远远不止这些。

有这样一个计算公式:图片展示所需内存 = 图片的宽度像素 × 图片的高度像素 × 每个像素的大小

那么,像素大小怎么知道呢?当我们点击图片另存为时,会出现下图:

依次来介绍一下其中的含义。

单色位图:要么黑、要么白,只需要一bit就可以表示,需要1/8个字节来表示一个像素值。在这种情况下上面的图片显示到加载入内存中需要:1920 * 1200 * 1/8 / 1024 / 1024= 0.2M。16位图:需要1/2一个字节来表示一个像素值。在这种情况下图片加载入内存准备显示需要:1920 * 1200 * 1 / 2 / 1024 / 1024 = 1.09M。256位图:表示一个像素值需要用1个字节来表示,在这种情况下图片加载入内存准备显示需要:1920 * 1200 * 1 / 1024 / 1024 = 2.19M。24位位图:表示一个像素值需要3个字节来表示,在这种情况下图片加载入内存准备显示需要:1920 * 1200 * 3 / 1024 / 1024 = 6.59M。而在Android操作系统中,使用的是ARGB来表示一个像素值其中A代表的是透明度;那么在这种情况下,在这种情况下图片加载入内存准备显示需要:1920 * 1200 * 4 / 1024 / 1024 = 8.79M。

如果在Andorid中,系统为应用默认提供的VM Heap是16M,在不对图片进行压缩处理的情况下,一定会出现OOM异常。

加载大图出现OOM

让我们加载一张大图试试吧,图片资源如下,大小为1.7M。

创建一个工程,,在布局中加一个ImageView控件,并在MainActivity中找到控件,设置图片。

ImageView iv = (ImageView) findViewById(R.id.iv);Bitmap bitmap = BitmapFactory.decodeFile(“/mnt/sdcard/dog.jpg”);iv.setImageBitmap(bitmap );

代码很简单,当运行时就出现了错误,让我们看看错误是什么:

可以看到,应用程序向系统申请了30720012个字节,然后就直接出现了OutOfMemoryError错误。

在下一节中将讲述如何解决加载大图而不出现OOM的方法。

缩放加载大的图片资源

我们可以看到,这样狗狗的图片是2400*3200的,而我们的手机只是320*480。如果完全直接放上去,一来是会出现异常;二来是浪费资源。

Google工程师已经我们准备好了解决办法,再使用BitmapFactory去解析资源时,先获取被加载图片的宽高,并结合手机设备的屏幕宽高计算出缩放比例,然后再去使用这个缩放比例加载图片资源到内存中。

代码也比较简单,请看:

ImageView iv = (ImageView) findViewById(R.id.iv);// ★1. 使用窗口管理者,获取手机屏幕的宽高WindowManager wm = (WindowManager) getSystemService(WINDOW_SERVICE);int screenHeight = wm.getDefaultDisplay().getHeight();int screenWidth = wm.getDefaultDisplay().getWidth();// ★2. 在不把图片加载入内存的情况下,获取图片的属性和配置BitmapFactory.Options options = new Options();// 此参数设置为true是,使用BitmapFactory解析资源并不会返回Bitmap,但是资源的相关配置却会被设置:例如图片的宽高options.inJustDecodeBounds = true;BitmapFactory.decodeFile(“/mnt/sdcard/dog.jpg”, options);// 拿到图片的宽高int outHeight = options.outHeight;int outWidth = options.outWidth;// ★3. 根据屏幕宽高和图片宽高计算出缩放比例int scale = 0;int scaleH = outHeight / screenHeight;int scaleW = outWidth / screenWidth;scale = scaleH > scaleW ? scaleH : scaleW;// ★4. 根据缩放比去解析图片资源,并返回Bitmapoptions.inJustDecodeBounds = false;options.inSampleSize = scale;Bitmap bitmap = BitmapFactory.decodeFile(“/mnt/sdcard/dog.jpg”, options);iv.setImageBitmap(bitmap);创建一个原图的副本

在Android加载到内存的Bitmap是不允许修改的,只能够在其副本上修改和作画。那么如何创建一个原图的副本呢?

需要以下这些步骤:①准备一个和原图宽高及配置完全一样的白纸②白纸放在画布上③准备一支笔④准备一个矩阵

代码也比较简单,其中涉及了Canvas、Paint、Matrix等类,下面是简单的拷贝原图的代码:

ImageView srcImageView = (ImageView) findViewById(R.id.iv_src);ImageView copyImageView = (ImageView) findViewById(R.id.iv_copy);// 原图Bitmap srcBitmap = BitmapFactory.decodeFile(“/mnt/sdcard/meinv.jpg”);srcImageView.setImageBitmap(srcBitmap);// 拷贝// 1. 准备一个和原图宽高完全一样的白纸Bitmap copyBitmap = Bitmap.createBitmap(srcBitmap.getWidth(), srcBitmap.getHeight(), srcBitmap.getConfig());// 2. 把白纸放在画布上Canvas canvas = new Canvas(copyBitmap);// 3. 准备一支笔Paint paint = new Paint();// 4. 准备一个矩阵Matrix matrix = new Matrix();// 使用指定的矩阵绘图canvas.drawBitmap(srcBitmap, matrix, paint);copyImageView.setImageBitmap(copyBitmap);

测试结果如下:

图形处理的常用的API

就如在学动画的时候,图形的处理操作也分为以下种类,操作的关键步骤是使用矩阵进行变化;Google工程师已经帮我们把这些操作封装的很完善了。

平移:使你疲倦的不是前面的高山,而是你鞋里的一粒沙子。

图形、解决大图OOM、绘画工具的使用和练习

相关文章:

你感兴趣的文章:

标签云: