要了解圖片壓縮首先要了解一些圖片相關(guān)的其他概念
點(diǎn)陣圖和矢量圖
1. 點(diǎn)陣圖(位圖)或者叫 像素圖
構(gòu)成點(diǎn)陣圖的最小單位是象素,位圖就是由象素陣列的排列來實(shí)現(xiàn)其顯示效果的,每個(gè)象素有自己的顏色信息。所以在對位圖圖像進(jìn)行編輯操作的時(shí)候,可操作的對象是每個(gè)象素,可以通過改變圖像的色相、飽和度、明度,從而改變圖像的顯示效果
總結(jié):點(diǎn)陣圖縮放會(huì)失真
2. 矢量圖(向量圖)
矢量圖每一點(diǎn)上紀(jì)錄的元素形狀及顏色的算法,而不是像素信息,在打開矢量圖的時(shí)候,軟件對圖形象對應(yīng)的函數(shù)進(jìn)行運(yùn)算,將運(yùn)算結(jié)果 即(圖形的形狀和顏色)顯示出來。無論顯示畫面是大還是小,畫面上的對象對應(yīng)的算法是不變的,so, 矢量圖無論放大縮小其顯示效果都一樣
總結(jié):圖片任意放大或縮小都不會(huì)失真
顏色
計(jì)算機(jī)在表示顏色的時(shí)候,有兩種形式,一種稱作索引顏色(Index Color),一種稱作直接顏色(Direct Color)。
索引色
用一個(gè)數(shù)字來代表(索引)一種顏色,在存儲(chǔ)圖片的時(shí)候,存儲(chǔ)一個(gè)數(shù)字的組合,同時(shí)存儲(chǔ)數(shù)字到圖片顏色的映射。這種方式只能存儲(chǔ)有限種顏色,通常是256種顏色,對應(yīng)到計(jì)算機(jī)系統(tǒng)中,使用一個(gè)字節(jié)的數(shù)字來索引一種顏色。
直接色
使用四個(gè)數(shù)字來代表一種顏色,這四個(gè)數(shù)字分別代表這個(gè)顏色中紅色、綠色、藍(lán)色以及透明度?,F(xiàn)在主流顯示設(shè)備可以在這四個(gè)維度分別支持256種變化,所以直接色可以表示2的32次方種顏色。當(dāng)然并非所有的直接色都支持這么多種,為壓縮空間使用,有可能只有表達(dá)紅、綠、藍(lán)的三個(gè)數(shù)字,每個(gè)數(shù)字也可能不支持256種變化之多。
壓縮方式
有損壓縮
指在壓縮文件大小的過程中會(huì)損失了一部分圖片的信息,也即降低了圖片的質(zhì)量,并且這種損失是不可逆的。
無損壓縮。
指在壓縮文件大小的過程中圖片的質(zhì)量沒有任何損耗。無損壓縮過的圖片中恢復(fù)出原來的信息
圖片格式
常見的圖片格式有 bmp、gif、png、jpeg(jpg)、webp 等6種格式
BMP (BitMap簡稱)
是無損的、既支持索引色也支持直接色的、點(diǎn)陣圖,這種圖片格式幾乎沒有對數(shù)據(jù)進(jìn)行壓縮,所以bmp格式的圖片文件通常比較大?,F(xiàn)在在Windows操作系統(tǒng)中比較常見,其他地方不常見
GIF (Graphics Interchange Format)
是無損的、采用索引色的、點(diǎn)陣圖。使用GIF格式保存圖片時(shí)不會(huì)降低圖片質(zhì)量。
優(yōu)點(diǎn):1.由于數(shù)據(jù)的壓縮,GIF格式的圖片要遠(yuǎn)小于BMP格式的圖片,
2.GIF格式還具有支持動(dòng)畫以及透明的優(yōu)點(diǎn)。
缺點(diǎn):GIF格式僅支持8bit的索引色,即在整個(gè)圖片中,只能存在256種不同的顏色。
JPEG(Joint Photographic Experts Group)
JPEG是有損的、采用直接色的、點(diǎn)陣圖。JPEG圖片格式的設(shè)計(jì)目標(biāo),是在不影響人類可分辨的圖片質(zhì)量的前提下,盡可能的壓縮文件大小。也就是說JPEG去掉了一部分圖片的原始信息,即是進(jìn)行了有損壓縮
優(yōu)點(diǎn):采用了直接色,色彩更豐富
缺點(diǎn):有損的
WebP
WebP是谷歌開發(fā)的一種新圖片格式,WebP是同時(shí)支持有損和無損壓縮的、使用直接色的、點(diǎn)陣圖。
WebP具有更小的文件體積。這種圖片格式在pc 端應(yīng)用更廣泛,因?yàn)閳D片體積小,可以將大大減少瀏覽器和服務(wù)器之間的數(shù)據(jù)傳輸量,進(jìn)而降低訪問延遲,提升訪問體驗(yàn)。
在無損壓縮的情況下,相同質(zhì)量的WebP圖片,文件大小要比PNG小26%;
在有損壓縮的情況下,具有相同圖片精度的WebP圖片,文件大小要比JPEG小25%~34%;
PNG(Portable Network Graphics)
PNG-8 是PNG的索引色版本,PNG-8是無損的、使用索引色的、點(diǎn)陣圖。
文件體積小,另外PNG-8支持透明度的調(diào)節(jié)、支持動(dòng)畫,只不過瀏覽器對這個(gè)支持不好,所以應(yīng)用并不廣泛
PNG-24 是PNG的直接色版本,PNG-24是無損的、使用直接色的、點(diǎn)陣圖
PNG-24和BMP類似,PNG-24和BMP對比,前者的文件體積更小,但是比JPEG,GIF,PNG-8 都要大
總結(jié)

