1. 程式人生 > >圖片壓縮CompressUtil解析

圖片壓縮CompressUtil解析

溢出 ans tps bounds 相關 ima method 結合 默認圖片

  • CompressUtil 流程圖:
    技術分享圖片

CompressUtil 類 具體解釋

public class CompressUtil {

/**
 * 終於封裝的壓縮方法
 * @param imgPath
 * @return
 */
public static Bitmap process(String imgPath){
    int degree = readPictureDegree(imgPath);    //獲取旋轉角度
    Bitmap bmp =getBmpByMaxSize(imgPath, 480);  //獲取當前路徑圖片的位圖,最大尺寸180*1024
    if (degree != 0) {
        bmp = rotaingImageView(degree, bmp);    //有旋轉角度的旋轉角度
    }
    return bmp;
}
/**
 * 讀取圖片屬性:旋轉的角度
 * @param path 圖片絕對路徑
 * @return degree旋轉的角度
 */
@SuppressLint("NewApi")
public static int readPictureDegree(String path){
    int degree  = 0;    //設置默認圖片角度為0度
    try {
        /**
         * Exif: 圖像信息
         * Exif是一種圖像文件格式。它的數據存儲與JPEG格式是全然同樣的。

* 實際上Exif格式就是在JPEG格式頭部插入了數碼照片的信息。包含 * 拍攝時的光圈、快門、白平衡、ISO、焦距、日期時間等各種和拍攝 * 條件以及相機品牌、型號、色彩編碼、拍攝時錄制的聲音以及GPS全 * 球定位系統數據、縮略圖等。

你能夠利用不論什麽能夠查看JPEG文件的 * 看圖軟件瀏覽Exif格式的照片,但並非全部的圖形程序都能處理 * Exif信息。 * * ExifInterface: 圖像信息接口類 */ /** * ExifInterface構造函數 ExifInterface(String filename) * 從指定的JPEG文件裏讀取EXIF標簽。

*/ //獲取指定圖片的圖片處理對象 ExifInterface exifInterface = new ExifInterface(path); /** * int <- getAttributeInt(String tag, int defaultValue) * 返回指定標記的整數值 * * Attribute : 屬性 * * tag : 指標 (一共21個) * ExifInterface.TAG_APERTURE => 光圈指標 * ExifInterface.TAG_DATETIME => 日期時間指標 * ExifInterface.TAG_EXPOSURE_TIME => 公布時間指標 * ExifInterface.TAG_FLASH => 閃光燈 * ExifInterface.TAG_FOCAL_LENGTH => 焦距指標 * ExifInterface.TAG_GPS_ALTITUDE => GPS海拔高度指標 * ExifInterface.TAG_GPS_ALTITUDE_REF => GPS海拔參考點指標 * ExifInterface.TAG_GPS_DATESTAMP => GPS日期戳指標 * ExifInterface.TAG_GPS_LATITUDE => GPS緯度指標 * ExifInterface.TAG_GPS_LATITUDE_REF => GPS緯度參考點指標 * ExifInterface.TAG_GPS_LONGITUDE => GPS經度指標 * ExifInterface.TAG_GPS_LONGITUDE_REF => GPS經度參考點指標 * ExifInterface.TAG_GPS_PROCESSING_METHOD => GPS加工指標 * ExifInterface.TAG_GPS_TIMESTAMP => GPS時間戳指標 * ExifInterface.TAG_IMAGE_LENGTH => 圖像長度指標 * ExifInterface.TAG_IMAGE_WIDTH => 圖像寬度指標 * ExifInterface.TAG_ISO => 標簽 * ExifInterface.TAG_MAKE => 制作 * ExifInterface.TAG_MODEL => 模型 * ExifInterface.TAG_ORIENTATION => 定位指標 * ExifInterface.TAG_WHITE_BALANCE => 白平衡指標 * * defaultValue : 默認值 (一共11個) * * ExifInterface.ORIENTATION_FLIP_HORIZONTAL => 水平翻轉 * ExifInterface.ORIENTATION_FLIP_VERTICAL => 垂直翻轉 * ExifInterface.ORIENTATION_NORMAL => 正常 * ExifInterface.ORIENTATION_ROTATE_180 => 旋轉180度 * ExifInterface.ORIENTATION_ROTATE_270 => 旋轉270度 * ExifInterface.ORIENTATION_ROTATE_90 => 旋轉90度 * ExifInterface.ORIENTATION_TRANSPOSE => 取向的轉置 * ExifInterface.ORIENTATION_TRANSVERSE => 方位橫向 * ExifInterface.ORIENTATION_UNDEFINED => 方向沒有定義 * ExifInterface.WHITEBALANCE_AUTO => 白平衡自己主動 * ExifInterface.WHITEBALANCE_MANUAL => 手動白平衡 */ // 獲取該圖片的方向參數 int orientation = exifInterface.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_ROTATE_90); switch (orientation) { case ExifInterface.ORIENTATION_ROTATE_90: degree = 90; break; case ExifInterface.ORIENTATION_ROTATE_180: degree = 180; break; case ExifInterface.ORIENTATION_ROTATE_270: degree = 270; break; } } catch (IOException e) { e.printStackTrace(); } return degree; } /** * 旋轉圖片 * @param angle * @param bitmap * @return Bitmap * * Bitmap : 位圖 * Bitmap是Android系統中的圖像處理的最重要類之中的一個。 * 用它能夠獲取圖像文件信息。進行圖像剪切、旋轉、縮 * 放等操作。並能夠指定格式保存圖像文件。 * * Bitmap實如今android.graphics包中。

可是Bitmap * 類的構造函數是私有的,外面並不能實例化。僅僅能是通 * 過JNI實例化。

這必定是 某個輔助類提供了創建Bitmap * 的接口,而這個類的實現通過JNI接口來實例化Bitmap的, * 這個類就是BitmapFactory。 * * decode : 解碼 * * BitmapFactory.decodeFile(String pathName) * BitmapFactory.decodeFile(String pathName,Options opts) * BitmapFactory.decodeResource(Resource res,int id) * BitmapFactory.decodeResource(Resource res,int id,Options opts) * * 利用BitmapFactory能夠從一個指定文件裏,利用decodeFile()解出Bitmap; * 也能夠定義的圖片資源中,利用decodeResource()解出Bitmap * * 當中Options是decode時的選項 * 在用法decodeFile()/decodeResource()時。都能夠指定一個BitmapFacotry.Options。 * * 利用Options的下列屬性,能夠指定decode的選項 * inPreferredConfig => decode到內存中,手機中所採用的編碼,可選值定義在Bitmap.Config中。缺省值是ARGB_8888 * inJustDecodeBounds => 假設設置為true,並不會把圖像的數據全然解碼,亦即decodeXyz()返回值為null,可是Options的outAbc中解出了圖像的基本信息 * inSampleSize => 設置decode時的縮放比例 * */ public static Bitmap rotaingImageView(int angle , Bitmap bitmap) { // Bitmap能夠和Matrix結合實現圖像的剪切、旋轉、縮放等操作 //獲取Matrix對象 Matrix matrix = new Matrix(); //設置旋轉角度 matrix.postRotate(angle); // 創建新的圖片 Bitmap resizedBitmap=bitmap; /** * 用原Bitmap通過變換生成新的Bitmap的方法: * * public static Bitmap createBitmap(Bitmap source,int x,int y,int width,int height,Matrix m,boolean filter) * 這樣的方法是終於的實現,後兩種僅僅是對這樣的方法的封裝 * * public static Bitmap createBitmap(Bitmap source,int x,int y,int width,int height) * 這樣的方法能夠從源Bitmap中指定區域(x,y, width, height)中挖出一塊來實現剪切 * * public static Bitmap createScaledBitmap(Bitmap src,int dstWidth,int dstHeight,boolean filter) * 這樣的方法能夠把源Bitmap縮放為dstWidth x dstHeight的Bitmap * * filter => 設為true => 對Bitmap進行濾波處理,會有抗鋸齒的效果 */ try { resizedBitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true); /** * Bitmap的recycle問題 * recycle : 回收 * 盡管Android有自己的垃圾回收機制,對於是不是要我們自己調用recycle。還的看情況而定。 * 假設僅僅是使用少量的幾張圖片,回收與否關系不大。可是若有大量bitmap須要垃圾回收處理, * 那必定垃圾回收須要做的次數就很多其它也發生地更頻繁。會對系統資源造成負荷。所以,這個時 * 候還是自己試用recycle來釋放的比較好。 * * 註意 => 僅僅有當你確認你不會在使用這個bitmap的時候。就能夠選擇調用recycle()方法釋放它。 * */ bitmap.recycle(); //進行垃圾回收 System.gc(); }catch (OutOfMemoryError e){ e.printStackTrace(); } return resizedBitmap; } /** * 降低圖片質量壓縮 * @param bmp * @param maxSize * @param fileSize * @return */ public static Bitmap compressBmp(Bitmap bmp, int maxSize, long fileSize) { Bitmap newBmp=bmp; //ByteArrayOutputStream => 捕獲內存緩沖區的數據,轉換成字節數組。 ByteArrayOutputStream baos=null; //ByteArrayInputStream => 將字節數組轉化為輸入流 ByteArrayInputStream bais=null; int quality = 100; if(fileSize>4*1024*1024){ quality=40; }else if(fileSize>2*1024*1024){ quality=50; }else if(fileSize>800*1024){ quality=60; } try { /** * ByteArrayOutputStream類是在創建它的實例時,程序內部創建一個byte型別數組的緩沖區, * 然後利用ByteArrayOutputStream和ByteArrayInputStream的實例向數組中寫入或讀出 * byte型數據。在網絡傳輸中我們往往要傳輸非常多變量。我們能夠利用ByteArrayOutputStream * 把全部的變量收集到一起。然後一次性把數據發送出去。

*/ baos = new ByteArrayOutputStream(); System.out.print("開始壓縮: " + quality); /** * 圖片壓縮 * Bitmap.compress(CompressFormat format, int quality, OutputStream stream) * 方法的參數format可設置JPEG或PNG格式;quality可選擇壓縮質量;fOut是輸出流(OutputStream) */ bmp.compress(Bitmap.CompressFormat.JPEG, quality, baos); float maxByte = maxSize * 1024; baos.flush(); float scale = 1; while (baos.size() > maxByte) { System.out.print("壓縮大小:" + baos.size() / 1024); System.out.print("壓縮大小2:" + baos.toByteArray().length / 1024); scale = Math.round((float) baos.size() / maxByte); if (scale < 1) scale = 1; quality -= scale * 2; baos.reset(); //重置流,使流計數=0。

重置該流丟棄全部當前累積輸出。 bmp.compress(Bitmap.CompressFormat.JPEG, quality, baos); baos.flush(); }

// File file=new File(FileUtil.getAudioPath()+File.separator+System.currentTimeMillis()+”.jpg”);
// BufferedOutputStream bos=new BufferedOutputStream(new FileOutputStream(file));
// bos.write(baos.toByteArray());
// bos.flush();
// bos.close();

        System.out.print("壓縮後大小:" + baos.size() / 1024);
        bais = new ByteArrayInputStream(baos.toByteArray());
        baos.flush();
        newBmp = BitmapFactory.decodeStream(bais);
        bmp.recycle();
        System.gc();
    }catch (OutOfMemoryError e){
        e.printStackTrace();
        //內存溢出則壓縮很多其它
        if(!bmp.isRecycled()) {
            try {
                quality=quality/2;
                baos = new ByteArrayOutputStream();
                bmp.compress(Bitmap.CompressFormat.JPEG, quality, baos);
                bais = new ByteArrayInputStream(baos.toByteArray());
                baos.flush();
                newBmp = BitmapFactory.decodeStream(bais);
            }catch (Exception ex){}
        }
    }catch (Exception e){
        e.printStackTrace();
    }finally{
        try {
            if (bais != null) {
                bais.close();
            }
            if (baos != null) {
                baos.close();
            }
        }catch (Exception e){}
    }
    return newBmp;
}


public static Bitmap getBmpByMaxSize(String path, int maxSize){
    /**
     * Android使用BitmapFactory.Options解決載入大圖片內存溢出問題
     *
     * 因為Android對圖片使用內存有限制。若是載入幾兆的大圖片便內存溢出。
     * Bitmap會將圖片的全部像素(即長x寬)載入到內存中,假設圖片分辨率
     * 過大,會直接導致內存溢出(java.lang.OutOfMemoryError),僅僅有
     * 在BitmapFactory載入圖片時使用BitmapFactory.Options對相關參
     * 數進行配置來降低載入的像素。
     *
     * BitmapFactory.Options這個類。有一個字段叫做 inJustDecodeBounds 。

* 假設我們把它設為true。那麽BitmapFactory.decodeFile(String path, * Options opt)並不會真的返回一個Bitmap給你,它僅僅會把它的寬。高取回 * 來給你,這樣就不會占用太多的內存。也就不會那麽頻繁的發生OOM(Out Of * Memory)了。

* */ BitmapFactory.Options options=new BitmapFactory.Options(); //使圖片最小邊框縮小到800像素 options.inJustDecodeBounds=true; //這裏返回的bitmap=null,但能夠通過options.outWidth 和 options.outHeight就是我們想要的寬和高了 BitmapFactory.decodeFile(path, options); //最短的永遠都是寬度 double width=options.outWidth<options.outHeight? options.outWidth: options.outHeight; //實際寬度/理想寬度 => 上傳圖片縮放比例 int sampleSize=(int)Math.round(width/480); System.out.print("上傳圖片縮放比例:" + sampleSize); if(sampleSize<1) sampleSize=1; /** * inSampleSize表示縮略圖大小為原始圖片大小的幾分之中的一個, * 即假設這個值為2,則取出的縮略圖的寬和高都是原始圖片的1/2,圖片大小就為原始大小的1/4。 */ options.inSampleSize = sampleSize; options.inJustDecodeBounds=false; options.inDither=false; /*不進行圖片抖動處理*/ options.inPreferredConfig=null; /*設置讓解碼器以最佳方式解碼*/ /** * 以下兩個字段須要組合使用 節約內存 */ options.inPurgeable = true; // 同意可清除 options.inInputShareable = true; long length=new File(path).length(); if(length>(1.5*1024*1024)){ //大於1.5M時 options.inSampleSize+=(int)(length/1024/1024)*0.5; //當大於2M時為避免內存溢出縮小 } Bitmap bitmap=null; try { bitmap = BitmapFactory.decodeFile(path, options); }catch (OutOfMemoryError e){ e.printStackTrace(); //內存溢出則將圖片縮小一半 if(options.inSampleSize<1) options.inSampleSize=1; options.inSampleSize=options.inSampleSize*2; bitmap=BitmapFactory.decodeFile(path, options); } if(length>(800*1024)) { //大於20K字節壓縮 bitmap = compressBmp(bitmap, maxSize, length); } return bitmap; }

}

圖片壓縮CompressUtil解析