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用户存储设备缓存

Lru

【转】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);

 

【转】ndroid图像处理-Paint之Xfermode

//【图片灰阶处理】
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源码!】

 

posted @ 2018-06-01 00:35  小爷宋  阅读(39500)  评论(0编辑  收藏  举报