圖片壓縮原理
質(zhì)量壓縮
原理:質(zhì)量壓縮是通過改變圖片的位深和透明度來來減小圖片占用的磁盤空間大小。
所以質(zhì)量壓縮不會(huì)改變圖片在內(nèi)存中的大小(PS 圖片內(nèi)存大小是根據(jù)圖片的寬度、高度和一個(gè)像素所占用的字節(jié)數(shù)來計(jì)算的,因?yàn)橘|(zhì)量壓縮沒有改變高度和寬度,自然內(nèi)存的大小不會(huì)改變)其次質(zhì)量壓縮不會(huì)改變分圖片的辨率
示例:
/**
* 壓縮圖片:限制圖片的質(zhì)量,比如需要把5M的圖片壓縮到2M,可采取此方法
*
* @param filePath 被壓縮的圖片路徑
* @param format 壓縮圖片格式,jpg,webp
* @param sizeLimit 大小限制 單位是kb
* @return 壓縮后的圖片
*/
public static Bitmap compressBitmap(String filePath, Bitmap.CompressFormat format, int sizeLimit) {
if (TextUtils.isEmpty(filePath)) {
return null;
}
try {
Bitmap bm = BitmapFactory.decodeFile(filePath);
ByteArrayOutputStream byteAos = new ByteArrayOutputStream();
// 質(zhì)量壓縮方法,這里100表示不壓縮,把壓縮后的數(shù)據(jù)存放到baos中
int quality = 100;
bm.compress(format, quality, byteAos);
if (enableLog) {
Log.d(TAG, " 壓縮前size = " + byteAos.toByteArray().length / 1024);
}
int minus = 10;
if (byteAos.toByteArray().length / 1024 > 3 * 1024) {
minus = 50;
}
// 循環(huán)判斷如果壓縮后圖片是否大于sizeLimit(單位是k),大于繼續(xù)壓縮
while (byteAos.toByteArray().length / 1024 > sizeLimit && quality > 0 && quality <= 100) {
// 重置baos即清空baos
byteAos.reset();
// 這里壓縮options%,把壓縮后的數(shù)據(jù)存放到baos中
bm.compress(format, quality, byteAos);
quality -= minus;// 每次都減少10
if (byteAos.toByteArray().length / 1024 > 2 * 1024) {
minus = 20;
} else if (byteAos.toByteArray().length / 1024 > 1024) {
minus = 10;
}
if (enableLog) {
Log.d(TAG, " quality = " + quality);
}
}
if (enableLog) {
Log.d(TAG, " 壓縮后size = " + byteAos.toByteArray().length / 1024);
}
byte[] result = byteAos.toByteArray();
byteAos.close();
if (result != null) {
return BitmapFactory.decodeStream(new ByteArrayInputStream(result), null, null);
} else {
return null;
}
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
采樣壓縮(按比例壓縮)
原理:采樣率壓縮是通過設(shè)置BitmapFactory.Options.inSampleSize,通過改變圖片的分辨率,進(jìn)而減小圖片所占用的磁盤空間和內(nèi)存大小。
示例:
computeSize 算法計(jì)算
private static int computeSize(int srcWidth, int srcHeight) {
srcWidth = srcWidth % 2 == 1 ? srcWidth + 1 : srcWidth;
srcHeight = srcHeight % 2 == 1 ? srcHeight + 1 : srcHeight;
int longSide = Math.max(srcWidth, srcHeight);
int shortSide = Math.min(srcWidth, srcHeight);
float scale = ((float) shortSide / longSide);
if (scale <= 1 && scale > 0.5625) {
if (longSide < 1664) {
return 1;
} else if (longSide < 4990) {
return 2;
} else if (longSide > 4990 && longSide < 10240) {
return 4;
} else {
return longSide / 1280 == 0 ? 1 : longSide / 1280;
}
} else if (scale <= 0.5625 && scale > 0.5) {
return longSide / 1280 == 0 ? 1 : longSide / 1280;
} else {
return (int) Math.ceil(longSide / (1280.0 / scale));
}
}
[1, 0.5625) 即圖片處于 [1:1 ~ 9:16) 比例范圍內(nèi)
[0.5625, 0.5) 即圖片處于 [9:16 ~ 1:2) 比例范圍內(nèi)
[0.5, 0) 即圖片處于 [1:2 ~ 1:∞) 比例范圍內(nèi)

例如測試機(jī)魅族t16屏幕比是4:3 ,那么圖片壓縮比就是3/4 =0.75,或者用圖片的的寬高進(jìn)行比較也行,, 拍出來的照片是 3024x 3042 3024/4032 =0.75
計(jì)算壓縮圖片的實(shí)際文件大小,圖片比例越大則文件越大
則圖片壓縮后的大小就是2268*3024
代碼示例:
* 按照比例壓縮,按照默認(rèn)要鎖后的比例
*
* @param context 上線文
* @param srcPath 圖片原始路徑
* @return Uri
*/
public static Uri ratioCompressSaveToGallery(Context context, String srcPath) {
try {
BitmapFactory.Options newOpts = new BitmapFactory.Options();
//開始讀入圖片,此時(shí)把options.inJustDecodeBounds 設(shè)回true了
newOpts.inJustDecodeBounds = true;
Bitmap bitmap = BitmapFactory.decodeFile(srcPath, newOpts);//此時(shí)返回bm為空
newOpts.inJustDecodeBounds = false;
int w = newOpts.outWidth;
int h = newOpts.outHeight;
if (enableLog) {
Log.d(TAG, "src width = " + w + " height = " + h);
}
int size = computeSize(w, h);
newOpts.inSampleSize = size;//設(shè)置縮放比例
if (enableLog) {
Log.d(TAG, "computeSize = " + size);
}
//重新讀入圖片,注意此時(shí)已經(jīng)把options.inJustDecodeBounds 設(shè)回false了
bitmap = BitmapFactory.decodeFile(srcPath, newOpts);
ByteArrayOutputStream byteAos = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.JPEG, 60, byteAos);
byte[] result = byteAos.toByteArray();
Bitmap resultBitMap = BitmapFactory.decodeStream(new ByteArrayInputStream(result), null, null);
if (enableLog) {
Log.d(TAG, "result width = " + resultBitMap.getWidth() + " height = " + resultBitMap.getHeight());
}
bitmap.recycle();
if (result != null) {
Uri uri = saveImageToGallery(context, result);
if (uri != null) {
return uri;
} else {
return null;
}
} else {
return null;
}
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
圖片庫對比
目前流行的開源庫對比
tiny官方圖片壓縮效果對比詳情
github 地址:https://github.com/Sunzxyong/Tiny

luban 官方圖片壓縮效果對比詳
gitHub地址:https://github.com/Curzibn/Luban

真機(jī)測試
魅族16th和OPPO findx 圖片壓縮對比參照

Luban 框架優(yōu)缺點(diǎn)
優(yōu)點(diǎn):
是根據(jù)微信圖片算法你推算的,適用真機(jī)測試豐富
缺點(diǎn):
1,當(dāng)沒有設(shè)定壓縮路徑時(shí),拋異常無閃退
2,源碼中,壓縮比率固定住60,無法修改
3,壓縮配置,參數(shù)不太適應(yīng)真實(shí)項(xiàng)目需求
4,不能指定壓縮大小,比如100kb 以內(nèi)
5,內(nèi)部封裝已AsyncTasky異步的圖片壓縮,對RxJava的支持不好
tiny 框架的優(yōu)缺點(diǎn)
優(yōu)點(diǎn):
1,支持批量壓縮
2,libjpeg-turbo的價(jià)值是利用SIMD指令集,加速了編解碼過程,時(shí)間縮短1/3
3, 解碼的時(shí)候是由內(nèi)部分配,不會(huì)造成資源浪費(fèi)
缺點(diǎn):
需要導(dǎo)入so庫導(dǎo)致包的體積會(huì)變大,
android 7.0一下的手機(jī)壓縮后比android7.0以上的手機(jī)壓縮大小會(huì)偏大一點(diǎn)