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源碼!】