Android Bitmap详解
Google Developer: Bitmap
一、基本信息
Bitmap位图包括像素以及长、宽、颜色等描述信息。长宽和像素位数是用来描述图片的,可以通过这些信息计算出图片的像素占用内存的大小。
位图可以理解为一个画架,把图放到上面然后可以对图片做一些列的处理。
位图文件图像显示效果好,但是非压缩格式,需要占用较大的存储空间。
1. Config:表示图片像素类型,包括ALPHA_8、RGB_565、ARGB_4444、ARGB_8888 A:透明度;RGB分别是Red、Green、Blue,三种原色
ARGB_8888:四个通道都是8位,每个像素占用4个字节,图片质量是最高的,但是占用的内存也是最大的;
ARGB_4444:四个通道都是4位,每个像素占用2个字节,图片的失真比较严重;
RGB_565:没有A通道,每个像素占用2个字节,图片失真小,但是没有透明度;
ALPHA_8:只有A通道,每个像素占用1个字节大大小,只有透明度,没有颜色值。
使用场景总结:ARGB_4444失真严重,基本不用;ALPHA_8使用场景特殊,比如设置遮盖效果等;不需要设置透明度,RGB_565是个不错的选择;既要设置透明度,对图片质量要求又高,就用ARGB_8888。
2. CompressFormat:Bitmap.CompressFormat.JPEG、Bitmap.CompressFormat.PNG、Bitmap.CompressFormat.WEBP三种压缩格式
JPEG:一种有损压缩(JPEG2000既可以有损也可以无损),".jpg"或者".jpeg"; 优点:采用了直接色,有丰富的色彩,适合存储照片和生动图像效果;缺点:有损,不适合用来存储logo、线框类图。
PNG: 一种无损压缩,".png"; 优点:支持透明、无损,主要用于小图标,透明背景等;缺点:若色彩复杂,则图片生成后文件很大;
WEBP:以WebP算法进行压缩;Google开发的新的图片格式,同时支持无损和有损压缩,使用直接色。无损压缩,相同质量的webp比PNG小大约26%;有损压缩,相同质量的webp比JPEG小25%-34% 支持动图,基本取代gif
详细介绍参见 【转】WebP原理和支持现状
二、加载
BitmapFactory提供了四类方法:decodeFile、decodeResource、decodeStream、decodeByteArray
1. 从文件中读取
try { FileInputStream in = new FileInputStream("/sdcard/Download/sample.png"); } catch (FileNotFoundException e) { e.printStackTrace(); } Bitmap bitmap = BitmapFactory.decodeStream(in);
Bitmap bm = BitmapFactory.decodeFile(sd_path); // 间接调用 BitmapFactory.decodeStream
2. 从资源中读取
Bitmap bitmap = BitmapFactory.decodeResource(getContext().getResources(), R.drawable.sample); // 间接调用 BitmapFactory.decodeStream
3. 从字节序列里读取
// InputStream转换成byte[] Bitmap bm = BitmapFactory.decodeByteArray(myByte,0,myByte.length);
巨图加载:BitmapRegionDecoder,可以按照区域进行加载
高效加载:核心其实也很简单,主要是采样压缩、缓存策略、异步加载等
1. 采样压缩 (-> 【自】高斯模糊)
BitmapFactory.Options options = new BitmapFactory.Options(); //inJustDecodeBounds为true,不返回bitmap,只返回这个bitmap的尺寸 options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(getResources(), images[position], options); //利用返回的原图片的宽高,我们就可以计算出缩放比inSampleSize(只能是2的整数次幂)
options.inSampleSize = caluelateInSampleSize(options, reqWidth, reqHeight);//使用RGB_565减少图片大小 options.inPreferredConfig = Bitmap.Config.RGB_565; //释放内存,共享引用(21版本后失效) options.inPurgeable = true; options.inInputShareable = true; //inJustDecodeBounds为false,返回bitmap options.inJustDecodeBounds = false; Bitmap bitmap = BitmapFactory.decodeResource(getResources(), images[position], options);
private int calculateSampleSize(BitmapFactory.Options options,int reqWidth,int reqHeight){int width = options.outWidth; int height =options.outHeight;int inSampleSize = 1; int halfWidth = width / 2; int halfHeight = height / 2; while((halfWidth / inSampleSize) >= reqWidth && (halfHeight / inSampleSize) >= reqHeight){ inSampleSize *= 2; } return inSampleSize; }
2. 缓存策略
LRU(最近最少使用) -> LruCache和DiskLruCache,LruCache用于内存缓存、DiskLruCache用户存储设备缓存
【转】Fresco缓存分析 三级缓存:Bitmap -> 未解码图片缓存 -> 磁盘缓存 ->网络
md5 -> 【自】密码学基础
3. 异步加载
【转】Fresco异步加载:线程池、Handler等
三、存储
根据android sdk版本有所不同。
2.3以前:图片像素存储在native内存中。缺点是虚拟机无法自动进行垃圾回收,必须手动使用recycle,很容易导致内存泄露。也不方便调试等;
3.0以后:图片像素存储在Java堆中,垃圾回收能够自动进行,内存占用也能方便的展示在monitor中;
4.0以后:传输方式发生变化,大数据会通过ashmem(匿名共享内存)来传递(不占用Java内存),小数据通过直接拷贝的方式(在内存中操作),放宽了图片大小的限制;
【转】Android4.0 Bitmap Parcel传输源码分析
6.0以后:加强了ashmen存储图片的方式
【转】Android6.0 Bitmap存储以及Parcel传输源码分析
enum class PixelStorageType { Invalid, External, Java, Ashmem, };
Fresco 5.0以前将图片存储在Ashmen中,不会自动GC,因为不在Java堆中,也不会造成OOM,很大的一个优势。5.0以后系统默认启用了Ashmen模式。
四、转换
// 定义矩阵 Matrix matrix = new Matrix(); // 【缩放图像】 matrix.postScale(0.8f, 0.9f); // 【向左旋转】 matrix.postRotate(-90); // 【移动图像】 matrix.postTranslate(100, 100); // 【裁减图像】 Bitmap.createBitmap(Bitmap source, int x, int y, int width, int height, Matrix m, boolean filter)
// 【圆角】
// 准备画笔 Paint paint = new Paint(); paint.setAntiAlias(true); // 准备裁剪的矩阵 Rect rect = new Rect(0, 0, originBitmap.getWidth(), originBitmap.getHeight()); RectF rectF = new RectF(new Rect(0, 0, originBitmap.getWidth(), originBitmap.getHeight())); Bitmap roundBitmap = Bitmap.createBitmap(originBitmap.getWidth(), originBitmap.getHeight(), Bitmap.Config.ARGB_8888); Canvas canvas = new Canvas(roundBitmap); // 圆角矩阵,radius为圆角大小 canvas.drawRoundRect(rectF, radius, radius, paint); paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN)); // 这个比较有意思,混合模式 SRC_IN:显示交汇SRC图 canvas.drawBitmap(originBitmap, rect, rect, paint);
//【图片灰阶处理】 Bitmap grayBitmap = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), Config.ARGB_8888); Canvas canvas = new Canvas(grayBitmap); Paint mPaint = new Paint(); //创建颜色变换矩阵 ColorMatrix colorMatrix = new ColorMatrix(); //设置饱和度为0,实现灰阶效果 colorMatrix.setSaturation(0); //创建颜色过滤矩阵 ColorMatrixColorFilter colorFilter = new ColorMatrixColorFilter(colorMatrix); //设置画笔的颜色过滤矩阵 paint.setColorFilter(colorFilter); //使用处理后的画笔绘制图像 canvas.drawBitmap(bitmap, 0, 0, paint);
// 【Alpha位图】 // 【倒影】 // 【图像合成】 // 无穷无尽啊...
五、内存优化
基于前面的原理来进行各方面的优化处理,缩放、Config、Compress选择、内存管理、缓存方式等等方面入手
六、开源框架
ImageLoader、Glide(google)、Fresco(FaceBook)、Picasso(Square)
Picasso包体积小、清晰,但功能有局限不能加载gif、只能缓存全尺寸;
Glide功能全面,擅长大型图片流,提交较大;
Fresco内存优化,减少oom,体积更大
【要求:起码阅读Glide和Fresco源码!】