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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM