Android 基于Bitmap的四種圖片壓縮方式
知識(shí)點(diǎn)介紹
Android 中圖片主要以 Bitmap 的形式存在,所以壓縮圖片主要就是減少 Bitmap 的大小。Bitmap 的大小可以通過如下的公式計(jì)算得到:size = width * height * 單個(gè)像素所占字節(jié)數(shù)。因此壓縮圖片通過改變公式中的三個(gè)變量即可實(shí)現(xiàn)。
單個(gè)像素所占空間大小在 Android 中有多種,詳見如下
格式 | 所占空間 | 說明 |
---|---|---|
Bitmap.Config.ALPHA_8 | 1B | 該種格式表示圖片只有透明度沒有顏色,1個(gè)像素占用8位 |
Bitmap.Config.ARGB_4444 | 2B | 該種格式表示圖片透明通道 A 及顏色 R、G、B 各占用4位,共16位 |
Bitmap.Config.ARGB_8888 | 4B | 該種格式表示圖片透明通道 A 及顏色 R、G、B 各占用8位,共32位 |
Bitmap.Config.RGB_565 | 2B | 該種格式表示圖片沒有透明通道,顏色 R、G、B 各占用5、6、6位,共16位 |
Android 中加載圖片默認(rèn)用的是 ARGB_8888 格式,所以加載一張3000 * 4000 的圖片默認(rèn)占用的空間為 45MB 左右,這個(gè)值還是很大的😂
測(cè)試代碼
fun showBitmapInfo(bitmap: Bitmap){ Log.d("Tag","壓縮后的圖片大?。?{bitmap.byteCount/1024/1024}MB,寬度:${bitmap.width},高度:${bitmap.height}") }
結(jié)果
正文
接下來(lái)介紹四種壓縮方式
1、質(zhì)量壓縮
質(zhì)量壓縮主要通過 Bitmap.compress()實(shí)現(xiàn),方法介紹
/** * * @param format 壓縮圖像的格式 * @param quality 提示壓縮機(jī),0-100。 根據(jù)Bitmap.CompressFormat不同,該值的解釋也不同。 * @param stream –寫入壓縮數(shù)據(jù)的輸出流。 * @return 如果成功壓縮到指定的流,則為true */ public boolean compress(CompressFormat format, int quality, OutputStream stream) { }
CompressFormat 表示圖片壓縮格式,Android 源碼中包含了五種格式
格式名 | 解釋 |
---|---|
CompressFormat.JPEG | 壓縮為JPEG格式。 quality 0表示壓縮為最小大小。 100表示壓縮以獲得最大視覺質(zhì)量。 |
CompressFormat.PNG | 壓縮為PNG格式。 PNG是無(wú)損的,因此quality被忽略。 |
CompressFormat.WEBP | 壓縮為WEBP格式。 quality 0表示壓縮為最小大小。 100表示壓縮以獲得最大視覺質(zhì)量。 從Build.VERSION_CODES.Q ,值100導(dǎo)致文件采用無(wú)損WEBP格式。 否則,文件將為有損WEBP格式 |
CompressFormat.WEBP_LOSSY | 壓縮為WEBP有損格式。 quality 0表示壓縮為最小大小。 100表示壓縮以獲得最大視覺質(zhì)量。 |
CompressFormat.WEBP_LOSSLESS | 壓縮為WEBP無(wú)損格式。 quality是指投入多少精力進(jìn)行壓縮。 值0表示快速壓縮,導(dǎo)致文件大小相對(duì)較大。 100表示要花費(fèi)更多時(shí)間進(jìn)行壓縮,從而使文件更小。 |
測(cè)試代碼
/** * 壓縮圖片質(zhì)量 */ fun getCompressBitmap(bitmap: Bitmap,quality:Int): Bitmap { val baos = ByteArrayOutputStream() bitmap.compress(Bitmap.CompressFormat.JPEG, quality, baos) val byte = baos.toByteArray() val ins = ByteArrayInputStream(byte) val bm = BitmapFactory.decodeStream(ins) ins.close() baos.close() return bm }
效果
根據(jù)上面的日志,你會(huì)看到質(zhì)量壓縮并不能改變圖片在內(nèi)存中的大小,因?yàn)橘|(zhì)量壓縮既不能改變圖片分辨率也不能改變圖片的單個(gè)像素大小。
那么你可能有些疑問:既然不能改變大小,那么還費(fèi)這么大功夫轉(zhuǎn)化而且圖片還失真是為了什么?
答:源碼中對(duì)于compress方法的解釋是,將位圖的壓縮版本寫入指定的輸出流。所以應(yīng)該是對(duì)輸出流中的字節(jié)數(shù)有影響
驗(yàn)證
val baos = ByteArrayOutputStream() bitmap.compress(Bitmap.CompressFormat.JPEG, quality, baos) val byte = baos.toByteArray() Log.d("Tag","quality=$quality,byte-size=${byte.size}")
結(jié)果真的是對(duì)輸出流的字節(jié)數(shù)有影響
2、采樣率壓縮
BitmapFactory.Options 中有個(gè)屬性 inSampleSize,系統(tǒng)中采樣率壓縮就是通過該屬性
/** * 如果設(shè)置為大于1的值,則請(qǐng)求解碼器對(duì)原始圖像進(jìn)行二次采樣,返回較小的圖像以節(jié)省內(nèi)存。 * 樣本大小是任一維度中與解碼后的位圖中的單個(gè)像素相對(duì)應(yīng)的像素?cái)?shù)。 例如,inSampleSize == 4 * 返回的圖像為原始寬度/高度的1/4,像素?cái)?shù)目的1/16。 任何小于等于1的值都與1相同。 * 注意:解碼器使用基于2的冪的最終值,任何其他值將四舍五入為最接近的2的冪。 **/ public int inSampleSize;
直接上代碼
/** * 根據(jù)設(shè)定的寬高計(jì)算縮放比 */ fun calculateInSampleSize(options: BitmapFactory.Options, reqWidth: Int, reqHeight: Int): Int { val height = options.outHeight val width = options.outWidth var inSampleSize = 1 if (height > reqHeight || width > reqWidth) { val heightRatio = round(height.toFloat() / reqHeight.toFloat()).toInt() val widthRatio = round(width.toFloat() / reqWidth.toFloat()).toInt() inSampleSize = if (heightRatio < widthRatio) heightRatio else widthRatio } return inSampleSize } /** * 獲取縮放后的圖片 */ fun getSmallBitmap(filePath: String,reqWidth: Int,reqHeight: Int): Bitmap { val options = BitmapFactory.Options() options.inJustDecodeBounds = true //不加載 bitmap 進(jìn)內(nèi)存,只獲取他的基本信息 BitmapFactory.decodeFile(filePath, options) options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight) options.inJustDecodeBounds = false return BitmapFactory.decodeFile(filePath, options) }
結(jié)果
采樣率壓縮的方式使用的還是挺多的,因?yàn)槲覀儷@取到的圖片它的尺寸可能很大,但是我們?cè)谑謾C(jī)上顯示的可能不需要那么大,那我們就將圖片縮放成我們需要的大小。
3、縮放法壓縮
這種方法主要是依賴 Matrix 矩陣變換的方式對(duì)圖片進(jìn)行處理。Matrix 中有很多對(duì)圖片變換的 api 這里只使用它的縮放功能,其他功能可以自行了解
代碼
/** * 通過矩陣縮放 */ fun matrixBitmap(bitmap: Bitmap,scale:Float):Bitmap{ val matrix = Matrix() matrix.setScale(scale,scale) var bm = Bitmap.createBitmap(bitmap,0,0,bitmap.width,bitmap.height,matrix,true) return bm }
當(dāng)設(shè)置縮放比為0.5時(shí),圖片整體就縮放為原來(lái)的1/4
4、RGB_565 通過改變圖片格式來(lái)實(shí)現(xiàn)壓縮
系統(tǒng)默認(rèn)使用的是ARGB_8888的格式,所以我們只要改變這個(gè) options 值就能實(shí)現(xiàn)
fun rgb565Bitmap(filePath: String):Bitmap{ val options = BitmapFactory.Options() options.inPreferredConfig = Bitmap.Config.RGB_565 var bitmap = BitmapFactory.decodeFile(filePath,options) return bitmap }
結(jié)果圖片變成了原圖的一半
總結(jié)
對(duì)于圖片的壓縮,首先可以先將圖片格式改為 RGB_565,這樣圖片先減小一半,然后對(duì)于圖片的顯示可以使用采樣率壓縮或者縮放壓縮的方式將圖片的分辨率改為我們顯示的大小,如果是要將圖片上傳服務(wù)器那么可以使用質(zhì)量壓縮的方式,但是這種方式不支持 png 格式的圖片。
以上就是Android 基于Bitmap的四種圖片壓縮方式的詳細(xì)內(nèi)容,更多關(guān)于Android Bitmap圖片壓縮的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Android Studio+Servlet+MySql實(shí)現(xiàn)登錄注冊(cè)
對(duì)于大多數(shù)的APP都有登錄注冊(cè)這個(gè)功能,本文就來(lái)介紹一下Android Studio+Servlet+MySql實(shí)現(xiàn)登錄注冊(cè),需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2021-05-05Android編程基礎(chǔ)之Menu功能菜單設(shè)計(jì)實(shí)例
這篇文章主要介紹了Android編程基礎(chǔ)之Menu功能菜單,結(jié)合實(shí)例形式分析了基本的Menu功能菜單原理、定義與響應(yīng)機(jī)制,需要的朋友可以參考下2016-10-10AndroidStudio接入U(xiǎn)nity工程并實(shí)現(xiàn)相互跳轉(zhuǎn)的示例代碼
這篇文章主要介紹了AndroidStudio接入U(xiǎn)nity工程并實(shí)現(xiàn)相互跳轉(zhuǎn),本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-12-12Android實(shí)現(xiàn)簡(jiǎn)單的下拉刷新控件
這篇文章主要為大家詳細(xì)介紹了Android實(shí)現(xiàn)簡(jiǎn)單的下拉刷新控件,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-09-09Android4.X讀取SIM卡短信和聯(lián)系人相關(guān)類實(shí)例分析
這篇文章主要介紹了Android 4.X讀取SIM卡短信和聯(lián)系人相關(guān)類,以實(shí)例形式分析了Android 4.X讀取SIM卡短信和聯(lián)系人的兩個(gè)相關(guān)類的功能、用法與注意事項(xiàng),具有一定參考借鑒價(jià)值,需要的朋友可以參考下2015-10-10Flexbox+ReclyclerView實(shí)現(xiàn)流式布局
這篇文章主要為大家詳細(xì)介紹了Flexbox+ReclyclerView實(shí)現(xiàn)流式布局,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-11-11Android控件ImageSwitcher實(shí)現(xiàn)左右圖片切換功能
這篇文章主要為大家詳細(xì)介紹了Android控件ImageSwitcher實(shí)現(xiàn)左右圖片切換功能,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-05-05Kotlin 高階函數(shù)與Lambda表達(dá)式示例詳解
這篇文章主要為大家介紹了Kotlin 高階函數(shù)與Lambda表達(dá)式示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-12-12Android 判斷某個(gè)Activity 是否在前臺(tái)運(yùn)行的實(shí)例
下面小編就為大家分享一篇Android 判斷某個(gè)Activity 是否在前臺(tái)運(yùn)行的實(shí)例,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來(lái)看看吧2018-03-03