圖片壓縮CompressUtil解析
- 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解析