android圖片壓縮的三種方式
為了避免oom的出現,幾乎每個應用都會對大圖進行壓縮,我現在手頭做的產品就有很多地方用到,以前封裝工具類的時候,都是在網上找東找西,然後拼拼湊湊,有效果就行了,一直用的迷迷糊糊,這幾天工作比較閒,正好系統的總結梳理一下圖片壓縮方式:
圖片壓縮現在常見的有三種方式:
1、等比壓縮,等比壓縮是保持原圖長寬比例的壓縮,只是圖片變小,展示的還是原圖的所有內容(區別於第二種通過Matrix壓縮,可以選取圖片的一部分,類似於上傳頭像時,讓你在圖上選一塊zoom的形式)。等比壓縮用的的主要是BitmapFactory.Options,通過options縮放比例的設定,來生成縮圖:
/** * @param path 圖片路徑 * @param targetSize 縮放後期待的長邊(圖片長和寬大的那一個邊)的長度 * @param targetW 期待的縮放後寬度 * @param targetH 期待的縮放後高度 * @return */ public static Bitmap equalRatioScale(String path,int targetW,int targetH){ // 獲取option BitmapFactory.Options options = new BitmapFactory.Options(); // inJustDecodeBounds設定為true,這樣使用該option decode出來的Bitmap是null, // 只是把長寬存放到option中 options.inJustDecodeBounds = true; // 此時bitmap為null Bitmap bitmap = BitmapFactory.decodeFile(path, options); int inSampleSize = 1; // 1是不縮放 // 計算寬高縮放比例 int inSampleSizeW = options.outWidth / targetW; int inSampleSizeH = options.outHeight / targetH; // 最終取大的那個為縮放比例,這樣才能適配,例如寬縮放3倍才能適配螢幕,而 // 高不縮放就可以,那樣的話如果按高縮放,寬在螢幕內就顯示不下了 if (inSampleSizeW > inSampleSizeH) { inSampleSize = inSampleSizeW; }else { inSampleSize = inSampleSizeH; } // 設定縮放比例 options.inSampleSize = inSampleSize; // 一定要記得將inJustDecodeBounds設為false,否則Bitmap為null options.inJustDecodeBounds = false; bitmap = BitmapFactory.decodeFile(path, options); return bitmap; }
2、通過Matrix進行更加靈活的縮放:這種方式主要是通過構建縮放矩陣和Bitmap.createBitmap方法來實現靈活縮放,寬和高縮放的比例可以不一致,而且通過Bitmap.createBitmap方法創建出來的是新的點陣圖,這個點陣圖可以是選取原圖的一部分,而不是對原圖進行整體縮放!類似於上傳頭像時,讓你在原圖上扣下來一塊的效果,控制非常靈活。
/** * @param path 原圖路徑 * @param offsetX 擷取開始點在X軸偏移量 * @param offsetY 擷取開始點在Y軸偏移量 * @param targetW 擷取多寬(畫素) * @param targetH 擷取多高(畫素) * @return */ public static Bitmap matrixScale(String path,int offsetX,int offsetY,int targetW,int targetH){ // 構建原始點陣圖 Bitmap bitmap = BitmapFactory.decodeFile(path); // 獲取原始寬高 int width = bitmap.getWidth(); int height = bitmap.getHeight(); // 計算寬高縮放比例,targetW,targetH即期待縮放完成後點陣圖的寬高 float scaleW = (float)targetW / width; float scaleH = (float)targetH / height; // 將縮放比例放進矩陣 Matrix matrix = new Matrix(); matrix.postScale(scaleW, scaleH); // 這個方法作用非常多,詳細解釋一下各個引數的意義! // bitmap:原始點陣圖 // 第二到第五個引數,即擷取原圖哪一部分構建新點陣圖, // offsetX和offsetY代表在X軸和Y軸上的畫素偏移量,即從哪個位置開始擷取 // width和height代表擷取多少個畫素,但是要注意,offsetX+width應該小於等於原圖的寬度 // offsetY+height小於等於原圖高度,要不然會報錯,因為截到原圖外面去了 // 像下面這樣填寫,就代表擷取整個原圖, // Bitmap.createBitmap(bitmap, 0, 0, width, height, matrix, false); // 如果填寫100,100,200,200,就代表 // 從原圖左上角往右和下各偏移100畫素,然後往後和往下各擷取200構建新點陣圖 // matrix:縮放矩陣 // 最後一個引數表示如果矩陣裡面還存放了過濾條件,是否按條件過濾(如果matrix裡面只放了平移資料),最後一個引數設定成什麼都不會生效 bitmap = Bitmap.createBitmap(bitmap, offsetX, offsetY, width, height, matrix, false); return bitmap; }
3、無失真壓縮,無失真壓縮是說圖片大小和清晰度看上去和原圖沒有什麼差別,但是確實size縮小了,這裡縮小的原理是犧牲了解析度等其他直觀看不到的東西,看起來和原圖一樣,但是一放大就立馬失真了,不會和原圖一樣放大很多才會逐漸變得不清晰。無失真壓縮後的圖片畫素並不會減少,而Bitmap佔用記憶體的定義就是畫素點佔的記憶體,所以以Bitmap的方式載入到記憶體中時,和壓縮前佔用的記憶體是同樣大的,原來會oom的圖片,質量壓縮後同樣會oom;但是,質量壓縮後將流輸出到檔案中,檔案的size會大幅度減小,所以質量壓縮特別適合在Android端進行圖片上傳的時候進行圖片壓縮,既能保持上傳後的清晰度,又能減小size。另外質量壓縮不是可以無限縮小的,降低到一定程度,就算把quality設定的再小,size也不會再降低了。另外這種方式最好返回儲存壓縮後的圖片儲存的檔案路徑,而不要直接返回Bitmap,示例就懶得改了。
<pre name="code" class="java">/**
* @param path 圖片路徑
* @param quality 質量 0-100,100表示原圖
* @return
*/
public static Bitmap losslessScale(String path,int quality){
Bitmap bitmap = BitmapFactory.decodeFile(path);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
bitmap.compress(CompressFormat.JPEG, quality, baos);
Log.e("哈哈","原始大小:" + baos.toByteArray().length);
// 因為質量壓縮不是可以無限縮小的,所以一張高質量的圖片,再怎麼壓縮,
// 最終size可能還是大於你指定的size,造成異常
// 所以不建議迴圈壓縮,而是指定quality,進行一次壓縮就可以了
// while (baos.toByteArray().length / 1024 > maxSize) {
// quality -= 10;
// baos.reset();
// bitmap.compress(CompressFormat.JPEG, quality, baos);
// Log.e("哈哈","過程中大小為:"
// + baos.toByteArray().length);
// }
bitmap.compress(CompressFormat.JPEG, quality, baos);
Log.e("哈哈","最終大小" + baos.toByteArray().length);
Bitmap compressedBitmap = BitmapFactory.decodeByteArray(
baos.toByteArray(), 0, baos.toByteArray().length);
return compressedBitmap;
}
一張圖片處理過程,建議先進行等比壓縮或者Matrix壓縮後,再進行質量壓縮,這樣組合使用,不管是生成縮圖還是圖片上傳,效果都不錯